mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
Merge pull request #41 from storytold/bflat-w2l-pages
w2l pages styling + animation
This commit is contained in:
7 files changed
+1231
-885
No files matched your search
@@ -1,26 +1,34 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useParams, Link, useHistory } from "react-router-dom";
|
||||
import {
|
||||
GetW2lResult,
|
||||
GetW2lResultIsOk,
|
||||
W2lResult,
|
||||
} from "../../../api/w2l/GetW2lResult";
|
||||
import { distance, delay, duration } from "../../../../data/animation";
|
||||
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { ApiConfig } from '@storyteller/components';
|
||||
import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { useParams, Link, useHistory } from 'react-router-dom';
|
||||
import { GetW2lResult, GetW2lResultIsOk, W2lResult } from '../../../api/w2l/GetW2lResult';
|
||||
const Fade = require("react-reveal/Fade");
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
function W2lResultDeleteFc(props: Props) {
|
||||
const history = useHistory();
|
||||
|
||||
let { token } = useParams() as { token : string };
|
||||
let { token } = useParams() as { token: string };
|
||||
|
||||
const [w2lInferenceResult, setW2lInferenceResult] = useState<W2lResult|undefined>(undefined);
|
||||
const [w2lInferenceResult, setW2lInferenceResult] = useState<
|
||||
W2lResult | undefined
|
||||
>(undefined);
|
||||
|
||||
const getInferenceResult = useCallback(async (token) => {
|
||||
const templateResponse = await GetW2lResult(token);
|
||||
if (GetW2lResultIsOk(templateResponse)) {
|
||||
setW2lInferenceResult(templateResponse)
|
||||
setW2lInferenceResult(templateResponse);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -30,7 +38,9 @@ function W2lResultDeleteFc(props: Props) {
|
||||
|
||||
const templateResultLink = FrontendUrlConfig.w2lResultPage(token);
|
||||
|
||||
const handleDeleteFormSubmit = (ev: React.FormEvent<HTMLFormElement>) : boolean => {
|
||||
const handleDeleteFormSubmit = (
|
||||
ev: React.FormEvent<HTMLFormElement>
|
||||
): boolean => {
|
||||
ev.preventDefault();
|
||||
|
||||
const endpointUrl = new ApiConfig().deleteW2lInferenceResult(token);
|
||||
@@ -38,108 +48,120 @@ function W2lResultDeleteFc(props: Props) {
|
||||
const request = {
|
||||
set_delete: !currentlyDeleted,
|
||||
as_mod: props.sessionWrapper.canDeleteOtherUsersW2lResults(),
|
||||
}
|
||||
};
|
||||
|
||||
fetch(endpointUrl, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
body: JSON.stringify(request),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
if (props.sessionWrapper.canDeleteOtherUsersW2lResults()) {
|
||||
history.push(templateResultLink); // Mods can perform further actions
|
||||
} else {
|
||||
history.push('/');
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
if (props.sessionWrapper.canDeleteOtherUsersW2lResults()) {
|
||||
history.push(templateResultLink); // Mods can perform further actions
|
||||
} else {
|
||||
history.push("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
});
|
||||
})
|
||||
.catch((e) => {});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let creatorLink = <span />;
|
||||
|
||||
if (!!w2lInferenceResult?.maybe_creator_display_name) {
|
||||
const creatorUrl = FrontendUrlConfig.userProfilePage(w2lInferenceResult?.maybe_creator_display_name);
|
||||
const creatorUrl = FrontendUrlConfig.userProfilePage(
|
||||
w2lInferenceResult?.maybe_creator_display_name
|
||||
);
|
||||
creatorLink = (
|
||||
<Link to={creatorUrl}>{w2lInferenceResult?.maybe_creator_display_name}</Link>
|
||||
<Link to={creatorUrl}>
|
||||
{w2lInferenceResult?.maybe_creator_display_name}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
let currentlyDeleted = !!w2lInferenceResult?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
!!w2lInferenceResult?.maybe_moderator_fields?.result_creator_deleted_at;
|
||||
let currentlyDeleted =
|
||||
!!w2lInferenceResult?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
!!w2lInferenceResult?.maybe_moderator_fields?.result_creator_deleted_at;
|
||||
|
||||
const h1Title = currentlyDeleted ? "Undelete Result?" : "Delete Result?";
|
||||
|
||||
const buttonTitle = currentlyDeleted ? "Confirm Undelete" : "Confirm Delete";
|
||||
|
||||
const buttonCss = currentlyDeleted ?
|
||||
"button is-warning is-large is-fullwidth" :
|
||||
"button is-danger is-large is-fullwidth";
|
||||
const buttonCss = currentlyDeleted
|
||||
? "btn btn-primary w-100"
|
||||
: "btn btn-primary w-100";
|
||||
|
||||
const formLabel = currentlyDeleted ?
|
||||
"Recover the W2L Result (makes it visible again)" :
|
||||
"Delete W2L Result (hides from everyone but mods)";
|
||||
const formLabel = currentlyDeleted
|
||||
? "Recover the W2L Result (makes it visible again)"
|
||||
: "Delete W2L Result (hides from everyone but mods)";
|
||||
|
||||
const durationSeconds = (w2lInferenceResult?.duration_millis || 0) / 1000;
|
||||
|
||||
return (
|
||||
<div className="content">
|
||||
<h1 className="title is-1"> {h1Title} </h1>
|
||||
|
||||
<p>
|
||||
<Link to={templateResultLink}>< Back to result</Link>
|
||||
</p>
|
||||
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><abbr title="Detail">Detail</abbr></th>
|
||||
<th><abbr title="Value">Value</abbr></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>
|
||||
{creatorLink}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Template title</th>
|
||||
<td>{w2lInferenceResult?.template_title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration</th>
|
||||
<td>{durationSeconds} seconds</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<Fade cascade bottom duration={duration} distance={distance}>
|
||||
<div className="container pt-5 pb-4 px-md-4 px-lg-5 px-xl-3">
|
||||
<h1 className="display-5 fw-bold">{h1Title}</h1>
|
||||
<div className="pt-3">
|
||||
<Link to={templateResultLink}>< Back to result</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
|
||||
<br />
|
||||
<Fade
|
||||
cascade
|
||||
bottom
|
||||
duration={duration}
|
||||
distance={distance}
|
||||
delay={delay}
|
||||
>
|
||||
<div className="container-panel pt-4 pb-5">
|
||||
<div className="panel p-3 py-4 p-lg-4">
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<abbr title="Detail">Detail</abbr>
|
||||
</th>
|
||||
<th>
|
||||
<abbr title="Value">Value</abbr>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>{creatorLink}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Template title</th>
|
||||
<td>{w2lInferenceResult?.template_title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration</th>
|
||||
<td>{durationSeconds} seconds</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleDeleteFormSubmit}>
|
||||
<label className="label">{formLabel}</label>
|
||||
|
||||
<p className="control">
|
||||
<button className={buttonCss}>
|
||||
{buttonTitle}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<Link to={templateResultLink}>< Back to result</Link>
|
||||
<div className="container pb-5">
|
||||
<form onSubmit={handleDeleteFormSubmit}>
|
||||
<button className={buttonCss}>{buttonTitle}</button>
|
||||
<p className="pt-4">{formLabel}</p>
|
||||
</form>
|
||||
</div>
|
||||
</Fade>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { W2lResultDeleteFc };
|
||||
@@ -1,65 +1,70 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { ApiConfig } from '@storyteller/components';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { useParams, Link, useHistory } from 'react-router-dom';
|
||||
import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig';
|
||||
import { VisibleIconFc } from '../../_icons/VisibleIcon';
|
||||
import { HiddenIconFc } from '../../_icons/HiddenIcon';
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useParams, Link, useHistory } from "react-router-dom";
|
||||
import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig";
|
||||
import { VisibleIconFc } from "../../_icons/VisibleIcon";
|
||||
import { HiddenIconFc } from "../../_icons/HiddenIcon";
|
||||
import { distance, delay, duration } from "../../../../data/animation";
|
||||
|
||||
const DEFAULT_VISIBILITY = 'public';
|
||||
const Fade = require("react-reveal/Fade");
|
||||
|
||||
const DEFAULT_VISIBILITY = "public";
|
||||
|
||||
interface W2lInferenceResultResponsePayload {
|
||||
success: boolean,
|
||||
result: W2lInferenceResult,
|
||||
success: boolean;
|
||||
result: W2lInferenceResult;
|
||||
}
|
||||
|
||||
interface W2lInferenceResult {
|
||||
w2l_result_token: string,
|
||||
w2l_result_token: string;
|
||||
|
||||
w2l_model_token: string,
|
||||
w2l_model_title: string,
|
||||
w2l_model_token: string;
|
||||
w2l_model_title: string;
|
||||
|
||||
raw_inference_text: 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_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,
|
||||
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,
|
||||
public_bucket_wav_audio_path: string;
|
||||
public_bucket_spectrogram_path: string;
|
||||
|
||||
creator_set_visibility?: string,
|
||||
creator_set_visibility?: string;
|
||||
|
||||
file_size_bytes: number,
|
||||
duration_millis: number,
|
||||
created_at: string,
|
||||
updated_at: string,
|
||||
file_size_bytes: number;
|
||||
duration_millis: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
|
||||
maybe_moderator_fields: W2lInferenceResultModeratorFields | null | undefined,
|
||||
maybe_moderator_fields: W2lInferenceResultModeratorFields | null | undefined;
|
||||
}
|
||||
|
||||
interface W2lInferenceResultModeratorFields {
|
||||
creator_ip_address: string,
|
||||
mod_deleted_at: string | undefined | null,
|
||||
user_deleted_at: string | undefined | null,
|
||||
creator_ip_address: string;
|
||||
mod_deleted_at: string | undefined | null;
|
||||
user_deleted_at: string | undefined | null;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
function W2lResultEditFc(props: Props) {
|
||||
let { token } : { token : string } = useParams();
|
||||
let { token }: { token: string } = useParams();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
const [w2lInferenceResult, setW2lInferenceResult] = useState<W2lInferenceResult|undefined>(undefined);
|
||||
const [w2lInferenceResult, setW2lInferenceResult] = useState<
|
||||
W2lInferenceResult | undefined
|
||||
>(undefined);
|
||||
const [visibility, setVisibility] = useState<string>(DEFAULT_VISIBILITY);
|
||||
|
||||
const getW2lResult = useCallback((token) => {
|
||||
@@ -67,24 +72,25 @@ function W2lResultEditFc(props: Props) {
|
||||
const endpointUrl = api.viewW2lInferenceResult(token);
|
||||
|
||||
fetch(endpointUrl, {
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
const modelsResponse : W2lInferenceResultResponsePayload = res;
|
||||
if (!modelsResponse.success) {
|
||||
return;
|
||||
}
|
||||
.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 => {
|
||||
});
|
||||
setW2lInferenceResult(modelsResponse.result);
|
||||
setVisibility(
|
||||
modelsResponse?.result?.creator_set_visibility || DEFAULT_VISIBILITY
|
||||
);
|
||||
})
|
||||
.catch((e) => {});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -92,12 +98,12 @@ function W2lResultEditFc(props: Props) {
|
||||
}, [token, getW2lResult]);
|
||||
|
||||
const handleVisibilityChange = (ev: React.FormEvent<HTMLSelectElement>) => {
|
||||
setVisibility((ev.target as HTMLSelectElement).value)
|
||||
setVisibility((ev.target as HTMLSelectElement).value);
|
||||
};
|
||||
|
||||
const resultLink = FrontendUrlConfig.w2lResultPage(token);
|
||||
|
||||
const handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
const handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!w2lInferenceResult) {
|
||||
@@ -108,76 +114,88 @@ function W2lResultEditFc(props: Props) {
|
||||
|
||||
const api = new ApiConfig();
|
||||
const endpointUrl = api.editW2lInferenceResult(resultToken);
|
||||
|
||||
|
||||
const request = {
|
||||
creator_set_visibility: visibility || DEFAULT_VISIBILITY,
|
||||
}
|
||||
};
|
||||
|
||||
fetch(endpointUrl, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
body: JSON.stringify(request),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res === undefined ||
|
||||
!res.success) {
|
||||
return; // Endpoint error?
|
||||
}
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (res === undefined || !res.success) {
|
||||
return; // Endpoint error?
|
||||
}
|
||||
|
||||
history.push(resultLink);
|
||||
})
|
||||
.catch(e => {
|
||||
});
|
||||
history.push(resultLink);
|
||||
})
|
||||
.catch((e) => {});
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
let isDisabled = !w2lInferenceResult;
|
||||
|
||||
const visibilityIcon = (visibility === 'public') ? <VisibleIconFc /> : <HiddenIconFc />;
|
||||
const visibilityIcon =
|
||||
visibility === "public" ? <VisibleIconFc /> : <HiddenIconFc />;
|
||||
|
||||
return (
|
||||
<div className="content">
|
||||
<h1 className="title is-1"> Edit Result Visibility </h1>
|
||||
<div>
|
||||
<Fade cascade bottom duration={duration} distance={distance}>
|
||||
<div className="container pb-4 pt-5 px-md-4 px-lg-5 px-xl-3">
|
||||
<h1 className="display-5 fw-bold">Edit Result Visibility</h1>
|
||||
<div className="pt-3">
|
||||
<Link to={resultLink}>< Back to result </Link>
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
|
||||
<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>
|
||||
<Fade
|
||||
cascade
|
||||
bottom
|
||||
duration={duration}
|
||||
distance={distance}
|
||||
delay={delay}
|
||||
>
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<div className="container-panel pt-4 pb-5">
|
||||
<div className="panel p-3 py-4 p-lg-4">
|
||||
<fieldset disabled={isDisabled}>
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
Result Visibility {visibilityIcon}
|
||||
</label>
|
||||
<div className="form-group">
|
||||
<select
|
||||
name="creator_set_visibility"
|
||||
onChange={handleVisibilityChange}
|
||||
value={visibility}
|
||||
className="form-control"
|
||||
>
|
||||
<option value="public">
|
||||
Public (visible from your profile)
|
||||
</option>
|
||||
<option value="hidden">Unlisted (shareable URLs)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<button className="button is-link is-large is-fullwidth">Update</button>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<div className="container pb-5">
|
||||
<button className="btn btn-primary w-100">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</Fade>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { W2lResultEditFc };
|
||||
|
||||
@@ -1,32 +1,44 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { Gravatar } from '@storyteller/components/src/elements/Gravatar';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import { ReportDiscordLinkFc } from '../../_common/DiscordReportLinkFc';
|
||||
import { BucketConfig } from '@storyteller/components/src/api/BucketConfig';
|
||||
import { HiddenIconFc } from '../../_icons/HiddenIcon';
|
||||
import { VisibleIconFc } from '../../_icons/VisibleIcon';
|
||||
import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig';
|
||||
import { DownloadIcon } from '../../_icons/DownloadIcon';
|
||||
import { GetW2lResult, GetW2lResultIsErr, GetW2lResultIsOk, W2lResult, W2lResultLookupError } from '../../../api/w2l/GetW2lResult';
|
||||
import { MetaTags } from '../../../../common/MetaTags';
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { Gravatar } from "@storyteller/components/src/elements/Gravatar";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useParams, Link } from "react-router-dom";
|
||||
import { ReportDiscordLinkFc } from "../../_common/DiscordReportLinkFc";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
import { HiddenIconFc } from "../../_icons/HiddenIcon";
|
||||
import { VisibleIconFc } from "../../_icons/VisibleIcon";
|
||||
import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig";
|
||||
import {
|
||||
GetW2lResult,
|
||||
GetW2lResultIsErr,
|
||||
GetW2lResultIsOk,
|
||||
W2lResult,
|
||||
W2lResultLookupError,
|
||||
} from "../../../api/w2l/GetW2lResult";
|
||||
import { MetaTags } from "../../../../common/MetaTags";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faDownload, faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { distance, delay, duration } from "../../../../data/animation";
|
||||
|
||||
const Fade = require("react-reveal/Fade");
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
function W2lResultViewFc(props: Props) {
|
||||
let { token } = useParams() as { token : string };
|
||||
let { token } = useParams() as { token: string };
|
||||
|
||||
const [w2lInferenceResult, setW2lInferenceResult] = useState<W2lResult|undefined>(undefined);
|
||||
const [w2lInferenceResult, setW2lInferenceResult] = useState<
|
||||
W2lResult | undefined
|
||||
>(undefined);
|
||||
const [notFoundState, setNotFoundState] = useState<boolean>(false);
|
||||
|
||||
const getInferenceResult = useCallback(async (token) => {
|
||||
const templateResponse = await GetW2lResult(token);
|
||||
if (GetW2lResultIsOk(templateResponse)) {
|
||||
setW2lInferenceResult(templateResponse)
|
||||
setW2lInferenceResult(templateResponse);
|
||||
} else if (GetW2lResultIsErr(templateResponse)) {
|
||||
switch(templateResponse) {
|
||||
switch (templateResponse) {
|
||||
case W2lResultLookupError.NotFound:
|
||||
setNotFoundState(true);
|
||||
break;
|
||||
@@ -39,58 +51,89 @@ function W2lResultViewFc(props: Props) {
|
||||
}, [token, getInferenceResult]); // NB: Empty array dependency sets to run ONLY on mount
|
||||
|
||||
if (notFoundState) {
|
||||
return (
|
||||
<h1 className="title is-1">Template result not found</h1>
|
||||
);
|
||||
return <h1 className="title is-1">Template result not found</h1>;
|
||||
}
|
||||
|
||||
if (!w2lInferenceResult) {
|
||||
return <div />
|
||||
return <div />;
|
||||
}
|
||||
|
||||
let videoLink = new BucketConfig().getGcsUrl(w2lInferenceResult?.public_bucket_video_path);
|
||||
let videoLink = new BucketConfig().getGcsUrl(
|
||||
w2lInferenceResult?.public_bucket_video_path
|
||||
);
|
||||
let templateLink = `/w2l/${w2lInferenceResult.maybe_w2l_template_token}`;
|
||||
let videoDownloadFilename = `vocodes-${w2lInferenceResult.w2l_result_token.replace(':', '')}.mp4`;
|
||||
let videoDownloadFilename = `vocodes-${w2lInferenceResult.w2l_result_token.replace(
|
||||
":",
|
||||
""
|
||||
)}.mp4`;
|
||||
|
||||
MetaTags.setVideoUrl(videoLink);
|
||||
MetaTags.setTitle('testing...');
|
||||
MetaTags.setTitle("testing...");
|
||||
|
||||
let durationSeconds = w2lInferenceResult?.duration_millis / 1000;
|
||||
|
||||
let templateName = w2lInferenceResult.template_title;
|
||||
|
||||
|
||||
let moderatorRows = null;
|
||||
|
||||
if (props.sessionWrapper.canDeleteOtherUsersW2lResults() || props.sessionWrapper.canDeleteOtherUsersW2lTemplates()) {
|
||||
if (
|
||||
props.sessionWrapper.canDeleteOtherUsersW2lResults() ||
|
||||
props.sessionWrapper.canDeleteOtherUsersW2lTemplates()
|
||||
) {
|
||||
moderatorRows = (
|
||||
<>
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<br />
|
||||
<h4 className="subtitle is-4"> Moderator Details </h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Template creator is banned</th>
|
||||
<td>{w2lInferenceResult?.maybe_moderator_fields?.template_creator_is_banned ? "banned" : "good standing" }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Result creator is banned (if user)</th>
|
||||
<td>{w2lInferenceResult?.maybe_moderator_fields?.result_creator_is_banned_if_user ? "banned" : "good standing" }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Result creator IP address</th>
|
||||
<td>{w2lInferenceResult?.maybe_moderator_fields?.result_creator_ip_address || "server error"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Mod deleted at (UTC)</th>
|
||||
<td>{w2lInferenceResult?.maybe_moderator_fields?.mod_deleted_at || "not deleted"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Result creator deleted at (UTC)</th>
|
||||
<td>{w2lInferenceResult?.maybe_moderator_fields?.result_creator_deleted_at || "not deleted"}</td>
|
||||
</tr>
|
||||
<Fade bottom duration={duration} distance={distance} delay={delay}>
|
||||
<div className="container-panel pt-3 pb-5">
|
||||
<div className="panel p-3 p-lg-4">
|
||||
<h2 className="panel-title fw-bold">Moderator Details</h2>
|
||||
<div className="py-6">
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Template creator is banned</th>
|
||||
<td>
|
||||
{w2lInferenceResult?.maybe_moderator_fields
|
||||
?.template_creator_is_banned
|
||||
? "banned"
|
||||
: "good standing"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Result creator is banned (if user)</th>
|
||||
<td>
|
||||
{w2lInferenceResult?.maybe_moderator_fields
|
||||
?.result_creator_is_banned_if_user
|
||||
? "banned"
|
||||
: "good standing"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Result creator IP address</th>
|
||||
<td>
|
||||
{w2lInferenceResult?.maybe_moderator_fields
|
||||
?.result_creator_ip_address || "server error"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Mod deleted at (UTC)</th>
|
||||
<td>
|
||||
{w2lInferenceResult?.maybe_moderator_fields
|
||||
?.mod_deleted_at || "not deleted"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Result creator deleted at (UTC)</th>
|
||||
<td>
|
||||
{w2lInferenceResult?.maybe_moderator_fields
|
||||
?.result_creator_deleted_at || "not deleted"}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -106,11 +149,13 @@ function W2lResultViewFc(props: Props) {
|
||||
<span>
|
||||
<Gravatar
|
||||
size={15}
|
||||
username={w2lInferenceResult.maybe_creator_display_name || ""}
|
||||
email_hash={w2lInferenceResult.maybe_creator_gravatar_hash || ""}
|
||||
/>
|
||||
username={w2lInferenceResult.maybe_creator_display_name || ""}
|
||||
email_hash={w2lInferenceResult.maybe_creator_gravatar_hash || ""}
|
||||
/>
|
||||
|
||||
<Link to={creatorLink}>{w2lInferenceResult.maybe_creator_display_name}</Link>
|
||||
<Link to={creatorLink}>
|
||||
{w2lInferenceResult.maybe_creator_display_name}
|
||||
</Link>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -122,135 +167,179 @@ function W2lResultViewFc(props: Props) {
|
||||
<span>
|
||||
<Gravatar
|
||||
size={15}
|
||||
username={w2lInferenceResult.maybe_template_creator_display_name || ""}
|
||||
email_hash={w2lInferenceResult.maybe_template_creator_gravatar_hash || ""}
|
||||
/>
|
||||
username={
|
||||
w2lInferenceResult.maybe_template_creator_display_name || ""
|
||||
}
|
||||
email_hash={
|
||||
w2lInferenceResult.maybe_template_creator_gravatar_hash || ""
|
||||
}
|
||||
/>
|
||||
|
||||
<Link to={templateCreatorLink}>{w2lInferenceResult.maybe_template_creator_display_name}</Link>
|
||||
<Link to={templateCreatorLink}>
|
||||
{w2lInferenceResult.maybe_template_creator_display_name}
|
||||
</Link>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
let resultVisibility = w2lInferenceResult?.creator_set_visibility === 'hidden' ?
|
||||
<span>Hidden <HiddenIconFc /></span> :
|
||||
<span>Public <VisibleIconFc /></span> ;
|
||||
let resultVisibility =
|
||||
w2lInferenceResult?.creator_set_visibility === "hidden" ? (
|
||||
<span>
|
||||
Hidden <HiddenIconFc />
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
Public <VisibleIconFc />
|
||||
</span>
|
||||
);
|
||||
|
||||
const currentlyDeleted = !!w2lInferenceResult?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
!!w2lInferenceResult?.maybe_moderator_fields?.result_creator_deleted_at;
|
||||
const currentlyDeleted =
|
||||
!!w2lInferenceResult?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
!!w2lInferenceResult?.maybe_moderator_fields?.result_creator_deleted_at;
|
||||
|
||||
const deleteButtonTitle = currentlyDeleted ? "Undelete Result?" : "Delete Result?";
|
||||
const deleteButtonTitle = currentlyDeleted
|
||||
? "Undelete Result?"
|
||||
: "Delete Result?";
|
||||
|
||||
const deleteButtonCss = currentlyDeleted ?
|
||||
"button is-warning is-large is-fullwidth" :
|
||||
"button is-danger is-large is-fullwidth";
|
||||
const deleteButtonCss = currentlyDeleted
|
||||
? "btn btn-secondary w-100"
|
||||
: "btn btn-destructive w-100";
|
||||
|
||||
let editButton = null;
|
||||
const canEdit = props.sessionWrapper.canEditW2lResultAsUserOrMod(w2lInferenceResult?.maybe_creator_user_token);
|
||||
const canEdit = props.sessionWrapper.canEditW2lResultAsUserOrMod(
|
||||
w2lInferenceResult?.maybe_creator_user_token
|
||||
);
|
||||
|
||||
if (canEdit) {
|
||||
editButton = (
|
||||
<>
|
||||
<br />
|
||||
<Link
|
||||
className="button is-info is-large is-fullwidth"
|
||||
<Link
|
||||
className="btn btn-secondary w-100"
|
||||
to={FrontendUrlConfig.w2lResultEditPage(token)}
|
||||
>Edit Result Visibility</Link>
|
||||
>
|
||||
<FontAwesomeIcon icon={faEdit} className="me-2" />
|
||||
Edit Result Visibility
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let deleteButton = null;
|
||||
const canDelete = props.sessionWrapper.canDeleteW2lResultAsUserOrMod(w2lInferenceResult?.maybe_creator_user_token);
|
||||
const canDelete = props.sessionWrapper.canDeleteW2lResultAsUserOrMod(
|
||||
w2lInferenceResult?.maybe_creator_user_token
|
||||
);
|
||||
|
||||
if (canDelete) {
|
||||
deleteButton = (
|
||||
<>
|
||||
<br />
|
||||
<Link
|
||||
<Link
|
||||
className={deleteButtonCss}
|
||||
to={FrontendUrlConfig.w2lResultDeletePage(token)}
|
||||
>{deleteButtonTitle}</Link>
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} className="me-2" />
|
||||
{deleteButtonTitle}
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title is-1"> Lipsync Result </h1>
|
||||
<Fade cascade bottom duration={duration} distance={distance}>
|
||||
<div className="container py-5 px-md-4 px-lg-5 px-xl-3">
|
||||
<h1 className="display-5 fw-bold text-center text-lg-start">
|
||||
Lipsync Result
|
||||
</h1>
|
||||
</div>
|
||||
</Fade>
|
||||
|
||||
<video width="100%" height="auto" controls={true}>
|
||||
<source src={videoLink} />
|
||||
Your device doesn't support video.
|
||||
</video>
|
||||
<Fade bottom duration={duration} distance={distance} delay={delay}>
|
||||
<div className="container">
|
||||
<video width="100%" height="auto" controls={true} className="rounded">
|
||||
<source src={videoLink} />
|
||||
Your device doesn't support video.
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<div className="container pt-4 pb-5">
|
||||
<a
|
||||
className="btn btn-primary w-100"
|
||||
href={videoLink}
|
||||
download={videoDownloadFilename}
|
||||
>
|
||||
{" "}
|
||||
<FontAwesomeIcon icon={faDownload} className="me-2" />
|
||||
Download File
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a className="button is-large is-primary is-fullwidth"
|
||||
href={videoLink}
|
||||
download={videoDownloadFilename}> <DownloadIcon /> Download File</a>
|
||||
<div className="container-panel pt-5 pb-5">
|
||||
<div className="panel p-3 p-lg-4">
|
||||
<h2 className="panel-title fw-bold">Result Details</h2>
|
||||
<div className="py-6">
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>{creatorDetails}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration</th>
|
||||
<td>{durationSeconds} seconds</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Visibility</th>
|
||||
<td>{resultVisibility}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container-panel pt-3 pb-5">
|
||||
<div className="panel p-3 p-lg-4">
|
||||
<h2 className="panel-title fw-bold"> Template Details </h2>
|
||||
<div className="py-6">
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Template used</th>
|
||||
<td>
|
||||
<Link to={templateLink}>{templateName}</Link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Template creator</th>
|
||||
<td>{templateCreatorDetails}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Dimensions</th>
|
||||
<td>
|
||||
{w2lInferenceResult?.frame_width} x{" "}
|
||||
{w2lInferenceResult?.frame_height}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<div>{moderatorRows}</div>
|
||||
|
||||
<table className="table is-fullwidth">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<h4 className="subtitle is-4"> Result Details </h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>
|
||||
{creatorDetails}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration</th>
|
||||
<td>{durationSeconds} seconds</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Visibility</th>
|
||||
<td>{resultVisibility}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<br />
|
||||
<h4 className="subtitle is-4"> Template Details </h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Template used</th>
|
||||
<td>
|
||||
<Link to={templateLink}>
|
||||
{templateName}
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Template creator</th>
|
||||
<td>
|
||||
{templateCreatorDetails}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Dimensions</th>
|
||||
<td>{w2lInferenceResult?.frame_width} x {w2lInferenceResult?.frame_height}</td>
|
||||
</tr>
|
||||
|
||||
{moderatorRows}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{editButton}
|
||||
|
||||
{deleteButton}
|
||||
|
||||
<br />
|
||||
<ReportDiscordLinkFc />
|
||||
<Fade bottom duration={duration} distance={distance} delay={delay}>
|
||||
<div className="container pb-5">
|
||||
<div className="d-flex gap-3 flex-column flex-md-row">
|
||||
{editButton}
|
||||
{deleteButton}
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<ReportDiscordLinkFc />
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { W2lResultViewFc };
|
||||
@@ -1,32 +1,44 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { ApiConfig } from '@storyteller/components';
|
||||
import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { useParams, Link, useHistory } from 'react-router-dom';
|
||||
import { GetW2lTemplate, GetW2lTemplateIsOk, W2lTemplate } from '../../../api/w2l/GetW2lTemplate';
|
||||
import { GetW2lTemplateUseCount } from '../../../api/w2l/GetW2lTemplateUseCount';
|
||||
import { BackLink } from '../../_common/BackLink';
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useParams, Link, useHistory } from "react-router-dom";
|
||||
import {
|
||||
GetW2lTemplate,
|
||||
GetW2lTemplateIsOk,
|
||||
W2lTemplate,
|
||||
} from "../../../api/w2l/GetW2lTemplate";
|
||||
import { GetW2lTemplateUseCount } from "../../../api/w2l/GetW2lTemplateUseCount";
|
||||
import { BackLink } from "../../_common/BackLink";
|
||||
import { distance, delay, delay2, duration } from "../../../../data/animation";
|
||||
const Fade = require("react-reveal/Fade");
|
||||
|
||||
const DEFAULT_APPROVED_STATE = true;
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
function W2lTemplateApproveFc(props: Props) {
|
||||
const history = useHistory();
|
||||
|
||||
let { templateToken } : { templateToken : string } = useParams();
|
||||
let { templateToken }: { templateToken: string } = useParams();
|
||||
|
||||
const [w2lTemplate, setW2lTemplate] = useState<W2lTemplate|undefined>(undefined);
|
||||
const [w2lTemplateUseCount, setW2lTemplateUseCount] = useState<number|undefined>(undefined);
|
||||
const [approvedState, setApprovedState] = useState<boolean|null>(DEFAULT_APPROVED_STATE);
|
||||
const [w2lTemplate, setW2lTemplate] = useState<W2lTemplate | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [w2lTemplateUseCount, setW2lTemplateUseCount] = useState<
|
||||
number | undefined
|
||||
>(undefined);
|
||||
const [approvedState, setApprovedState] = useState<boolean | null>(
|
||||
DEFAULT_APPROVED_STATE
|
||||
);
|
||||
|
||||
const getTemplate = useCallback(async (templateToken) => {
|
||||
const template = await GetW2lTemplate(templateToken);
|
||||
|
||||
if (GetW2lTemplateIsOk(template)) {
|
||||
setW2lTemplate(template)
|
||||
setW2lTemplate(template);
|
||||
const currentlyApproved = template?.is_public_listing_approved;
|
||||
setApprovedState(currentlyApproved);
|
||||
}
|
||||
@@ -34,7 +46,7 @@ function W2lTemplateApproveFc(props: Props) {
|
||||
|
||||
const getTemplateUseCount = useCallback(async (templateToken) => {
|
||||
const count = await GetW2lTemplateUseCount(templateToken);
|
||||
setW2lTemplateUseCount(count || 0)
|
||||
setW2lTemplateUseCount(count || 0);
|
||||
}, []);
|
||||
|
||||
const templateLink = FrontendUrlConfig.w2lTemplatePage(templateToken);
|
||||
@@ -44,45 +56,48 @@ function W2lTemplateApproveFc(props: Props) {
|
||||
getTemplateUseCount(templateToken);
|
||||
}, [templateToken, getTemplate, getTemplateUseCount]);
|
||||
|
||||
const handleModApprovalChange= (ev: React.FormEvent<HTMLSelectElement>) => {
|
||||
const handleModApprovalChange = (ev: React.FormEvent<HTMLSelectElement>) => {
|
||||
const value = (ev.target as HTMLSelectElement).value;
|
||||
const updatedValue = value === "true" ? true : false;
|
||||
setApprovedState(updatedValue)
|
||||
setApprovedState(updatedValue);
|
||||
};
|
||||
|
||||
const handleApproveFormSubmit = (ev: React.FormEvent<HTMLFormElement>) : boolean => {
|
||||
const handleApproveFormSubmit = (
|
||||
ev: React.FormEvent<HTMLFormElement>
|
||||
): boolean => {
|
||||
ev.preventDefault();
|
||||
|
||||
const endpointUri = new ApiConfig().moderateW2l(templateToken);
|
||||
|
||||
const request = {
|
||||
is_approved: approvedState || false,
|
||||
}
|
||||
};
|
||||
|
||||
fetch(endpointUri, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
body: JSON.stringify(request),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
history.push(templateLink);
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
});
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
history.push(templateLink);
|
||||
}
|
||||
})
|
||||
.catch((e) => {});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let creatorLink = <span />;
|
||||
|
||||
if (!!w2lTemplate?.creator_display_name) {
|
||||
const creatorUrl = FrontendUrlConfig.userProfilePage(w2lTemplate?.creator_display_name);
|
||||
const creatorUrl = FrontendUrlConfig.userProfilePage(
|
||||
w2lTemplate?.creator_display_name
|
||||
);
|
||||
creatorLink = (
|
||||
<Link to={creatorUrl}>{w2lTemplate?.creator_display_name}</Link>
|
||||
);
|
||||
@@ -90,9 +105,11 @@ function W2lTemplateApproveFc(props: Props) {
|
||||
|
||||
const currentlyApproved = w2lTemplate?.is_public_listing_approved;
|
||||
|
||||
const h1Title = currentlyApproved ? "Unapprove Template?" : "Approve Template?";
|
||||
const h1Title = currentlyApproved
|
||||
? "Unapprove Template?"
|
||||
: "Approve Template?";
|
||||
|
||||
let humanUseCount : string | number = 'Fetching...';
|
||||
let humanUseCount: string | number = "Fetching...";
|
||||
|
||||
const approvedFormDefaultState = approvedState ? "true" : "false";
|
||||
|
||||
@@ -101,73 +118,89 @@ function W2lTemplateApproveFc(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="content">
|
||||
<h1 className="title is-1"> {h1Title} </h1>
|
||||
|
||||
<p>
|
||||
<BackLink link={templateLink} text="Back to template" />
|
||||
</p>
|
||||
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><abbr title="Detail">Detail</abbr></th>
|
||||
<th><abbr title="Value">Value</abbr></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>
|
||||
{creatorLink}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Use Count</th>
|
||||
<td>{humanUseCount}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<td>{w2lTemplate?.title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Upload Date (UTC)</th>
|
||||
<td>{w2lTemplate?.created_at}</td>
|
||||
</tr>
|
||||
<div>
|
||||
<Fade bottom cascade duration={duration} distance={distance}>
|
||||
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
|
||||
<div className="d-flex flex-column">
|
||||
<h1 className="display-5 fw-bold">{h1Title}</h1>
|
||||
</div>
|
||||
<div className="pt-3">
|
||||
<BackLink link={templateLink} text="Back to template" />
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<Fade
|
||||
bottom
|
||||
cascade
|
||||
duration={duration}
|
||||
delay={delay}
|
||||
distance={distance}
|
||||
>
|
||||
<div className="container-panel pt-4 pb-5">
|
||||
<div className="panel p-3 py-4 p-lg-4">
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<abbr title="Detail">Detail</abbr>
|
||||
</th>
|
||||
<th>
|
||||
<abbr title="Value">Value</abbr>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>{creatorLink}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Use Count</th>
|
||||
<td>{humanUseCount}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<td>{w2lTemplate?.title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Upload Date (UTC)</th>
|
||||
<td>{w2lTemplate?.created_at}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<form onSubmit={handleApproveFormSubmit}>
|
||||
<div className="container-panel pt-1 pb-5">
|
||||
<div className="panel p-3 py-4 p-lg-4">
|
||||
<label className="sub-title">
|
||||
Mod Approval (sets public list visibility)
|
||||
</label>
|
||||
|
||||
<form onSubmit={handleApproveFormSubmit}>
|
||||
<label className="label">Mod Approval (sets public list visibility)</label>
|
||||
|
||||
<div className="field is-grouped">
|
||||
<div className="control">
|
||||
|
||||
<div className="select is-info is-large">
|
||||
<select name="approve" value={approvedFormDefaultState} onChange={handleModApprovalChange}>
|
||||
<option value="true">Approve</option>
|
||||
<option value="false">Disapprove</option>
|
||||
</select>
|
||||
<div>
|
||||
<div className="form-group">
|
||||
<select
|
||||
name="approve"
|
||||
value={approvedFormDefaultState}
|
||||
onChange={handleModApprovalChange}
|
||||
className="form-select"
|
||||
>
|
||||
<option value="true">Approve</option>
|
||||
<option value="false">Disapprove</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="control">
|
||||
<button className="button is-info is-large">
|
||||
Moderate
|
||||
</button>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<BackLink link={templateLink} text="Back to template" />
|
||||
<div className="container pb-5">
|
||||
<button className="btn btn-primary w-100">Moderate</button>
|
||||
</div>
|
||||
</form>
|
||||
</Fade>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { W2lTemplateApproveFc };
|
||||
@@ -1,35 +1,45 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { ApiConfig } from '@storyteller/components';
|
||||
import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { useParams, Link, useHistory } from 'react-router-dom';
|
||||
import { GetW2lTemplate, GetW2lTemplateIsOk, W2lTemplate } from '../../../api/w2l/GetW2lTemplate';
|
||||
import { GetW2lTemplateUseCount } from '../../../api/w2l/GetW2lTemplateUseCount';
|
||||
import { BackLink } from '../../_common/BackLink';
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useParams, Link, useHistory } from "react-router-dom";
|
||||
import {
|
||||
GetW2lTemplate,
|
||||
GetW2lTemplateIsOk,
|
||||
W2lTemplate,
|
||||
} from "../../../api/w2l/GetW2lTemplate";
|
||||
import { GetW2lTemplateUseCount } from "../../../api/w2l/GetW2lTemplateUseCount";
|
||||
import { BackLink } from "../../_common/BackLink";
|
||||
import { distance, delay, duration } from "../../../../data/animation";
|
||||
const Fade = require("react-reveal/Fade");
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
function W2lTemplateDeleteFc(props: Props) {
|
||||
const history = useHistory();
|
||||
|
||||
let { templateToken } : { templateToken : string } = useParams();
|
||||
let { templateToken }: { templateToken: string } = useParams();
|
||||
|
||||
const [w2lTemplate, setW2lTemplate] = useState<W2lTemplate|undefined>(undefined);
|
||||
const [w2lTemplateUseCount, setW2lTemplateUseCount] = useState<number|undefined>(undefined);
|
||||
const [w2lTemplate, setW2lTemplate] = useState<W2lTemplate | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [w2lTemplateUseCount, setW2lTemplateUseCount] = useState<
|
||||
number | undefined
|
||||
>(undefined);
|
||||
|
||||
const getTemplate = useCallback(async (templateToken) => {
|
||||
const template = await GetW2lTemplate(templateToken);
|
||||
|
||||
if (GetW2lTemplateIsOk(template)) {
|
||||
setW2lTemplate(template)
|
||||
setW2lTemplate(template);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getTemplateUseCount = useCallback(async (templateToken) => {
|
||||
const count = await GetW2lTemplateUseCount(templateToken);
|
||||
setW2lTemplateUseCount(count || 0)
|
||||
setW2lTemplateUseCount(count || 0);
|
||||
}, []);
|
||||
|
||||
const templateLink = FrontendUrlConfig.w2lTemplatePage(templateToken);
|
||||
@@ -39,7 +49,9 @@ function W2lTemplateDeleteFc(props: Props) {
|
||||
getTemplateUseCount(templateToken);
|
||||
}, [templateToken, getTemplate, getTemplateUseCount]);
|
||||
|
||||
const handleDeleteFormSubmit = (ev: React.FormEvent<HTMLFormElement>) : boolean => {
|
||||
const handleDeleteFormSubmit = (
|
||||
ev: React.FormEvent<HTMLFormElement>
|
||||
): boolean => {
|
||||
ev.preventDefault();
|
||||
|
||||
const api = new ApiConfig();
|
||||
@@ -47,58 +59,62 @@ function W2lTemplateDeleteFc(props: Props) {
|
||||
|
||||
const request = {
|
||||
set_delete: !currentlyDeleted,
|
||||
as_mod: props.sessionWrapper.deleteTtsResultAsMod(w2lTemplate?.creator_user_token)
|
||||
}
|
||||
as_mod: props.sessionWrapper.deleteTtsResultAsMod(
|
||||
w2lTemplate?.creator_user_token
|
||||
),
|
||||
};
|
||||
|
||||
fetch(endpointUrl, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
body: JSON.stringify(request),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
if (props.sessionWrapper.canDeleteOtherUsersTtsResults()) {
|
||||
history.push(templateLink); // Mods can perform further actions
|
||||
} else {
|
||||
history.push('/');
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
if (props.sessionWrapper.canDeleteOtherUsersTtsResults()) {
|
||||
history.push(templateLink); // Mods can perform further actions
|
||||
} else {
|
||||
history.push("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
});
|
||||
})
|
||||
.catch((e) => {});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let creatorLink = <span />;
|
||||
|
||||
if (!!w2lTemplate?.creator_display_name) {
|
||||
const creatorUrl = FrontendUrlConfig.userProfilePage(w2lTemplate?.creator_display_name);
|
||||
const creatorUrl = FrontendUrlConfig.userProfilePage(
|
||||
w2lTemplate?.creator_display_name
|
||||
);
|
||||
creatorLink = (
|
||||
<Link to={creatorUrl}>{w2lTemplate?.creator_display_name}</Link>
|
||||
);
|
||||
}
|
||||
|
||||
let currentlyDeleted = !!w2lTemplate?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
!!w2lTemplate?.maybe_moderator_fields?.user_deleted_at;
|
||||
let currentlyDeleted =
|
||||
!!w2lTemplate?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
!!w2lTemplate?.maybe_moderator_fields?.user_deleted_at;
|
||||
|
||||
const h1Title = currentlyDeleted ? "Undelete Template?" : "Delete Template?";
|
||||
|
||||
const buttonTitle = currentlyDeleted ? "Confirm Undelete" : "Confirm Delete";
|
||||
|
||||
const buttonCss = currentlyDeleted ?
|
||||
"button is-warning is-large is-fullwidth" :
|
||||
"button is-danger is-large is-fullwidth";
|
||||
const buttonCss = currentlyDeleted
|
||||
? "btn btn-primary w-100"
|
||||
: "btn btn-primary w-100";
|
||||
|
||||
const formLabel = currentlyDeleted ?
|
||||
"Recover the W2L Template (makes it visible again)" :
|
||||
"Delete W2L Template (hides from everyone but mods)";
|
||||
const formLabel = currentlyDeleted
|
||||
? "Recover the W2L Template (makes it visible again)"
|
||||
: "Delete W2L Template (hides from everyone but mods)";
|
||||
|
||||
let humanUseCount : string | number = 'Fetching...';
|
||||
let humanUseCount: string | number = "Fetching...";
|
||||
|
||||
if (w2lTemplateUseCount !== undefined && w2lTemplateUseCount !== null) {
|
||||
humanUseCount = w2lTemplateUseCount;
|
||||
@@ -106,90 +122,120 @@ function W2lTemplateDeleteFc(props: Props) {
|
||||
|
||||
let moderatorRows = null;
|
||||
|
||||
if (props.sessionWrapper.canDeleteOtherUsersTtsResults() || props.sessionWrapper.canDeleteOtherUsersW2lTemplates()) {
|
||||
if (
|
||||
props.sessionWrapper.canDeleteOtherUsersTtsResults() ||
|
||||
props.sessionWrapper.canDeleteOtherUsersW2lTemplates()
|
||||
) {
|
||||
moderatorRows = (
|
||||
<>
|
||||
<tr>
|
||||
<th>Creator is banned</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.creator_is_banned ? "banned" : "good standing" }</td>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields?.creator_is_banned
|
||||
? "banned"
|
||||
: "good standing"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creator IP Address (Creation)</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.creator_ip_address_creation || "server error"}</td>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields?.creator_ip_address_creation ||
|
||||
"server error"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creator IP Address (Update)</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.creator_ip_address_last_update || "server error"}</td>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields
|
||||
?.creator_ip_address_last_update || "server error"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Mod Deleted At (UTC)</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.mod_deleted_at || "not deleted"}</td>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
"not deleted"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>User Deleted At (UTC)</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.user_deleted_at || "not deleted"}</td>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields?.user_deleted_at ||
|
||||
"not deleted"}
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="content">
|
||||
<h1 className="title is-1"> {h1Title} </h1>
|
||||
|
||||
<p>
|
||||
<BackLink link={templateLink} text="Back to template" />
|
||||
</p>
|
||||
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><abbr title="Detail">Detail</abbr></th>
|
||||
<th><abbr title="Value">Value</abbr></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>
|
||||
{creatorLink}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Use Count</th>
|
||||
<td>{humanUseCount}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<td>{w2lTemplate?.title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Upload Date (UTC)</th>
|
||||
<td>{w2lTemplate?.created_at}</td>
|
||||
</tr>
|
||||
<div>
|
||||
<Fade bottom cascade duration={duration} distance={distance}>
|
||||
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
|
||||
<div className="d-flex flex-column">
|
||||
<h1 className="display-5 fw-bold">{h1Title}</h1>
|
||||
</div>
|
||||
<div className="pt-3">
|
||||
<BackLink link={templateLink} text="Back to template" />
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
|
||||
{moderatorRows}
|
||||
<Fade
|
||||
bottom
|
||||
cascade
|
||||
duration={duration}
|
||||
delay={delay}
|
||||
distance={distance}
|
||||
>
|
||||
<div className="container pt-4 pb-5">
|
||||
<div className="panel p-3 py-4 p-lg-4">
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<abbr title="Detail">Detail</abbr>
|
||||
</th>
|
||||
<th>
|
||||
<abbr title="Value">Value</abbr>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>{creatorLink}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Use Count</th>
|
||||
<td>{humanUseCount}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<td>{w2lTemplate?.title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Upload Date (UTC)</th>
|
||||
<td>{w2lTemplate?.created_at}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
{moderatorRows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<form onSubmit={handleDeleteFormSubmit}>
|
||||
<label className="label">{formLabel}</label>
|
||||
|
||||
<p className="control">
|
||||
<button className={buttonCss}>
|
||||
{buttonTitle}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<BackLink link={templateLink} text="Back to template" />
|
||||
<form onSubmit={handleDeleteFormSubmit}>
|
||||
<div className="container">
|
||||
<button className={buttonCss}>{buttonTitle}</button>
|
||||
</div>
|
||||
<div className="container pb-5">
|
||||
<p className="pt-4">{formLabel}</p>
|
||||
</div>
|
||||
</form>
|
||||
</Fade>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { W2lTemplateDeleteFc };
|
||||
@@ -1,25 +1,33 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { ApiConfig } from '@storyteller/components';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { useParams, useHistory } from 'react-router-dom';
|
||||
import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig';
|
||||
import { VisibleIconFc } from '../../_icons/VisibleIcon';
|
||||
import { HiddenIconFc } from '../../_icons/HiddenIcon';
|
||||
import { GetW2lTemplate, GetW2lTemplateIsOk, W2lTemplate } from '../../../api/w2l/GetW2lTemplate';
|
||||
import { BackLink } from '../../_common/BackLink';
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useParams, useHistory } from "react-router-dom";
|
||||
import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig";
|
||||
import { VisibleIconFc } from "../../_icons/VisibleIcon";
|
||||
import { HiddenIconFc } from "../../_icons/HiddenIcon";
|
||||
import {
|
||||
GetW2lTemplate,
|
||||
GetW2lTemplateIsOk,
|
||||
W2lTemplate,
|
||||
} from "../../../api/w2l/GetW2lTemplate";
|
||||
import { BackLink } from "../../_common/BackLink";
|
||||
import { distance, delay, delay2, duration } from "../../../../data/animation";
|
||||
const Fade = require("react-reveal/Fade");
|
||||
|
||||
const DEFAULT_VISIBILITY = 'public';
|
||||
const DEFAULT_VISIBILITY = "public";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
function W2lTemplateEditFc(props: Props) {
|
||||
let { templateToken } : { templateToken : string } = useParams();
|
||||
let { templateToken }: { templateToken: string } = useParams();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
const [w2lTemplate, setW2lTemplate] = useState<W2lTemplate|undefined>(undefined);
|
||||
const [w2lTemplate, setW2lTemplate] = useState<W2lTemplate | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [title, setTitle] = useState<string>("");
|
||||
const [descriptionMarkdown, setDescriptionMarkdown] = useState<string>("");
|
||||
const [visibility, setVisibility] = useState<string>(DEFAULT_VISIBILITY);
|
||||
@@ -28,27 +36,27 @@ function W2lTemplateEditFc(props: Props) {
|
||||
const template = await GetW2lTemplate(token);
|
||||
|
||||
if (GetW2lTemplateIsOk(template)) {
|
||||
setTitle(template.title || "")
|
||||
setDescriptionMarkdown(template.description_markdown || "")
|
||||
setTitle(template.title || "");
|
||||
setDescriptionMarkdown(template.description_markdown || "");
|
||||
setVisibility(template.creator_set_visibility || DEFAULT_VISIBILITY);
|
||||
setW2lTemplate(template)
|
||||
setW2lTemplate(template);
|
||||
}
|
||||
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
getTemplate(templateToken);
|
||||
}, [templateToken, getTemplate]);
|
||||
|
||||
const handleTitleChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
const handleTitleChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
ev.preventDefault();
|
||||
const textValue = (ev.target as HTMLInputElement).value;
|
||||
setTitle(textValue);
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleDescriptionMarkdownChange = (ev: React.FormEvent<HTMLTextAreaElement>) => {
|
||||
const handleDescriptionMarkdownChange = (
|
||||
ev: React.FormEvent<HTMLTextAreaElement>
|
||||
) => {
|
||||
ev.preventDefault();
|
||||
const textValue = (ev.target as HTMLTextAreaElement).value;
|
||||
setDescriptionMarkdown(textValue);
|
||||
@@ -56,12 +64,12 @@ function W2lTemplateEditFc(props: Props) {
|
||||
};
|
||||
|
||||
const handleVisibilityChange = (ev: React.FormEvent<HTMLSelectElement>) => {
|
||||
setVisibility((ev.target as HTMLSelectElement).value)
|
||||
setVisibility((ev.target as HTMLSelectElement).value);
|
||||
};
|
||||
|
||||
const templateLink = FrontendUrlConfig.w2lTemplatePage(templateToken);
|
||||
|
||||
const handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
const handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (w2lTemplate === undefined) {
|
||||
@@ -75,103 +83,126 @@ function W2lTemplateEditFc(props: Props) {
|
||||
const templateToken = w2lTemplate!.template_token;
|
||||
|
||||
const endpointUrl = new ApiConfig().editW2lTemplate(templateToken);
|
||||
|
||||
|
||||
const request = {
|
||||
title: title,
|
||||
description_markdown: descriptionMarkdown,
|
||||
creator_set_visibility: visibility || DEFAULT_VISIBILITY,
|
||||
}
|
||||
};
|
||||
|
||||
fetch(endpointUrl, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: 'include',
|
||||
credentials: "include",
|
||||
body: JSON.stringify(request),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res === undefined ||
|
||||
!res.success) {
|
||||
return; // Endpoint error?
|
||||
}
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (res === undefined || !res.success) {
|
||||
return; // Endpoint error?
|
||||
}
|
||||
|
||||
history.push(templateLink);
|
||||
})
|
||||
.catch(e => {
|
||||
});
|
||||
history.push(templateLink);
|
||||
})
|
||||
.catch((e) => {});
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
let isDisabled = w2lTemplate === undefined;
|
||||
|
||||
const visibilityIcon = (visibility === 'public') ? <VisibleIconFc /> : <HiddenIconFc />;
|
||||
const visibilityIcon =
|
||||
visibility === "public" ? <VisibleIconFc /> : <HiddenIconFc />;
|
||||
|
||||
return (
|
||||
<div className="content">
|
||||
<h1 className="title is-1"> Edit Template </h1>
|
||||
|
||||
<p>
|
||||
<BackLink link={templateLink} text="Back to template" />
|
||||
</p>
|
||||
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<fieldset disabled={isDisabled}>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">Template title</label>
|
||||
<div className="control has-icons-left has-icons-right">
|
||||
<input
|
||||
onChange={handleTitleChange}
|
||||
className="input"
|
||||
type="text"
|
||||
placeholder="Template title"
|
||||
value={title}
|
||||
/>
|
||||
</div>
|
||||
{/*<p className="help">{invalidReason}</p>*/}
|
||||
<div>
|
||||
<Fade bottom cascade duration={duration} distance={distance}>
|
||||
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
|
||||
<div className="d-flex flex-column">
|
||||
<h1 className="display-5 fw-bold">Edit Template</h1>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">Description (supports Markdown)</label>
|
||||
<div className="control">
|
||||
<textarea
|
||||
onChange={handleDescriptionMarkdownChange}
|
||||
className="textarea is-large"
|
||||
placeholder="Model description (ie. source of data, training duration, etc)"
|
||||
value={descriptionMarkdown}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
<BackLink link={templateLink} text="Back to template" />
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">
|
||||
Template 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>
|
||||
<Fade
|
||||
bottom
|
||||
cascade
|
||||
duration={duration}
|
||||
delay={delay}
|
||||
distance={distance}
|
||||
>
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<fieldset disabled={isDisabled}>
|
||||
<div className="container-panel pt-4 pb-5">
|
||||
<div className="panel p-3 py-4 p-lg-4">
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<div>
|
||||
<label className="sub-title">Template title</label>
|
||||
<div className="form-group">
|
||||
<input
|
||||
onChange={handleTitleChange}
|
||||
className="form-control"
|
||||
type="text"
|
||||
placeholder="Template title"
|
||||
value={title}
|
||||
/>
|
||||
</div>
|
||||
{/*<p className="help">{invalidReason}</p>*/}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
Description (supports Markdown)
|
||||
</label>
|
||||
<div className="form-group">
|
||||
<textarea
|
||||
onChange={handleDescriptionMarkdownChange}
|
||||
className="form-control"
|
||||
placeholder="Model description (ie. source of data, training duration, etc)"
|
||||
value={descriptionMarkdown}
|
||||
rows={5}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
Template Visibility {visibilityIcon}
|
||||
</label>
|
||||
<div className="form-group">
|
||||
<select
|
||||
name="creator_set_visibility"
|
||||
onChange={handleVisibilityChange}
|
||||
value={visibility}
|
||||
className="form-control"
|
||||
>
|
||||
<option value="public">
|
||||
Public (visible from your profile)
|
||||
</option>
|
||||
<option value="hidden">
|
||||
Unlisted (shareable URLs)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<button className="button is-link is-large is-fullwidth">Update Template</button>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<div className="container pb-5">
|
||||
<button className="btn btn-primary w-100">Update Template</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</Fade>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { W2lTemplateEditFc };
|
||||
@@ -1,73 +1,90 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import axios from 'axios';
|
||||
import { ApiConfig } from '@storyteller/components';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { Gravatar } from '@storyteller/components/src/elements/Gravatar';
|
||||
import { W2lInferenceJob } from '../../../../App';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { SessionW2lInferenceResultListFc } from '../../_common/SessionW2lInferenceResultsListFc';
|
||||
import { ReportDiscordLinkFc } from '../../_common/DiscordReportLinkFc';
|
||||
import { BucketConfig } from '@storyteller/components/src/api/BucketConfig';
|
||||
import { UploadIcon } from '../../_icons/UploadIcon';
|
||||
import { VisibleIconFc } from '../../_icons/VisibleIcon';
|
||||
import { HiddenIconFc } from '../../_icons/HiddenIcon';
|
||||
import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig';
|
||||
import { GetW2lTemplate, GetW2lTemplateIsErr, GetW2lTemplateIsOk, W2lTemplate, W2lTemplateLookupError } from '../../../api/w2l/GetW2lTemplate';
|
||||
import { GetW2lTemplateUseCount } from '../../../api/w2l/GetW2lTemplateUseCount';
|
||||
import { BackLink } from '../../_common/BackLink';
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import axios from "axios";
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { Gravatar } from "@storyteller/components/src/elements/Gravatar";
|
||||
import { W2lInferenceJob } from "../../../../App";
|
||||
import { useParams, Link } from "react-router-dom";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { SessionW2lInferenceResultListFc } from "../../_common/SessionW2lInferenceResultsListFc";
|
||||
import { ReportDiscordLinkFc } from "../../_common/DiscordReportLinkFc";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
import { VisibleIconFc } from "../../_icons/VisibleIcon";
|
||||
import { HiddenIconFc } from "../../_icons/HiddenIcon";
|
||||
import { FrontendUrlConfig } from "../../../../common/FrontendUrlConfig";
|
||||
import {
|
||||
GetW2lTemplate,
|
||||
GetW2lTemplateIsErr,
|
||||
GetW2lTemplateIsOk,
|
||||
W2lTemplate,
|
||||
W2lTemplateLookupError,
|
||||
} from "../../../api/w2l/GetW2lTemplate";
|
||||
import { GetW2lTemplateUseCount } from "../../../api/w2l/GetW2lTemplateUseCount";
|
||||
import { BackLink } from "../../_common/BackLink";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { distance, delay, duration } from "../../../../data/animation";
|
||||
|
||||
const Fade = require("react-reveal/Fade");
|
||||
|
||||
interface EnqueueJobResponsePayload {
|
||||
success: boolean,
|
||||
inference_job_token?: string,
|
||||
success: boolean;
|
||||
inference_job_token?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
enqueueW2lJob: (jobToken: string) => void,
|
||||
w2lInferenceJobs: Array<W2lInferenceJob>,
|
||||
sessionWrapper: SessionWrapper;
|
||||
enqueueW2lJob: (jobToken: string) => void;
|
||||
w2lInferenceJobs: Array<W2lInferenceJob>;
|
||||
}
|
||||
|
||||
function W2lTemplateViewFc(props: Props) {
|
||||
let { templateSlug } : { templateSlug : string } = useParams();
|
||||
let { templateSlug }: { templateSlug: string } = useParams();
|
||||
|
||||
// Ajax
|
||||
const [w2lTemplate, setW2lTemplate] = useState<W2lTemplate|undefined>(undefined);
|
||||
const [w2lTemplateUseCount, setW2lTemplateUseCount] = useState<number|undefined>(undefined);
|
||||
const [w2lTemplate, setW2lTemplate] = useState<W2lTemplate | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [w2lTemplateUseCount, setW2lTemplateUseCount] = useState<
|
||||
number | undefined
|
||||
>(undefined);
|
||||
const [notFoundState, setNotFoundState] = useState<boolean>(false);
|
||||
|
||||
// Inference
|
||||
const [audioFile, setAudioFile] = useState<File|undefined>(undefined);
|
||||
const [audioFile, setAudioFile] = useState<File | undefined>(undefined);
|
||||
|
||||
// Moderation
|
||||
const [modApprovedFormValue, setModApprovedFormValue] = useState<boolean>(true);
|
||||
const [modApprovedFormValue, setModApprovedFormValue] =
|
||||
useState<boolean>(true);
|
||||
|
||||
const getTemplate = useCallback(async (templateSlug: string) => {
|
||||
const templateResponse = await GetW2lTemplate(templateSlug);
|
||||
const getTemplate = useCallback(
|
||||
async (templateSlug: string) => {
|
||||
const templateResponse = await GetW2lTemplate(templateSlug);
|
||||
|
||||
if (GetW2lTemplateIsOk(templateResponse)) {
|
||||
setW2lTemplate(templateResponse)
|
||||
if (GetW2lTemplateIsOk(templateResponse)) {
|
||||
setW2lTemplate(templateResponse);
|
||||
|
||||
let modApprovalState = templateResponse?.is_public_listing_approved;
|
||||
let modApprovalState = templateResponse?.is_public_listing_approved;
|
||||
|
||||
if (modApprovedFormValue === undefined || modApprovalState === null) {
|
||||
modApprovalState = true;
|
||||
if (modApprovedFormValue === undefined || modApprovalState === null) {
|
||||
modApprovalState = true;
|
||||
}
|
||||
|
||||
setModApprovedFormValue(modApprovalState);
|
||||
} else if (GetW2lTemplateIsErr(templateResponse)) {
|
||||
switch (templateResponse) {
|
||||
case W2lTemplateLookupError.NotFound:
|
||||
setNotFoundState(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setModApprovedFormValue(modApprovalState);
|
||||
|
||||
} else if (GetW2lTemplateIsErr(templateResponse)) {
|
||||
switch(templateResponse) {
|
||||
case W2lTemplateLookupError.NotFound:
|
||||
setNotFoundState(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [modApprovedFormValue]);
|
||||
},
|
||||
[modApprovedFormValue]
|
||||
);
|
||||
|
||||
const getTemplateUseCount = useCallback(async (templateToken) => {
|
||||
const count = await GetW2lTemplateUseCount(templateToken);
|
||||
setW2lTemplateUseCount(count || 0)
|
||||
setW2lTemplateUseCount(count || 0);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -76,19 +93,15 @@ function W2lTemplateViewFc(props: Props) {
|
||||
}, [templateSlug, getTemplate, getTemplateUseCount]);
|
||||
|
||||
if (notFoundState) {
|
||||
return (
|
||||
<h1 className="title is-1">Template not found</h1>
|
||||
);
|
||||
return <h1 className="title is-1">Template not found</h1>;
|
||||
}
|
||||
|
||||
if (!w2lTemplate) {
|
||||
return <div />
|
||||
return <div />;
|
||||
}
|
||||
|
||||
const handleAudioFileChange = (fileList: FileList|null) => {
|
||||
if (fileList === null
|
||||
|| fileList === undefined
|
||||
|| fileList.length < 1) {
|
||||
const handleAudioFileChange = (fileList: FileList | null) => {
|
||||
if (fileList === null || fileList === undefined || fileList.length < 1) {
|
||||
setAudioFile(undefined);
|
||||
}
|
||||
|
||||
@@ -96,7 +109,9 @@ function W2lTemplateViewFc(props: Props) {
|
||||
setAudioFile(file);
|
||||
};
|
||||
|
||||
const handleInferenceFormSubmit = (ev: React.FormEvent<HTMLFormElement>) : boolean => {
|
||||
const handleInferenceFormSubmit = (
|
||||
ev: React.FormEvent<HTMLFormElement>
|
||||
): boolean => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (audioFile === undefined) {
|
||||
@@ -110,46 +125,52 @@ function W2lTemplateViewFc(props: Props) {
|
||||
const templateToken = w2lTemplate!.template_token;
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append('audio', audioFile!);
|
||||
formData.append('template_token', templateToken);
|
||||
formData.append('uuid_idempotency_token', uuidv4()!);
|
||||
formData.append("audio", audioFile!);
|
||||
formData.append("template_token", templateToken);
|
||||
formData.append("uuid_idempotency_token", uuidv4()!);
|
||||
|
||||
const api = new ApiConfig();
|
||||
const endpointUrl = api.inferW2l();
|
||||
|
||||
// NB: Using 'axios' because 'fetch' was having problems with form-multipart
|
||||
// and then interpreting the resultant JSON. Maybe I didn't try hard enough?
|
||||
axios.post(endpointUrl, formData, { withCredentials: true })
|
||||
.then(res => res.data)
|
||||
.then(res => {
|
||||
console.log('w2l submitted');
|
||||
let response : EnqueueJobResponsePayload = res;
|
||||
axios
|
||||
.post(endpointUrl, formData, { withCredentials: true })
|
||||
.then((res) => res.data)
|
||||
.then((res) => {
|
||||
console.log("w2l submitted");
|
||||
let response: EnqueueJobResponsePayload = res;
|
||||
if (!response.success || response.inference_job_token === undefined) {
|
||||
return;
|
||||
}
|
||||
console.log('w2l enqueueing');
|
||||
console.log("w2l enqueueing");
|
||||
props.enqueueW2lJob(response.inference_job_token);
|
||||
});
|
||||
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
let object : string|undefined = undefined;
|
||||
|
||||
if (w2lTemplate?.maybe_image_object_name !== undefined && w2lTemplate?.maybe_image_object_name !== null) {
|
||||
let object: string | undefined = undefined;
|
||||
|
||||
if (
|
||||
w2lTemplate?.maybe_image_object_name !== undefined &&
|
||||
w2lTemplate?.maybe_image_object_name !== null
|
||||
) {
|
||||
object = w2lTemplate!.maybe_image_object_name;
|
||||
} else if (w2lTemplate?.maybe_video_object_name !== undefined && w2lTemplate?.maybe_video_object_name !== null) {
|
||||
} else if (
|
||||
w2lTemplate?.maybe_video_object_name !== undefined &&
|
||||
w2lTemplate?.maybe_video_object_name !== null
|
||||
) {
|
||||
object = w2lTemplate!.maybe_video_object_name;
|
||||
} else {
|
||||
}
|
||||
|
||||
let url = new BucketConfig().getGcsUrl(object);
|
||||
|
||||
let audioFilename = '(select a file)';
|
||||
if (audioFile !== undefined) {
|
||||
audioFilename = audioFile?.name;
|
||||
}
|
||||
// let audioFilename = "(select a file)";
|
||||
// if (audioFile !== undefined) {
|
||||
// audioFilename = audioFile?.name;
|
||||
// }
|
||||
|
||||
let modApprovalStatus = <span />;
|
||||
|
||||
@@ -157,8 +178,15 @@ function W2lTemplateViewFc(props: Props) {
|
||||
case null:
|
||||
modApprovalStatus = (
|
||||
<span>
|
||||
Not yet (ask for approval in our <a
|
||||
href="https://discord.gg/H72KFXm" target="_blank" rel="noopener noreferrer">Discord</a>)
|
||||
Not yet (ask for approval in our
|
||||
<a
|
||||
href="https://discord.gg/H72KFXm"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Discord
|
||||
</a>
|
||||
)
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
@@ -172,35 +200,61 @@ function W2lTemplateViewFc(props: Props) {
|
||||
|
||||
let moderatorRows = null;
|
||||
|
||||
if (props.sessionWrapper.canDeleteOtherUsersW2lResults() || props.sessionWrapper.canDeleteOtherUsersW2lTemplates()) {
|
||||
if (
|
||||
props.sessionWrapper.canDeleteOtherUsersW2lResults() ||
|
||||
props.sessionWrapper.canDeleteOtherUsersW2lTemplates()
|
||||
) {
|
||||
moderatorRows = (
|
||||
<>
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<br />
|
||||
<h4 className="subtitle is-4"> Moderator Details </h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creator is banned</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.creator_is_banned ? "banned" : "good standing" }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Create IP address</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.creator_ip_address_creation || "server error"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Update IP address</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.creator_ip_address_last_update || "server error"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Mod deleted at (UTC)</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.mod_deleted_at || "not deleted"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>User deleted at (UTC)</th>
|
||||
<td>{w2lTemplate?.maybe_moderator_fields?.user_deleted_at || "not deleted"}</td>
|
||||
</tr>
|
||||
<Fade bottom duration={duration} distance={distance} delay={delay}>
|
||||
<div className="container-panel pt-3 pb-5">
|
||||
<div className="panel p-3 p-lg-4">
|
||||
<h2 className="panel-title fw-bold">Moderator Details</h2>
|
||||
<div className="py-6">
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Creator is banned</th>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields?.creator_is_banned
|
||||
? "banned"
|
||||
: "good standing"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Create IP address</th>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields
|
||||
?.creator_ip_address_creation || "server error"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Update IP address</th>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields
|
||||
?.creator_ip_address_last_update || "server error"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Mod deleted at (UTC)</th>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
"not deleted"}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>User deleted at (UTC)</th>
|
||||
<td>
|
||||
{w2lTemplate?.maybe_moderator_fields?.user_deleted_at ||
|
||||
"not deleted"}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -208,21 +262,23 @@ function W2lTemplateViewFc(props: Props) {
|
||||
let creatorLink = <span />;
|
||||
|
||||
if (!!w2lTemplate?.creator_display_name) {
|
||||
const creatorUrl = FrontendUrlConfig.userProfilePage(w2lTemplate?.creator_username);
|
||||
const creatorUrl = FrontendUrlConfig.userProfilePage(
|
||||
w2lTemplate?.creator_username
|
||||
);
|
||||
creatorLink = (
|
||||
<span>
|
||||
<Gravatar
|
||||
size={15}
|
||||
username={w2lTemplate.creator_display_name || ""}
|
||||
email_hash={w2lTemplate.creator_gravatar_hash || ""}
|
||||
/>
|
||||
username={w2lTemplate.creator_display_name || ""}
|
||||
email_hash={w2lTemplate.creator_gravatar_hash || ""}
|
||||
/>
|
||||
|
||||
<Link to={creatorUrl}>{w2lTemplate.creator_display_name}</Link>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
let humanUseCount : string | number = 'Fetching...';
|
||||
let humanUseCount: string | number = "Fetching...";
|
||||
|
||||
if (w2lTemplateUseCount !== undefined && w2lTemplateUseCount !== null) {
|
||||
humanUseCount = w2lTemplateUseCount;
|
||||
@@ -230,14 +286,20 @@ function W2lTemplateViewFc(props: Props) {
|
||||
|
||||
let editButton = <span />;
|
||||
|
||||
if (props.sessionWrapper.canEditTtsModelByUserToken(w2lTemplate?.creator_user_token)) {
|
||||
if (
|
||||
props.sessionWrapper.canEditTtsModelByUserToken(
|
||||
w2lTemplate?.creator_user_token
|
||||
)
|
||||
) {
|
||||
editButton = (
|
||||
<>
|
||||
<br />
|
||||
<Link
|
||||
className={"button is-large is-info is-fullwidth"}
|
||||
<Link
|
||||
className={"btn btn-secondary w-100"}
|
||||
to={FrontendUrlConfig.w2lTemplateEditPage(templateSlug)}
|
||||
>Edit Template Details</Link>
|
||||
>
|
||||
<FontAwesomeIcon icon={faEdit} className="me-2" />
|
||||
Edit Template Details
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -247,43 +309,54 @@ function W2lTemplateViewFc(props: Props) {
|
||||
if (props.sessionWrapper.canApproveW2lTemplates()) {
|
||||
const currentlyApproved = w2lTemplate?.is_public_listing_approved;
|
||||
|
||||
const approveButtonTitle = currentlyApproved? "Unapprove Template?" : "Approve Template?";
|
||||
const approveButtonTitle = currentlyApproved
|
||||
? "Unapprove Template?"
|
||||
: "Approve Template?";
|
||||
|
||||
const approveButtonCss = currentlyApproved?
|
||||
"button is-danger is-light is-large is-fullwidth" :
|
||||
"button is-info is-light is-large is-fullwidth";
|
||||
const approveButtonCss = currentlyApproved
|
||||
? "btn btn-primary w-100"
|
||||
: "btn btn-primary w-100";
|
||||
|
||||
approveButton = (
|
||||
<>
|
||||
<br />
|
||||
<Link
|
||||
<Link
|
||||
className={approveButtonCss}
|
||||
to={FrontendUrlConfig.w2lTemplateApprovalPage(templateSlug)}
|
||||
>{approveButtonTitle}</Link>
|
||||
>
|
||||
{approveButtonTitle}
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let deleteButton = <span />;
|
||||
|
||||
if (props.sessionWrapper.canDeleteTtsModelByUserToken(w2lTemplate?.creator_user_token)) {
|
||||
if (
|
||||
props.sessionWrapper.canDeleteTtsModelByUserToken(
|
||||
w2lTemplate?.creator_user_token
|
||||
)
|
||||
) {
|
||||
const currentlyDeleted =
|
||||
!!w2lTemplate?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
!!w2lTemplate?.maybe_moderator_fields?.user_deleted_at;
|
||||
|
||||
const currentlyDeleted = !!w2lTemplate?.maybe_moderator_fields?.mod_deleted_at ||
|
||||
!!w2lTemplate?.maybe_moderator_fields?.user_deleted_at;
|
||||
const deleteButtonTitle = currentlyDeleted
|
||||
? "Undelete Template?"
|
||||
: "Delete Template?";
|
||||
|
||||
const deleteButtonTitle = currentlyDeleted ? "Undelete Template?" : "Delete Template?";
|
||||
|
||||
const deleteButtonCss = currentlyDeleted ?
|
||||
"button is-warning is-large is-fullwidth" :
|
||||
"button is-danger is-large is-fullwidth";
|
||||
const deleteButtonCss = currentlyDeleted
|
||||
? "btn btn-secondary w-100"
|
||||
: "btn btn-destructive w-100";
|
||||
|
||||
deleteButton = (
|
||||
<>
|
||||
<br />
|
||||
<Link
|
||||
<Link
|
||||
className={deleteButtonCss}
|
||||
to={FrontendUrlConfig.w2lTemplateDeletePage(templateSlug)}
|
||||
>{deleteButtonTitle}</Link>
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} className="me-2" />
|
||||
{deleteButtonTitle}
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -293,154 +366,188 @@ function W2lTemplateViewFc(props: Props) {
|
||||
if (!!w2lTemplate?.description_rendered_html) {
|
||||
templateDescription = (
|
||||
<>
|
||||
<h4 className="title is-4"> Model Description </h4>
|
||||
<div
|
||||
className="profile content is-medium"
|
||||
dangerouslySetInnerHTML={{__html: w2lTemplate?.description_rendered_html || ""}}
|
||||
/>
|
||||
<Fade bottom duration={duration} distance={distance} delay={delay}>
|
||||
<div className="container-panel pt-3 pb-5">
|
||||
<div className="panel p-3 p-lg-4">
|
||||
<h2 className="panel-title fw-bold">Model Description</h2>
|
||||
<div className="py-6">
|
||||
<div
|
||||
className="profile content is-medium"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: w2lTemplate?.description_rendered_html || "",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const resultVisibility = w2lTemplate?.creator_set_visibility === 'hidden' ?
|
||||
<span>Hidden <HiddenIconFc /></span> :
|
||||
<span>Public <VisibleIconFc /></span> ;
|
||||
const resultVisibility =
|
||||
w2lTemplate?.creator_set_visibility === "hidden" ? (
|
||||
<span>
|
||||
Hidden <HiddenIconFc />
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
Public <VisibleIconFc />
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title is-1"> Video lip sync template </h1>
|
||||
<Fade bottom cascade duration={duration} distance={distance}>
|
||||
<div className="container py-5 px-md-4 px-lg-5 px-xl-3">
|
||||
<h1 className="display-5 fw-bold">Video Lip Sync Template</h1>
|
||||
<h4>Template: {w2lTemplate?.title}</h4>
|
||||
<div className="pt-3">
|
||||
<BackLink
|
||||
link={FrontendUrlConfig.w2lListPage()}
|
||||
text="Back to all templates"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
|
||||
<div className="content">
|
||||
<p>
|
||||
<BackLink link={FrontendUrlConfig.w2lListPage()} text="Back to all templates" />
|
||||
</p>
|
||||
</div>
|
||||
<Fade
|
||||
bottom
|
||||
cascade
|
||||
duration={duration}
|
||||
distance={distance}
|
||||
delay={delay}
|
||||
>
|
||||
<form onSubmit={handleInferenceFormSubmit}>
|
||||
<div className="container-panel pt-4 pb-5">
|
||||
<div className="panel p-3 p-lg-4">
|
||||
<h2 className="panel-title fw-bold">Upload Audio</h2>
|
||||
<div className="py-6">
|
||||
<div className="d-flex flex-column flex-lg-row gap-5">
|
||||
<div className="d-flex flex-column gap-4 w-100 w-lg-50">
|
||||
<p>
|
||||
Upload audio from FakeYou or any other source (eg. music),
|
||||
then submit. You'll get a beautifully lipsynced video.
|
||||
</p>
|
||||
<div>
|
||||
<label className="sub-title">Select an audio file</label>
|
||||
<input
|
||||
id="formFileLg"
|
||||
type="file"
|
||||
name="audio"
|
||||
className="form-control form-control-lg"
|
||||
onChange={(e) => handleAudioFileChange(e.target.files)}
|
||||
/>
|
||||
</div>
|
||||
<button className="btn btn-primary w-100 mt-3">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
<div className="d-flex w-100 w-lg-50 justify-content-center">
|
||||
<div className="template-preview ">
|
||||
<img
|
||||
src={url}
|
||||
alt="template preview"
|
||||
className="img-fluid rounded"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="content is-size-4">
|
||||
<p>
|
||||
Upload audio from FakeYou or any other source (eg. music),
|
||||
then submit. You'll get a beautifully lipsynced video.
|
||||
</p>
|
||||
</div>
|
||||
<div>{templateDescription}</div>
|
||||
|
||||
<form onSubmit={handleInferenceFormSubmit}>
|
||||
|
||||
<div className="upload-box">
|
||||
<div className="file has-name is-large">
|
||||
<label className="file-label">
|
||||
<input
|
||||
type="file"
|
||||
name="audio"
|
||||
className="file-input"
|
||||
onChange={ (e) => handleAudioFileChange(e.target.files) }
|
||||
/>
|
||||
<span className="file-cta">
|
||||
<UploadIcon />
|
||||
<span className="file-label">
|
||||
Choose audio file…
|
||||
</span>
|
||||
</span>
|
||||
<span className="file-name">
|
||||
{audioFilename}
|
||||
</span>
|
||||
</label>
|
||||
<div className="container-panel pt-3 pb-5">
|
||||
<div className="panel p-3 p-lg-4">
|
||||
<h2 className="panel-title fw-bold">Template Details</h2>
|
||||
<div className="py-6">
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<td>{w2lTemplate?.title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>{creatorLink}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Use count</th>
|
||||
<td>{humanUseCount}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Visibility</th>
|
||||
<td>{resultVisibility}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Is public listing approved?</th>
|
||||
<td>{modApprovalStatus}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button className="button is-large is-fullwidth is-success">Submit</button>
|
||||
<div className="container-panel pt-3 pb-5">
|
||||
<div className="panel p-3 p-lg-4">
|
||||
<h2 className="panel-title fw-bold">Media Details</h2>
|
||||
<div className="py-6">
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Media type</th>
|
||||
<td>{w2lTemplate?.template_type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Dimensions</th>
|
||||
<td>
|
||||
{w2lTemplate?.frame_width} x {w2lTemplate?.frame_height}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration (milliseconds)</th>
|
||||
<td>{w2lTemplate?.duration_millis}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Created at (UTC)</th>
|
||||
<td>{w2lTemplate?.created_at}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Updated at (UTC)</th>
|
||||
<td>{w2lTemplate?.updated_at}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<div>{moderatorRows}</div>
|
||||
|
||||
<br />
|
||||
<div className="container">
|
||||
<div className="d-flex flex-column flex-lg-row gap-3">
|
||||
{editButton}
|
||||
{approveButton}
|
||||
{deleteButton}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="template-preview">
|
||||
<img src={url} alt="template preview" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="container pt-4 pb-5">
|
||||
<ReportDiscordLinkFc />
|
||||
</div>
|
||||
</div>
|
||||
</Fade>
|
||||
|
||||
<br />
|
||||
|
||||
{templateDescription}
|
||||
|
||||
<table className="table is-fullwidth">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<h4 className="subtitle is-4"> Template Details </h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<td>{w2lTemplate?.title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>
|
||||
{creatorLink}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Use count</th>
|
||||
<td>{humanUseCount}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Visibility</th>
|
||||
<td>{resultVisibility}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Is public listing approved?</th>
|
||||
<td>{modApprovalStatus}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<br />
|
||||
<h4 className="subtitle is-4"> Media Details </h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Media type</th>
|
||||
<td>{w2lTemplate?.template_type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Dimensions</th>
|
||||
<td>{w2lTemplate?.frame_width} x {w2lTemplate?.frame_height}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration (milliseconds)</th>
|
||||
<td>{w2lTemplate?.duration_millis}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Created at (UTC)</th>
|
||||
<td>{w2lTemplate?.created_at}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Updated at (UTC)</th>
|
||||
<td>{w2lTemplate?.updated_at}</td>
|
||||
</tr>
|
||||
|
||||
{moderatorRows}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{editButton}
|
||||
|
||||
{approveButton}
|
||||
|
||||
{deleteButton}
|
||||
|
||||
<br />
|
||||
<SessionW2lInferenceResultListFc w2lInferenceJobs={props.w2lInferenceJobs} />
|
||||
|
||||
<div className="content">
|
||||
<p>
|
||||
<BackLink link={FrontendUrlConfig.w2lListPage()} text="Back to all templates" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<ReportDiscordLinkFc />
|
||||
<SessionW2lInferenceResultListFc
|
||||
w2lInferenceJobs={props.w2lInferenceJobs}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { W2lTemplateViewFc };
|
||||
Reference in new issue
Block a user