mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 09:48:44 +00:00
Merge branch 'master' into olivicmic-fy-voice-designer-consume-api-react
This commit is contained in:
commit
f65cab31c0
11 files changed
+376
-233
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -5,8 +5,8 @@ import "../Select/Select.scss";
|
||||
import ReactSelect, { Props as ReactSelectProps } from "react-select";
|
||||
|
||||
export interface Option {
|
||||
value: string,
|
||||
label: string
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface SelectProps extends ReactSelectProps {
|
||||
@@ -14,34 +14,64 @@ export interface SelectProps extends ReactSelectProps {
|
||||
label?: string;
|
||||
rounded?: boolean;
|
||||
small?: boolean;
|
||||
options: Option[],
|
||||
options: Option[];
|
||||
onChange?: (value: any) => void;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default function Select({ label, icon, name, onChange: inChange = () => {}, options = [], rounded, small, value, ...rest }: SelectProps) {
|
||||
const valueLabel = options.find((option: any)=> option.value === value)?.label || "";
|
||||
const onChange = ({ value }: any) => inChange({ target: { value, name, type: "select" } });
|
||||
export default function Select({
|
||||
label,
|
||||
icon,
|
||||
name,
|
||||
onChange: inChange = () => {},
|
||||
options = [],
|
||||
rounded,
|
||||
small,
|
||||
value,
|
||||
...rest
|
||||
}: SelectProps) {
|
||||
const valueLabel =
|
||||
options.find((option: any) => option.value === value)?.label || "";
|
||||
const onChange = ({ value }: any) =>
|
||||
inChange({ target: { value, name, type: "select" } });
|
||||
|
||||
const classNames = {
|
||||
control: ({ isFocused }: { isFocused: boolean }) =>
|
||||
`select${ icon ? " with-icon" : "" }${ rounded ? " rounded-full" : "" }${ isFocused ? " focused" : "" }`,
|
||||
`select${icon ? " with-icon" : ""}${rounded ? " rounded-full" : ""}${
|
||||
isFocused ? " focused" : ""
|
||||
}`,
|
||||
option: () => "select-option",
|
||||
input: () => "select-input",
|
||||
input: () => "select-input",
|
||||
placeholder: () => "select-placeholder",
|
||||
singleValue: () => "select-value",
|
||||
menu: () => "select-container",
|
||||
indicatorSeparator: () => "select-separator",
|
||||
};
|
||||
|
||||
return <>
|
||||
{ label && <label className="sub-title">{ label }</label> }
|
||||
<div className={`form-group${ icon ? " input-icon" : "" }${ small ? " select-small" : "" }`}>
|
||||
{ icon && <FontAwesomeIcon icon={icon} className="form-control-feedback" /> }
|
||||
<div className="w-100">
|
||||
<ReactSelect{...{ classNamePrefix: "select", classNames, name, onChange, options, value: { label: valueLabel, value }, ...rest }}/>
|
||||
return (
|
||||
<div>
|
||||
{label && <label className="sub-title">{label}</label>}
|
||||
<div
|
||||
className={`form-group${icon ? " input-icon" : ""}${
|
||||
small ? " select-small" : ""
|
||||
}`}
|
||||
>
|
||||
{icon && (
|
||||
<FontAwesomeIcon icon={icon} className="form-control-feedback" />
|
||||
)}
|
||||
<div className="w-100">
|
||||
<ReactSelect
|
||||
{...{
|
||||
classNamePrefix: "select",
|
||||
classNames,
|
||||
name,
|
||||
onChange,
|
||||
options,
|
||||
value: { label: valueLabel, value },
|
||||
...rest,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>;
|
||||
};
|
||||
);
|
||||
}
|
||||
@@ -21,6 +21,12 @@ interface PageHeaderProps {
|
||||
extension?: React.ReactNode;
|
||||
buttonLabel?: string;
|
||||
button?: ButtonProps;
|
||||
secondaryButtonLabel?: string;
|
||||
secondaryButton?: ButtonProps;
|
||||
secondaryButtonVariant?: "primary" | "secondary" | "danger";
|
||||
secondaryButtonTo?: string;
|
||||
secondaryButtonIcon?: IconDefinition;
|
||||
secondaryButtonOnClick?: () => void;
|
||||
buttonVariant?: "primary" | "secondary" | "danger";
|
||||
buttonTo?: string;
|
||||
buttonIcon?: IconDefinition;
|
||||
@@ -51,22 +57,40 @@ export default function PageHeader({
|
||||
showBackButton,
|
||||
backbuttonTo,
|
||||
backbuttonLabel,
|
||||
secondaryButton,
|
||||
secondaryButtonLabel,
|
||||
secondaryButtonVariant = "secondary",
|
||||
secondaryButtonTo,
|
||||
secondaryButtonIcon,
|
||||
secondaryButtonOnClick,
|
||||
}: PageHeaderProps) {
|
||||
|
||||
const icon = (
|
||||
<>{titleIcon && <FontAwesomeIcon icon={titleIcon} className="me-3" />}</>
|
||||
);
|
||||
|
||||
const buttonProps = {
|
||||
...button ? button : {},
|
||||
...buttonIcon && { icon: buttonIcon },
|
||||
...buttonLabel && { label: buttonLabel },
|
||||
...buttonOnClick && { onClick: buttonOnClick },
|
||||
...buttonTo && { to: buttonTo },
|
||||
...buttonVariant && { variant: buttonVariant }
|
||||
...(button ? button : {}),
|
||||
...(buttonIcon && { icon: buttonIcon }),
|
||||
...(buttonLabel && { label: buttonLabel }),
|
||||
...(buttonOnClick && { onClick: buttonOnClick }),
|
||||
...(buttonTo && { to: buttonTo }),
|
||||
...(buttonVariant && { variant: buttonVariant }),
|
||||
};
|
||||
|
||||
const HeaderBtn = () => button || showButton ? <Button { ...buttonProps }/> : null;
|
||||
const secondaryButtonProps = {
|
||||
...(secondaryButton ? secondaryButton : {}),
|
||||
...(secondaryButtonIcon && { icon: secondaryButtonIcon }),
|
||||
...(secondaryButtonLabel && { label: secondaryButtonLabel }),
|
||||
...(secondaryButtonOnClick && { onClick: secondaryButtonOnClick }),
|
||||
...(secondaryButtonTo && { to: secondaryButtonTo }),
|
||||
...(secondaryButtonVariant && { variant: secondaryButtonVariant }),
|
||||
};
|
||||
|
||||
const HeaderBtn = () =>
|
||||
button || showButton ? <Button {...buttonProps} /> : null;
|
||||
|
||||
const HeaderBtnTwo = () =>
|
||||
secondaryButton ? <Button {...secondaryButtonProps} /> : null;
|
||||
|
||||
if (!panel) {
|
||||
return (
|
||||
@@ -95,9 +119,10 @@ export default function PageHeader({
|
||||
{subText}
|
||||
</p>
|
||||
</div>
|
||||
{ ( button || showButton ) && (
|
||||
<div className="d-md-flex">
|
||||
{(button || showButton) && (
|
||||
<div className="d-flex gap-3">
|
||||
<HeaderBtn />
|
||||
<HeaderBtnTwo />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -644,7 +644,10 @@ class PageContainer extends React.Component<
|
||||
path="/voice-designer/create"
|
||||
>
|
||||
<VoiceDesignerFormPage {...{
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}}/>
|
||||
</Route>
|
||||
|
||||
@@ -654,7 +657,10 @@ class PageContainer extends React.Component<
|
||||
path="/voice-designer/dataset/:dataset_token/edit"
|
||||
>
|
||||
<VoiceDesignerFormPage {...{
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}}/>
|
||||
</Route>
|
||||
|
||||
@@ -664,7 +670,10 @@ class PageContainer extends React.Component<
|
||||
path="/voice-designer/dataset/:dataset_token/upload"
|
||||
>
|
||||
<VoiceDesignerFormPage {...{
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}}/>
|
||||
</Route>
|
||||
|
||||
@@ -687,6 +696,9 @@ class PageContainer extends React.Component<
|
||||
|
||||
<Route path="/voice-designer">
|
||||
<VoiceDesignerMainPage {...{
|
||||
sessionWrapper:this.props.sessionWrapper,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper,
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
inferenceJobs: this.props.inferenceJobs,
|
||||
inferenceJobsByCategory: this.props.inferenceJobsByCategory
|
||||
|
||||
@@ -137,11 +137,15 @@ function LandingPage(props: Props) {
|
||||
const randomHeroImage = useMemo(() => {
|
||||
const images = [
|
||||
// Main Images:
|
||||
"mascot/kitsune_pose2.webp",
|
||||
// "mascot/kitsune_pose2.webp",
|
||||
// "mascot/may4th.webp",
|
||||
// "mascot/halloween_1.webp",
|
||||
// "mascot/halloween_2.webp",
|
||||
// "mascot/halloween_3.webp",
|
||||
"mascot/xmas_1.webp",
|
||||
"mascot/xmas_2.webp",
|
||||
"mascot/xmas_3.webp",
|
||||
"mascot/xmas_4.webp",
|
||||
];
|
||||
|
||||
return images[Math.floor(Math.random() * images.length)];
|
||||
@@ -317,7 +321,7 @@ function LandingPage(props: Props) {
|
||||
</div>
|
||||
|
||||
<h6 className="fw-normal opacity-75 text-white">
|
||||
Make yourself sound like anyone
|
||||
Create your own AI voice
|
||||
</h6>
|
||||
</div>
|
||||
<Link to="/voice-designer" className="btn btn-square mt-1">
|
||||
@@ -327,7 +331,7 @@ function LandingPage(props: Props) {
|
||||
|
||||
<img
|
||||
className="img-fluid"
|
||||
src="/images/landing/hanashi-after.webp"
|
||||
src="/images/landing/select-vd.webp"
|
||||
alt="Voice Designer"
|
||||
/>
|
||||
</Link>
|
||||
|
||||
@@ -17,6 +17,9 @@ import { WebUrl } from "../../../../common/WebUrl";
|
||||
import { BeginStripeCheckoutFlow } from "../../../../common/BeginStripeCheckoutFlow";
|
||||
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
|
||||
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
|
||||
import Container from "components/common/Container";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
import Panel from "components/common/Panel";
|
||||
|
||||
enum FieldTriState {
|
||||
EMPTY_FALSE,
|
||||
@@ -185,10 +188,10 @@ function SignupPage(props: Props) {
|
||||
Analytics.accountSignupAttempt();
|
||||
|
||||
if (
|
||||
(usernameValid !== FieldTriState.TRUE) ||
|
||||
(emailValid !== FieldTriState.TRUE) ||
|
||||
(passwordValid !== FieldTriState.TRUE) ||
|
||||
(passwordConfirmationValid !== FieldTriState.TRUE)
|
||||
usernameValid !== FieldTriState.TRUE ||
|
||||
emailValid !== FieldTriState.TRUE ||
|
||||
passwordValid !== FieldTriState.TRUE ||
|
||||
passwordConfirmationValid !== FieldTriState.TRUE
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -346,118 +349,115 @@ function SignupPage(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pt-lg-5">
|
||||
<div className="container-panel pb-5 pt-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")}
|
||||
</h1>
|
||||
<div className="py-6">
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
{t("account.SignUpPage.inputs.username")}
|
||||
</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faUser} />
|
||||
</span>
|
||||
<input
|
||||
className={usernameInputClass}
|
||||
type="text"
|
||||
placeholder={t(
|
||||
"account.SignUpPage.inputs.usernamePlaceholder"
|
||||
)}
|
||||
value={username}
|
||||
onChange={handleUsernameChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={usernameHelpClass}>{usernameInvalidReason}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
{t("account.SignUpPage.inputs.email")}
|
||||
</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faEnvelope} />
|
||||
</span>
|
||||
<input
|
||||
className={emailInputClass}
|
||||
type="email"
|
||||
placeholder={t(
|
||||
"account.SignUpPage.inputs.emailPlaceholder"
|
||||
)}
|
||||
value={email}
|
||||
onChange={handleEmailChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={emailHelpClass}>{emailInvalidReason}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
{t("account.SignUpPage.inputs.password")}
|
||||
</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faKey} />
|
||||
</span>
|
||||
<input
|
||||
className={passwordInputClass}
|
||||
type="password"
|
||||
placeholder={t(
|
||||
"account.SignUpPage.inputs.passwordPlaceholder"
|
||||
)}
|
||||
value={password}
|
||||
onChange={handlePasswordChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={passwordHelpClass}>{passwordInvalidReason}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
{t("account.SignUpPage.inputs.passwordConfirm")}
|
||||
</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faKey} />
|
||||
</span>
|
||||
<input
|
||||
className={passwordConfirmationInputClass}
|
||||
type="password"
|
||||
placeholder={t(
|
||||
"account.SignUpPage.inputs.passwordConfirmPlaceholder"
|
||||
)}
|
||||
value={passwordConfirmation}
|
||||
onChange={handlePasswordConfirmationChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={passwordConfirmationHelpClass}>
|
||||
{passwordConfirmationInvalidReason}
|
||||
</p>
|
||||
</div>
|
||||
{/*<div className="alert alert-warning mb-0">
|
||||
<Container type="panel" className="login-panel">
|
||||
<PageHeader
|
||||
title="Sign Up"
|
||||
subText="Create a new account."
|
||||
panel={false}
|
||||
/>
|
||||
|
||||
<Panel padding={true}>
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
{t("account.SignUpPage.inputs.username")}
|
||||
</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faUser} />
|
||||
</span>
|
||||
<input
|
||||
className={usernameInputClass}
|
||||
type="text"
|
||||
placeholder={t(
|
||||
"account.SignUpPage.inputs.usernamePlaceholder"
|
||||
)}
|
||||
value={username}
|
||||
onChange={handleUsernameChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={usernameHelpClass}>{usernameInvalidReason}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
{t("account.SignUpPage.inputs.email")}
|
||||
</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faEnvelope} />
|
||||
</span>
|
||||
<input
|
||||
className={emailInputClass}
|
||||
type="email"
|
||||
placeholder={t("account.SignUpPage.inputs.emailPlaceholder")}
|
||||
value={email}
|
||||
onChange={handleEmailChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={emailHelpClass}>{emailInvalidReason}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
{t("account.SignUpPage.inputs.password")}
|
||||
</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faKey} />
|
||||
</span>
|
||||
<input
|
||||
className={passwordInputClass}
|
||||
type="password"
|
||||
placeholder={t(
|
||||
"account.SignUpPage.inputs.passwordPlaceholder"
|
||||
)}
|
||||
value={password}
|
||||
onChange={handlePasswordChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={passwordHelpClass}>{passwordInvalidReason}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="sub-title">
|
||||
{t("account.SignUpPage.inputs.passwordConfirm")}
|
||||
</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faKey} />
|
||||
</span>
|
||||
<input
|
||||
className={passwordConfirmationInputClass}
|
||||
type="password"
|
||||
placeholder={t(
|
||||
"account.SignUpPage.inputs.passwordConfirmPlaceholder"
|
||||
)}
|
||||
value={passwordConfirmation}
|
||||
onChange={handlePasswordConfirmationChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={passwordConfirmationHelpClass}>
|
||||
{passwordConfirmationInvalidReason}
|
||||
</p>
|
||||
</div>
|
||||
{/*<div className="alert alert-warning mb-0">
|
||||
<strong>Remember your password!</strong> We don't have
|
||||
password reset currently, and it'll be a few more weeks before
|
||||
it's added (there are more important features to work on). If
|
||||
you lose your password, please let us know in Discord.
|
||||
</div>*/}
|
||||
<button className="btn btn-primary btn-lg w-100 mt-2">
|
||||
{t("account.SignUpPage.signUpButton")}
|
||||
</button>
|
||||
<p>
|
||||
<Trans i18nKey="account.SignUpPage.signInInstead">
|
||||
Already have an account?{" "}
|
||||
<Link to="/login">Log in instead.</Link>
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<button className="btn btn-primary btn-lg w-100 mt-2">
|
||||
{t("account.SignUpPage.signUpButton")}
|
||||
</button>
|
||||
<p>
|
||||
<Trans i18nKey="account.SignUpPage.signInInstead">
|
||||
Already have an account?{" "}
|
||||
<Link to="/login">Log in instead.</Link>
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Panel>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,13 @@ import useVoiceRequests from "./useVoiceRequests";
|
||||
import useUploadedFiles from "hooks/useUploadedFiles";
|
||||
|
||||
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
|
||||
interface RouteParams {
|
||||
dataset_token?: string;
|
||||
}
|
||||
|
||||
function VoiceDesignerFormPage({ enqueueInferenceJob }: { enqueueInferenceJob: any }) {
|
||||
function VoiceDesignerFormPage({ enqueueInferenceJob, sessionWrapper }: { enqueueInferenceJob: any, sessionWrapper: SessionWrapper }) {
|
||||
const history = useHistory();
|
||||
const { datasets, inputCtrl, languages, visibilityOptions, voices } = useVoiceRequests({});
|
||||
const [language, languageSet] = useState("en");
|
||||
@@ -166,6 +167,10 @@ function VoiceDesignerFormPage({ enqueueInferenceJob }: { enqueueInferenceJob: a
|
||||
}
|
||||
},[dataset_token,datasets,fetched]);
|
||||
|
||||
if (!sessionWrapper.isLoggedIn()) {
|
||||
history.push("/voice-designer");
|
||||
}
|
||||
|
||||
return (
|
||||
<Container type="panel">
|
||||
<PageHeader
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import React, { useState } from "react";
|
||||
import { Redirect, useHistory, useLocation } from "react-router-dom";
|
||||
import { faPlus, faWaveform } from "@fortawesome/pro-solid-svg-icons";
|
||||
import {
|
||||
faPenToSquare,
|
||||
faPlus,
|
||||
faRightToBracket,
|
||||
// faStar,
|
||||
faWaveform,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import InferenceJobsList from "components/layout/InferenceJobsList";
|
||||
import { useLocalize } from "hooks";
|
||||
import Panel from "components/common/Panel";
|
||||
@@ -19,12 +25,17 @@ import {
|
||||
FrontendInferenceJobType,
|
||||
// InferenceJob,
|
||||
} from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
|
||||
import { useSession } from "hooks";
|
||||
|
||||
function VoiceDesignerMainPage({
|
||||
inferenceJobsByCategory,
|
||||
}: {
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
|
||||
inferenceJobsByCategory: any;
|
||||
}) {
|
||||
}
|
||||
|
||||
function VoiceDesignerMainPage(props: Props) {
|
||||
usePrefixedDocumentTitle("AI Voice Designer");
|
||||
const { pathname } = useLocation();
|
||||
const { t } = useLocalize("FaceAnimator");
|
||||
@@ -37,8 +48,8 @@ function VoiceDesignerMainPage({
|
||||
title: "",
|
||||
text: "",
|
||||
});
|
||||
|
||||
const history = useHistory();
|
||||
const { user } = useSession();
|
||||
|
||||
if (pathname === "/voice-designer" || pathname === "/voice-designer/") {
|
||||
return <Redirect to="/voice-designer/voices" />;
|
||||
@@ -147,12 +158,24 @@ function VoiceDesignerMainPage({
|
||||
"Voice created successfully",
|
||||
][status];
|
||||
|
||||
const button = {
|
||||
const createVoiceButton = {
|
||||
label: `Create new voice`,
|
||||
icon: faPlus,
|
||||
to: "/voice-designer/create",
|
||||
};
|
||||
|
||||
const signUpButton = {
|
||||
label: `Sign Up`,
|
||||
icon: faPenToSquare,
|
||||
to: "/signup",
|
||||
};
|
||||
|
||||
// const pricingButton = {
|
||||
// label: `View Pricing`,
|
||||
// icon: faStar,
|
||||
// to: "/pricing",
|
||||
// };
|
||||
|
||||
const dataPlaceholder = () => <div className="d-flex flex-column list-items p-5 align-items-center">
|
||||
<h5 className="fw-semibold mb-3">
|
||||
You haven't created any voices.
|
||||
@@ -165,58 +188,89 @@ function VoiceDesignerMainPage({
|
||||
/>
|
||||
</div>;
|
||||
|
||||
const LogggedInView = () => <>
|
||||
<InferenceJobsList
|
||||
{...{
|
||||
t,
|
||||
inferenceJobs: props.inferenceJobsByCategory.get(
|
||||
FrontendInferenceJobType.VoiceDesignerCreateVoice
|
||||
),
|
||||
statusTxt,
|
||||
}}
|
||||
/>
|
||||
<Panel>
|
||||
<nav>
|
||||
<ul className="nav nav-tabs">
|
||||
<div className="d-flex flex-grow-1">
|
||||
<li className="nav-item">
|
||||
<NavLink
|
||||
to="/voice-designer/voices"
|
||||
className="nav-link fs-6 px-3 px-lg-4"
|
||||
activeClassName="active"
|
||||
>
|
||||
My Voices
|
||||
</NavLink>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<NavLink
|
||||
to="/voice-designer/datasets"
|
||||
className="nav-link fs-6"
|
||||
activeClassName="active"
|
||||
>
|
||||
My Datasets
|
||||
</NavLink>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div className="p-3 p-lg-4">
|
||||
<ListItems {...{ data: view ? actionVoices : actionDataSets, dataPlaceholder, isLoading }}/>
|
||||
</div>
|
||||
</Panel>
|
||||
</>;
|
||||
|
||||
const LoggedOutView = () => <Panel padding={true}>
|
||||
<div className="d-flex flex-column align-items-center py-3 my-3 py-md-4 my-md-4 gap-4">
|
||||
<div className="text-center">
|
||||
<h4 className="fw-bold">Please log in to access voice creation.</h4>
|
||||
|
||||
<p className="text-center opacity-75">
|
||||
If you don't have an account yet, sign up now to unlock this
|
||||
feature!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="d-flex gap-3 align-items-center">
|
||||
<Button
|
||||
label="Sign Up"
|
||||
variant="primary"
|
||||
icon={faPenToSquare}
|
||||
to="/signup"
|
||||
/>
|
||||
<Button
|
||||
label="Login"
|
||||
variant="secondary"
|
||||
icon={faRightToBracket}
|
||||
to="/login"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container type="panel">
|
||||
<PageHeader
|
||||
button={button}
|
||||
title="Voice Designer"
|
||||
titleIcon={faWaveform}
|
||||
subText="Create your own AI voice by providing audio files of the voice you want to clone."
|
||||
panel={false}
|
||||
imageUrl="/images/header/voice-designer.png"
|
||||
/>
|
||||
<InferenceJobsList
|
||||
{...{
|
||||
t,
|
||||
inferenceJobs: inferenceJobsByCategory.get(
|
||||
FrontendInferenceJobType.VoiceDesignerCreateVoice
|
||||
),
|
||||
statusTxt,
|
||||
}}
|
||||
/>
|
||||
<Panel mb={true}>
|
||||
<nav>
|
||||
<ul className="nav nav-tabs">
|
||||
<div className="d-flex flex-grow-1">
|
||||
<li className="nav-item">
|
||||
<NavLink
|
||||
to="/voice-designer/voices"
|
||||
className="nav-link fs-6 px-3 px-lg-4"
|
||||
activeClassName="active"
|
||||
>
|
||||
My Voices
|
||||
</NavLink>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<NavLink
|
||||
to="/voice-designer/datasets"
|
||||
className="nav-link fs-6"
|
||||
activeClassName="active"
|
||||
>
|
||||
My Datasets
|
||||
</NavLink>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div className="p-3 p-lg-4">
|
||||
<ListItems {...{ data: view ? actionVoices : actionDataSets, dataPlaceholder, isLoading }}/>
|
||||
</div>
|
||||
</Panel>
|
||||
</Container>
|
||||
|
||||
<PageHeader {...{
|
||||
button: user ? createVoiceButton : signUpButton,
|
||||
...!user ? { secondaryButton: signUpButton } : {},
|
||||
title: "Voice Designer",
|
||||
titleIcon: faWaveform,
|
||||
subText: "Create your own AI voice by providing audio files of the voice you want to clone.",
|
||||
panel: false,
|
||||
imageUrl: "/images/header/voice-designer.png"
|
||||
}}/>
|
||||
{ user ? <LogggedInView /> : <LoggedOutView /> }
|
||||
{/* Delete Modal */}
|
||||
<Modal
|
||||
show={isDeleteModalOpen}
|
||||
@@ -225,6 +279,7 @@ function VoiceDesignerMainPage({
|
||||
content={<p>{deleteText.text}</p>}
|
||||
onConfirm={handleDelete}
|
||||
/>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import { GetVoice } from "@storyteller/components/src/api/voice_designer/voices/
|
||||
import Skeleton from "components/common/Skeleton";
|
||||
import useVoiceRequests from "./useVoiceRequests";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useHistory } from "react-router-dom";
|
||||
|
||||
interface VoiceDesignerUseVoicePageProps {
|
||||
sessionWrapper: SessionWrapper;
|
||||
@@ -62,6 +63,7 @@ export default function VoiceDesignerUseVoicePage(
|
||||
});
|
||||
const { inference } = useVoiceRequests({});
|
||||
const [isEnqueuing, setIsEnqueuing] = useState(false);
|
||||
const history = useHistory();
|
||||
|
||||
const getVoiceDetails = useCallback(async (voice_token) => {
|
||||
try {
|
||||
@@ -189,6 +191,10 @@ export default function VoiceDesignerUseVoicePage(
|
||||
);
|
||||
}
|
||||
|
||||
if (!props.sessionWrapper.isLoggedIn()) {
|
||||
history.push("/voice-designer");
|
||||
}
|
||||
|
||||
return (
|
||||
<Container type="panel">
|
||||
<PageHeader title={voiceData.title} subText={subText} />
|
||||
|
||||
+37
-32
@@ -19,38 +19,40 @@ function VoiceDesignerVoiceEditPage() {
|
||||
const [visibility, visibilitySet] = useState("hidden");
|
||||
const [title, titleSet] = useState("");
|
||||
|
||||
const [fetched,fetchedSet] = useState(false);
|
||||
const [fetched, fetchedSet] = useState(false);
|
||||
|
||||
const history = useHistory();
|
||||
const { voice_token } = useParams();
|
||||
|
||||
const { inputCtrl, languages, visibilityOptions, voices } = useVoiceRequests({});
|
||||
const { inputCtrl, languages, visibilityOptions, voices } = useVoiceRequests(
|
||||
{}
|
||||
);
|
||||
|
||||
usePrefixedDocumentTitle("Edit Voice");
|
||||
|
||||
const onClick = () => voices.update(voice_token,{
|
||||
title,
|
||||
creator_set_visibility: visibility,
|
||||
ietf_language_tag: language,
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res && res.success) {
|
||||
history.push("/voice-designer");
|
||||
}
|
||||
});
|
||||
const onClick = () =>
|
||||
voices
|
||||
.update(voice_token, {
|
||||
title,
|
||||
creator_set_visibility: visibility,
|
||||
ietf_language_tag: language,
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res && res.success) {
|
||||
history.push("/voice-designer");
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!fetched && voice_token) {
|
||||
fetchedSet(true);
|
||||
voices.get(voice_token,{})
|
||||
.then((res) => {
|
||||
voices.get(voice_token, {}).then((res) => {
|
||||
languageSet(res.ietf_language_tag);
|
||||
titleSet(res.title);
|
||||
visibilitySet(res.creator_set_visibility);
|
||||
});
|
||||
}
|
||||
|
||||
},[fetched, voice_token, voices]);
|
||||
}, [fetched, voice_token, voices]);
|
||||
|
||||
return (
|
||||
<Container type="panel">
|
||||
@@ -67,23 +69,26 @@ function VoiceDesignerVoiceEditPage() {
|
||||
<Panel padding={true}>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<div className="row gy-4">
|
||||
<TempInput {...{
|
||||
label: "Title",
|
||||
placeholder: "Voice name",
|
||||
onChange: inputCtrl(titleSet),
|
||||
value: title
|
||||
}}/>
|
||||
<TempInput
|
||||
{...{
|
||||
label: "Title",
|
||||
placeholder: "Voice name",
|
||||
onChange: inputCtrl(titleSet),
|
||||
value: title,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TempSelect {...{
|
||||
icon: faLanguage,
|
||||
label: "Language",
|
||||
// placeholder: "Voice name",
|
||||
onChange: inputCtrl(languageSet),
|
||||
options: languages,
|
||||
value: language
|
||||
}}
|
||||
<TempSelect
|
||||
{...{
|
||||
icon: faLanguage,
|
||||
label: "Language",
|
||||
// placeholder: "Voice name",
|
||||
onChange: inputCtrl(languageSet),
|
||||
options: languages,
|
||||
value: language,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -95,12 +100,12 @@ function VoiceDesignerVoiceEditPage() {
|
||||
// placeholder: "Voice name",
|
||||
onChange: inputCtrl(visibilitySet),
|
||||
options: visibilityOptions,
|
||||
value: visibility
|
||||
value: visibility,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button {...{ label: "Save", onClick }}/>
|
||||
<Button {...{ label: "Save", onClick }} />
|
||||
</div>
|
||||
</Panel>
|
||||
</Container>
|
||||
|
||||
+1
@@ -6,6 +6,7 @@ import moment from 'moment';
|
||||
|
||||
import ListItems from "../NewList";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
// import { SessionContext } from 'context';
|
||||
|
||||
import { ListSamplesForDataset } from "@storyteller/components/src/api/voice_designer/voice_dataset_samples/ListSamplesForDataset";
|
||||
import { UploadSample } from "@storyteller/components/src/api/voice_designer/voice_dataset_samples/UploadSample";
|
||||
|
||||
Reference in new issue
Block a user