This commit is contained in:
BFlat authored and Brandon Thomas committed 2022-07-21 23:39:14 -04:00
1 parent 5d6ab1fef0
commit df569a9180
6 files changed
+462 -281

No files matched your search

@@ -335,6 +335,14 @@ styles with important tags:
.content p:not(:last-child) {
margin-bottom: 0.5rem;
}
tbody tr {
@for $i from 1 through 12 {
&:nth-of-type(#{$i}) {
animation-delay: $i * 20ms !important;
}
}
}
}
@media (min-width: 992px) {
+107 -48
View File
@@ -5,7 +5,7 @@ import { MigrationTopNavSession } from "../../migration/MigrationTopNav_Session"
import { FrontendUrlConfig } from "../../common/FrontendUrlConfig";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faGavel,
faGavel,
faGrinBeamSweat,
faMicrophone,
faVolumeUp,
@@ -35,7 +35,8 @@ function NewTopNavFc(props: Props) {
myDataLink = `/profile/${username}`;
}
const [mobileHamburgerIsActive, setMobileHamburgerIsActive] = useState<boolean>(false);
const [mobileHamburgerIsActive, setMobileHamburgerIsActive] =
useState<boolean>(false);
const toggleHamburger = () => {
setMobileHamburgerIsActive(!mobileHamburgerIsActive);
@@ -47,18 +48,29 @@ function NewTopNavFc(props: Props) {
setMobileHamburgerIsActive(false);
};
const navbarClasses = mobileHamburgerIsActive ? "navbar-menu is-active" : "navbar-menu";
const navbarBurgerClasses = mobileHamburgerIsActive ? "navbar-burger is-active" : "navbar-burger";
const navbarClasses = mobileHamburgerIsActive
? "navbar-menu is-active"
: "navbar-menu";
const navbarBurgerClasses = mobileHamburgerIsActive
? "navbar-burger is-active"
: "navbar-burger";
if (!USE_REFRESH) {
return (
<>
<nav className="navbar is-transparent padding-bottom-1em">
<div className="navbar-brand">
<Link className="navbar-item" to="/">
<img src="/fakeyou/fakeyou-nav-logo-mascot.webp" alt="FakeYou: Cartoon and Celebrity Text to Speech" />
<div className="navbar-brand">
<Link className="navbar-item" to="/">
<img
src="/fakeyou/fakeyou-nav-logo-mascot.webp"
alt="FakeYou: Cartoon and Celebrity Text to Speech"
/>
</Link>
<div className={navbarBurgerClasses} data-target="navbarExampleTransparentExample" onClick={() => toggleHamburger()}>
<div
className={navbarBurgerClasses}
data-target="navbarExampleTransparentExample"
onClick={() => toggleHamburger()}
>
<span></span>
<span></span>
<span></span>
@@ -67,93 +79,136 @@ function NewTopNavFc(props: Props) {
<div id="navbarExampleTransparentExample" className={navbarClasses}>
<div className="navbar-start">
<Link to={FrontendUrlConfig.cloneRequestPage()}
<Link
to={FrontendUrlConfig.cloneRequestPage()}
className="navbar-item "
onClick={() => closeHamburger()}
>Clone&nbsp;My&nbsp;Voice!</Link>
>
Clone&nbsp;My&nbsp;Voice!
</Link>
{/* DROPDOWN: CREATE */}
<div className="navbar-item has-dropdown is-hoverable">
<Link to={FrontendUrlConfig.indexPage()}
<Link
to={FrontendUrlConfig.indexPage()}
className="navbar-link"
onClick={() => closeHamburger()}
>{t('coreUi.topNav.create')}</Link>
>
{t("coreUi.topNav.create")}
</Link>
<div className="navbar-dropdown is-boxed">
{/* NB: There's an "is-active" class that looks nice. */}
<Link to="/"
<Link
to="/"
className="navbar-item"
onClick={() => closeHamburger()}
>TTS</Link>
>
TTS
</Link>
<Link to="/video"
<Link
to="/video"
className="navbar-item"
onClick={() => closeHamburger()}
>{t('coreUi.topNav.video')}</Link>
>
{t("coreUi.topNav.video")}
</Link>
</div>
</div>
{/* DROPDOWN: COMMUNITY */}
<div className="navbar-item has-dropdown is-hoverable">
<Link to={myDataLink}
<Link
to={myDataLink}
className="navbar-link"
onClick={() => closeHamburger()}
>{t('coreUi.topNav.community')}</Link>
>
{t("coreUi.topNav.community")}
</Link>
<div className="navbar-dropdown is-boxed">
{/* NB: There's an "is-active" class that looks nice. */}
<Link to="/contribute"
<Link
to="/contribute"
className="navbar-item"
onClick={() => closeHamburger()}
><FontAwesomeIcon icon={faUpload} />&nbsp;&nbsp;{t('coreUi.topNav.contributeUpload')}</Link>
>
<FontAwesomeIcon icon={faUpload} />
&nbsp;&nbsp;{t("coreUi.topNav.contributeUpload")}
</Link>
<Link to="/leaderboard"
<Link
to="/leaderboard"
className="navbar-item"
onClick={() => closeHamburger()}
><FontAwesomeIcon icon={faTrophy} />&nbsp;&nbsp;{t('coreUi.topNav.leaderboard')}</Link>
>
<FontAwesomeIcon icon={faTrophy} />
&nbsp;&nbsp;{t("coreUi.topNav.leaderboard")}
</Link>
<Link to={FrontendUrlConfig.patronsPage()}
<Link
to={FrontendUrlConfig.patronsPage()}
className="navbar-item"
onClick={() => closeHamburger()}
><FontAwesomeIcon icon={faPatreon}/>&nbsp;&nbsp;{t('coreUi.topNav.patrons')}</Link>
>
<FontAwesomeIcon icon={faPatreon} />
&nbsp;&nbsp;{t("coreUi.topNav.patrons")}
</Link>
<Link to="/firehose"
<Link
to="/firehose"
className="navbar-item"
onClick={() => closeHamburger()}
><FontAwesomeIcon icon={faList} />&nbsp;&nbsp;{t('coreUi.topNav.feed')}</Link>
>
<FontAwesomeIcon icon={faList} />
&nbsp;&nbsp;{t("coreUi.topNav.feed")}
</Link>
<hr className="navbar-divider" />
<Link to={myDataLink}
<Link
to={myDataLink}
className="navbar-item"
onClick={() => closeHamburger()}
><FontAwesomeIcon icon={faUser} />&nbsp;&nbsp;{t('coreUi.topNav.myData')}</Link>
>
<FontAwesomeIcon icon={faUser} />
&nbsp;&nbsp;{t("coreUi.topNav.myData")}
</Link>
<hr className="navbar-divider" />
<Link to={FrontendUrlConfig.aboutUsPage()}
<Link
to={FrontendUrlConfig.aboutUsPage()}
className="navbar-item"
onClick={() => closeHamburger()}
><FontAwesomeIcon icon={faGrinBeamSweat} />&nbsp;&nbsp;{t('coreUi.topNav.aboutUs')}</Link>
>
<FontAwesomeIcon icon={faGrinBeamSweat} />
&nbsp;&nbsp;{t("coreUi.topNav.aboutUs")}
</Link>
<Link to={FrontendUrlConfig.termsPage()}
<Link
to={FrontendUrlConfig.termsPage()}
className="navbar-item"
onClick={() => closeHamburger()}
><FontAwesomeIcon icon={faGavel} />&nbsp;&nbsp;{t('coreUi.topNav.terms')}</Link>
>
<FontAwesomeIcon icon={faGavel} />
&nbsp;&nbsp;{t("coreUi.topNav.terms")}
</Link>
</div>
</div>
<a href={FrontendUrlConfig.developerDocs()}
<a
href={FrontendUrlConfig.developerDocs()}
className="navbar-item"
onClick={() => closeHamburger()}
>{t('coreUi.topNav.developers')}</a>
>
{t("coreUi.topNav.developers")}
</a>
</div>
<div className="navbar-end">
@@ -165,7 +220,7 @@ function NewTopNavFc(props: Props) {
enableAlpha={true}
querySessionAction={props.querySessionCallback}
closeHamburgerAction={() => closeHamburger()}
/>
/>
</p>
</div>
</div>
@@ -211,7 +266,7 @@ function NewTopNavFc(props: Props) {
<div className="container">
<Link className="navbar-brand me-5 pr-8" to="/">
<img
src="fakeyou/FakeYou-Logo.png"
src="/fakeyou/FakeYou-Logo.png"
alt="FakeYou: Cartoon and Celebrity Text to Speech"
height="34"
/>
@@ -232,9 +287,13 @@ function NewTopNavFc(props: Props) {
aria-labelledby="offcanvasNavbar2Label"
>
<div className="offcanvas-header">
<Link data-bs-toggle="offcanvas" className="navbar-brand me-5 pr-8" to="/">
<Link
data-bs-toggle="offcanvas"
className="navbar-brand me-5 pr-8"
to="/"
>
<img
src="fakeyou/FakeYou-Logo.png"
src="/fakeyou/FakeYou-Logo.png"
alt="FakeYou: Cartoon and Celebrity Text to Speech"
height="34"
/>
@@ -326,7 +385,7 @@ function NewTopNavFc(props: Props) {
</Link>
</li>
<li data-bs-toggle="offcanvas">
<Link className="dropdown-item" to="/firehose">
<Link className="dropdown-item" to="/firehose">
<FontAwesomeIcon icon={faList} className="me-2" />
Feed
</Link>
@@ -357,11 +416,7 @@ function NewTopNavFc(props: Props) {
<li className="nav-item d-lg-none">
{/* TODO(echelon): Fix the build warnings about href not being accessible. */}
<a
className="nav-link"
aria-current="page"
href="/"
>
<a className="nav-link" aria-current="page" href="/">
Developers
</a>
</li>
@@ -385,7 +440,11 @@ function NewTopNavFc(props: Props) {
</li>
</ul>
<div className="d-grid gap-2 d-flex justify-content-start align-items-center pt-4 ps-3 pt-lg-0 ps-lg-0">
<Link data-bs-toggle="offcanvas" className="nav-login me-3" to="/login">
<Link
data-bs-toggle="offcanvas"
className="nav-login me-3"
to="/login"
>
Login
</Link>
<Link data-bs-toggle="offcanvas" to="/signup">
@@ -297,14 +297,14 @@ function ProfileFc(props: Props) {
</div>
<div
className="container content mb-5 text-center text-lg-start px-4 px-md-5 px-lg-3"
className="container content mb-0 mb-lg-5 text-center text-lg-start px-4 px-md-5 px-lg-3"
dangerouslySetInnerHTML={{
__html: userData.profile_rendered_html || "",
}}
/>
<div className="container-panel py-5">
<div className="panel p-3 p-lg-4 mt-5 mt-lg-0">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">
<FontAwesomeIcon icon={faAward} className="me-3" />
Badges{" "}
@@ -315,7 +315,7 @@ function ProfileFc(props: Props) {
</div>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4 mt-5 mt-lg-0">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">
<FontAwesomeIcon icon={faVolumeHigh} className="me-3" />
TTS Results
@@ -327,7 +327,7 @@ function ProfileFc(props: Props) {
</div>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4 mt-5 mt-lg-0">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">
<FontAwesomeIcon icon={faVideo} className="me-3" />
Lipsync Results
@@ -1,88 +1,100 @@
import React, { useState, useEffect, useCallback } from 'react';
import { ApiConfig, ListTtsInferenceResultsForUserArgs } from '@storyteller/components';
import { Link } from 'react-router-dom';
import { formatDistance } from 'date-fns';
import { VisibleIconFc } from '../../_icons/VisibleIcon';
import { HiddenIconFc } from '../../_icons/HiddenIcon';
import { PlayCircleIcon } from '../../_icons/PlayCircleIcon';
import React, { useState, useEffect, useCallback } from "react";
import {
ApiConfig,
ListTtsInferenceResultsForUserArgs,
} from "@storyteller/components";
import { Link } from "react-router-dom";
import { formatDistance } from "date-fns";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faCirclePlay,
faArrowLeft,
faArrowRight,
faEye,
faEyeSlash,
} from "@fortawesome/free-solid-svg-icons";
const Fade = require("react-reveal/Fade");
interface TtsInferenceResultListResponsePayload {
success: boolean,
results: Array<TtsInferenceResult>,
cursor_next: string | null | undefined,
cursor_previous: string | null | undefined,
success: boolean;
results: Array<TtsInferenceResult>;
cursor_next: string | null | undefined;
cursor_previous: string | null | undefined;
}
interface TtsInferenceResult {
tts_result_token: string,
tts_result_token: string;
tts_model_token: string,
tts_model_title: string,
tts_model_token: string;
tts_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_user_token?: string;
maybe_creator_username?: string;
maybe_creator_display_name?: string;
maybe_creator_result_id: number | null,
maybe_creator_result_id: number | null;
file_size_bytes: number,
duration_millis: number,
file_size_bytes: number;
duration_millis: number;
visibility: string,
visibility: string;
created_at: string,
updated_at: string,
created_at: string;
updated_at: string;
}
interface Props {
username: string,
username: string;
}
function ProfileTtsInferenceResultsListFc(props: Props) {
const [ttsResults, setTtsResults] = useState<Array<TtsInferenceResult>>([]);
const [nextCursor, setNextCursor] = useState<string|null>(null);
const [previousCursor, setPreviousCursor] = useState<string|null>(null);
const [nextCursor, setNextCursor] = useState<string | null>(null);
const [previousCursor, setPreviousCursor] = useState<string | null>(null);
const getPage = useCallback((cursor : string|null, reverse: boolean) => {
let args : ListTtsInferenceResultsForUserArgs = {
username: props.username,
limit: 25,
};
const getPage = useCallback(
(cursor: string | null, reverse: boolean) => {
let args: ListTtsInferenceResultsForUserArgs = {
username: props.username,
limit: 25,
};
if (cursor !== null) {
args.cursor = cursor;
if (reverse) {
args.cursor_is_reversed = true;
}
}
const api = new ApiConfig();
const endpointUrl = api.listTtsInferenceResultsForUser(args);
fetch(endpointUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include',
})
.then(res => res.json())
.then(res => {
const modelsResponse : TtsInferenceResultListResponsePayload = res;
if (!modelsResponse.success) {
return;
if (cursor !== null) {
args.cursor = cursor;
if (reverse) {
args.cursor_is_reversed = true;
}
}
setTtsResults(modelsResponse.results)
setNextCursor(modelsResponse.cursor_next || null)
setPreviousCursor(modelsResponse.cursor_previous || null)
})
.catch(e => {
});
}, [props.username]);
const api = new ApiConfig();
const endpointUrl = api.listTtsInferenceResultsForUser(args);
fetch(endpointUrl, {
method: "GET",
headers: {
Accept: "application/json",
},
credentials: "include",
})
.then((res) => res.json())
.then((res) => {
const modelsResponse: TtsInferenceResultListResponsePayload = res;
if (!modelsResponse.success) {
return;
}
setTtsResults(modelsResponse.results);
setNextCursor(modelsResponse.cursor_next || null);
setPreviousCursor(modelsResponse.cursor_previous || null);
})
.catch((e) => {});
},
[props.username]
);
useEffect(() => {
getPage(null, false);
@@ -90,35 +102,45 @@ function ProfileTtsInferenceResultsListFc(props: Props) {
const now = new Date();
let rows : Array<JSX.Element> = [];
ttsResults.forEach(result => {
let rows: Array<JSX.Element> = [];
ttsResults.slice(0, 10).forEach((result) => {
const duration_seconds = result.duration_millis / 1000;
const inferenceLink = `/tts/result/${result.tts_result_token}`;
const modelLink = `/tts/${result.tts_model_token}`;
const text = result.raw_inference_text.length < 5 ? `Result: ${result.raw_inference_text}` : result.raw_inference_text;
const text =
result.raw_inference_text.length < 5
? `Result: ${result.raw_inference_text}`
: result.raw_inference_text;
const createTime = new Date(result.created_at);
const relativeCreateTime = formatDistance(createTime, now, { addSuffix: true });
const relativeCreateTime = formatDistance(createTime, now, {
addSuffix: true,
});
const visibilityIcon = (result.visibility === 'public') ? <VisibleIconFc /> : <HiddenIconFc /> ;
const visibilityIcon =
result.visibility === "public" ? (
<FontAwesomeIcon icon={faEye} />
) : (
<FontAwesomeIcon icon={faEyeSlash} />
);
rows.push(
<tr key={result.tts_result_token}>
<td>{result.maybe_creator_result_id}</td>
<td>{visibilityIcon}</td>
<th className="overflow-fix">
<Link to={inferenceLink}>
<PlayCircleIcon title="play" />
&nbsp;
<FontAwesomeIcon icon={faCirclePlay} className="me-2" />
{text}
</Link>
&nbsp;
</th>
<th><Link to={modelLink}>Model: {result.tts_model_title}</Link></th>
<th>
<Link to={modelLink}>Model: {result.tts_model_title}</Link>
</th>
<td>{duration_seconds} s</td>
<td>{relativeCreateTime}</td>
</tr>
@@ -126,40 +148,70 @@ function ProfileTtsInferenceResultsListFc(props: Props) {
});
// Disable if there is no "next" or "previous" cursor.
// However, let the buttosn show up if there are no results (empty payload)
// However, let the buttosn show up if there are no results (empty payload)
// to get unstuck. Come up with a better fix for this.
let prevDisabled = !previousCursor && rows.length !== 0;
let nextDisabled = !nextCursor && rows.length !== 0;
return (
<div>
<table className="table">
<thead>
<tr>
<th><abbr title="Number">#</abbr></th>
<th><abbr title="Visibility"><VisibleIconFc /></abbr></th>
<th><abbr title="Download">Download &amp; Play Link</abbr></th>
<th><abbr title="Model">Model</abbr></th>
<th><abbr title="Duration">Duration</abbr></th>
<th><abbr title="Created">Creation Time</abbr></th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div className="table-responsive">
<table className="table">
<thead>
<tr>
<th>
<abbr title="Number">#</abbr>
</th>
<th>
<abbr title="Visibility">
<FontAwesomeIcon icon={faEye} />
</abbr>
</th>
<th>
<abbr title="Download">Download &amp; Play Link</abbr>
</th>
<th>
<abbr title="Model">Model</abbr>
</th>
<th>
<abbr title="Duration">Duration</abbr>
</th>
<th>
<abbr title="Created">Creation Time</abbr>
</th>
</tr>
</thead>
<Fade cascade bottom duration="200" distance="10px">
<tbody>{rows}</tbody>
</Fade>
</table>
</div>
<p>
Note: Results marked public (<VisibleIconFc />) are visible by anyone visiting your profile.
<p className="text-center py-3">
Note: Results marked public (<FontAwesomeIcon icon={faEye} />) are
visible by anyone visiting your profile.
{/*You can change this by editing results. You can also set a default preference for new results.*/}
</p>
<div className="columns is-centered">
<button className="button is-info" onClick={() => getPage(previousCursor, true)} disabled={prevDisabled}>&lt; Get newer</button> &nbsp;
<button className="button is-info" onClick={() => getPage(nextCursor, false)} disabled={nextDisabled}>Get older&gt;</button>
<div className="justify-content-center d-flex gap-3">
<button
className="btn btn-secondary"
onClick={() => getPage(previousCursor, true)}
disabled={prevDisabled}
>
<FontAwesomeIcon icon={faArrowLeft} className="me-2" /> Get newer
</button>
<button
className="btn btn-secondary"
onClick={() => getPage(nextCursor, false)}
disabled={nextDisabled}
>
Get older
<FontAwesomeIcon icon={faArrowRight} className="ms-2" />
</button>
</div>
</div>
)
);
}
export { ProfileTtsInferenceResultsListFc };
@@ -1,26 +1,26 @@
import React, { useState, useEffect } from 'react';
import { ApiConfig } from '@storyteller/components';
import { Link } from 'react-router-dom';
import { formatDistance } from 'date-fns';
import React, { useState, useEffect } from "react";
import { ApiConfig } from "@storyteller/components";
import { Link } from "react-router-dom";
import { formatDistance } from "date-fns";
interface TtsModelListResponsePayload {
success: boolean,
models: Array<TtsModel>,
success: boolean;
models: Array<TtsModel>;
}
interface TtsModel {
model_token: string,
tts_model_type: string,
title: string,
updatable_slug: string,
model_token: string;
tts_model_type: string;
title: string;
updatable_slug: string;
// TODO: No need for "creator_*" fields. Remove them from backend.
is_mod_disabled: boolean,
created_at: string,
updated_at: string,
is_mod_disabled: boolean;
created_at: string;
updated_at: string;
}
interface Props {
username: string,
username: string;
}
function ProfileTtsModelListFc(props: Props) {
@@ -31,40 +31,44 @@ function ProfileTtsModelListFc(props: Props) {
const endpointUrl = api.listTtsModelsForUser(props.username);
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 : TtsModelListResponsePayload = res;
if (!modelsResponse.success) {
return;
}
.then((res) => res.json())
.then((res) => {
const modelsResponse: TtsModelListResponsePayload = res;
if (!modelsResponse.success) {
return;
}
setTtsModels(modelsResponse.models)
})
.catch(e => {
});
setTtsModels(modelsResponse.models);
})
.catch((e) => {});
}, [props.username]); // NB: Empty array dependency sets to run ONLY on mount
const now = new Date();
let rows : Array<JSX.Element> = [];
ttsModels.forEach(model => {
const modelTitle = model.title.length < 5 ? `Model: ${model.title}` : model.title;
let rows: Array<JSX.Element> = [];
ttsModels.slice(0, 10).forEach((model) => {
const modelTitle =
model.title.length < 5 ? `Model: ${model.title}` : model.title;
const modelLink = `/tts/${model.model_token}`;
const createTime = new Date(model.created_at);
const relativeCreateTime = formatDistance(createTime, now, { addSuffix: true });
const relativeCreateTime = formatDistance(createTime, now, {
addSuffix: true,
});
rows.push(
<tr key={model.model_token}>
<th><Link to={modelLink}>{modelTitle}</Link></th>
<th>
<Link to={modelLink}>{modelTitle}</Link>
</th>
<td>{relativeCreateTime}</td>
</tr>
);
@@ -75,16 +79,19 @@ function ProfileTtsModelListFc(props: Props) {
<table className="table">
<thead>
<tr>
<th><abbr title="Model Name">Model Name</abbr></th>
<th><abbr title="Creation Date">Creation Time</abbr></th>
<th>
<abbr title="Model Name">Model Name</abbr>
</th>
<th>
<abbr title="Creation Date">Creation Time</abbr>
</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
<tbody>{rows}</tbody>
</table>
<div className="text-center">ADD OLDER/NEWER PAGINATION BUTTON HERE</div>
</div>
)
);
}
export { ProfileTtsModelListFc };
@@ -1,90 +1,102 @@
import React, { useState, useEffect, useCallback } from 'react';
import { ApiConfig, ListW2lInferenceResultsForUserArgs } from '@storyteller/components';
import { Link } from 'react-router-dom';
import { formatDistance } from 'date-fns'
import { VisibleIconFc } from '../../_icons/VisibleIcon';
import { HiddenIconFc } from '../../_icons/HiddenIcon';
import { FilmIcon } from '../../_icons/FilmIcon';
import React, { useState, useEffect, useCallback } from "react";
import {
ApiConfig,
ListW2lInferenceResultsForUserArgs,
} from "@storyteller/components";
import { Link } from "react-router-dom";
import { formatDistance } from "date-fns";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faFilm,
faArrowLeft,
faArrowRight,
faEye,
faEyeSlash,
} from "@fortawesome/free-solid-svg-icons";
const Fade = require("react-reveal/Fade");
interface W2lInferenceResultListResponsePayload {
success: boolean,
results: Array<W2lInferenceResult>,
cursor_next: string | null | undefined,
cursor_previous: string | null | undefined,
success: boolean;
results: Array<W2lInferenceResult>;
cursor_next: string | null | undefined;
cursor_previous: string | null | undefined;
}
interface W2lInferenceResult {
w2l_result_token: string,
maybe_w2l_template_token?: string,
maybe_tts_inference_result_token?: string,
w2l_result_token: string;
maybe_w2l_template_token?: string;
maybe_tts_inference_result_token?: string;
template_type: string,
template_title: string,
template_type: string;
template_title: string;
maybe_creator_user_token?: string,
maybe_creator_username?: string,
maybe_creator_display_name?: string,
maybe_creator_user_token?: string;
maybe_creator_username?: string;
maybe_creator_display_name?: string;
maybe_creator_result_id: number | null,
file_size_bytes: number,
frame_width: number,
frame_height: number,
duration_millis: number,
maybe_creator_result_id: number | null;
visibility: string,
file_size_bytes: number;
frame_width: number;
frame_height: number;
duration_millis: number;
created_at: string,
updated_at: string,
visibility: string;
created_at: string;
updated_at: string;
}
interface Props {
username: string,
username: string;
}
function ProfileW2lInferenceResultsListFc(props: Props) {
const [w2lResults, setW2lResults] = useState<Array<W2lInferenceResult>>([]);
const [nextCursor, setNextCursor] = useState<string|null>(null);
const [previousCursor, setPreviousCursor] = useState<string|null>(null);
const [nextCursor, setNextCursor] = useState<string | null>(null);
const [previousCursor, setPreviousCursor] = useState<string | null>(null);
const getPage = useCallback((cursor : string|null, reverse: boolean) => {
let args : ListW2lInferenceResultsForUserArgs = {
username: props.username,
limit: 5,
};
const getPage = useCallback(
(cursor: string | null, reverse: boolean) => {
let args: ListW2lInferenceResultsForUserArgs = {
username: props.username,
limit: 5,
};
if (cursor !== null) {
args.cursor = cursor;
if (reverse) {
args.cursor_is_reversed = true;
}
}
const api = new ApiConfig();
const endpointUrl = api.listW2lInferenceResultsForUser(args);
fetch(endpointUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include',
})
.then(res => res.json())
.then(res => {
const templatesResponse : W2lInferenceResultListResponsePayload = res;
if (!templatesResponse.success) {
return;
if (cursor !== null) {
args.cursor = cursor;
if (reverse) {
args.cursor_is_reversed = true;
}
}
setW2lResults(templatesResponse.results)
setNextCursor(templatesResponse.cursor_next || null)
setPreviousCursor(templatesResponse.cursor_previous || null)
})
.catch(e => {
});
}, [props.username]);
const api = new ApiConfig();
const endpointUrl = api.listW2lInferenceResultsForUser(args);
fetch(endpointUrl, {
method: "GET",
headers: {
Accept: "application/json",
},
credentials: "include",
})
.then((res) => res.json())
.then((res) => {
const templatesResponse: W2lInferenceResultListResponsePayload = res;
if (!templatesResponse.success) {
return;
}
setW2lResults(templatesResponse.results);
setNextCursor(templatesResponse.cursor_next || null);
setPreviousCursor(templatesResponse.cursor_previous || null);
})
.catch((e) => {});
},
[props.username]
);
useEffect(() => {
getPage(null, false);
@@ -92,19 +104,29 @@ function ProfileW2lInferenceResultsListFc(props: Props) {
const now = new Date();
let rows : Array<JSX.Element> = [];
w2lResults.forEach(result => {
let rows: Array<JSX.Element> = [];
w2lResults.slice(0, 5).forEach((result) => {
const duration_seconds = result.duration_millis / 1000;
const templateTitle = result.template_title.length < 5 ? `Title: ${result.template_title}` : result.template_title;
const templateTitle =
result.template_title.length < 5
? `Title: ${result.template_title}`
: result.template_title;
const inferenceLink = `/w2l/result/${result.w2l_result_token}`;
const templateLink = `/w2l/${result.maybe_w2l_template_token}`;
const createTime = new Date(result.created_at);
const relativeCreateTime = formatDistance(createTime, now, { addSuffix: true });
const relativeCreateTime = formatDistance(createTime, now, {
addSuffix: true,
});
const visibilityIcon = (result.visibility === 'public') ? <VisibleIconFc /> : <HiddenIconFc /> ;
const visibilityIcon =
result.visibility === "public" ? (
<FontAwesomeIcon icon={faEye} />
) : (
<FontAwesomeIcon icon={faEyeSlash} />
);
rows.push(
<tr key={result.w2l_result_token}>
@@ -112,12 +134,13 @@ function ProfileW2lInferenceResultsListFc(props: Props) {
<td>{visibilityIcon}</td>
<th>
<Link to={inferenceLink}>
<FilmIcon title="Play &amp; Download" />
&nbsp;
<FontAwesomeIcon icon={faFilm} className="me-2" />
Result
</Link>
</th>
<th><Link to={templateLink}>{templateTitle}</Link></th>
<th>
<Link to={templateLink}>{templateTitle}</Link>
</th>
<td>(custom audio)</td>
<td>{duration_seconds} s</td>
<td>{relativeCreateTime}</td>
@@ -126,41 +149,73 @@ function ProfileW2lInferenceResultsListFc(props: Props) {
});
// Disable if there is no "next" or "previous" cursor.
// However, let the buttosn show up if there are no results (empty payload)
// However, let the buttosn show up if there are no results (empty payload)
// to get unstuck. Come up with a better fix for this.
let prevDisabled = !previousCursor && rows.length !== 0;
let nextDisabled = !nextCursor && rows.length !== 0;
return (
<div>
<table className="table">
<thead>
<tr>
<th><abbr title="Number">#</abbr></th>
<th><abbr title="Visibility"><VisibleIconFc /></abbr></th>
<th><abbr title="Result View">Result Link</abbr></th>
<th><abbr title="Template">Template</abbr></th>
<th><abbr title="Source">Audio Source</abbr></th>
<th><abbr title="Duration">Duration</abbr></th>
<th><abbr title="Created">Creation Time</abbr></th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div className="table-responsive">
<table className="table">
<thead>
<tr>
<th>
<abbr title="Number">#</abbr>
</th>
<th>
<abbr title="Visibility">
<FontAwesomeIcon icon={faEye} />
</abbr>
</th>
<th>
<abbr title="Result View">Result Link</abbr>
</th>
<th>
<abbr title="Template">Template</abbr>
</th>
<th>
<abbr title="Source">Audio Source</abbr>
</th>
<th>
<abbr title="Duration">Duration</abbr>
</th>
<th>
<abbr title="Created">Creation Time</abbr>
</th>
</tr>
</thead>
<Fade cascade bottom duration="200" distance="10px">
<tbody>{rows}</tbody>
</Fade>
</table>
</div>
<p>
Note: Results marked public (<VisibleIconFc />) are visible by anyone visiting your profile.
<p className="text-center py-3">
Note: Results marked public (<FontAwesomeIcon icon={faEye} />) are
visible by anyone visiting your profile.
{/*You can change this by editing results. You can also set a default preference for new results.*/}
</p>
<div className="columns is-centered">
<button className="button is-info" onClick={() => getPage(previousCursor, true)} disabled={prevDisabled}>&lt; Get newer</button> &nbsp;
<button className="button is-info" onClick={() => getPage(nextCursor, false)} disabled={nextDisabled}>Get older&gt;</button>
<div className="justify-content-center d-flex gap-3">
<button
className="btn btn-secondary"
onClick={() => getPage(previousCursor, true)}
disabled={prevDisabled}
>
<FontAwesomeIcon icon={faArrowLeft} className="me-2" /> Get newer
</button>
<button
className="btn btn-secondary"
onClick={() => getPage(nextCursor, false)}
disabled={nextDisabled}
>
Get older
<FontAwesomeIcon icon={faArrowRight} className="ms-2" />
</button>
</div>
</div>
)
);
}
export { ProfileW2lInferenceResultsListFc };