diff --git a/vocodes/src/common/ApiConfig.ts b/vocodes/src/common/ApiConfig.ts index 44f394aac5..4dc04fb840 100644 --- a/vocodes/src/common/ApiConfig.ts +++ b/vocodes/src/common/ApiConfig.ts @@ -80,6 +80,10 @@ class ApiConfig { return `${this.getScheme()}://${this.getNewApiHost()}/tts/result/${resultToken}/delete`; } + editTtsInferenceResult(resultToken: string) : string { + return `${this.getScheme()}://${this.getNewApiHost()}/tts/result/${resultToken}/edit`; + } + listTtsModelsForUser(username: string) : string { return `${this.getScheme()}://${this.getNewApiHost()}/user/${username}/tts_models`; } diff --git a/vocodes/src/common/FrontendUrlConfig.ts b/vocodes/src/common/FrontendUrlConfig.ts index 97e9c950db..731c4223e4 100644 --- a/vocodes/src/common/FrontendUrlConfig.ts +++ b/vocodes/src/common/FrontendUrlConfig.ts @@ -20,6 +20,14 @@ class FrontendUrlConfig { return `/tts/${modelToken}/delete`; } + static ttsResultPage(resultToken: string) : string { + return `/tts/result/${resultToken}`; + } + + static ttsResultEditPage(resultToken: string) : string { + return `/tts/result/${resultToken}/edit`; + } + static ttsResultDeletePage(resultToken: string) : string { return `/tts/result/${resultToken}/delete`; } diff --git a/vocodes/src/session/SessionWrapper.ts b/vocodes/src/session/SessionWrapper.ts index efcc0fd813..8155a28c00 100644 --- a/vocodes/src/session/SessionWrapper.ts +++ b/vocodes/src/session/SessionWrapper.ts @@ -119,6 +119,13 @@ export class SessionWrapper { return this.sessionStateResponse?.user?.can_delete_other_users_w2l_results || false; } + public canEditTtsResultAsUserOrMod(creatorUserToken: MaybeString) : boolean { + if (this.userTokenMatches(creatorUserToken)) { + return true; + } + return this.canDeleteOtherUsersTtsResults(); // TODO: There is no granular permission for this yet. + } + public deleteTtsResultAsMod(creatorUserToken: MaybeString) : boolean { if (this.userTokenMatches(creatorUserToken)) { return true; diff --git a/vocodes/src/v2/NewVocodesContainer.tsx b/vocodes/src/v2/NewVocodesContainer.tsx index 4891502389..bc4db11b31 100644 --- a/vocodes/src/v2/NewVocodesContainer.tsx +++ b/vocodes/src/v2/NewVocodesContainer.tsx @@ -30,6 +30,7 @@ import { W2lResultViewFc } from './w2l/w2l_result_view/W2lResultViewFc'; 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'; interface Props { sessionWrapper: SessionWrapper, @@ -107,6 +108,12 @@ class NewVocodesContainer extends React.Component { /> + + + + (undefined); + const [visibility, setVisibility] = useState(DEFAULT_VISIBILITY); + + const getTtsResult = useCallback((token) => { + const api = new ApiConfig(); + const endpointUrl = api.viewTtsInferenceResult(token); + + fetch(endpointUrl, { + method: 'GET', + headers: { + 'Accept': 'application/json', + }, + credentials: 'include', + }) + .then(res => res.json()) + .then(res => { + const modelsResponse : TtsInferenceResultResponsePayload = res; + if (!modelsResponse.success) { + return; + } + + setTtsInferenceResult(modelsResponse.result) + }) + .catch(e => { + }); + }, []); + + useEffect(() => { + getTtsResult(token); + }, [token, getTtsResult]); + + const handleVisibilityChange = (ev: React.FormEvent) => { + setVisibility((ev.target as HTMLSelectElement).value) + }; + + const resultLink = FrontendUrlConfig.ttsResultPage(token); + + const handleFormSubmit = (ev: React.FormEvent) => { + ev.preventDefault(); + + if (!ttsInferenceResult) { + return false; + } + + const resultToken = ttsInferenceResult!.tts_result_token; + + const api = new ApiConfig(); + const endpointUrl = api.editTtsInferenceResult(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 = !ttsInferenceResult; + + const visibilityIcon = (visibility === 'public') ? : ; + + return ( +
+

Edit Result

+ +

+ < Back to result +

+ +
+
+ +
+ +
+ +
+
+ +
+ + + +
+
+ +
+ ) +} + +export { TtsResultEditFc }; + diff --git a/vocodes/src/v2/tts/tts_result_view/TtsResultViewFc.tsx b/vocodes/src/v2/tts/tts_result_view/TtsResultViewFc.tsx index 87123a4b6b..e96de30e4a 100644 --- a/vocodes/src/v2/tts/tts_result_view/TtsResultViewFc.tsx +++ b/vocodes/src/v2/tts/tts_result_view/TtsResultViewFc.tsx @@ -178,12 +178,43 @@ function TtsResultViewFc(props: Props) { const currentlyDeleted = !!ttsInferenceResult?.maybe_moderator_fields?.mod_deleted_at || !!ttsInferenceResult?.maybe_moderator_fields?.user_deleted_at; - const deleteButtonTitle = currentlyDeleted ? "Undelete?" : "Delete?"; + 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.canEditTtsResultAsUserOrMod(ttsInferenceResult?.maybe_creator_user_token); + + if (canEdit) { + editButton = ( + <> +
+ Edit Result + + ); + } + + let deleteButton = null; + const canDelete = props.sessionWrapper.deleteTtsResultAsMod(ttsInferenceResult?.maybe_creator_user_token); + + if (canDelete) { + deleteButton = ( + <> +
+ {deleteButtonTitle} + + ); + } + return (

{headingTitle}

@@ -267,11 +298,9 @@ function TtsResultViewFc(props: Props) { + {editButton} - {deleteButtonTitle} + {deleteButton}