Merge branch 'master' into olivicmic-fy-voice-designer-consume-api-react

This commit is contained in:
Victor committed 2023-11-23 17:49:11 -08:00
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} />
@@ -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>
@@ -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";