Edit w2l result privacy (temporarily breaks delete)

This commit is contained in:
Brandon Thomas committed 2021-08-17 22:22:52 -04:00
1 parent b4d1812d78
commit e55ff2d060
6 files changed
+265 -9

No files matched your search

+4
View File
@@ -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`;
}
+14
View File
@@ -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 {
+14
View File
@@ -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) {
+7
View File
@@ -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}>&lt; Back to result </Link>
</p>
<form onSubmit={handleFormSubmit}>
<fieldset disabled={isDisabled}>
<div className="field">
<label className="label">
Result Visibility&nbsp;{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 />