mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-08 11:01:35 +00:00
edit result page (not functional yet)
This commit is contained in:
1 parent
d8f0ab9b23
commit
2b4db1d955
6 files changed
+242
-5
No files matched your search
@@ -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`;
|
||||
}
|
||||
|
||||
@@ -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`;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Props, State> {
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/tts/result/:token/edit">
|
||||
<TtsResultEditFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/tts/result/:token/delete">
|
||||
<TtsResultDeleteFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
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 TtsInferenceResultResponsePayload {
|
||||
success: boolean,
|
||||
result: TtsInferenceResult,
|
||||
}
|
||||
|
||||
interface TtsInferenceResult {
|
||||
tts_result_token: string,
|
||||
|
||||
tts_model_token: string,
|
||||
tts_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: TtsInferenceResultModeratorFields | null | undefined,
|
||||
}
|
||||
|
||||
interface TtsInferenceResultModeratorFields {
|
||||
creator_ip_address: string,
|
||||
mod_deleted_at: string | undefined | null,
|
||||
user_deleted_at: string | undefined | null,
|
||||
}
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
}
|
||||
|
||||
function TtsResultEditFc(props: Props) {
|
||||
let { token } : { token : string } = useParams();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
const [ttsInferenceResult, setTtsInferenceResult] = useState<TtsInferenceResult|undefined>(undefined);
|
||||
const [visibility, setVisibility] = useState<string>(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<HTMLSelectElement>) => {
|
||||
setVisibility((ev.target as HTMLSelectElement).value)
|
||||
};
|
||||
|
||||
const resultLink = FrontendUrlConfig.ttsResultPage(token);
|
||||
|
||||
const handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
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') ? <VisibleIconFc /> : <HiddenIconFc />;
|
||||
|
||||
return (
|
||||
<div className="content">
|
||||
<h1 className="title is-1"> Edit Result </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 { TtsResultEditFc };
|
||||
|
||||
@@ -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 = (
|
||||
<>
|
||||
<br />
|
||||
<Link
|
||||
className="button is-info is-large is-fullwidth"
|
||||
to={FrontendUrlConfig.ttsResultEditPage(token)}
|
||||
>Edit Result</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let deleteButton = null;
|
||||
const canDelete = props.sessionWrapper.deleteTtsResultAsMod(ttsInferenceResult?.maybe_creator_user_token);
|
||||
|
||||
if (canDelete) {
|
||||
deleteButton = (
|
||||
<>
|
||||
<br />
|
||||
<Link
|
||||
className={deleteButtonCss}
|
||||
to={FrontendUrlConfig.ttsResultDeletePage(token)}
|
||||
>{deleteButtonTitle}</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title is-1"> {headingTitle} </h1>
|
||||
@@ -267,11 +298,9 @@ function TtsResultViewFc(props: Props) {
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{editButton}
|
||||
|
||||
<Link
|
||||
className={deleteButtonCss}
|
||||
to={FrontendUrlConfig.ttsResultDeletePage(token)}
|
||||
>{deleteButtonTitle}</Link>
|
||||
{deleteButton}
|
||||
|
||||
<br />
|
||||
<ReportDiscordLinkFc />
|
||||
|
||||
Reference in new issue
Block a user