Merge pull request #264 from storytold/bflat-fy-animation-removal

framer motion animation full removal
This commit is contained in:
Brandon Thomas authored and GitHub committed 2023-08-15 11:02:08 -04:00
commit b5f684ca89
56 files changed
+1134 -1310

No files matched your search

@@ -1,6 +1,4 @@
import React from "react";
import { motion } from "framer-motion";
import { image } from "../../../data/animation";
interface Props {
headerImage: string;
@@ -17,11 +15,10 @@ function PageHeaderWithImage(props: Props) {
<div className="panel">
<div className="row gx-3 flex-md-row-reverse">
<div className="col-12 col-md-5 hero-img-container d-none d-md-block">
<motion.img
<img
src={props.headerImage}
className="hero-img"
alt="Hero Image"
variants={image}
alt="Hero Header"
/>
</div>
<div className="col-12 col-md-7">
@@ -11,8 +11,7 @@ import {
faLink,
faHeadphonesSimple,
} from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, sessionItem } from "../../../data/animation";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { Analytics } from "../../../common/Analytics";
import { SessionTtsAudioPlayer } from "./SessionTtsAudioPlayer";
@@ -56,7 +55,10 @@ function SessionTtsInferenceResultList(props: Props) {
};
// TODO: We're having an outage and need to lower this.
//const interval = setInterval(async () => fetch(), 15000);
const refreshInterval = Math.max(DEFAULT_QUEUE_REFRESH_INTERVAL_MILLIS, pendingTtsJobs.refresh_interval_millis);
const refreshInterval = Math.max(
DEFAULT_QUEUE_REFRESH_INTERVAL_MILLIS,
pendingTtsJobs.refresh_interval_millis
);
const interval = setInterval(async () => fetch(), refreshInterval);
fetch();
return () => clearInterval(interval);
@@ -66,7 +68,7 @@ function SessionTtsInferenceResultList(props: Props) {
// TODO(bt,2023-04-08): Clean this utter garbage duplication up.
// ============================= GENERIC INFERENCE =============================
// ============================= GENERIC INFERENCE =============================
props.inferenceJobs.forEach((job) => {
if (!job.maybeResultToken) {
@@ -113,9 +115,7 @@ function SessionTtsInferenceResultList(props: Props) {
<div key={job.jobToken}>
<div>
<div>
<motion.div className={cssStyle} variants={sessionItem}>
{stateDescription}
</motion.div>
<div className={cssStyle}>{stateDescription}</div>
</div>
</div>
</div>
@@ -135,10 +135,7 @@ function SessionTtsInferenceResultList(props: Props) {
<button className="delete" aria-label="delete"></button>
</div>*/}
<div>
<motion.div
className="panel panel-tts-results p-4 gap-3 d-flex flex-column"
variants={sessionItem}
>
<div className="panel panel-tts-results p-4 gap-3 d-flex flex-column">
<div>
<h5 className="mb-2">{job.maybeModelTitle}</h5>
<p>{job.maybeRawInferenceText}</p>
@@ -170,7 +167,7 @@ function SessionTtsInferenceResultList(props: Props) {
{t("common.SessionTtsInferenceResults.result.shareDownload")}
</Link>
</div>
</motion.div>
</div>
</div>
</div>
);
@@ -179,7 +176,7 @@ function SessionTtsInferenceResultList(props: Props) {
// TODO(bt,2023-04-08): Clean this utter garbage duplication up.
// ============================= LEGACY TTS =============================
// ============================= LEGACY TTS =============================
props.ttsInferenceJobs.forEach((job) => {
if (!job.maybeResultToken) {
@@ -226,9 +223,7 @@ function SessionTtsInferenceResultList(props: Props) {
<div key={job.jobToken}>
<div>
<div>
<motion.div className={cssStyle} variants={sessionItem}>
{stateDescription}
</motion.div>
<div className={cssStyle}>{stateDescription}</div>
</div>
</div>
</div>
@@ -248,10 +243,7 @@ function SessionTtsInferenceResultList(props: Props) {
<button className="delete" aria-label="delete"></button>
</div>*/}
<div>
<motion.div
className="panel panel-tts-results p-4 gap-3 d-flex flex-column"
variants={sessionItem}
>
<div className="panel panel-tts-results p-4 gap-3 d-flex flex-column">
<div>
<h5 className="mb-2">{job.title}</h5>
<p>{job.rawInferenceText}</p>
@@ -283,14 +275,14 @@ function SessionTtsInferenceResultList(props: Props) {
{t("common.SessionTtsInferenceResults.result.shareDownload")}
</Link>
</div>
</motion.div>
</div>
</div>
</div>
);
}
});
// ============================= END LEGACY TTS =============================
// ============================= END LEGACY TTS =============================
let noResultsSection = (
<div className="panel panel-inner text-center p-5 rounded-5 h-100">
@@ -317,10 +309,7 @@ function SessionTtsInferenceResultList(props: Props) {
) {
upgradeNotice = (
<div className="d-flex flex-column gap-3 sticky-top zi-2">
<motion.div
className="alert alert-warning alert-cta mb-0"
variants={sessionItem}
>
<div className="alert alert-warning alert-cta mb-0">
<FontAwesomeIcon icon={faClock} className="me-2" />
<Trans i18nKey="common.SessionTtsInferenceResults.premium.ad">
Don't want to wait? Step to the back of the line with a{" "}
@@ -336,7 +325,7 @@ function SessionTtsInferenceResultList(props: Props) {
</Trans>{" "}
(TTS Queued:{" "}
<span className="text-red">~{pendingTtsJobs.pending_job_count}</span>)
</motion.div>
</div>
</div>
);
}
@@ -345,14 +334,14 @@ function SessionTtsInferenceResultList(props: Props) {
results.reverse();
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div>
<div className="d-flex flex-column gap-3">
{upgradeNotice}
<div className="d-flex flex-column gap-3">{results}</div>
</div>
</div>
</motion.div>
</div>
);
}
@@ -2,8 +2,6 @@ import React from "react";
import { Link } from "react-router-dom";
import { JobState } from "@storyteller/components/src/jobs/JobStates";
import { TtsModelUploadJob } from "@storyteller/components/src/jobs/TtsModelUploadJobs";
import { motion } from "framer-motion";
import { container, sessionItem, item } from "../../../data/animation";
interface Props {
modelUploadJobs: Array<TtsModelUploadJob>;
@@ -53,15 +51,12 @@ function SessionTtsModelUploadResultList(props: Props) {
results.push(
<div key={job.jobToken}>
<motion.div
className="panel py-4 p-3 p-lg-4 gap-4"
variants={sessionItem}
>
<div className="panel py-4 p-3 p-lg-4 gap-4">
Complete!
<Link to={ttsPermalink} className="btn btn-primary ms-4">
See &amp; use TTS model
</Link>
</motion.div>
</div>
</div>
);
}
@@ -70,9 +65,9 @@ function SessionTtsModelUploadResultList(props: Props) {
let title = <span />;
if (results.length !== 0) {
title = (
<motion.h2 className="text-center text-lg-start fw-bold" variants={item}>
<h2 className="text-center text-lg-start fw-bold">
TTS Model Upload Status
</motion.h2>
</h2>
);
}
@@ -83,12 +78,12 @@ function SessionTtsModelUploadResultList(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container-panel pt-4 pb-5">
<div className="pb-4">{title}</div>
<div className="d-flex flex-column gap-4">{results}</div>
</div>
</motion.div>
</div>
);
}
@@ -1,8 +1,7 @@
import React from "react";
import { Link } from "react-router-dom";
import { JobState } from "@storyteller/components/src/jobs/JobStates";
import { motion } from "framer-motion";
import { container, sessionItem, item } from "../../../data/animation";
import { VocoderUploadJob } from "@storyteller/components/src/jobs/VocoderUploadJobs";
interface Props {
@@ -53,15 +52,12 @@ function SessionVocoderUploadResultList(props: Props) {
results.push(
<div key={job.jobToken}>
<motion.div
className="panel py-4 p-3 p-lg-4 gap-4"
variants={sessionItem}
>
<div className="panel py-4 p-3 p-lg-4 gap-4">
Complete!
<Link to={permalink} className="btn btn-primary ms-4">
See &amp; use vocoder model
</Link>
</motion.div>
</div>
</div>
);
}
@@ -70,19 +66,19 @@ function SessionVocoderUploadResultList(props: Props) {
let title = <span />;
if (results.length !== 0) {
title = (
<motion.h2 className="text-center text-lg-start fw-bold" variants={item}>
<h2 className="text-center text-lg-start fw-bold">
Vocoder Upload Status
</motion.h2>
</h2>
);
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container-panel pt-4 pb-5">
<div className="pb-4">{title}</div>
<div className="d-flex flex-column gap-4">{results}</div>
</div>
</motion.div>
</div>
);
}
@@ -1,8 +1,7 @@
import React from "react";
import { Link } from "react-router-dom";
import { JobState } from "@storyteller/components/src/jobs/JobStates";
import { motion } from "framer-motion";
import { container, sessionItem, item } from "../../../data/animation";
import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/VoiceConversionModelUploadJob";
interface Props {
@@ -53,15 +52,12 @@ function SessionVoiceConversionModelUploadResultList(props: Props) {
results.push(
<div key={job.jobToken}>
<motion.div
className="panel py-4 p-3 p-lg-4 gap-4"
variants={sessionItem}
>
<div className="panel py-4 p-3 p-lg-4 gap-4">
Complete!
<Link to={permalink} className="btn btn-primary ms-4">
See &amp; use voice conversion model
</Link>
</motion.div>
</div>
</div>
);
}
@@ -70,19 +66,19 @@ function SessionVoiceConversionModelUploadResultList(props: Props) {
let title = <span />;
if (results.length !== 0) {
title = (
<motion.h2 className="text-center text-lg-start fw-bold" variants={item}>
<h2 className="text-center text-lg-start fw-bold">
Voice Conversion Model Upload Status
</motion.h2>
</h2>
);
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container-panel pt-4 pb-5">
<div className="pb-4">{title}</div>
<div className="d-flex flex-column gap-4">{results}</div>
</div>
</motion.div>
</div>
);
}
@@ -10,8 +10,7 @@ import {
faHeadphonesSimple,
faDownload,
} from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, sessionItem } from "../../../data/animation";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { Analytics } from "../../../common/Analytics";
import { SessionTtsAudioPlayer } from "./SessionTtsAudioPlayer";
@@ -54,7 +53,10 @@ function SessionVoiceConversionResultsList(props: Props) {
};
// TODO: We're having an outage and need to lower this.
//const interval = setInterval(async () => fetch(), 15000);
const refreshInterval = Math.max(DEFAULT_QUEUE_REFRESH_INTERVAL_MILLIS, pendingTtsJobs.refresh_interval_millis);
const refreshInterval = Math.max(
DEFAULT_QUEUE_REFRESH_INTERVAL_MILLIS,
pendingTtsJobs.refresh_interval_millis
);
const interval = setInterval(async () => fetch(), refreshInterval);
fetch();
return () => clearInterval(interval);
@@ -64,7 +66,7 @@ function SessionVoiceConversionResultsList(props: Props) {
// TODO(bt,2023-04-08): Clean this utter garbage duplication up.
// ============================= GENERIC INFERENCE =============================
// ============================= GENERIC INFERENCE =============================
props.inferenceJobs.forEach((job) => {
if (!job.maybeResultToken) {
@@ -111,9 +113,7 @@ function SessionVoiceConversionResultsList(props: Props) {
<div key={job.jobToken}>
<div>
<div>
<motion.div className={cssStyle} variants={sessionItem}>
{stateDescription}
</motion.div>
<div className={cssStyle}>{stateDescription}</div>
</div>
</div>
</div>
@@ -135,10 +135,7 @@ function SessionVoiceConversionResultsList(props: Props) {
<button className="delete" aria-label="delete"></button>
</div>*/}
<div>
<motion.div
className="panel panel-tts-results p-4 gap-3 d-flex flex-column"
variants={sessionItem}
>
<div className="panel panel-tts-results p-4 gap-3 d-flex flex-column">
<div>
<h5 className="mb-2">{job.maybeModelTitle}</h5>
<p>{job.maybeRawInferenceText}</p>
@@ -182,7 +179,7 @@ function SessionVoiceConversionResultsList(props: Props) {
Download File{" "}
</a>
</div>
</motion.div>
</div>
</div>
</div>
);
@@ -214,10 +211,7 @@ function SessionVoiceConversionResultsList(props: Props) {
) {
upgradeNotice = (
<div className="d-flex flex-column gap-3 sticky-top zi-2">
<motion.div
className="alert alert-warning alert-cta mb-0"
variants={sessionItem}
>
<div className="alert alert-warning alert-cta mb-0">
<FontAwesomeIcon icon={faClock} className="me-2" />
<Trans i18nKey="common.SessionTtsInferenceResults.premium.ad">
Don't want to wait? Step to the back of the line with a{" "}
@@ -233,7 +227,7 @@ function SessionVoiceConversionResultsList(props: Props) {
</Trans>{" "}
(TTS Queued:{" "}
<span className="text-red">~{pendingTtsJobs.pending_job_count}</span>)
</motion.div>
</div>
</div>
);
}
@@ -242,14 +236,14 @@ function SessionVoiceConversionResultsList(props: Props) {
results.reverse();
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div>
<div className="d-flex flex-column gap-3">
{upgradeNotice}
<div className="d-flex flex-column gap-3">{results}</div>
</div>
</div>
</motion.div>
</div>
);
}
@@ -4,8 +4,6 @@ import { W2lInferenceJob } from "../../../App";
import { JobState } from "@storyteller/components/src/jobs/JobStates";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faList } from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, item, sessionItem } from "../../../data/animation";
interface Props {
w2lInferenceJobs: Array<W2lInferenceJob>;
@@ -47,9 +45,7 @@ function SessionW2lInferenceResultList(props: Props) {
results.push(
<div key={job.jobToken}>
<motion.div className="alert alert-primary" variants={sessionItem}>
{stateDescription}
</motion.div>
<div className="alert alert-primary">{stateDescription}</div>
</div>
);
} else {
@@ -57,15 +53,12 @@ function SessionW2lInferenceResultList(props: Props) {
results.push(
<div key={job.jobToken}>
<motion.div
className="panel py-4 p-3 p-lg-4 gap-4"
variants={sessionItem}
>
<div className="panel py-4 p-3 p-lg-4 gap-4">
Complete!{" "}
<Link to={w2lPermalink} className="btn btn-primary ms-4">
Permalink &amp; download
</Link>
</motion.div>
</div>
</div>
);
}
@@ -74,10 +67,10 @@ function SessionW2lInferenceResultList(props: Props) {
let title = <span />;
if (results.length !== 0) {
title = (
<motion.h2 className="text-center text-lg-start fw-bold" variants={item}>
<h2 className="text-center text-lg-start fw-bold">
<FontAwesomeIcon icon={faList} className="me-3" />
Session W2L Results
</motion.h2>
</h2>
);
}
@@ -88,12 +81,12 @@ function SessionW2lInferenceResultList(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container-panel pt-4 pb-5">
<div className="pb-4">{title}</div>
<div className="d-flex flex-column gap-4">{results}</div>
</div>
</motion.div>
</div>
);
}
@@ -2,8 +2,6 @@ import React from "react";
import { Link } from "react-router-dom";
import { JobState } from "@storyteller/components/src/jobs/JobStates";
import { W2lTemplateUploadJob } from "@storyteller/components/src/jobs/W2lTemplateUploadJobs";
import { motion } from "framer-motion";
import { container, item, sessionItem } from "../../../data/animation";
interface Props {
w2lTemplateUploadJobs: Array<W2lTemplateUploadJob>;
@@ -55,15 +53,12 @@ function SessionW2lTemplateUploadResultList(props: Props) {
results.push(
<div key={job.jobToken}>
<motion.div
className="panel py-4 p-3 p-lg-4 gap-4"
variants={sessionItem}
>
<div className="panel py-4 p-3 p-lg-4 gap-4">
Complete!
<Link to={w2lPermalink} className="btn btn-primary ms-4">
See &amp; use template
</Link>
</motion.div>
</div>
</div>
);
}
@@ -72,9 +67,9 @@ function SessionW2lTemplateUploadResultList(props: Props) {
let title = <span />;
if (results.length !== 0) {
title = (
<motion.h2 className="text-center text-lg-start fw-bold" variants={item}>
<h2 className="text-center text-lg-start fw-bold">
W2L Template Upload Status
</motion.h2>
</h2>
);
}
@@ -85,12 +80,12 @@ function SessionW2lTemplateUploadResultList(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container-panel pt-4 pb-5">
<div className="pb-4">{title}</div>
<div className="d-flex flex-column gap-4">{results}</div>
</div>
</motion.div>
</div>
);
}
@@ -3,8 +3,7 @@ import { formatDistance } from "date-fns";
import { Comment } from "@storyteller/components/src/api/comments/ListComments";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { SafeDeleteCommentButton } from "./SafeDeleteCommentButton";
import { motion } from "framer-motion";
import { container, item } from "../../../../data/animation";
import { Gravatar } from "@storyteller/components/src/elements/Gravatar";
import { Link } from "react-router-dom";
@@ -60,7 +59,7 @@ function CommentList(props: Props) {
let profileLink = `/profile/${comment.username}`;
rows.push(
<motion.tr key={comment.token} variants={item}>
<tr key={comment.token}>
<td className="px-0">
<div className="d-flex gap-3 py-3">
<Gravatar size={40} email_hash={comment.user_gravatar_hash} />
@@ -91,19 +90,14 @@ function CommentList(props: Props) {
</div>
</div>
</td>
</motion.tr>
</tr>
);
});
return (
<motion.table
className="table mb-0"
initial="hidden"
animate="visible"
variants={container}
>
<table className="table mb-0">
<tbody>{rows}</tbody>
</motion.table>
</table>
);
}
@@ -1,7 +1,6 @@
import React from "react";
import { Link } from "react-router-dom";
import { motion } from "framer-motion";
import { container, item, image, panel } from "../../../../../data/animation";
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -12,40 +11,37 @@ function AboutPage(props: Props) {
usePrefixedDocumentTitle("About Us");
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container mb-4">
<div className="row gx-3 flex-lg-row-reverse align-items-center">
<div className="col-lg-6">
<div className="d-flex justify-content-center">
<motion.img
<img
src="/mascot/kitsune_pose3.webp"
className="img-fluid"
width="516"
height="444"
alt="FakeYou Kitsune Mascot!"
variants={image}
/>
</div>
</div>
<div className="col-lg-6 px-md-2 ps-lg-5 ps-xl-2">
<div className="text-center text-lg-start">
<div>
<motion.h1 className=" fw-bold lh-1 mb-4" variants={item}>
A Glimpse of the Future
</motion.h1>
<h1 className=" fw-bold lh-1 mb-4">A Glimpse of the Future</h1>
</div>
<div>
<motion.p className="lead" variants={item}>
<p className="lead">
We're building FakeYou as just one component of a broad set of
production and creative tooling.
</motion.p>
</p>
</div>
</div>
</div>
</div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={panel}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0">
<h1 className="panel-title fw-bold">About FakeYou</h1>
<div className="py-6 d-flex flex-column gap-4">
@@ -306,8 +302,8 @@ function AboutPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -1,6 +1,5 @@
import React from "react";
import { motion } from "framer-motion";
import { container, item, panel, image } from "../../../../../data/animation";
import { Link } from "react-router-dom";
import { DiscordLink2 } from "@storyteller/components/src/elements/DiscordLink2";
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
@@ -13,51 +12,45 @@ function GuidePage(props: Props) {
usePrefixedDocumentTitle("Guide to all things Deep Fake");
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container">
<div className="row gx-3 flex-lg-row-reverse align-items-center">
<div className="col-lg-6">
<div className="d-flex justify-content-center">
<motion.img
<img
src="/mascot/guide.webp"
className="img-fluid"
width="516"
height="444"
alt="FakeYou Kitsune Mascot!"
variants={image}
/>
</div>
</div>
<div className="col-lg-6 px-md-2 ps-lg-5 ps-xl-2">
<div className="text-center text-lg-start">
<div>
<motion.h1 className=" fw-bold lh-1 mb-3" variants={item}>
FakeYou TTS Guide
</motion.h1>
<h1 className=" fw-bold lh-1 mb-3">FakeYou TTS Guide</h1>
</div>
<div className="mb-5">
<motion.p className="lead px-5 px-lg-0" variants={item}>
<p className="lead px-5 px-lg-0">
<h4>How to generate the best sounding TTS</h4>
</motion.p>
</p>
</div>
<div>
<motion.div
variants={item}
className="d-flex justify-content-center justify-content-lg-start mb-5 mb-lg-0"
>
<div className="d-flex justify-content-center justify-content-lg-start mb-5 mb-lg-0">
<Link to="/">
<button className="btn btn-primary">
Generate your TTS
</button>
</Link>
</motion.div>
</div>
</div>
</div>
</div>
</div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={panel}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0">
<h1 className="panel-title fw-bold">Quick Tips</h1>
<div className="py-6 d-flex flex-column gap-4">
@@ -150,9 +143,9 @@ function GuidePage(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={panel}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0">
<h2 className="panel-title fw-bold">Generate your TTS now!</h2>
<div className="py-6 d-flex flex-column gap-4">
@@ -165,8 +158,8 @@ function GuidePage(props: Props) {
</div>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -1,6 +1,5 @@
import React from "react";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -11,25 +10,19 @@ function PrivacyPage(props: Props) {
usePrefixedDocumentTitle("Privacy Statement");
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pb-0 pt-5 pb-lg-5 px-md-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1
className=" fw-bold text-center text-lg-start"
variants={item}
>
<h1 className=" fw-bold text-center text-lg-start">
FakeYou Privacy Statement
</motion.h1>
<motion.h4
className="mt-1 mb-4 opacity-75 text-center text-lg-start"
variants={item}
>
</h1>
<h4 className="mt-1 mb-4 opacity-75 text-center text-lg-start">
Updated: November 18, 2022
</motion.h4>
</h4>
</div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={panel}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0">
<h1 className="panel-title fw-bold">FakeYou and You</h1>
<div className="py-6 d-flex flex-column gap-4">
@@ -1216,8 +1209,8 @@ function PrivacyPage(props: Props) {
</p>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -1,6 +1,5 @@
import React from "react";
import { motion } from "framer-motion";
import { container, item, panel, image } from "../../../../../data/animation";
import { ThirdPartyLinks } from "@storyteller/components/src/constants/ThirdPartyLinks";
import { Link } from "react-router-dom";
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
@@ -13,39 +12,36 @@ function TermsPage(props: Props) {
usePrefixedDocumentTitle("Terms of Use");
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container">
<div className="row gx-3 flex-lg-row-reverse align-items-center">
<div className="col-lg-6">
<div className="d-flex justify-content-center">
<motion.img
<img
src="/mascot/tou.webp"
className="img-fluid"
width="516"
height="444"
alt="FakeYou Kitsune Mascot!"
variants={image}
/>
</div>
</div>
<div className="col-lg-6 px-md-2 ps-lg-5 ps-xl-2">
<div className="text-center text-lg-start">
<div>
<motion.h1 className=" fw-bold lh-1 mb-3" variants={item}>
Terms and Conditions
</motion.h1>
<h1 className=" fw-bold lh-1 mb-3">Terms and Conditions</h1>
</div>
<div>
<motion.p className="lead" variants={item}>
<p className="lead">
<h4>Please use this technology responsibly.</h4>
</motion.p>
</p>
</div>
</div>
</div>
</div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={panel}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0">
<h1 className="panel-title fw-bold">Terms and Conditions of Use</h1>
<div className="py-6 d-flex flex-column gap-4">
@@ -1175,8 +1171,8 @@ function TermsPage(props: Props) {
<p>support@storyteller.io</p>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -10,8 +10,7 @@ import {
} from "@storyteller/components/src/api/category/CreateCategory";
import { BackLink } from "../../_common/BackLink";
import { WebUrl } from "../../../../common/WebUrl";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
const DEFAULT_CAN_DIRECTLY_HAVE_MODELS = true;
@@ -195,35 +194,33 @@ function CreateCategoryPage(props: Props) {
if (props.sessionWrapper.canEditCategories()) {
moderateCategoriesLink = (
<>
<motion.div className="container pt-4 pb-5" variants={item}>
<div className="container pt-4 pb-5">
<Link
to={WebUrl.moderationTtsCategoryList()}
className="btn btn-secondary w-100"
>
Moderate categories
</Link>
</motion.div>
</div>
</>
);
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pb-4 pt-5 px-md-4 px-lg-5 px-xl-3">
<motion.h1 className=" fw-bold" variants={item}>
{categoryActionName} Category
</motion.h1>
<motion.div className="pt-3" variants={item}>
<h1 className=" fw-bold">{categoryActionName} Category</h1>
<div className="pt-3">
<BackLink
link={WebUrl.contributePage()}
text="Back to contribute page"
/>
</motion.div>
</div>
</div>
{errorFlash}
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<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">
@@ -259,15 +256,15 @@ function CreateCategoryPage(props: Props) {
</div>
</div>
<motion.div className="container" variants={item}>
<div className="container">
<button className="btn btn-primary w-100">
{categoryActionName}
</button>
</motion.div>
</motion.form>
</div>
</form>
{moderateCategoriesLink}
</motion.div>
</div>
);
}
@@ -1,10 +1,10 @@
import React from "react";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { motion } from "framer-motion";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
import { Link } from "react-router-dom";
import { container, item } from "../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface Props {
@@ -17,7 +17,7 @@ function ChannelsPage(props: Props) {
PosthogClient.recordPageview();
let videoBlock = (
<motion.div className="col-12 col-sm-6 col-md-4" variants={item}>
<div className="col-12 col-sm-6 col-md-4">
<Link to="/">
<div className="ratio ratio-16x9 channel-thumbnail">
<img
@@ -40,19 +40,12 @@ function ChannelsPage(props: Props) {
</div>
</div>
</Link>
</motion.div>
</div>
);
return (
<motion.div
className="container py-2 py-lg-5 px-md-4 px-lg-5 px-xl-3"
initial="hidden"
animate="visible"
variants={container}
>
<motion.h1 className=" fw-bold mb-4" variants={item}>
Channels
</motion.h1>
<div className="container py-2 py-lg-5 px-md-4 px-lg-5 px-xl-3">
<h1 className=" fw-bold mb-4">Channels</h1>
<div className="row gy-4">
{videoBlock}
@@ -65,7 +58,7 @@ function ChannelsPage(props: Props) {
{videoBlock}
{videoBlock}
</div>
</motion.div>
</div>
);
}
@@ -31,8 +31,7 @@ import {
faDeleteLeft,
faVolumeHigh,
} from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, panel } from "../../../../../data/animation";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { TtsPageHero } from "./TrumpTtsPageHero";
import { Analytics } from "../../../../../common/Analytics";
@@ -167,7 +166,7 @@ function TrumpTtsPage(props: Props) {
let trumpModels = models.filter((model) => {
return PAGE_MODEL_TOKENS.has(model.model_token);
})
});
let model = trumpModels[Math.floor(Math.random() * trumpModels.length)];
@@ -366,24 +365,27 @@ function TrumpTtsPage(props: Props) {
let trumpModels = props.ttsModels.filter((model) => {
return PAGE_MODEL_TOKENS.has(model.model_token);
})
});
let maybeSelectedModel = props.maybeSelectedTtsModel;
if (maybeSelectedModel !== undefined && !PAGE_MODEL_TOKENS.has(maybeSelectedModel.model_token)) {
// If we don't select a trump model by default (which is rare), the list will be empty.
if (
maybeSelectedModel !== undefined &&
!PAGE_MODEL_TOKENS.has(maybeSelectedModel.model_token)
) {
// If we don't select a trump model by default (which is rare), the list will be empty.
// We'll set it here.
maybeSelectedModel = trumpModels[0];
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<TtsPageHero
sessionWrapper={props.sessionWrapper}
sessionSubscriptionsWrapper={props.sessionSubscriptionsWrapper}
/>
<motion.div className="container-panel pb-5 mb-4" variants={panel}>
<div className="container-panel pb-5 mb-4">
<div className="panel p-3 py-4 p-md-4">
<i className="fas fa-volume-high"></i>
@@ -490,8 +492,8 @@ function TrumpTtsPage(props: Props) {
</form>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -23,8 +23,6 @@ import {
} from "@storyteller/components/src/api/clone_requests/CheckVoiceCloneApplication";
import { Link } from "react-router-dom";
import { WebUrl } from "../../../../common/WebUrl";
import { motion } from "framer-motion";
import { container, item, image, panel } from "../../../../data/animation";
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -251,32 +249,27 @@ function VoiceCloneRequestPage(props: Props) {
<div className="row gx-3 flex-lg-row-reverse align-items-center">
<div className="col-lg-6">
<div className="d-flex justify-content-center">
<motion.img
<img
src="/mascot/kitsune_pose4.webp"
className="img-fluid"
width="516"
height="472"
alt="FakeYou Mascot"
variants={image}
/>
</div>
</div>
<div className="col-lg-6 px-3 px-md-2 px-lg-5 px-xl-2">
<div className="text-center text-lg-start">
<motion.h1 className=" fw-bold lh-1" variants={item}>
Professionally Clone Your Voice
</motion.h1>
<motion.h3 className="mb-4" variants={item}>
(or any voice) for just $70
</motion.h3>
<h1 className=" fw-bold lh-1">Professionally Clone Your Voice</h1>
<h3 className="mb-4">(or any voice) for just $70</h3>
<div className="d-flex flex-column justify-content-center">
<motion.h3 className="px-5 px-md-0" variants={item}>
<h3 className="px-5 px-md-0">
Want a Custom Voice You Can Use?
</motion.h3>
<motion.p className="lead" variants={item}>
</h3>
<p className="lead">
For Music, Videos, Twitch Rewards, API, Friends, Family…
whatever you want!
</motion.p>
</p>
</div>
</div>
</div>
@@ -342,10 +335,10 @@ function VoiceCloneRequestPage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
{header}
<motion.div className="container-panel pt-4 pb-5" variants={panel}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0">
<h1 className="panel-title fw-bold">Clone Your Voice</h1>
<div className="py-6">
@@ -658,8 +651,8 @@ function VoiceCloneRequestPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -1,7 +1,6 @@
import React from "react";
import { Link } from "react-router-dom";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../data/animation";
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faWaveformLines } from "@fortawesome/pro-solid-svg-icons";
@@ -15,7 +14,7 @@ function CommunityCommissionsPage(props: Props) {
PosthogClient.recordPageview();
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-3 pt-lg-5 pb-5">
<div className="row gx-3 align-items-center">
<div className="col-lg-5">
@@ -31,14 +30,11 @@ function CommunityCommissionsPage(props: Props) {
<div className="col-lg-7 px-md-2 ps-lg-5 ps-xl-2">
<div className="text-center text-lg-start">
<div>
<motion.h1
className=" fw-bold lh-1 mb-4 display-5"
variants={item}
>
<h1 className=" fw-bold lh-1 mb-4 display-5">
Voice to Voice
<br />
Community Commissions
</motion.h1>
</h1>
</div>
<div>
<p className="lead">
@@ -70,7 +66,7 @@ function CommunityCommissionsPage(props: Props) {
</div>
</div>
<motion.div className="container-panel pt-lg-5 pb-5" variants={panel}>
<div className="container-panel pt-lg-5 pb-5">
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0">
<h1 className="panel-title fw-bold">Details</h1>
<div className="py-6 d-flex flex-column gap-4">
@@ -108,8 +104,8 @@ function CommunityCommissionsPage(props: Props) {
</a>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -13,8 +13,7 @@ import {
faTags,
faHandsHelping,
} from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../data/animation";
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
import { PageHeader } from "../../_common/PageHeader";
import {
@@ -57,20 +56,20 @@ function ContributeIndexPage(props: Props) {
);
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
{/* <div className="container py-5 px-md-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
<h1 className=" fw-bold" >
{t("pages.contributeIndex.heroTitle")}
</motion.h1>
<motion.h3 className="mb-4" variants={item}>
</h1>
<h3 className="mb-4" >
<Trans i18nKey="pages.contributeIndex.heroSubtitle">
You make FakeYou <strong>better</strong> by contributing
</Trans>
</motion.h3>
<motion.p className="pb-4" variants={item}>
</h3>
<p className="pb-4" >
{t("pages.contributeIndex.introText")}
</motion.p>
</p>
</div>
</div> */}
<PageHeader
@@ -80,7 +79,7 @@ function ContributeIndexPage(props: Props) {
titleIcon={titleIcon}
/>
<motion.div className="container-panel pb-3" variants={panel}>
<div className="container-panel pb-3">
<div className="panel p-3 py-4 p-md-4 text-center text-lg-start">
<h2 className="fw-bold">
<FontAwesomeIcon icon={faFileArrowUp} className="me-3" />
@@ -134,9 +133,9 @@ function ContributeIndexPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-3 pb-3" variants={item}>
<div className="container-panel pt-3 pb-3">
<div className="panel p-3 py-4 p-md-4 text-center text-lg-start">
<h2 className="fw-bold">
<FontAwesomeIcon icon={faTags} className="me-3" />
@@ -156,7 +155,7 @@ function ContributeIndexPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
<div className="container-panel pt-3">
<div className="panel p-3 py-4 p-md-4 text-center text-lg-start">
<h2 className="fw-bold">
@@ -177,7 +176,7 @@ function ContributeIndexPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
);
}
@@ -15,8 +15,8 @@ import {
faDiscord,
faTwitch,
} from "@fortawesome/free-brands-svg-icons";
import { motion } from "framer-motion";
import { duration, delay, container, item } from "../../../../data/animation";
import { duration, delay } from "../../../../data/animation";
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -60,7 +60,6 @@ function FirehoseEventListPage(props: Props) {
[]
);
const fetchEvents = () => {
const api = new ApiConfig();
const endpointUrl = api.firehoseEvents();
@@ -324,19 +323,17 @@ function FirehoseEventListPage(props: Props) {
usePrefixedDocumentTitle("Firehose Event Feed");
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5 px-md-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
Firehose Event Feed
</motion.h1>
<motion.h4 className="mb-4" variants={item}>
<h1 className=" fw-bold">Firehose Event Feed</h1>
<h4 className="mb-4">
The latest FakeYou events refreshed every few seconds.
</motion.h4>
<motion.p className="lead" variants={item}>
</h4>
<p className="lead">
As you can see, we're really popular. But we owe it to you, our
users. Thank you!
</motion.p>
</p>
</div>
</div>
@@ -345,7 +342,7 @@ function FirehoseEventListPage(props: Props) {
<ul className="firehose-ul d-flex flex-column gap-3">{eventItems}</ul>
</Fade>
</div>
</motion.div>
</div>
);
}
@@ -1,7 +1,6 @@
import React, { useMemo } from "react";
import { Link } from "react-router-dom";
import { motion } from "framer-motion";
import { container } from "../../../../data/animation";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { t } from "i18next";
@@ -145,7 +144,7 @@ function LandingPage(props: Props) {
}, []);
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pb-5 pb-lg-0 pt-lg-4 px-md-5 px-lg-5 px-xl-3">
{/* Community Commissions Alert */}
{/* <div className="alert alert-info">
@@ -516,7 +515,7 @@ function LandingPage(props: Props) {
/>
</div>
</div>
</motion.div>
</div>
);
}
@@ -12,8 +12,7 @@ import {
LeaderboardLookupError,
} from "@storyteller/components/src/api/leaderboard/GetLeaderboard";
import { DiscordLink2 } from "@storyteller/components/src/elements/DiscordLink2";
import { motion } from "framer-motion";
import { container, item, panel } from "data/animation";
import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -32,7 +31,7 @@ function LeaderboardPage(props: Props) {
Array<LeaderboardRow> | undefined
>(undefined);
const [retryCount, setRetryCount] = useState(0);
PosthogClient.recordPageview();
const getLeaderboard = useCallback(async () => {
@@ -121,23 +120,19 @@ function LeaderboardPage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5 px-md-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
Leaderboard
</motion.h1>
<motion.h3 className="mb-4" variants={item}>
Our most frequent contributors!
</motion.h3>
<motion.p className="lead" variants={item}>
<h1 className=" fw-bold">Leaderboard</h1>
<h3 className="mb-4">Our most frequent contributors!</h3>
<p className="lead">
Want to be on the leaderboard?{" "}
<DiscordLink2>Join our Discord</DiscordLink2> and learn more!
</motion.p>
</p>
</div>
</div>
<motion.div className="container-panel pt-5 pb-5" variants={panel}>
<div className="container-panel pt-5 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">TTS Models Uploaded</h2>
<div className="py-6">
@@ -146,9 +141,9 @@ function LeaderboardPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">W2L Templates Uploaded</h2>
<div className="py-6">
@@ -157,8 +152,8 @@ function LeaderboardPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -9,8 +9,7 @@ import {
} from "@storyteller/components/src/api/session/CreateSession";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faUser, faKey } from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, panel } from "../../../../data/animation";
import { Analytics } from "../../../../common/Analytics";
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -89,11 +88,8 @@ function LoginPage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<motion.div
className="container-panel pb-5 pt-lg-5 my-lg-5 login-panel"
variants={panel}
>
<div>
<div className="container-panel pb-5 pt-lg-5 my-lg-5 login-panel">
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0 px-md-4">
<h1 className="panel-title fw-bold">Login</h1>
<div className="py-6">
@@ -138,10 +134,7 @@ function LoginPage(props: Props) {
<button className="btn btn-primary w-100 mt-2">Login</button>
<p>
Don’t have an account? &nbsp;
<Link
to={WebUrl.signupPage()}
className="text-link"
>
<Link to={WebUrl.signupPage()} className="text-link">
Create an account now.
</Link>
</p>
@@ -149,8 +142,8 @@ function LoginPage(props: Props) {
</form>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -1,8 +1,8 @@
import React, { useEffect } from "react";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { motion } from "framer-motion";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { container, item } from "../../../../data/animation";
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
import { TwitchPlayer, TwitchChat } from "react-twitch-embed";
import { Link } from "react-router-dom";
@@ -112,18 +112,18 @@ function NewsPage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container-panel py-1 py-lg-4 px-md-4 px-lg-5 px-xl-3">
<div className="row gx-3 gy-3">
<div className="col-12 col-lg-8 d-flex flex-column gap-3">
{/* <div className="d-flex flex-column ms-3 ms-lg-0">
<motion.h1 className="fw-bold" variants={item}>
<h1 className="fw-bold" >
Media Feed
</motion.h1>
</h1>
</div> */}
{/* Feed Content */}
<motion.div variants={item} className="d-flex flex-column gap-3">
<div className="d-flex flex-column gap-3">
<TwitchPlayer
channel={TWITCH_CHANNEL}
width="100%"
@@ -177,7 +177,7 @@ function NewsPage(props: Props) {
Want to give us feedback? Please join our <DiscordLink2 />.
</p>
</div>
</motion.div>
</div>
</div>
{/* Side column */}
@@ -191,7 +191,7 @@ function NewsPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
);
}
@@ -4,8 +4,7 @@ import { PATRONS } from "../../../../data/Patrons";
import { PatreonLink } from "@storyteller/components/src/elements/PatreonLink";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPatreon } from "@fortawesome/free-brands-svg-icons";
import { motion } from "framer-motion";
import { container, item, image, panel } from "../../../../data/animation";
import { ThirdPartyLinks } from "@storyteller/components/src/constants/ThirdPartyLinks";
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -19,25 +18,20 @@ function PatronPage(props: Props) {
PosthogClient.recordPageview();
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5 pt-lg-0">
<div className="row">
<div className="col-12 col-lg-7 d-flex flex-column justify-content-center text-center text-lg-start px-3 px-md-2 px-lg-5 px-xl-3">
<motion.h1 className=" fw-bold" variants={item}>
Thanks to our Patrons!
</motion.h1>
<motion.h3 className="mb-4 px-4 px-md-0" variants={item}>
<h1 className=" fw-bold">Thanks to our Patrons!</h1>
<h3 className="mb-4 px-4 px-md-0">
Our Patrons help support our work.
</motion.h3>
<motion.p className="lead mb-5" variants={item}>
</h3>
<p className="lead mb-5">
Our Patrons help pay offset (but not completely cover) our
expensive server bills.
</motion.p>
</p>
<motion.div
className="d-flex justify-content-center justify-content-lg-start"
variants={item}
>
<div className="d-flex justify-content-center justify-content-lg-start">
<a
href={ThirdPartyLinks.FAKEYOU_PATREON}
target="_blank"
@@ -47,22 +41,21 @@ function PatronPage(props: Props) {
<FontAwesomeIcon icon={faPatreon} className="me-2" />
Support us on Patreon
</a>
</motion.div>
</div>
</div>
<div className="col-12 col-lg-5 d-flex flex-column align-items-center">
<motion.img
<img
className="img-fluid mt-5 mw-50"
src="/mascot/kitsune_pose7.webp"
alt="FakeYou Mascot"
width="423"
height="387"
variants={image}
/>
</div>
</div>
</div>
<motion.div className="container-panel pb-5" variants={panel}>
<div className="container-panel pb-5">
<div className="panel p-3 p-lg-4">
<h1 className="panel-title fw-bold">Our Patrons</h1>
<div className="py-6">
@@ -81,9 +74,9 @@ function PatronPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<div>
<p>
Patrons will get first looks at new features, get dedicated access
@@ -95,8 +88,8 @@ function PatronPage(props: Props) {
<PatreonLink text="donating on Patreon" iconAfterText={true} />
</p>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -1,7 +1,6 @@
import React from "react";
import { Link } from "react-router-dom";
import { motion } from "framer-motion";
import { container, item, image } from "../../../../data/animation";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -14,44 +13,41 @@ function CheckoutCancelPage(props: Props) {
PosthogClient.recordPageview();
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container mb-4">
<div className="row gx-3 flex-lg-row-reverse align-items-center">
<div className="col-lg-6">
<div className="d-flex justify-content-center">
<motion.img
<img
src="/mascot/kitsune_pose7.webp"
className="img-fluid"
width="516"
height="444"
alt="FakeYou Kitsune Mascot!"
variants={image}
/>
</div>
</div>
<div className="col-lg-6 px-md-2 ps-lg-5 ps-xl-2">
<div className="text-center text-lg-start">
<div>
<motion.h1 className=" fw-bold lh-1 mb-4" variants={item}>
Oh no!
</motion.h1>
<h1 className=" fw-bold lh-1 mb-4">Oh no!</h1>
</div>
<div>
<motion.p className="lead" variants={item}>
<p className="lead">
It's okay if you don't want to support FakeYou right now, but
we'd appreciate it if you reconsider us in the future.
</motion.p>
</p>
<br />
<motion.p className="lead" variants={item}>
<p className="lead">
Paid plans go directly to helping us afford more GPUs,
engineering, and research talent.
</motion.p>
</p>
<br />
<motion.p className="lead" variants={item}>
<p className="lead">
We're trying to build a film and music production system that
you can use to make any content you dream up. Please consider
supporting us monetarily.
</motion.p>
</p>
</div>
</div>
</div>
@@ -71,7 +67,7 @@ function CheckoutCancelPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
);
}
@@ -1,7 +1,6 @@
import React from "react";
import { Link } from "react-router-dom";
import { motion } from "framer-motion";
import { container, item, image } from "../../../../data/animation";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -14,44 +13,39 @@ function CheckoutSuccessPage(props: Props) {
PosthogClient.recordPageview();
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container mb-4">
<div className="row gx-3 flex-lg-row-reverse align-items-center">
<div className="col-lg-6">
<div className="d-flex justify-content-center">
<motion.img
<img
src="/mascot/kitsune_pose3.webp"
className="img-fluid"
width="516"
height="444"
alt="FakeYou Kitsune Mascot!"
variants={image}
/>
</div>
</div>
<div className="col-lg-6 px-md-2 ps-lg-5 ps-xl-2">
<div className="text-center text-lg-start">
<div>
<motion.h1 className=" fw-bold lh-1 mb-4" variants={item}>
Thank you!
</motion.h1>
<h1 className=" fw-bold lh-1 mb-4">Thank you!</h1>
</div>
<br />
<div>
<motion.p className="lead" variants={item}>
<p className="lead">
We're grateful you're helping FakeYou build the future
creative suite. We're building tools for small creators and
have an eye on building a full AI-powered Hollywood and music
production studio.
</motion.p>
</p>
<br />
<motion.p className="lead" variants={item}>
Your support helps us immensely!
</motion.p>
<p className="lead">Your support helps us immensely!</p>
<br />
<motion.p className="lead" variants={item}>
<p className="lead">
Please give it a few minutes for your purchase to take effect.
</motion.p>
</p>
</div>
</div>
</div>
@@ -64,7 +58,7 @@ function CheckoutSuccessPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
);
}
@@ -1,7 +1,6 @@
import React from "react";
import { Link } from "react-router-dom";
import { motion } from "framer-motion";
import { container, item, image } from "../../../../data/animation";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -14,35 +13,28 @@ function PortalSuccessPage(props: Props) {
PosthogClient.recordPageview();
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container mb-4">
<div className="row gx-3 flex-lg-row-reverse align-items-center">
<div className="col-lg-6">
<div className="d-flex justify-content-center">
<motion.img
<img
src="/mascot/kitsune_pose3.webp"
className="img-fluid"
width="516"
height="444"
alt="FakeYou Kitsune Mascot!"
variants={image}
/>
</div>
</div>
<div className="col-lg-6 px-md-2 ps-lg-5 ps-xl-2">
<div className="text-center text-lg-start">
<div>
<motion.h1 className=" fw-bold lh-1 mb-4" variants={item}>
Thank You!
</motion.h1>
<h1 className=" fw-bold lh-1 mb-4">Thank You!</h1>
</div>
<div>
<motion.p className="lead" variants={item}>
Your support means a lot to us.
</motion.p>
<motion.p className="lead" variants={item}>
Please continue to enjoy FakeYou!
</motion.p>
<p className="lead">Your support means a lot to us.</p>
<p className="lead">Please continue to enjoy FakeYou!</p>
</div>
</div>
</div>
@@ -55,7 +47,7 @@ function PortalSuccessPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
);
}
@@ -10,8 +10,7 @@ import {
CreateStripePortalRedirectIsError,
CreateStripePortalRedirectIsSuccess,
} from "@storyteller/components/src/api/premium/CreateStripePortalRedirect";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../data/animation";
import { FakeYouFrontendEnvironment } from "@storyteller/components/src/env/FakeYouFrontendEnvironment";
import { Analytics } from "../../../../common/Analytics";
import { WebUrl } from "../../../../common/WebUrl";
@@ -148,26 +147,21 @@ function PricingPage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 text-center">
<motion.h1 className=" fw-bold" variants={item}>
Pricing
</motion.h1>
<h1 className=" fw-bold">Pricing</h1>
{/* <p className="fs-5">
By purchasing FakeYou premium, you help us build more!
</p> */}
<motion.div
className="alert alert-warning mt-4 alert-pricing mb-2"
variants={item}
>
<div className="alert alert-warning mt-4 alert-pricing mb-2">
<FontAwesomeIcon icon={faHeart} className="text-red me-3" />
By purchasing FakeYou premium, you help us build more!
</motion.div>
</div>
</div>
<div className="container mb-5">
<div className="row gx-3 gy-4">
{/* Starter Tier */}
{/*<motion.div className="col-12 col-sm-6 col-lg-3" variants={panel}>
{/*<div className="col-12 col-sm-6 col-lg-3" >
<div className="rounded panel p-4 h-100">
<h2 className="text-center my-2 fw-bold mb-4">
{FYP.starter.tier}
@@ -218,11 +212,11 @@ function PricingPage(props: Props) {
})}
</ul>
</div>
</motion.div>
</div>
*/}
{/* Plus Tier */}
<motion.div className="col-12 col-sm-6 col-lg-4" variants={panel}>
<div className="col-12 col-sm-6 col-lg-4">
<div className="rounded panel p-4 h-100">
<h2 className="text-center my-2 fw-bold mb-4">{FYP.plus.tier}</h2>
<button
@@ -319,10 +313,10 @@ function PricingPage(props: Props) {
+ Future feature updates
</h6>
</div>
</motion.div>
</div>
{/* Pro Tier */}
<motion.div className="col-12 col-sm-6 col-lg-4" variants={panel}>
<div className="col-12 col-sm-6 col-lg-4">
<div className={proBorderCss}>
<h2 className="text-center my-2 fw-bold mb-4">{FYP.pro.tier}</h2>
<button
@@ -457,10 +451,10 @@ function PricingPage(props: Props) {
+ Future feature updates
</h6>
</div>
</motion.div>
</div>
{/* Elite Tier */}
<motion.div className="col-12 col-sm-6 col-lg-4" variants={panel}>
<div className="col-12 col-sm-6 col-lg-4">
<div className="rounded panel p-4 h-100">
<h2 className="text-center my-2 fw-bold mb-4">
{FYP.elite.tier}
@@ -609,7 +603,7 @@ function PricingPage(props: Props) {
+ Future feature updates
</h6>
</div>
</motion.div>
</div>
</div>
{unsubscribeSection}
@@ -719,7 +713,7 @@ function PricingPage(props: Props) {
</div>
</div> */}
</div>
</motion.div>
</div>
);
}
@@ -1,7 +1,6 @@
import React, { useState } from "react";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { motion } from "framer-motion";
import { container, panel } from "data/animation";
import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -61,11 +60,8 @@ function ProductUsageInfoPage(props: Props) {
};
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<motion.div
className="container-panel pt-lg-5 my-lg-5 login-panel"
variants={panel}
>
<div>
<div className="container-panel pt-lg-5 my-lg-5 login-panel">
<div className="panel p-4 p-lg-4 mt-5 mt-lg-0 px-md-4">
<h2 className="fw-bold pb-0">Product Usage Survey</h2>
<hr className="my-4" />
@@ -138,8 +134,8 @@ function ProductUsageInfoPage(props: Props) {
<button className="btn btn-primary">Submit</button>
</form>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -9,8 +9,7 @@ import {
User,
} from "@storyteller/components/src/api/user/GetUserByUsername";
import { BackLink } from "../../../_common/BackLink";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface Props {
@@ -111,17 +110,15 @@ function ProfileBanFc(props: Props) {
let isDisabled = userData === undefined;
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
<motion.h1 className=" fw-bold mb-3" variants={item}>
Profile &amp; Preferences
</motion.h1>
<motion.div variants={item}>
<h1 className=" fw-bold mb-3">Profile &amp; Preferences</h1>
<div>
<BackLink link={viewLinkUrl} text="Back to profile" />
</motion.div>
</div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<fieldset disabled={isDisabled}>
<div className="container-panel py-5">
<div className="panel p-3 p-lg-4">
@@ -164,21 +161,21 @@ function ProfileBanFc(props: Props) {
</div>
</div>
<motion.div className="container" variants={item}>
<div className="container">
<button className="btn btn-primary w-100">Update Ban</button>
</motion.div>
</div>
</fieldset>
</motion.form>
</form>
<motion.div className="container py-5" variants={item}>
<div className="container py-5">
<p>Notes on banned users:</p>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -19,8 +19,7 @@ import {
faGlobe,
} from "@fortawesome/free-solid-svg-icons";
import { BackLink } from "../../../_common/BackLink";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
const DEFAULT_VISIBILITY = "public";
@@ -63,7 +62,7 @@ interface UserPayload {
function ProfileEditFc(props: Props) {
const { username } = useParams() as { username: string };
PosthogClient.recordPageview();
const userProfilePage = `/profile/${username}`;
const history = useHistory();
@@ -237,17 +236,15 @@ function ProfileEditFc(props: Props) {
);
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
<motion.h1 className=" fw-bold mb-3" variants={item}>
Profile &amp; Preferences
</motion.h1>
<motion.div variants={item}>
<h1 className=" fw-bold mb-3">Profile &amp; Preferences</h1>
<div>
<BackLink link={viewLinkUrl} text="Back to profile" />
</motion.div>
</div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<fieldset disabled={isDisabled}>
<div className="container-panel py-5">
<div className="panel p-3 p-lg-4">
@@ -301,7 +298,7 @@ function ProfileEditFc(props: Props) {
</div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Profile Picture</h2>
<div className="py-6">
@@ -323,9 +320,9 @@ function ProfileEditFc(props: Props) {
</p>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Profile Details</h2>
<div className="py-6">
@@ -507,15 +504,15 @@ function ProfileEditFc(props: Props) {
</div>
</div>
</div>
</motion.div>
</div>
<motion.div className="container" variants={item}>
<div className="container">
<button className="btn btn-primary w-100">Update</button>
</motion.div>
</div>
</fieldset>
</motion.form>
</form>
<motion.div className="container-panel pt-5" variants={item}>
<div className="container-panel pt-5">
<div className="panel p-4 mt-5">
<h4 className="fw-semibold mb-3">Cancel My Subscription</h4>
<p className="mb-4 fs-14">
@@ -527,8 +524,8 @@ function ProfileEditFc(props: Props) {
</Link>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -8,43 +8,49 @@ import { ProfileW2lInferenceResultsListFc } from "./Profile_W2lInferenceResultLi
import { ProfileW2lTemplateListFc } from "./Profile_W2lTemplateListFc";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { Route, Switch, useLocation, useParams, useRouteMatch } from "react-router-dom";
import {
Route,
Switch,
useLocation,
useParams,
useRouteMatch,
} from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faDiscord,
faGithub,
faTwitch,
faTwitter,
faDiscord,
faGithub,
faTwitch,
faTwitter,
} from "@fortawesome/free-brands-svg-icons";
import {
faBan,
faGear,
faGlobe,
faDollarSign,
faStar,
faBan,
faGear,
faGlobe,
faDollarSign,
faStar,
} from "@fortawesome/free-solid-svg-icons";
import {
GetUserByUsername,
GetUserByUsernameIsErr,
GetUserByUsernameIsOk,
User,
UserLookupError,
GetUserByUsername,
GetUserByUsernameIsErr,
GetUserByUsernameIsOk,
User,
UserLookupError,
} from "@storyteller/components/src/api/user/GetUserByUsername";
import { format } from "date-fns";
import { WebUrl } from "../../../../../common/WebUrl";
import { container, item, panel } from "../../../../../data/animation";
import Tippy from "@tippyjs/react";
import "tippy.js/dist/tippy.css";
import { motion } from "framer-motion";
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
import { faCalendarAlt } from "@fortawesome/pro-solid-svg-icons";
import { CommentComponent } from "../../../_common/comments/CommentComponent";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface Props {
sessionWrapper: SessionWrapper;
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
sessionWrapper: SessionWrapper;
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
}
// function copyToClipboard(username: string): any {
@@ -52,425 +58,474 @@ interface Props {
// }
function ProfilePage(this: any, props: Props) {
const { username }: { username: string } = useParams();
PosthogClient.recordPageview();
const { username }: { username: string } = useParams();
PosthogClient.recordPageview();
const { pathname } = useLocation();
const { url } = useRouteMatch();
const { pathname } = useLocation();
const { url } = useRouteMatch();
const [userData, setUserData] = useState<User | undefined>(undefined);
const [notFoundState, setNotFoundState] = useState<boolean>(false);
const [userData, setUserData] = useState<User | undefined>(undefined);
const [notFoundState, setNotFoundState] = useState<boolean>(false);
const getUser = useCallback(async (username) => {
const response = await GetUserByUsername(username);
if (GetUserByUsernameIsOk(response)) {
setUserData(response);
} else if (GetUserByUsernameIsErr(response)) {
switch (response) {
case UserLookupError.NotFound:
setNotFoundState(true);
break;
}
const getUser = useCallback(async (username) => {
const response = await GetUserByUsername(username);
if (GetUserByUsernameIsOk(response)) {
setUserData(response);
} else if (GetUserByUsernameIsErr(response)) {
switch (response) {
case UserLookupError.NotFound:
setNotFoundState(true);
break;
}
}, []);
}
}, []);
useEffect(() => {
getUser(username);
}, [username, getUser]);
useEffect(() => {
getUser(username);
}, [username, getUser]);
const documentTitle =
userData?.display_name === undefined
? undefined
: `${userData.display_name}`;
usePrefixedDocumentTitle(documentTitle);
const documentTitle =
userData?.display_name === undefined
? undefined
: `${userData.display_name}`;
usePrefixedDocumentTitle(documentTitle);
if (notFoundState) {
return (
<div className="container py-5">
<div className="py-5">
<h1 className="fw-semibold text-center mb-4">User not found</h1>
<div className="text-center">
<Link className="btn btn-primary" to="/">
Back to main
</Link>
</div>
</div>
</div>
);
}
if (!userData) {
return <div />;
}
let userEmailHash = userData.email_gravatar_hash;
let editProfileButton = undefined;
let banUserButton = undefined;
let upgradeButton = undefined;
if (props.sessionWrapper.isLoggedIn()) {
if (props.sessionWrapper.userTokenMatches(userData.user_token)) {
if (!props.sessionSubscriptionsWrapper.hasPaidFeatures()) {
const upgradeLinkUrl = WebUrl.pricingPage();
upgradeButton = (
<>
<Link className="btn btn-primary" to={upgradeLinkUrl}>
<FontAwesomeIcon icon={faStar} className="me-2" />
<span>Upgrade</span>
</Link>
</>
);
}
}
}
if (props.sessionWrapper.canBanUsers()) {
const currentlyBanned = userData.maybe_moderator_fields?.is_banned;
const banLinkUrl = WebUrl.userProfileBanPage(userData.username);
const buttonLabel = currentlyBanned ? "Unban" : "Ban";
const banButtonCss = currentlyBanned
? "btn btn-secondary"
: "btn btn-destructive";
banUserButton = (
<>
<Link className={banButtonCss} to={banLinkUrl}>
<FontAwesomeIcon icon={faBan} className="me-2" />
{buttonLabel}
if (notFoundState) {
return (
<div className="container py-5">
<div className="py-5">
<h1 className="fw-semibold text-center mb-4">User not found</h1>
<div className="text-center">
<Link className="btn btn-primary" to="/">
Back to main
</Link>
</>
);
}
let profileButtonsMobile = <span />;
if (props.sessionWrapper.canEditUserProfile(userData.username)) {
const editLinkUrl = WebUrl.userProfileEditPage(userData.username);
// Mods shouldn't edit preferences.
const buttonLabel = props.sessionWrapper.userTokenMatches(
userData.user_token
)
? "Edit Profile & Preferences"
: "Edit Profile";
editProfileButton = (
<>
<Link className={"btn btn-secondary"} to={editLinkUrl}>
<FontAwesomeIcon icon={faGear} className="me-2" />
{buttonLabel}
</Link>
</>
);
profileButtonsMobile = (
<div className="container d-flex d-lg-none mb-4">
<div className="d-flex w-100 gap-3 justify-content-center flex-column flex-md-row">
{upgradeButton}
{editProfileButton}
{banUserButton}
</div>
</div>
);
}
let profileDesc = (
<motion.div className="mt-3 text-center text-lg-start opacity-50">
No profile description.
</motion.div>
);
if (!!userData.profile_rendered_html) {
profileDesc = (
<motion.div
className="mt-3 text-center text-lg-start"
variants={item}
dangerouslySetInnerHTML={{
__html: userData.profile_rendered_html || "",
}}
/>
);
}
let profileRows: Array<JSX.Element> = [];
let profileJoinDate: Array<JSX.Element> = [];
const createdAt = new Date(userData.created_at);
const joinDate = format(createdAt, "LLLL y");
profileJoinDate.push(
<div
key="created"
className="d-flex align-items-center justify-content-center justify-content-lg-start"
>
<FontAwesomeIcon icon={faCalendarAlt} className="me-2" />
<p className="fw-normal">Joined {joinDate}</p>
</div>
</div>
</div>
);
);
}
if (userData.website_url !== undefined && userData.website_url !== null) {
let websiteUrl = <span>{userData.website_url}</span>;
if (
userData.website_url?.startsWith("http://") ||
userData.website_url?.startsWith("https://")
) {
websiteUrl = (
<Tippy
content="Website"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a
href={userData.website_url}
target="_blank"
rel="noopener noreferrer nofollow"
>
<FontAwesomeIcon icon={faGlobe} />
</a>
</Tippy>
);
}
if (!userData) {
return <div />;
}
profileRows.push(<div key="website">{websiteUrl}</div>);
}
let userEmailHash = userData.email_gravatar_hash;
if (userData.twitch_username) {
let twitchUrl = `https://twitch.com/${userData.twitch_username}`;
let twitchLink = (
<Tippy
content="Twitch"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a href={twitchUrl} target="_blank" rel="noopener noreferrer nofollow">
<FontAwesomeIcon icon={faTwitch} />
</a>
</Tippy>
);
let editProfileButton = undefined;
profileRows.push(<div key="twitch">{twitchLink}</div>);
}
let banUserButton = undefined;
if (userData.twitter_username) {
let twitterUrl = `https://twitter.com/${userData.twitter_username}`;
let twitterLink = (
<Tippy
content="Twitter"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a href={twitterUrl} target="_blank" rel="noopener noreferrer nofollow">
<FontAwesomeIcon icon={faTwitter} />
</a>
</Tippy>
);
profileRows.push(<div key="twitter">{twitterLink}</div>);
}
let upgradeButton = undefined;
if (userData.discord_username) {
profileRows.push(
<Tippy
allowHTML
content={
<div className="text-center">
Discord
<br />
<p className="fw-semibold fs-6">{userData.discord_username}</p>
</div>
}
interactive
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a
// eslint-disable-next-line no-script-url
href="javascript:;"
// onClick={copyToClipboard(userData.discord_username)}
>
<FontAwesomeIcon icon={faDiscord} />
</a>
</Tippy>
);
}
if (props.sessionWrapper.isLoggedIn()) {
if (props.sessionWrapper.userTokenMatches(userData.user_token)) {
if (!props.sessionSubscriptionsWrapper.hasPaidFeatures()) {
const upgradeLinkUrl = WebUrl.pricingPage();
if (userData.github_username) {
let githubUrl = `https://github.com/${userData.github_username}`;
let githubLink = (
<Tippy
content="GitHub"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a
href={githubUrl}
target="_blank"
rel="noopener noreferrer nofollow"
className="fw-normal"
>
<FontAwesomeIcon icon={faGithub} />
</a>
</Tippy>
);
profileRows.push(<div key="github">{githubLink}</div>);
}
if (userData.cashapp_username) {
// NB: URL includes a dollar sign
let cashAppUrl = `https://cash.me/$${userData.cashapp_username}`;
let cashAppLink = (
<Tippy
content="CashApp"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a href={cashAppUrl} target="_blank" rel="noopener noreferrer nofollow">
<FontAwesomeIcon icon={faDollarSign} />
</a>
</Tippy>
);
profileRows.push(<div key="cashapp">{cashAppLink}</div>);
}
const Badges = () => {
return userData.badges.length ? <ul>{userData.badges.map((badge) => <li key={badge.slug}>{badge.title}</li>)}</ul> : <div>None yet</div>
};
const Comments = () => <div className="mt-3 mt-lg-0">
<CommentComponent { ...{ entityType: 'user', entityToken: userData?.user_token, sessionWrapper: props.sessionWrapper }}/>
</div>;
const tabs = [
{ Component: ProfileTtsInferenceResultsListFc, val: 'ttsresults', txt: 'TTS Results' },
{ Component: ProfileW2lInferenceResultsListFc, val: 'w2lresults', txt: 'Lipsync Results' },
{ Component: ProfileTtsModelListFc, val: 'uploadedtts', txt: 'Uploaded TTS Models' },
{ Component: ProfileW2lTemplateListFc, val: 'uploadedw2l', txt: 'Uploaded Templates' },
{ Component: Badges, val: 'badges', txt: 'Badges' },
{ Component: Comments, val: 'comments', txt: 'Comments' }
];
const txtToUrl = (txt = '') => txt.replaceAll(' ','-').toLowerCase();
const tabLinks = tabs.map(({ val, txt },key) => {
let subParam = pathname.split(url + '/')[1] || '';
let isActive = txtToUrl(txt) === subParam || (!subParam && !key);
return <li {...{ className: 'nav-item', key, role: 'presentation' }}>
<Link {...{
'aria-controls': val,
'aria-selected': isActive,
className: `nav-link${ isActive ? ' active' : '' }`,
id: `${val}-tab`,
role: 'tab',
to: `${url.replace(/\/$/, '')}/${txtToUrl(txt)}`
}}>
{ txt }
upgradeButton = (
<>
<Link className="btn btn-primary" to={upgradeLinkUrl}>
<FontAwesomeIcon icon={faStar} className="me-2" />
<span>Upgrade</span>
</Link>
</li>;
});
</>
);
}
}
}
const BaseRoute = ({ baseUrl }: { baseUrl?: string }) => {
return <Route {...{ path: baseUrl || url }}>
<motion.div {...{ 'aria-labelledby': 'ttsresults-tab', className: 'tab-pane fade show active', id: 'ttsresults', role: 'tabpanel', variants: panel }}>
<ProfileTtsInferenceResultsListFc username={userData.username}/>
</motion.div>
</Route>;
};
if (props.sessionWrapper.canBanUsers()) {
const currentlyBanned = userData.maybe_moderator_fields?.is_banned;
const banLinkUrl = WebUrl.userProfileBanPage(userData.username);
const buttonLabel = currentlyBanned ? "Unban" : "Ban";
const banButtonCss = currentlyBanned
? "btn btn-secondary"
: "btn btn-destructive";
const tabPanels = tabs.map(({ Component, txt, val },key) => {
let subRoute = `${url}/${txtToUrl(txt)}`
return !key ? BaseRoute({ baseUrl: subRoute }) : <Route {...{ key, path: subRoute }}>
<motion.div {...{ 'aria-labelledby': `${val}-tab`, id: 'val', role: 'tabpanel', variants: panel }}
className="tab-pane fade show active p-3 p-lg-4"
>
<Component username={userData.username}/>
</motion.div>
banUserButton = (
<>
<Link className={banButtonCss} to={banLinkUrl}>
<FontAwesomeIcon icon={faBan} className="me-2" />
{buttonLabel}
</Link>
</>
);
}
let profileButtonsMobile = <span />;
if (props.sessionWrapper.canEditUserProfile(userData.username)) {
const editLinkUrl = WebUrl.userProfileEditPage(userData.username);
// Mods shouldn't edit preferences.
const buttonLabel = props.sessionWrapper.userTokenMatches(
userData.user_token
)
? "Edit Profile & Preferences"
: "Edit Profile";
editProfileButton = (
<>
<Link className={"btn btn-secondary"} to={editLinkUrl}>
<FontAwesomeIcon icon={faGear} className="me-2" />
{buttonLabel}
</Link>
</>
);
profileButtonsMobile = (
<div className="container d-flex d-lg-none mb-4">
<div className="d-flex w-100 gap-3 justify-content-center flex-column flex-md-row">
{upgradeButton}
{editProfileButton}
{banUserButton}
</div>
</div>
);
}
let profileDesc = (
<div className="mt-3 text-center text-lg-start opacity-50">
No profile description.
</div>
);
if (!!userData.profile_rendered_html) {
profileDesc = (
<div
className="mt-3 text-center text-lg-start"
dangerouslySetInnerHTML={{
__html: userData.profile_rendered_html || "",
}}
/>
);
}
let profileRows: Array<JSX.Element> = [];
let profileJoinDate: Array<JSX.Element> = [];
const createdAt = new Date(userData.created_at);
const joinDate = format(createdAt, "LLLL y");
profileJoinDate.push(
<div
key="created"
className="d-flex align-items-center justify-content-center justify-content-lg-start"
>
<FontAwesomeIcon icon={faCalendarAlt} className="me-2" />
<p className="fw-normal">Joined {joinDate}</p>
</div>
);
if (userData.website_url !== undefined && userData.website_url !== null) {
let websiteUrl = <span>{userData.website_url}</span>;
if (
userData.website_url?.startsWith("http://") ||
userData.website_url?.startsWith("https://")
) {
websiteUrl = (
<Tippy
content="Website"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a
href={userData.website_url}
target="_blank"
rel="noopener noreferrer nofollow"
>
<FontAwesomeIcon icon={faGlobe} />
</a>
</Tippy>
);
}
profileRows.push(<div key="website">{websiteUrl}</div>);
}
if (userData.twitch_username) {
let twitchUrl = `https://twitch.com/${userData.twitch_username}`;
let twitchLink = (
<Tippy
content="Twitch"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a href={twitchUrl} target="_blank" rel="noopener noreferrer nofollow">
<FontAwesomeIcon icon={faTwitch} />
</a>
</Tippy>
);
profileRows.push(<div key="twitch">{twitchLink}</div>);
}
if (userData.twitter_username) {
let twitterUrl = `https://twitter.com/${userData.twitter_username}`;
let twitterLink = (
<Tippy
content="Twitter"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a href={twitterUrl} target="_blank" rel="noopener noreferrer nofollow">
<FontAwesomeIcon icon={faTwitter} />
</a>
</Tippy>
);
profileRows.push(<div key="twitter">{twitterLink}</div>);
}
if (userData.discord_username) {
profileRows.push(
<Tippy
allowHTML
content={
<div className="text-center">
Discord
<br />
<p className="fw-semibold fs-6">{userData.discord_username}</p>
</div>
}
interactive
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a
// eslint-disable-next-line no-script-url
href="javascript:;"
// onClick={copyToClipboard(userData.discord_username)}
>
<FontAwesomeIcon icon={faDiscord} />
</a>
</Tippy>
);
}
if (userData.github_username) {
let githubUrl = `https://github.com/${userData.github_username}`;
let githubLink = (
<Tippy
content="GitHub"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a
href={githubUrl}
target="_blank"
rel="noopener noreferrer nofollow"
className="fw-normal"
>
<FontAwesomeIcon icon={faGithub} />
</a>
</Tippy>
);
profileRows.push(<div key="github">{githubLink}</div>);
}
if (userData.cashapp_username) {
// NB: URL includes a dollar sign
let cashAppUrl = `https://cash.me/$${userData.cashapp_username}`;
let cashAppLink = (
<Tippy
content="CashApp"
hideOnClick
placement="bottom"
theme="fakeyou"
arrow={false}
>
<a href={cashAppUrl} target="_blank" rel="noopener noreferrer nofollow">
<FontAwesomeIcon icon={faDollarSign} />
</a>
</Tippy>
);
profileRows.push(<div key="cashapp">{cashAppLink}</div>);
}
const Badges = () => {
return userData.badges.length ? (
<ul>
{userData.badges.map((badge) => (
<li key={badge.slug}>{badge.title}</li>
))}
</ul>
) : (
<div>None yet</div>
);
};
const Comments = () => (
<div className="mt-3 mt-lg-0">
<CommentComponent
{...{
entityType: "user",
entityToken: userData?.user_token,
sessionWrapper: props.sessionWrapper,
}}
/>
</div>
);
const tabs = [
{
Component: ProfileTtsInferenceResultsListFc,
val: "ttsresults",
txt: "TTS Results",
},
{
Component: ProfileW2lInferenceResultsListFc,
val: "w2lresults",
txt: "Lipsync Results",
},
{
Component: ProfileTtsModelListFc,
val: "uploadedtts",
txt: "Uploaded TTS Models",
},
{
Component: ProfileW2lTemplateListFc,
val: "uploadedw2l",
txt: "Uploaded Templates",
},
{ Component: Badges, val: "badges", txt: "Badges" },
{ Component: Comments, val: "comments", txt: "Comments" },
];
const txtToUrl = (txt = "") => txt.replaceAll(" ", "-").toLowerCase();
const tabLinks = tabs.map(({ val, txt }, key) => {
let subParam = pathname.split(url + "/")[1] || "";
let isActive = txtToUrl(txt) === subParam || (!subParam && !key);
return (
<li {...{ className: "nav-item", key, role: "presentation" }}>
<Link
{...{
"aria-controls": val,
"aria-selected": isActive,
className: `nav-link${isActive ? " active" : ""}`,
id: `${val}-tab`,
role: "tab",
to: `${url.replace(/\/$/, "")}/${txtToUrl(txt)}`,
}}
>
{txt}
</Link>
</li>
);
});
const BaseRoute = ({ baseUrl }: { baseUrl?: string }) => {
return (
<Route {...{ path: baseUrl || url }}>
<div
{...{
"aria-labelledby": "ttsresults-tab",
className: "tab-pane fade show active",
id: "ttsresults",
role: "tabpanel",
}}
>
<ProfileTtsInferenceResultsListFc username={userData.username} />
</div>
</Route>
});
);
};
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
<motion.div
className="d-flex flex-column flex-lg-row w-100"
variants={item}
>
<div className="mb-3 me-lg-4 mb-lg-0">
<div className="border-3 d-none d-lg-block">
<Gravatar
size={150}
username={userData.display_name}
email_hash={userEmailHash}
avatarIndex={userData.default_avatar_index}
backgroundIndex={userData.default_avatar_color_index}
/>
</div>
<div className="border-3 text-center d-lg-none">
<Gravatar
size={100}
username={userData.display_name}
email_hash={userEmailHash}
avatarIndex={userData.default_avatar_index}
backgroundIndex={userData.default_avatar_color_index}
/>
</div>
</div>
<div className="d-flex flex-column w-100">
<div className="d-flex">
<h1 className="fw-bold mb-0 flex-grow-1 text-center text-lg-start">
{userData.display_name}
</h1>
<div className="gap-3 d-none d-lg-flex">
{banUserButton}
{upgradeButton}
{editProfileButton}
</div>
</div>
<div className="opacity-75 mt-1">{profileJoinDate}</div>
<div>{profileDesc}</div>
<div className="d-flex mt-3 gap-4 gap-lg-3 profile-social-icons justify-content-center justify-content-lg-start">
{profileRows}
</div>
</div>
</motion.div>
</div>
const tabPanels = tabs.map(({ Component, txt, val }, key) => {
let subRoute = `${url}/${txtToUrl(txt)}`;
return !key ? (
BaseRoute({ baseUrl: subRoute })
) : (
<Route {...{ key, path: subRoute }}>
<div
{...{
"aria-labelledby": `${val}-tab`,
id: "val",
role: "tabpanel",
}}
className="tab-pane fade show active p-3 p-lg-4"
>
<Component username={userData.username} />
</div>
</Route>
);
});
{profileButtonsMobile}
<div className="container-panel mt-5">
<div className="panel">
<motion.ul
className="nav nav-tabs nav-profile justify-content-lg-center"
id="myTab"
role="tablist"
variants={panel}
>
{ tabLinks }
</motion.ul>
<div className="tab-content" id="myTabContent">
<Switch>
{ tabPanels }
{<BaseRoute />}
</Switch>
</div>
return (
<div>
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column flex-lg-row w-100">
<div className="mb-3 me-lg-4 mb-lg-0">
<div className="border-3 d-none d-lg-block">
<Gravatar
size={150}
username={userData.display_name}
email_hash={userEmailHash}
avatarIndex={userData.default_avatar_index}
backgroundIndex={userData.default_avatar_color_index}
/>
</div>
</div>
</motion.div>
);
<div className="border-3 text-center d-lg-none">
<Gravatar
size={100}
username={userData.display_name}
email_hash={userEmailHash}
avatarIndex={userData.default_avatar_index}
backgroundIndex={userData.default_avatar_color_index}
/>
</div>
</div>
<div className="d-flex flex-column w-100">
<div className="d-flex">
<h1 className="fw-bold mb-0 flex-grow-1 text-center text-lg-start">
{userData.display_name}
</h1>
<div className="gap-3 d-none d-lg-flex">
{banUserButton}
{upgradeButton}
{editProfileButton}
</div>
</div>
<div className="opacity-75 mt-1">{profileJoinDate}</div>
<div>{profileDesc}</div>
<div className="d-flex mt-3 gap-4 gap-lg-3 profile-social-icons justify-content-center justify-content-lg-start">
{profileRows}
</div>
</div>
</div>
</div>
{profileButtonsMobile}
<div className="container-panel mt-5">
<div className="panel">
<ul
className="nav nav-tabs nav-profile justify-content-lg-center"
id="myTab"
role="tablist"
>
{tabLinks}
</ul>
<div className="tab-content" id="myTabContent">
<Switch>
{tabPanels}
{<BaseRoute />}
</Switch>
</div>
</div>
</div>
</div>
);
}
export { ProfilePage };
@@ -10,8 +10,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faUser, faEnvelope, faKey } from "@fortawesome/free-solid-svg-icons";
import { Link, useHistory } from "react-router-dom";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { motion } from "framer-motion";
import { container, panel } from "../../../../data/animation";
import { Analytics } from "../../../../common/Analytics";
import queryString from "query-string";
import { WebUrl } from "../../../../common/WebUrl";
@@ -71,8 +70,7 @@ function SignupPage(props: Props) {
usernameInvalidReason = t("account.SignUpPage.errors.usernameTooShort");
} else if (newUsername.length > 15) {
usernameValid = FieldTriState.FALSE;
usernameInvalidReason = t("account.SignUpPage.errors.usernameTooLong")
usernameInvalidReason = t("account.SignUpPage.errors.usernameTooLong");
} else {
usernameValid = FieldTriState.TRUE;
}
@@ -100,7 +98,6 @@ function SignupPage(props: Props) {
} else if (!newEmail.includes("@")) {
emailValid = FieldTriState.FALSE;
emailInvalidReason = t("account.SignUpPage.errors.emailInvalid");
} else {
emailValid = FieldTriState.TRUE;
}
@@ -133,8 +130,9 @@ function SignupPage(props: Props) {
if (newPassword !== passwordConfirmation) {
passwordConfirmationValid = FieldTriState.FALSE;
passwordConfirmationInvalidReason = t("account.SignUpPage.errors.passwordsDoNotMatch");
passwordConfirmationInvalidReason = t(
"account.SignUpPage.errors.passwordsDoNotMatch"
);
} else {
passwordConfirmationValid = FieldTriState.TRUE;
passwordConfirmationInvalidReason = "";
@@ -163,7 +161,9 @@ function SignupPage(props: Props) {
if (newPasswordConfirmation.length > 1) {
if (newPasswordConfirmation !== password) {
passwordConfirmationValid = FieldTriState.FALSE;
passwordConfirmationInvalidReason = t("account.SignUpPage.errors.passwordsDoNotMatch");
passwordConfirmationInvalidReason = t(
"account.SignUpPage.errors.passwordsDoNotMatch"
);
} else {
passwordConfirmationValid = FieldTriState.TRUE;
passwordConfirmationInvalidReason = "";
@@ -254,7 +254,7 @@ function SignupPage(props: Props) {
};
const afterSignupRedirect = async () => {
const maybeInternalPlanKey = parsedQueryString['sub'] as string | undefined;
const maybeInternalPlanKey = parsedQueryString["sub"] as string | undefined;
if (maybeInternalPlanKey !== undefined) {
return await BeginStripeCheckoutFlow(maybeInternalPlanKey);
@@ -264,7 +264,7 @@ function SignupPage(props: Props) {
history.push(redirectUrl);
return true;
}
};
usePrefixedDocumentTitle("Create an account");
@@ -346,11 +346,8 @@ function SignupPage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<motion.div
className="container-panel pb-5 pt-lg-5 my-lg-5 login-panel"
variants={panel}
>
<div>
<div className="container-panel pb-5 pt-lg-5 my-lg-5 login-panel">
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0 px-md-4">
<h1 className="panel-title fw-bold ">
{t("account.SignUpPage.signUpTitle")}
@@ -369,7 +366,9 @@ function SignupPage(props: Props) {
<input
className={usernameInputClass}
type="text"
placeholder={t("account.SignUpPage.inputs.usernamePlaceholder")}
placeholder={t(
"account.SignUpPage.inputs.usernamePlaceholder"
)}
value={username}
onChange={handleUsernameChange}
/>
@@ -387,7 +386,9 @@ function SignupPage(props: Props) {
<input
className={emailInputClass}
type="email"
placeholder={t("account.SignUpPage.inputs.emailPlaceholder")}
placeholder={t(
"account.SignUpPage.inputs.emailPlaceholder"
)}
value={email}
onChange={handleEmailChange}
/>
@@ -405,7 +406,9 @@ function SignupPage(props: Props) {
<input
className={passwordInputClass}
type="password"
placeholder={t("account.SignUpPage.inputs.passwordPlaceholder")}
placeholder={t(
"account.SignUpPage.inputs.passwordPlaceholder"
)}
value={password}
onChange={handlePasswordChange}
/>
@@ -423,7 +426,9 @@ function SignupPage(props: Props) {
<input
className={passwordConfirmationInputClass}
type="password"
placeholder={t("account.SignUpPage.inputs.passwordConfirmPlaceholder")}
placeholder={t(
"account.SignUpPage.inputs.passwordConfirmPlaceholder"
)}
value={passwordConfirmation}
onChange={handlePasswordConfirmationChange}
/>
@@ -451,8 +456,8 @@ function SignupPage(props: Props) {
</form>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -32,8 +32,7 @@ import {
faXmark,
} from "@fortawesome/free-solid-svg-icons";
import { useParams, Link } from "react-router-dom";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface Props {
@@ -259,25 +258,20 @@ function TtsEditCategoriesPage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5 pb-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
Edit Categories
</motion.h1>
<motion.h4 className="mb-4" variants={item}>
{" "}
TTS Model: {ttsModel.title}{" "}
</motion.h4>
<motion.p variants={item}>
<h1 className=" fw-bold">Edit Categories</h1>
<h4 className="mb-4"> TTS Model: {ttsModel.title} </h4>
<p>
<BackLink link={modelLink} text="Back to model" />
</motion.p>
</p>
</div>
</div>
{errorFlash}
<motion.div className="container-panel py-5" variants={panel}>
<div className="container-panel py-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Current categories</h2>
<div className="py-6">
@@ -287,9 +281,9 @@ function TtsEditCategoriesPage(props: Props) {
</ul>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Add new category</h2>
<div className="py-6">
@@ -307,8 +301,8 @@ function TtsEditCategoriesPage(props: Props) {
</div>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -4,8 +4,7 @@ import { WebUrl } from "../../../../../common/WebUrl";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { useParams, Link, useHistory } from "react-router-dom";
import { BackLink } from "../../../_common/BackLink";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface TtsModelViewResponsePayload {
@@ -215,19 +214,17 @@ function TtsModelDeletePage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5 pb-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold mb-3" variants={item}>
{h1Title}
</motion.h1>
<motion.p variants={item}>
<h1 className=" fw-bold mb-3">{h1Title}</h1>
<p>
<BackLink link={modelLink} text="Back to model" />
</motion.p>
</p>
</div>
</div>
<motion.form onSubmit={handleDeleteFormSubmit} variants={panel}>
<form onSubmit={handleDeleteFormSubmit}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 py-4 p-lg-4">
<table className="table">
@@ -273,14 +270,12 @@ function TtsModelDeletePage(props: Props) {
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className={buttonCss}>{buttonTitle}</button>
<motion.label className="pt-4" variants={item}>
{formLabel}
</motion.label>
</motion.div>
</motion.form>
</motion.div>
<label className="pt-4">{formLabel}</label>
</div>
</form>
</div>
);
}
@@ -35,8 +35,7 @@ import {
TEXT_PIPELINE_NAMES,
TEXT_PIPELINE_NAMES_FOR_MODERATORS,
} from "@storyteller/components/src/constants/TextPipeline";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { faFunction } from "@fortawesome/pro-solid-svg-icons";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -468,19 +467,17 @@ function TtsModelEditPage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5 pb-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold mb-3" variants={item}>
Edit Model
</motion.h1>
<motion.p variants={item}>
<h1 className=" fw-bold mb-3">Edit Model</h1>
<p>
<BackLink link={modelLink} text="Back to model" />
</motion.p>
</p>
</div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<fieldset disabled={isDisabled}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 py-4 p-lg-4">
@@ -627,12 +624,12 @@ function TtsModelEditPage(props: Props) {
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className="btn btn-primary w-100">Update</button>
</motion.div>
</div>
</fieldset>
</motion.form>
</motion.div>
</form>
</div>
);
}
@@ -7,7 +7,10 @@ import { SessionTtsModelUploadResultList } from "../../../_common/SessionTtsMode
import { SessionW2lInferenceResultList } from "../../../_common/SessionW2lInferenceResultsList";
import { SessionW2lTemplateUploadResultList } from "../../../_common/SessionW2lTemplateUploadResultsList";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { FrontendInferenceJobType, InferenceJob } from "@storyteller/components/src/jobs/InferenceJob";
import {
FrontendInferenceJobType,
InferenceJob,
} from "@storyteller/components/src/jobs/InferenceJob";
import { TtsInferenceJob } from "@storyteller/components/src/jobs/TtsInferenceJobs";
import { TtsModelUploadJob } from "@storyteller/components/src/jobs/TtsModelUploadJobs";
import { W2lInferenceJob } from "@storyteller/components/src/jobs/W2lInferenceJobs";
@@ -44,8 +47,7 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { GenericNotice } from "./notices/GenericNotice";
import { DiscordLink2 } from "@storyteller/components/src/elements/DiscordLink2";
import { motion } from "framer-motion";
import { container, panel } from "../../../../../data/animation";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { TtsPageHero } from "./TtsPageHero";
import { Analytics } from "../../../../../common/Analytics";
@@ -93,7 +95,10 @@ interface Props {
isShowingBootstrapLanguageNotice: boolean;
clearBootstrapLanguageNotice: () => void;
enqueueInferenceJob: (jobToken: string, frontendInferenceJobType: FrontendInferenceJobType) => void;
enqueueInferenceJob: (
jobToken: string,
frontendInferenceJobType: FrontendInferenceJobType
) => void;
inferenceJobs: Array<InferenceJob>;
inferenceJobsByCategory: Map<FrontendInferenceJobType, Array<InferenceJob>>;
@@ -303,11 +308,13 @@ function TtsModelListPage(props: Props) {
setMaybeTtsError(undefined);
if (response.inference_job_token_type === "generic") {
props.enqueueInferenceJob(response.inference_job_token, FrontendInferenceJobType.TextToSpeech);
props.enqueueInferenceJob(
response.inference_job_token,
FrontendInferenceJobType.TextToSpeech
);
} else {
props.enqueueTtsJob(response.inference_job_token);
}
} else if (GenerateTtsAudioIsError(response)) {
setMaybeTtsError(response.error);
}
@@ -469,7 +476,8 @@ function TtsModelListPage(props: Props) {
}
// NB: If the text is too long, don't allow submission
let remainingCharactersButtonDisabled = props.textBuffer.trim().length >
let remainingCharactersButtonDisabled =
props.textBuffer.trim().length >
props.sessionSubscriptionsWrapper.ttsMaximumLength();
let noTextInputButtonDisabled = props.textBuffer.trim() === "";
@@ -499,7 +507,7 @@ function TtsModelListPage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
{bootstrapLanguageNotice}
{pleaseFollowNotice}
@@ -515,7 +523,7 @@ function TtsModelListPage(props: Props) {
sessionSubscriptionsWrapper={props.sessionSubscriptionsWrapper}
/>
<motion.div className="container-panel pb-5 mb-4" variants={panel}>
<div className="container-panel pb-5 mb-4">
<div className="panel p-3 py-4 p-md-4">
<i className="fas fa-volume-high"></i>
@@ -615,7 +623,11 @@ function TtsModelListPage(props: Props) {
</h4>
<div className="d-flex flex-column gap-3 session-tts-section">
<SessionTtsInferenceResultList
inferenceJobs={props.inferenceJobsByCategory.get(FrontendInferenceJobType.TextToSpeech)!}
inferenceJobs={
props.inferenceJobsByCategory.get(
FrontendInferenceJobType.TextToSpeech
)!
}
ttsInferenceJobs={props.ttsInferenceJobs}
sessionSubscriptionsWrapper={
props.sessionSubscriptionsWrapper
@@ -633,7 +645,7 @@ function TtsModelListPage(props: Props) {
{/* <div className="pt-5">
<BackLink link="/" text="Back to main page" />
</div> */}
</motion.div>
</div>
<SessionW2lInferenceResultList
w2lInferenceJobs={props.w2lInferenceJobs}
@@ -646,7 +658,7 @@ function TtsModelListPage(props: Props) {
<SessionTtsModelUploadResultList
modelUploadJobs={props.ttsModelUploadJobs}
/>
</motion.div>
</div>
);
}
@@ -48,8 +48,7 @@ import {
faBarsStaggered,
faLock,
} from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import {
TwitterShareButton,
@@ -344,7 +343,7 @@ function TtsModelViewPage(props: Props) {
if (!!ttsModel?.description_rendered_html) {
modelDescription = (
<>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<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">
@@ -355,7 +354,7 @@ function TtsModelViewPage(props: Props) {
/>
</div>
</div>
</motion.div>
</div>
</>
);
}
@@ -416,7 +415,7 @@ function TtsModelViewPage(props: Props) {
modelCategoriesSection = (
<>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Model Categories</h2>
<div className="py-6">
@@ -424,7 +423,7 @@ function TtsModelViewPage(props: Props) {
{editModelCategoriesButton}
</div>
</div>
</motion.div>
</div>
</>
);
}
@@ -706,12 +705,10 @@ function TtsModelViewPage(props: Props) {
//const createdAtRelative = createdAt !== undefined ? formatDistance(createdAt, new Date(), { addSuffix: true }) : undefined;
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5">
<div className="d-flex flex-column">
<motion.h1 className="fw-bold mb-3" variants={item}>
{title}
</motion.h1>
<h1 className="fw-bold mb-3">{title}</h1>
{/* Rate Voice Model Buttons */}
<div className="d-flex gap-3">
{ratingButtons}
@@ -723,16 +720,16 @@ function TtsModelViewPage(props: Props) {
/>
</div>
</div>
<motion.div className="mb-3 mt-4 pt-2" variants={item}>
<div className="mb-3 mt-4 pt-2">
<BackLink link="/" text="Back to all models" />
</motion.div>
</div>
</div>
<div>{modelDescription}</div>
<div>{modelCategoriesSection}</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">TTS Model Details</h2>
<div className="py-6">
@@ -781,9 +778,9 @@ function TtsModelViewPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Vocoder Details</h2>
<div className="py-6">
@@ -792,18 +789,18 @@ function TtsModelViewPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
<motion.div variants={panel}>{moderatorRows}</motion.div>
<div>{moderatorRows}</div>
<motion.div className="container pb-4" variants={panel}>
<div className="container pb-4">
<div className="d-flex flex-column flex-md-row gap-3">
{editModelButton}
{deleteModelButton}
</div>
</motion.div>
</div>
<motion.div className="container-panel py-5" variants={panel}>
<div className="container-panel py-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Use Model</h2>
<div className="py-6">
@@ -832,17 +829,15 @@ function TtsModelViewPage(props: Props) {
</form>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={item}>
{socialSharing}
</motion.div>
<div className="container-panel pt-4 pb-5">{socialSharing}</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<BackLink link="/" text="Back to all models" />
</motion.div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={item}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="fw-bold panel-title">Comments</h2>
<div className="py-6">
@@ -853,9 +848,9 @@ function TtsModelViewPage(props: Props) {
/>
</div>
</div>
</motion.div>
</div>
<motion.div className="container" variants={item}>
<div className="container">
<h4 className="text-center text-lg-start mb-4">
<FontAwesomeIcon icon={faBarsStaggered} className="me-3" />
Session TTS Results
@@ -865,8 +860,8 @@ function TtsModelViewPage(props: Props) {
ttsInferenceJobs={props.ttsInferenceJobs}
sessionSubscriptionsWrapper={props.sessionSubscriptionsWrapper}
/>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -4,8 +4,7 @@ import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapp
import { useParams, Link, useHistory } from "react-router-dom";
import { Gravatar } from "@storyteller/components/src/elements/Gravatar";
import { WebUrl } from "../../../../../common/WebUrl";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface TtsInferenceResultResponsePayload {
@@ -187,17 +186,15 @@ function TtsResultDeletePage(props: Props) {
: "Delete TTS Result (hides from everyone but mods)";
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
<motion.h1 className=" fw-bold mb-3" variants={item}>
{h1Title}
</motion.h1>
<motion.div variants={item}>
<h1 className=" fw-bold mb-3">{h1Title}</h1>
<div>
<Link to={resultLink}>&lt; Back to result</Link>
</motion.div>
</div>
</div>
<motion.form onSubmit={handleDeleteFormSubmit} variants={panel}>
<form onSubmit={handleDeleteFormSubmit}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4">
<table className="table tts-result-table">
@@ -241,14 +238,12 @@ function TtsResultDeletePage(props: Props) {
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className=" btn btn-primary w-100">{buttonTitle}</button>
<motion.p className="mt-4" variants={item}>
{formLabel}
</motion.p>
</motion.div>
</motion.form>
</motion.div>
<p className="mt-4">{formLabel}</p>
</div>
</form>
</div>
);
}
@@ -5,8 +5,7 @@ import { useParams, Link, useHistory } from "react-router-dom";
import { WebUrl } from "../../../../../common/WebUrl";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
const DEFAULT_VISIBILITY = "public";
@@ -152,17 +151,15 @@ function TtsResultEditPage(props: Props) {
);
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
<motion.h1 className=" fw-bold mb-3" variants={item}>
Edit Result Visibility
</motion.h1>
<motion.div variants={item}>
<h1 className=" fw-bold mb-3">Edit Result Visibility</h1>
<div>
<Link to={resultLink}>&lt; Back to result</Link>
</motion.div>
</div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<fieldset disabled={isDisabled}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 py-4 p-lg-4">
@@ -186,12 +183,12 @@ function TtsResultEditPage(props: Props) {
</div>
</div>
</div>
<motion.div className="container" variants={item}>
<div className="container">
<button className="btn btn-primary w-100 mb-5">Update</button>
</motion.div>
</div>
</fieldset>
</motion.form>
</motion.div>
</form>
</div>
);
}
@@ -24,8 +24,7 @@ import {
faEyeSlash,
faUser,
} from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { Analytics } from "../../../../../common/Analytics";
import {
TwitterShareButton,
@@ -431,12 +430,10 @@ function TtsResultViewPage(props: Props) {
);
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold mb-2" variants={item}>
{headingTitle}
</motion.h1>
<h1 className=" fw-bold mb-2">{headingTitle}</h1>
{/* Rate Result Buttons */}
{/* Commented out because not ready yet. */}
{/*<div className="d-flex gap-3">
@@ -478,16 +475,16 @@ function TtsResultViewPage(props: Props) {
</div>
</div>*/}
<motion.p className="mb-3 result-text pt-2">
<p className="mb-3 result-text pt-2">
<TextExpander
text={ttsInferenceResult.raw_inference_text}
cutLength={240}
/>
</motion.p>
</p>
</div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
{subtitle}
<div className="py-6">
@@ -495,7 +492,7 @@ function TtsResultViewPage(props: Props) {
{downloadButton}
</div>
</div>
</motion.div>
</div>
{/* Without wavesurfer,
<audio
@@ -505,18 +502,16 @@ function TtsResultViewPage(props: Props) {
<code>audio</code> element.
</audio>*/}
<motion.div className="container-panel pt-3 pb-5" variants={item}>
{socialSharing}
</motion.div>
<div className="container-panel pt-3 pb-5">{socialSharing}</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Spectrogram</h2>
<SpectrogramImage spectrogramJsonLink={spectrogramLink} />
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Result Details</h2>
<div className="py-6">
@@ -552,9 +547,9 @@ function TtsResultViewPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<div className="container-panel pt-3 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="panel-title fw-bold">Model Used</h2>
<div className="py-6">
@@ -584,25 +579,25 @@ function TtsResultViewPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
<motion.div variants={panel}>{moderatorRows}</motion.div>
<div>{moderatorRows}</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<div className="d-flex flex-column flex-md-row gap-3 mb-4">
{editButton}
{deleteButton}
</div>
<motion.p className="text-center text-lg-start" variants={item}>
<p className="text-center text-lg-start">
<ReportDiscordLink />
</motion.p>
</p>
<motion.div className="pt-5" variants={item}>
<div className="pt-5">
<BackLink link="/" text="Back to all models" />
</motion.div>
</motion.div>
</div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={item}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="fw-bold panel-title">Comments</h2>
<div className="py-6">
@@ -613,8 +608,8 @@ function TtsResultViewPage(props: Props) {
/>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -9,8 +9,7 @@ import { v4 as uuidv4 } from "uuid";
import { BackLink } from "../../_common/BackLink";
import { Link } from "react-router-dom";
import { WebUrl } from "../../../../common/WebUrl";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface Props {
@@ -120,23 +119,21 @@ function UploadTtsModelPage(props: Props) {
};
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-md-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
Upload Voice (TTS Model)
</motion.h1>
<motion.h4 variants={item}>This works just like YouTube!</motion.h4>
<motion.div className="my-3" variants={item}>
<h1 className=" fw-bold">Upload Voice (TTS Model)</h1>
<h4>This works just like YouTube!</h4>
<div className="my-3">
<BackLink
link={WebUrl.contributePage()}
text="Back to contribute page"
/>
</motion.div>
</div>
</div>
</div>
<motion.div className="container px-md-4 px-lg-5 px-xl-3" variants={item}>
<div className="container px-md-4 px-lg-5 px-xl-3">
<div className="alert alert-primary">
<strong>Content Creator Rewards!</strong>
{/*<p>You can help FakeYou grow by uploading Tacotron2 models.
@@ -149,12 +146,9 @@ function UploadTtsModelPage(props: Props) {
featured roles in Discord, queue priority, and more!
</div>
</div>
</motion.div>
</div>
<motion.div
className="container pt-3 d-flex flex-column gap-3 px-md-4 px-lg-5 px-xl-3"
variants={item}
>
<div className="container pt-3 d-flex flex-column gap-3 px-md-4 px-lg-5 px-xl-3">
<p>
If you're new to voice cloning, join our{" "}
<span>
@@ -180,18 +174,15 @@ function UploadTtsModelPage(props: Props) {
Please do not upload voices that you didn't train yourself or voices of individuals
who wish to not be voice cloned. We'll post a list of banned voices soon.
</p>*/}
</motion.div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<div className="container-panel py-5">
<div className="panel p-3 py-4 p-lg-4">
<div className="d-flex flex-column gap-4">
{/* Model Type */}
<div>
<label className="sub-title">
TTS Model Type
</label>
<label className="sub-title">TTS Model Type</label>
<div className="control select">
<select
className="form-select"
@@ -199,12 +190,8 @@ function UploadTtsModelPage(props: Props) {
onChange={handleModelTypeChange}
value={modelType}
>
<option value="tacotron2">
Tacotron 2
</option>
<option value="vits">
VITS
</option>
<option value="tacotron2">Tacotron 2</option>
<option value="vits">VITS</option>
</select>
</div>
</div>
@@ -242,20 +229,19 @@ function UploadTtsModelPage(props: Props) {
</div>
<p className="help">{downloadUrlInvalidReason}</p>
</div>
</div>
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className="btn btn-primary w-100">Upload</button>
</motion.div>
</motion.form>
</div>
</form>
<SessionTtsModelUploadResultList
modelUploadJobs={props.ttsModelUploadJobs}
/>
</motion.div>
</div>
);
}
@@ -10,8 +10,7 @@ import { v4 as uuidv4 } from "uuid";
import { BackLink } from "../../_common/BackLink";
import { Link } from "react-router-dom";
import { WebUrl } from "../../../../common/WebUrl";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../data/animation";
import { VocoderUploadJob } from "@storyteller/components/src/jobs/VocoderUploadJobs";
import { SessionVocoderUploadResultList } from "../../_common/SessionVocoderUploadResultsList";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -88,26 +87,21 @@ function UploadVocoderPage(props: Props) {
};
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-md-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
Upload Vocoder Model
</motion.h1>
<motion.h4 variants={item}>Make your voice sound better!</motion.h4>
<motion.div className="my-3" variants={item}>
<h1 className=" fw-bold">Upload Vocoder Model</h1>
<h4>Make your voice sound better!</h4>
<div className="my-3">
<BackLink
link={WebUrl.contributePage()}
text="Back to contribute page"
/>
</motion.div>
</div>
</div>
</div>
<motion.div
className="container pt-3 d-flex flex-column gap-3 px-md-4 px-lg-5 px-xl-3"
variants={item}
>
<div className="container pt-3 d-flex flex-column gap-3 px-md-4 px-lg-5 px-xl-3">
<p>
If you're new to voice cloning, join our{" "}
<span>
@@ -127,9 +121,9 @@ function UploadVocoderPage(props: Props) {
Please do not upload voices that you didn't train yourself or voices of individuals
who wish to not be voice cloned. We'll post a list of banned voices soon.
</p>*/}
</motion.div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<div className="container-panel py-5">
<div className="panel p-3 py-4 p-lg-4">
<div className="d-flex flex-column gap-4">
@@ -172,15 +166,15 @@ function UploadVocoderPage(props: Props) {
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className="btn btn-primary w-100">Upload</button>
</motion.div>
</motion.form>
</div>
</form>
<SessionVocoderUploadResultList
vocoderUploadJobs={props.vocoderUploadJobs}
/>
</motion.div>
</div>
);
}
@@ -10,8 +10,7 @@ import { v4 as uuidv4 } from "uuid";
import { BackLink } from "../../_common/BackLink";
import { Link } from "react-router-dom";
import { WebUrl } from "../../../../common/WebUrl";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../data/animation";
import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/VoiceConversionModelUploadJob";
import { SessionVoiceConversionModelUploadResultList } from "../../_common/SessionVoiceConversionModelUploadResultsList";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -93,28 +92,21 @@ function UploadVoiceConversionModel(props: Props) {
};
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-3 px-md-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
Upload Voice to Voice Model
</motion.h1>
<motion.h4 variants={item}>
Transform your voice into another!
</motion.h4>
<motion.div className="my-3" variants={item}>
<h1 className=" fw-bold">Upload Voice to Voice Model</h1>
<h4>Transform your voice into another!</h4>
<div className="my-3">
<BackLink
link={WebUrl.contributePage()}
text="Back to contribute page"
/>
</motion.div>
</div>
</div>
</div>
<motion.div
className="container pt-3 d-flex flex-column gap-3 px-md-4 px-lg-5 px-xl-3"
variants={item}
>
<div className="container pt-3 d-flex flex-column gap-3 px-md-4 px-lg-5 px-xl-3">
<p>
If you're new to voice cloning, join our{" "}
<span>
@@ -129,18 +121,15 @@ function UploadVoiceConversionModel(props: Props) {
able to use it from the main page. Others will be able to use it too,
and you'll get credited.
</p>
</motion.div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<div className="container-panel py-5">
<div className="panel p-3 py-4 p-lg-4">
<div className="d-flex flex-column gap-4">
{/* Model Type */}
<div>
<label className="sub-title">
Voice-to-Voice Model Type
</label>
<label className="sub-title">Voice-to-Voice Model Type</label>
<div className="control select">
<select
className="form-select"
@@ -148,12 +137,8 @@ function UploadVoiceConversionModel(props: Props) {
onChange={handleModelTypeChange}
value={modelType}
>
<option value="so_vits_svc">
so-vits-svc
</option>
<option value="rvc_v2">
rvc (v2)
</option>
<option value="so_vits_svc">so-vits-svc</option>
<option value="rvc_v2">rvc (v2)</option>
</select>
</div>
</div>
@@ -195,15 +180,15 @@ function UploadVoiceConversionModel(props: Props) {
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className="btn btn-primary w-100">Upload</button>
</motion.div>
</motion.form>
</div>
</form>
<SessionVoiceConversionModelUploadResultList
voiceConversionModelUploadJobs={props.voiceConversionModelUploadJobs}
/>
</motion.div>
</div>
);
}
@@ -7,8 +7,7 @@ import { v4 as uuidv4 } from "uuid";
import { SessionW2lTemplateUploadResultList } from "../../_common/SessionW2lTemplateUploadResultsList";
import { BackLink } from "../../_common/BackLink";
import { WebUrl } from "../../../../common/WebUrl";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface W2lTemplateUploadJobResponsePayload {
@@ -93,30 +92,28 @@ function UploadW2lPhotoPage(props: Props) {
};
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-3 px-md-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
Upload Photo (w2l template)
</motion.h1>
<motion.div className="my-3" variants={item}>
<h1 className=" fw-bold">Upload Photo (w2l template)</h1>
<div className="my-3">
<BackLink
link={WebUrl.contributePage()}
text="Back to contribute page"
/>
</motion.div>
</div>
</div>
</div>
<motion.div className="container px-md-4 px-lg-5 px-xl-3" variants={item}>
<div className="container px-md-4 px-lg-5 px-xl-3">
<p>
The photos you upload can be used for lipsyncing with audio using the
Wav2Lip model. In the future, you'll be able to use these for
first-order-model and much more!
</p>
</motion.div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<div className="container-panel py-5">
<div className="panel p-3 py-4 p-lg-4">
<div className="d-flex flex-column gap-4">
@@ -158,21 +155,21 @@ function UploadW2lPhotoPage(props: Props) {
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className="btn btn-primary w-100">Upload</button>
</motion.div>
</div>
{/*<div className="field is-grouped">
<div className="control">
<button className="button is-link is-large is-fullwidth">Upload</button>
</div>
</div>*/}
</motion.form>
</form>
<SessionW2lTemplateUploadResultList
w2lTemplateUploadJobs={props.w2lTemplateUploadJobs}
/>
</motion.div>
</div>
);
}
@@ -7,8 +7,7 @@ import { useHistory } from "react-router-dom";
import { v4 as uuidv4 } from "uuid";
import { BackLink } from "../../_common/BackLink";
import { WebUrl } from "../../../../common/WebUrl";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface W2lTemplateUploadJobResponsePayload {
@@ -93,29 +92,27 @@ function UploadW2lVideoPage(props: Props) {
};
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-3 px-md-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
Upload Video (w2l template)
</motion.h1>
<motion.div className="my-3" variants={item}>
<h1 className=" fw-bold">Upload Video (w2l template)</h1>
<div className="my-3">
<BackLink
link={WebUrl.contributePage()}
text="Back to contribute page"
/>
</motion.div>
</div>
</div>
</div>
<motion.div className="container px-md-4 px-lg-5 px-xl-3" variants={item}>
<div className="container px-md-4 px-lg-5 px-xl-3">
<p>
The videos you upload can be used for lipsyncing with audio using the
Wav2Lip model.
</p>
</motion.div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<div className="container-panel py-5">
<div className="panel p-3 py-4 p-lg-4">
<div className="d-flex flex-column gap-4">
@@ -157,21 +154,21 @@ function UploadW2lVideoPage(props: Props) {
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className="btn btn-primary w-100">Upload</button>
</motion.div>
</div>
{/*<div className="field is-grouped">
<div className="control">
<button className="button is-link is-large is-fullwidth">Upload</button>
</div>
</div>*/}
</motion.form>
</form>
<SessionW2lTemplateUploadResultList
w2lTemplateUploadJobs={props.w2lTemplateUploadJobs}
/>
</motion.div>
</div>
);
}
@@ -1,9 +1,9 @@
import React, { useState, useEffect, useCallback } from "react";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { motion } from "framer-motion";
import { v4 as uuidv4 } from "uuid";
import { container, panel } from "../../../../../data/animation";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { VcPageHero } from "./components/VcPageHero";
import {
@@ -74,8 +74,9 @@ function VcModelListPage(props: Props) {
const [autoConvertF0, setAutoConvertF0] = useState(false);
const [maybeF0MethodOverride, setMaybeF0MethodOverride] =
useState<EnqueueVoiceConversionFrequencyMethod | undefined>(undefined);
const [maybeF0MethodOverride, setMaybeF0MethodOverride] = useState<
EnqueueVoiceConversionFrequencyMethod | undefined
>(undefined);
const [semitones, setSemitones] = useState(0);
@@ -199,9 +200,7 @@ function VcModelListPage(props: Props) {
changeConvertIdempotencyToken();
};
const handleAutoF0Change = (
ev: React.FormEvent<HTMLInputElement>
) => {
const handleAutoF0Change = (ev: React.FormEvent<HTMLInputElement>) => {
const value = (ev.target as HTMLInputElement).checked;
setAutoConvertF0(value);
changeConvertIdempotencyToken();
@@ -217,13 +216,13 @@ function VcModelListPage(props: Props) {
props.maybeSelectedVoiceConversionModel !== undefined;
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<VcPageHero
sessionWrapper={props.sessionWrapper}
sessionSubscriptionsWrapper={props.sessionSubscriptionsWrapper}
/>
{/* <motion.div variants={panel} className="container">
{/* <div className="container">
<div className="alert alert-info">
<FontAwesomeIcon icon={faMoneyBill} className="me-2" />
<span className="fw-medium">
@@ -234,9 +233,9 @@ function VcModelListPage(props: Props) {
See details <FontAwesomeIcon icon={faArrowRight} className="ms-1" />
</Link>
</div>
</motion.div> */}
</div> */}
<motion.div className="container-panel pb-5 mb-4" variants={panel}>
<div className="container-panel pb-5 mb-4">
<div className="panel p-3 py-4 p-md-4">
<div className="d-flex gap-4">
<form
@@ -339,14 +338,17 @@ function VcModelListPage(props: Props) {
/>
</div>
<div className="form-check">
<input
id="autoF0Checkbox"
className="form-check-input"
type="checkbox"
<input
id="autoF0Checkbox"
className="form-check-input"
type="checkbox"
checked={autoConvertF0}
onChange={handleAutoF0Change}
/>
<label className="form-check-label" htmlFor="autoF0Checkbox">
/>
<label
className="form-check-label"
htmlFor="autoF0Checkbox"
>
Auto F0 (off for singing, on for speech)
</label>
</div>
@@ -440,14 +442,17 @@ function VcModelListPage(props: Props) {
/>
</div>
<div className="form-check">
<input
id="autoF0CheckboxMic"
className="form-check-input"
type="checkbox"
<input
id="autoF0CheckboxMic"
className="form-check-input"
type="checkbox"
checked={autoConvertF0}
onChange={handleAutoF0Change}
/>
<label className="form-check-label" htmlFor="autoF0CheckboxMic">
/>
<label
className="form-check-label"
htmlFor="autoF0CheckboxMic"
>
Auto F0 (off for singing, on for speech)
</label>
</div>
@@ -508,8 +513,8 @@ function VcModelListPage(props: Props) {
{/* <div className="pt-5">
<BackLink link="/" text="Back to main page" />
</div> */}
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -8,8 +8,7 @@ import {
GetW2lResultIsOk,
W2lResult,
} from "@storyteller/components/src/api/w2l/GetW2lResult";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface Props {
@@ -106,17 +105,15 @@ function W2lResultDeletePage(props: Props) {
const durationSeconds = (w2lInferenceResult?.duration_millis || 0) / 1000;
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-md-4 px-lg-5 px-xl-3">
<motion.h1 className=" fw-bold" variants={item}>
{h1Title}
</motion.h1>
<motion.div className="pt-3" variants={item}>
<h1 className=" fw-bold">{h1Title}</h1>
<div className="pt-3">
<Link to={templateResultLink}>&lt; Back to result</Link>
</motion.div>
</div>
</div>
<motion.form onSubmit={handleDeleteFormSubmit} variants={panel}>
<form onSubmit={handleDeleteFormSubmit}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 py-4 p-lg-4">
<table className="table">
@@ -148,14 +145,12 @@ function W2lResultDeletePage(props: Props) {
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className={buttonCss}>{buttonTitle}</button>
<motion.p className="pt-4" variants={item}>
{formLabel}
</motion.p>
</motion.div>
</motion.form>
</motion.div>
<p className="pt-4">{formLabel}</p>
</div>
</form>
</div>
);
}
@@ -5,8 +5,7 @@ import { useParams, Link, useHistory } from "react-router-dom";
import { WebUrl } from "../../../../../common/WebUrl";
import { VisibleIconFc } from "../../../_icons/VisibleIcon";
import { HiddenIconFc } from "../../../_icons/HiddenIcon";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
const DEFAULT_VISIBILITY = "public";
@@ -148,17 +147,15 @@ function W2lResultEditPage(props: Props) {
visibility === "public" ? <VisibleIconFc /> : <HiddenIconFc />;
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pb-4 pt-5 px-md-4 px-lg-5 px-xl-3">
<motion.h1 className=" fw-bold" variants={item}>
Edit Result Visibility
</motion.h1>
<motion.div className="pt-3" variants={item}>
<h1 className=" fw-bold">Edit Result Visibility</h1>
<div className="pt-3">
<Link to={resultLink}>&lt; Back to result </Link>
</motion.div>
</div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 py-4 p-lg-4">
<fieldset disabled={isDisabled}>
@@ -183,11 +180,11 @@ function W2lResultEditPage(props: Props) {
</fieldset>
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className="btn btn-primary w-100">Update</button>
</motion.div>
</motion.form>
</motion.div>
</div>
</form>
</div>
);
}
@@ -17,8 +17,7 @@ import {
import { MetaTags } from "../../../../../common/MetaTags";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faDownload, faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
import { CommentComponent } from "../../../_common/comments/CommentComponent";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
@@ -264,24 +263,19 @@ function W2lResultViewPage(props: Props) {
);
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5 px-md-4 px-lg-5 px-xl-3">
<motion.h1
className=" fw-bold text-center text-lg-start"
variants={item}
>
Lipsync Result
</motion.h1>
<h1 className=" fw-bold text-center text-lg-start">Lipsync Result</h1>
</div>
<motion.div className="container" variants={item}>
<div className="container">
<video width="100%" height="auto" controls={true} className="rounded">
<source src={videoLink} />
Your device doesn't support video.
</video>
</motion.div>
</div>
<motion.div className="container pt-4 pb-5" variants={item}>
<div className="container pt-4 pb-5">
<a
className="btn btn-primary w-100"
href={videoLink}
@@ -291,9 +285,9 @@ function W2lResultViewPage(props: Props) {
<FontAwesomeIcon icon={faDownload} className="me-2" />
Download File
</a>
</motion.div>
</div>
<motion.div className="container-panel pt-5 pb-5" variants={panel}>
<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">
@@ -315,9 +309,9 @@ function W2lResultViewPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<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">
@@ -344,21 +338,21 @@ function W2lResultViewPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
<motion.div variants={panel}>{moderatorRows}</motion.div>
<div>{moderatorRows}</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<div className="d-flex gap-3 flex-column flex-md-row">
{editButton}
{deleteButton}
</div>
<motion.div className="mt-4" variants={item}>
<div className="mt-4">
<ReportDiscordLink />
</motion.div>
</motion.div>
</div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={item}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="fw-bold panel-title">Comments</h2>
<div className="py-6">
@@ -369,8 +363,8 @@ function W2lResultViewPage(props: Props) {
/>
</div>
</div>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -10,8 +10,7 @@ import {
} from "@storyteller/components/src/api/w2l/GetW2lTemplate";
import { GetW2lTemplateUseCount } from "@storyteller/components/src/api/w2l/GetW2lTemplateUseCount";
import { BackLink } from "../../../_common/BackLink";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
const DEFAULT_APPROVED_STATE = true;
@@ -121,19 +120,17 @@ function W2lTemplateApprovePage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
{h1Title}
</motion.h1>
<h1 className=" fw-bold">{h1Title}</h1>
</div>
<motion.div className="pt-3" variants={item}>
<div className="pt-3">
<BackLink link={templateLink} text="Back to template" />
</motion.div>
</div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={panel}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 py-4 p-lg-4">
<table className="table">
<thead>
@@ -166,9 +163,9 @@ function W2lTemplateApprovePage(props: Props) {
</tbody>
</table>
</div>
</motion.div>
</div>
<motion.form onSubmit={handleApproveFormSubmit} variants={panel}>
<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">
@@ -190,11 +187,11 @@ function W2lTemplateApprovePage(props: Props) {
</div>
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className="btn btn-primary w-100">Moderate</button>
</motion.div>
</motion.form>
</motion.div>
</div>
</form>
</div>
);
}
@@ -10,8 +10,7 @@ import {
} from "@storyteller/components/src/api/w2l/GetW2lTemplate";
import { GetW2lTemplateUseCount } from "@storyteller/components/src/api/w2l/GetW2lTemplateUseCount";
import { BackLink } from "../../../_common/BackLink";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
interface Props {
@@ -172,19 +171,17 @@ function W2lTemplateDeletePage(props: Props) {
}
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<motion.h1 className=" fw-bold" variants={item}>
{h1Title}
</motion.h1>
<h1 className=" fw-bold">{h1Title}</h1>
</div>
<motion.div className="pt-3" variants={item}>
<div className="pt-3">
<BackLink link={templateLink} text="Back to template" />
</motion.div>
</div>
</div>
<motion.form onSubmit={handleDeleteFormSubmit} variants={panel}>
<form onSubmit={handleDeleteFormSubmit}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 py-4 p-lg-4">
<table className="table">
@@ -222,14 +219,14 @@ function W2lTemplateDeletePage(props: Props) {
</div>
</div>
<motion.div className="container" variants={item}>
<div className="container">
<button className={buttonCss}>{buttonTitle}</button>
</motion.div>
<motion.div className="container pb-5" variants={item}>
</div>
<div className="container pb-5">
<p className="pt-4">{formLabel}</p>
</motion.div>
</motion.form>
</motion.div>
</div>
</form>
</div>
);
}
@@ -11,8 +11,7 @@ import {
W2lTemplate,
} from "@storyteller/components/src/api/w2l/GetW2lTemplate";
import { BackLink } from "../../../_common/BackLink";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
const DEFAULT_VISIBILITY = "public";
@@ -121,7 +120,7 @@ function W2lTemplateEditPage(props: Props) {
visibility === "public" ? <VisibleIconFc /> : <HiddenIconFc />;
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container pt-5 pb-4 px-lg-5 px-xl-3">
<div className="d-flex flex-column">
<h1 className=" fw-bold">Edit Template</h1>
@@ -131,7 +130,7 @@ function W2lTemplateEditPage(props: Props) {
</div>
</div>
<motion.form onSubmit={handleFormSubmit} variants={panel}>
<form onSubmit={handleFormSubmit}>
<fieldset disabled={isDisabled}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 py-4 p-lg-4">
@@ -187,12 +186,12 @@ function W2lTemplateEditPage(props: Props) {
</div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<button className="btn btn-primary w-100">Update Template</button>
</motion.div>
</div>
</fieldset>
</motion.form>
</motion.div>
</form>
</div>
);
}
@@ -3,19 +3,13 @@ import { ApiConfig } from "@storyteller/components";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { Link } from "react-router-dom";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
import {
distance,
duration,
container,
item,
panel,
} from "../../../../../data/animation";
import { distance, duration } from "../../../../../data/animation";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faChevronLeft,
faChevronRight,
} from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
import { PageHeader } from "../../../_common/PageHeader";
import { faVideo } from "@fortawesome/pro-solid-svg-icons";
@@ -206,7 +200,7 @@ function W2lTemplateListPage(props: Props) {
const titleIcon = <FontAwesomeIcon icon={faVideo} className="me-3 me-lg-4" />;
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<PageHeader
title={title}
subText={subText}
@@ -214,7 +208,7 @@ function W2lTemplateListPage(props: Props) {
titleIcon={titleIcon}
/>
<motion.div className="container" variants={item}>
<div className="container">
<div className="d-flex w-100 gap-3 mb-4">
<button
className="btn btn-secondary w-100 d-flex align-items-center justify-content-center"
@@ -234,9 +228,9 @@ function W2lTemplateListPage(props: Props) {
<FontAwesomeIcon icon={faChevronRight} className="ms-2" />
</button>
</div>
</motion.div>
</div>
<motion.div className="container-panel" variants={panel}>
<div className="container-panel">
<div className="panel p-3 p-lg-4">
<Fade bottom cascade duration={duration} distance={distance}>
<div className="row gy-3 gx-3 gx-md-4 gy-md-4 w2l-ani">
@@ -244,9 +238,9 @@ function W2lTemplateListPage(props: Props) {
</div>
</Fade>
</div>
</motion.div>
</div>
<motion.div className="container" variants={item}>
<div className="container">
<div className="d-flex w-100 gap-3 my-4">
<button
className="btn btn-secondary w-100 d-flex align-items-center justify-content-center"
@@ -266,15 +260,15 @@ function W2lTemplateListPage(props: Props) {
<FontAwesomeIcon icon={faChevronRight} className="ms-2" />
</button>
</div>
</motion.div>
</div>
<motion.div className="container pb-5" variants={item}>
<div className="container pb-5">
<p>
This feature is based on Wav2Lip by by Prajwal, K R and Mukhopadhyay,
Rudrabha and Namboodiri, Vinay P. and Jawahar, C.V.
</p>
</motion.div>
</motion.div>
</div>
</div>
);
}
@@ -23,8 +23,7 @@ import { GetW2lTemplateUseCount } from "@storyteller/components/src/api/w2l/GetW
import { BackLink } from "../../../_common/BackLink";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
import { motion } from "framer-motion";
import { container, item, panel } from "../../../../../data/animation";
import { ThirdPartyLinks } from "@storyteller/components/src/constants/ThirdPartyLinks";
import { usePrefixedDocumentTitle } from "../../../../../common/UsePrefixedDocumentTitle";
import { CommentComponent } from "../../../_common/comments/CommentComponent";
@@ -228,7 +227,7 @@ function W2lTemplateViewPage(props: Props) {
) {
moderatorRows = (
<>
<motion.div className="container-panel pt-3 pb-5" variants={item}>
<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">
@@ -274,7 +273,7 @@ function W2lTemplateViewPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
</>
);
}
@@ -384,7 +383,7 @@ function W2lTemplateViewPage(props: Props) {
if (!!w2lTemplate?.description_rendered_html) {
templateDescription = (
<>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<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">
@@ -396,7 +395,7 @@ function W2lTemplateViewPage(props: Props) {
/>
</div>
</div>
</motion.div>
</div>
</>
);
}
@@ -413,18 +412,16 @@ function W2lTemplateViewPage(props: Props) {
);
return (
<motion.div initial="hidden" animate="visible" variants={container}>
<div>
<div className="container py-5 px-md-4 px-lg-5 px-xl-3">
<motion.h1 className=" fw-bold" variants={item}>
Video Lip Sync Template
</motion.h1>
<motion.h4 variants={item}>Template: {w2lTemplate?.title}</motion.h4>
<motion.div className="pt-3" variants={item}>
<h1 className=" fw-bold">Video Lip Sync Template</h1>
<h4>Template: {w2lTemplate?.title}</h4>
<div className="pt-3">
<BackLink link={WebUrl.w2lListPage()} text="Back to all templates" />
</motion.div>
</div>
</div>
<motion.form onSubmit={handleInferenceFormSubmit} variants={panel}>
<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>
@@ -460,11 +457,11 @@ function W2lTemplateViewPage(props: Props) {
</div>
</div>
</div>
</motion.form>
</form>
<motion.div variants={panel}>{templateDescription}</motion.div>
<div>{templateDescription}</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<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">
@@ -494,9 +491,9 @@ function W2lTemplateViewPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-3 pb-5" variants={panel}>
<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">
@@ -528,25 +525,25 @@ function W2lTemplateViewPage(props: Props) {
</table>
</div>
</div>
</motion.div>
</div>
<motion.div variants={panel}>{moderatorRows}</motion.div>
<div>{moderatorRows}</div>
<motion.div className="container" variants={item}>
<div className="container">
<div className="d-flex flex-column flex-lg-row gap-3">
{editButton}
{approveButton}
{deleteButton}
</div>
</motion.div>
</div>
<motion.div variants={item}>
<div>
<div className="container pt-4 pb-5">
<ReportDiscordLink />
</div>
</motion.div>
</div>
<motion.div className="container-panel pt-4 pb-5" variants={item}>
<div className="container-panel pt-4 pb-5">
<div className="panel p-3 p-lg-4">
<h2 className="fw-bold panel-title">Comments</h2>
<div className="py-6">
@@ -557,12 +554,12 @@ function W2lTemplateViewPage(props: Props) {
/>
</div>
</div>
</motion.div>
</div>
<SessionW2lInferenceResultList
w2lInferenceJobs={props.w2lInferenceJobs}
/>
</motion.div>
</div>
);
}