mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
Edit w2l result privacy (temporarily breaks delete)
This commit is contained in:
1 parent
b4d1812d78
commit
e55ff2d060
6 files changed
+265
-9
No files matched your search
@@ -155,6 +155,10 @@ class ApiConfig {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/w2l/result/${token}`;
|
||||
}
|
||||
|
||||
editW2lInferenceResult(token: string) : string {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/w2l/result/${token}/edit`;
|
||||
}
|
||||
|
||||
deleteW2lInferenceResult(resultToken: string) : string {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/w2l/result/${resultToken}/delete`;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,20 @@ class FrontendUrlConfig {
|
||||
return `/tts/result/${resultToken}/delete`;
|
||||
}
|
||||
|
||||
// W2L template links
|
||||
|
||||
static w2lResultPage(resultToken: string) : string {
|
||||
return `/w2l/result/${resultToken}`;
|
||||
}
|
||||
|
||||
static w2lResultEditPage(resultToken: string) : string {
|
||||
return `/w2l/result/${resultToken}/edit`;
|
||||
}
|
||||
|
||||
static w2lResultDeletePage(resultToken: string) : string {
|
||||
return `/w2l/result/${resultToken}/delete`;
|
||||
}
|
||||
|
||||
// User links
|
||||
|
||||
static userProfilePage(userDisplayName: string) : string {
|
||||
|
||||
@@ -140,6 +140,20 @@ export class SessionWrapper {
|
||||
return this.canDeleteOtherUsersTtsModels();
|
||||
}
|
||||
|
||||
public canEditW2lResultAsUserOrMod(creatorUserToken: MaybeString) : boolean {
|
||||
if (this.userTokenMatches(creatorUserToken)) {
|
||||
return true;
|
||||
}
|
||||
return this.canDeleteOtherUsersW2lResults(); // TODO: There is no granular permission for this yet.
|
||||
}
|
||||
|
||||
public canDeleteW2lResultAsUserOrMod(creatorUserToken: MaybeString) : boolean {
|
||||
if (this.userTokenMatches(creatorUserToken)) {
|
||||
return true;
|
||||
}
|
||||
return this.canDeleteOtherUsersW2lResults();
|
||||
}
|
||||
|
||||
public userTokenMatches(otherUserToken: MaybeString) : boolean {
|
||||
// Default to false if user token on either side is falsey.
|
||||
if (!otherUserToken) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import { W2lTemplateListFc } from './w2l/w2l_template_list/W2lTemplateListFc';
|
||||
import { W2lTemplateUploadJob } from '../jobs/W2lTemplateUploadJobs';
|
||||
import { W2lTemplateViewFc } from './w2l/w2l_template_view/W2lTemplateViewFc';
|
||||
import { TtsResultEditFc } from './tts/tts_result_edit/TtsResultEditFc';
|
||||
import { W2lResultEditFc } from './w2l/w2l_result_edit/W2lResultEditFc';
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
@@ -156,6 +157,12 @@ class NewVocodesContainer extends React.Component<Props, State> {
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/w2l/result/:token/edit">
|
||||
<W2lResultEditFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/w2l/result/:token">
|
||||
<W2lResultViewFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { ApiConfig } from '../../../common/ApiConfig';
|
||||
import { SessionWrapper } from '../../../session/SessionWrapper';
|
||||
import { useParams, Link, useHistory } from 'react-router-dom';
|
||||
import { FrontendUrlConfig } from '../../../common/FrontendUrlConfig';
|
||||
import { VisibleIconFc } from '../../../icons/VisibleIconFc';
|
||||
import { HiddenIconFc } from '../../../icons/HiddenIconFc';
|
||||
|
||||
const DEFAULT_VISIBILITY = 'public';
|
||||
|
||||
interface W2lInferenceResultResponsePayload {
|
||||
success: boolean,
|
||||
result: W2lInferenceResult,
|
||||
}
|
||||
|
||||
interface W2lInferenceResult {
|
||||
w2l_result_token: string,
|
||||
|
||||
w2l_model_token: string,
|
||||
w2l_model_title: string,
|
||||
|
||||
raw_inference_text: string,
|
||||
|
||||
maybe_creator_user_token?: string,
|
||||
maybe_creator_username?: string,
|
||||
maybe_creator_display_name?: string,
|
||||
maybe_creator_gravatar_hash?: string,
|
||||
|
||||
maybe_model_creator_user_token?: string,
|
||||
maybe_model_creator_username?: string,
|
||||
maybe_model_creator_display_name?: string,
|
||||
maybe_model_creator_gravatar_hash?: string,
|
||||
|
||||
public_bucket_wav_audio_path: string,
|
||||
public_bucket_spectrogram_path: string,
|
||||
|
||||
creator_set_visibility?: string,
|
||||
|
||||
file_size_bytes: number,
|
||||
duration_millis: number,
|
||||
created_at: string,
|
||||
updated_at: string,
|
||||
|
||||
maybe_moderator_fields: W2lInferenceResultModeratorFields | null | undefined,
|
||||
}
|
||||
|
||||
interface W2lInferenceResultModeratorFields {
|
||||
creator_ip_address: string,
|
||||
mod_deleted_at: string | undefined | null,
|
||||
user_deleted_at: string | undefined | null,
|
||||
}
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
}
|
||||
|
||||
function W2lResultEditFc(props: Props) {
|
||||
let { token } : { token : string } = useParams();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
const [w2lInferenceResult, setW2lInferenceResult] = useState<W2lInferenceResult|undefined>(undefined);
|
||||
const [visibility, setVisibility] = useState<string>(DEFAULT_VISIBILITY);
|
||||
|
||||
const getW2lResult = useCallback((token) => {
|
||||
const api = new ApiConfig();
|
||||
const endpointUrl = api.viewW2lInferenceResult(token);
|
||||
|
||||
fetch(endpointUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
const modelsResponse : W2lInferenceResultResponsePayload = res;
|
||||
if (!modelsResponse.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
setW2lInferenceResult(modelsResponse.result);
|
||||
setVisibility(modelsResponse?.result?.creator_set_visibility || DEFAULT_VISIBILITY);
|
||||
})
|
||||
.catch(e => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getW2lResult(token);
|
||||
}, [token, getW2lResult]);
|
||||
|
||||
const handleVisibilityChange = (ev: React.FormEvent<HTMLSelectElement>) => {
|
||||
setVisibility((ev.target as HTMLSelectElement).value)
|
||||
};
|
||||
|
||||
const resultLink = FrontendUrlConfig.w2lResultPage(token);
|
||||
|
||||
const handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!w2lInferenceResult) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const resultToken = w2lInferenceResult!.w2l_result_token;
|
||||
|
||||
const api = new ApiConfig();
|
||||
const endpointUrl = api.editW2lInferenceResult(resultToken);
|
||||
|
||||
const request = {
|
||||
creator_set_visibility: visibility || DEFAULT_VISIBILITY,
|
||||
}
|
||||
|
||||
fetch(endpointUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(request),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res === undefined ||
|
||||
!res.success) {
|
||||
return; // Endpoint error?
|
||||
}
|
||||
|
||||
history.push(resultLink);
|
||||
})
|
||||
.catch(e => {
|
||||
});
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
let isDisabled = !w2lInferenceResult;
|
||||
|
||||
const visibilityIcon = (visibility === 'public') ? <VisibleIconFc /> : <HiddenIconFc />;
|
||||
|
||||
return (
|
||||
<div className="content">
|
||||
<h1 className="title is-1"> Edit Result Visibility </h1>
|
||||
|
||||
<p>
|
||||
<Link to={resultLink}>< Back to result </Link>
|
||||
</p>
|
||||
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<fieldset disabled={isDisabled}>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">
|
||||
Result Visibility {visibilityIcon}
|
||||
</label>
|
||||
<div className="control select">
|
||||
<select
|
||||
name="creator_set_visibility"
|
||||
onChange={handleVisibilityChange}
|
||||
value={visibility}
|
||||
>
|
||||
<option value="public">Public (visible from your profile)</option>
|
||||
<option value="hidden">Unlisted (shareable URLs)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<button className="button is-link is-large is-fullwidth">Update</button>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { W2lResultEditFc };
|
||||
|
||||
@@ -3,11 +3,11 @@ import { ApiConfig } from '../../../common/ApiConfig';
|
||||
import { GravatarFc } from '../../common/GravatarFc';
|
||||
import { SessionWrapper } from '../../../session/SessionWrapper';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import { W2lResultViewDeleteFc } from './W2lResultView_DeleteFc';
|
||||
import { ReportDiscordLinkFc } from '../../common/DiscordReportLinkFc';
|
||||
import { BucketConfig } from '../../../common/BucketConfig';
|
||||
import { HiddenIconFc } from '../../../icons/HiddenIconFc';
|
||||
import { VisibleIconFc } from '../../../icons/VisibleIconFc';
|
||||
import { FrontendUrlConfig } from '../../../common/FrontendUrlConfig';
|
||||
|
||||
interface W2lInferenceResultResponsePayload {
|
||||
success: boolean,
|
||||
@@ -96,7 +96,6 @@ function W2lResultViewFc(props: Props) {
|
||||
|
||||
let templateName = w2lInferenceResult.template_title;
|
||||
|
||||
const currentlyDeleted = !!w2lInferenceResult?.maybe_moderator_fields?.mod_deleted_at || !!w2lInferenceResult?.maybe_moderator_fields?.user_deleted_at;
|
||||
|
||||
let moderatorRows = null;
|
||||
|
||||
@@ -165,6 +164,45 @@ function W2lResultViewFc(props: Props) {
|
||||
<span>Hidden <HiddenIconFc /></span> :
|
||||
<span>Public <VisibleIconFc /></span> ;
|
||||
|
||||
const currentlyDeleted = !!w2lInferenceResult?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
!!w2lInferenceResult?.maybe_moderator_fields?.user_deleted_at;
|
||||
|
||||
const deleteButtonTitle = currentlyDeleted ? "Undelete Result?" : "Delete Result?";
|
||||
|
||||
const deleteButtonCss = currentlyDeleted ?
|
||||
"button is-warning is-large is-fullwidth" :
|
||||
"button is-danger is-large is-fullwidth";
|
||||
|
||||
|
||||
let editButton = null;
|
||||
const canEdit = props.sessionWrapper.canEditW2lResultAsUserOrMod(w2lInferenceResult?.maybe_creator_user_token);
|
||||
|
||||
if (canEdit) {
|
||||
editButton = (
|
||||
<>
|
||||
<br />
|
||||
<Link
|
||||
className="button is-info is-large is-fullwidth"
|
||||
to={FrontendUrlConfig.w2lResultEditPage(token)}
|
||||
>Edit Result Visibility</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let deleteButton = null;
|
||||
const canDelete = props.sessionWrapper.canDeleteW2lResultAsUserOrMod(w2lInferenceResult?.maybe_creator_user_token);
|
||||
|
||||
if (canDelete) {
|
||||
deleteButton = (
|
||||
<>
|
||||
<br />
|
||||
<Link
|
||||
className={deleteButtonCss}
|
||||
to={FrontendUrlConfig.w2lResultDeletePage(token)}
|
||||
>{deleteButtonTitle}</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title is-1"> Lipsync Result </h1>
|
||||
@@ -184,7 +222,6 @@ function W2lResultViewFc(props: Props) {
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
||||
<table className="table is-fullwidth">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -236,12 +273,9 @@ function W2lResultViewFc(props: Props) {
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<W2lResultViewDeleteFc
|
||||
sessionWrapper={props.sessionWrapper}
|
||||
resultToken={token}
|
||||
currentlyDeleted={currentlyDeleted}
|
||||
maybeCreatorUserToken={w2lInferenceResult?.maybe_creator_user_token}
|
||||
/>
|
||||
{editButton}
|
||||
|
||||
{deleteButton}
|
||||
|
||||
<br />
|
||||
<ReportDiscordLinkFc />
|
||||
|
||||
Reference in new issue
Block a user