mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 09:48:44 +00:00
Merge branch 'master' into bflat-fy-voice-designer-use-voice
This commit is contained in:
32 files changed
+809
-203
No files matched your search
@@ -36,6 +36,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-spring/types": "^9.7.3",
|
||||
"universal-cookie": "^6.1.1",
|
||||
"yarn": "^1.22.19"
|
||||
},
|
||||
"packageManager": "yarn@3.3.1"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import MakeRequest from "../../MakeRequest";
|
||||
|
||||
export interface DreateVoiceRequest {
|
||||
export interface DeleteVoiceRequest {
|
||||
set_delete: boolean;
|
||||
as_mod: boolean;
|
||||
}
|
||||
|
||||
export interface DreateVoiceResponse {
|
||||
export interface DeleteVoiceResponse {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export const DeleteVoice = MakeRequest<string, DreateVoiceRequest, DreateVoiceResponse>({
|
||||
export const DeleteDataset = MakeRequest<string, DeleteVoiceRequest, DeleteVoiceResponse>({
|
||||
method: "DELETE",
|
||||
routingFunction: (voiceToken: string) => `/v1/voice_designer/dataset/${ voiceToken }/delete`,
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import { UserDetailsLight } from "../../_common/UserDetailsLight";
|
||||
|
||||
export interface ListDatasetsByUserRequest {}
|
||||
|
||||
export interface ListDatasetsByUserResponse {
|
||||
export interface Dataset {
|
||||
dataset_token: string,
|
||||
title: string,
|
||||
|
||||
@@ -17,6 +17,11 @@ export interface ListDatasetsByUserResponse {
|
||||
updated_at: Date,
|
||||
}
|
||||
|
||||
export interface ListDatasetsByUserResponse {
|
||||
success: boolean,
|
||||
datasets: Dataset[],
|
||||
}
|
||||
|
||||
export const ListDatasetsByUser = MakeRequest<string, ListDatasetsByUserRequest, ListDatasetsByUserResponse>({
|
||||
method: "GET",
|
||||
routingFunction: (userName: string) => `/v1/voice_designer/dataset/user/${ userName }/list`,
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { ActiveSubscription, ListActiveSubscriptions, ListActiveSubscriptionsIsSuccess, ListActiveSubscriptionsSuccessResponse } from "../api/premium/ListActiveSubscriptions";
|
||||
import {
|
||||
ActiveSubscription,
|
||||
ListActiveSubscriptions,
|
||||
ListActiveSubscriptionsIsSuccess,
|
||||
ListActiveSubscriptionsSuccessResponse,
|
||||
} from "../api/premium/ListActiveSubscriptions";
|
||||
import { FakeYouFrontendEnvironment } from "../env/FakeYouFrontendEnvironment";
|
||||
|
||||
const FAKEYOU_NAMESPACE = "fakeyou";
|
||||
@@ -72,8 +77,25 @@ export class SessionSubscriptionsWrapper {
|
||||
return maybePlan !== undefined;
|
||||
}
|
||||
|
||||
private findActiveSubscription(namespace: string, product_slug: string) : ActiveSubscription | undefined {
|
||||
const subs = this.listActiveSubscriptionResponse?.active_subscriptions || [];
|
||||
return subs.find((sub) => sub.namespace === namespace && sub.product_slug === product_slug);
|
||||
public getActiveProductSlug(): string | undefined {
|
||||
if (this.hasActiveEliteSubscription()) {
|
||||
return "fakeyou_elite";
|
||||
} else if (this.hasActiveProSubscription()) {
|
||||
return "fakeyou_pro";
|
||||
} else if (this.hasActivePlusSubscription()) {
|
||||
return "fakeyou_plus";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private findActiveSubscription(
|
||||
namespace: string,
|
||||
product_slug: string
|
||||
): ActiveSubscription | undefined {
|
||||
const subs =
|
||||
this.listActiveSubscriptionResponse?.active_subscriptions || [];
|
||||
return subs.find(
|
||||
(sub) => sub.namespace === namespace && sub.product_slug === product_slug
|
||||
);
|
||||
}
|
||||
}
|
||||
+124
-103
@@ -3,6 +3,7 @@ import "scss/custom-bootstrap.scss";
|
||||
|
||||
import React from "react";
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
import Cookies from 'universal-cookie';
|
||||
import {
|
||||
DetectLocale,
|
||||
DetectLocaleIsOk,
|
||||
@@ -58,7 +59,7 @@ import { VoiceConversionModelUploadJob } from "@storyteller/components/src/jobs/
|
||||
import { VoiceConversionModelListItem } from "@storyteller/components/src/api/voice_conversion/ListVoiceConversionModels";
|
||||
import HttpBackend from "i18next-http-backend";
|
||||
|
||||
import { InferenceJobs } from "components/providers";
|
||||
import { InferenceJobs, Session as SessionProvider } from "components/providers";
|
||||
|
||||
// NB: We're transitioning over to this instance of i18n-next that loads translations over HTTP from Json Files.
|
||||
// The old i18n-next instance (see below) bakes in translations into the compiled javascript blob.
|
||||
@@ -256,6 +257,7 @@ class App extends React.Component<Props, State> {
|
||||
voiceConversionModels: [],
|
||||
maybeSelectedVoiceConversionModel: undefined,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
@@ -293,13 +295,18 @@ class App extends React.Component<Props, State> {
|
||||
querySession = async () => {
|
||||
const sessionWrapper = await SessionWrapper.lookupSession();
|
||||
const username = sessionWrapper.getDisplayName();
|
||||
const cookies = new Cookies();
|
||||
|
||||
if (username !== undefined) {
|
||||
// Track only logged-in users (for now)
|
||||
PosthogClient.enablePosthog();
|
||||
PosthogClient.setUsername(username);
|
||||
window.dataLayer.push({
|
||||
'user_id': username,
|
||||
});
|
||||
cookies.set('logged_in_username', username, { path: '/', expires: new Date(Date.now()+3 * 86400000) });
|
||||
window.dataLayer.push({
|
||||
'user_id': username,
|
||||
});
|
||||
} else {
|
||||
cookies.remove('logged_in_username', { path: '/' });
|
||||
}
|
||||
this.setState({
|
||||
sessionWrapper: sessionWrapper,
|
||||
@@ -307,8 +314,18 @@ class App extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
querySessionSubscriptions = async () => {
|
||||
const cookies = new Cookies();
|
||||
|
||||
const sessionSubscriptionsWrapper =
|
||||
await SessionSubscriptionsWrapper.lookupActiveSubscriptions();
|
||||
await SessionSubscriptionsWrapper.lookupActiveSubscriptions();
|
||||
|
||||
const plan = sessionSubscriptionsWrapper.getActiveProductSlug();
|
||||
if (plan !== undefined) {
|
||||
cookies.set('logged_in_user_plan', plan, { path: '/', expires: new Date(Date.now()+3 * 86400000) });
|
||||
} else {
|
||||
cookies.remove('logged_in_user_plan', { path: '/' });
|
||||
}
|
||||
|
||||
this.setState({
|
||||
sessionSubscriptionsWrapper: sessionSubscriptionsWrapper,
|
||||
});
|
||||
@@ -789,104 +806,108 @@ class App extends React.Component<Props, State> {
|
||||
*/}
|
||||
|
||||
<div className="migrationComponentWrapper">
|
||||
<InferenceJobs {...{ byCategory: this.state.inferenceJobsByCategory }}>
|
||||
<Switch>
|
||||
<Route path="/">
|
||||
<PageContainer
|
||||
sessionWrapper={this.state.sessionWrapper}
|
||||
querySessionAction={this.querySession}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.state.sessionSubscriptionsWrapper
|
||||
}
|
||||
querySessionSubscriptionsAction={
|
||||
this.querySessionSubscriptions
|
||||
}
|
||||
isShowingVocodesNotice={this.state.isShowingVocodesNotice}
|
||||
clearVocodesNotice={this.clearVocodesNotice}
|
||||
isShowingLangaugeNotice={this.state.isShowingLanguageNotice}
|
||||
clearLanguageNotice={this.clearLanguageNotice}
|
||||
displayLanguage={this.state.displayLanguage}
|
||||
primaryLanguageCode={this.state.primaryLanguageCode}
|
||||
isShowingTwitchTtsNotice={
|
||||
this.state.isShowingTwitchTtsNotice
|
||||
}
|
||||
clearTwitchTtsNotice={this.clearTwitchTtsNotice}
|
||||
isShowingPleaseFollowNotice={
|
||||
this.state.isShowingPleaseFollowNotice
|
||||
}
|
||||
clearPleaseFollowNotice={this.clearPleaseFollowNotice}
|
||||
isShowingBootstrapLanguageNotice={
|
||||
this.state.isShowingBootstrapLanguageNotice
|
||||
}
|
||||
clearBootstrapLanguageNotice={
|
||||
this.clearBootstrapLanguageNotice
|
||||
}
|
||||
enqueueInferenceJob={this.enqueueInferenceJob}
|
||||
inferenceJobs={this.state.inferenceJobs}
|
||||
inferenceJobsByCategory={this.state.inferenceJobsByCategory}
|
||||
enqueueTtsJob={this.enqueueTtsJob}
|
||||
ttsInferenceJobs={this.state.ttsInferenceJobs}
|
||||
enqueueW2lJob={this.enqueueW2lJob}
|
||||
w2lInferenceJobs={this.state.w2lInferenceJobs}
|
||||
enqueueTtsModelUploadJob={this.enqueueTtsModelUploadJob}
|
||||
ttsModelUploadJobs={this.state.ttsModelUploadJobs}
|
||||
enqueueW2lTemplateUploadJob={
|
||||
this.enqueueW2lTemplateUploadJob
|
||||
}
|
||||
w2lTemplateUploadJobs={this.state.w2lTemplateUploadJobs}
|
||||
enqueueVocoderUploadJob={this.enqueueVocoderUploadJob}
|
||||
vocoderUploadJobs={this.state.vocoderUploadJobs}
|
||||
enqueueVoiceConversionModelUploadJob={
|
||||
this.enqueueVoiceConversionModelUploadJob
|
||||
}
|
||||
voiceConversionModelUploadJobs={
|
||||
this.state.voiceConversionModelUploadJobs
|
||||
}
|
||||
textBuffer={this.state.textBuffer}
|
||||
setTextBuffer={this.setTextBuffer}
|
||||
clearTextBuffer={this.clearTextBuffer}
|
||||
ttsModels={this.props.allTtsModels}
|
||||
setTtsModels={this.props.setAllTtsModels}
|
||||
allTtsCategories={this.props.allTtsCategories}
|
||||
setAllTtsCategories={this.props.setAllTtsCategories}
|
||||
computedTtsCategoryAssignments={
|
||||
this.props.computedTtsCategoryAssignments
|
||||
}
|
||||
setComputedTtsCategoryAssignments={
|
||||
this.props.setComputedTtsCategoryAssignments
|
||||
}
|
||||
allTtsCategoriesByTokenMap={
|
||||
this.props.allTtsCategoriesByTokenMap
|
||||
}
|
||||
allTtsModelsByTokenMap={this.props.allTtsModelsByTokenMap}
|
||||
ttsModelsByCategoryToken={
|
||||
this.props.ttsModelsByCategoryToken
|
||||
}
|
||||
dropdownCategories={this.props.dropdownCategories}
|
||||
setDropdownCategories={this.props.setDropdownCategories}
|
||||
selectedCategories={this.props.selectedCategories}
|
||||
setSelectedCategories={this.props.setSelectedCategories}
|
||||
maybeSelectedTtsModel={this.props.maybeSelectedTtsModel}
|
||||
setMaybeSelectedTtsModel={
|
||||
this.props.setMaybeSelectedTtsModel
|
||||
}
|
||||
selectedTtsLanguageScope={
|
||||
this.props.selectedTtsLanguageScope
|
||||
}
|
||||
setSelectedTtsLanguageScope={
|
||||
this.props.setSelectedTtsLanguageScope
|
||||
}
|
||||
voiceConversionModels={this.state.voiceConversionModels}
|
||||
setVoiceConversionModels={this.setAllVoiceConversionModels}
|
||||
maybeSelectedVoiceConversionModel={
|
||||
this.state.maybeSelectedVoiceConversionModel
|
||||
}
|
||||
setMaybeSelectedVoiceConversionModel={
|
||||
this.setMaybeSelectedVoiceConversionModel
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
<InferenceJobs {...{
|
||||
byCategory: this.state.inferenceJobsByCategory
|
||||
}}>
|
||||
<SessionProvider {...{ sessionWrapper: this.state.sessionWrapper }}>
|
||||
<Switch>
|
||||
<Route path="/">
|
||||
<PageContainer
|
||||
sessionWrapper={this.state.sessionWrapper}
|
||||
querySessionAction={this.querySession}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.state.sessionSubscriptionsWrapper
|
||||
}
|
||||
querySessionSubscriptionsAction={
|
||||
this.querySessionSubscriptions
|
||||
}
|
||||
isShowingVocodesNotice={this.state.isShowingVocodesNotice}
|
||||
clearVocodesNotice={this.clearVocodesNotice}
|
||||
isShowingLangaugeNotice={this.state.isShowingLanguageNotice}
|
||||
clearLanguageNotice={this.clearLanguageNotice}
|
||||
displayLanguage={this.state.displayLanguage}
|
||||
primaryLanguageCode={this.state.primaryLanguageCode}
|
||||
isShowingTwitchTtsNotice={
|
||||
this.state.isShowingTwitchTtsNotice
|
||||
}
|
||||
clearTwitchTtsNotice={this.clearTwitchTtsNotice}
|
||||
isShowingPleaseFollowNotice={
|
||||
this.state.isShowingPleaseFollowNotice
|
||||
}
|
||||
clearPleaseFollowNotice={this.clearPleaseFollowNotice}
|
||||
isShowingBootstrapLanguageNotice={
|
||||
this.state.isShowingBootstrapLanguageNotice
|
||||
}
|
||||
clearBootstrapLanguageNotice={
|
||||
this.clearBootstrapLanguageNotice
|
||||
}
|
||||
enqueueInferenceJob={this.enqueueInferenceJob}
|
||||
inferenceJobs={this.state.inferenceJobs}
|
||||
inferenceJobsByCategory={this.state.inferenceJobsByCategory}
|
||||
enqueueTtsJob={this.enqueueTtsJob}
|
||||
ttsInferenceJobs={this.state.ttsInferenceJobs}
|
||||
enqueueW2lJob={this.enqueueW2lJob}
|
||||
w2lInferenceJobs={this.state.w2lInferenceJobs}
|
||||
enqueueTtsModelUploadJob={this.enqueueTtsModelUploadJob}
|
||||
ttsModelUploadJobs={this.state.ttsModelUploadJobs}
|
||||
enqueueW2lTemplateUploadJob={
|
||||
this.enqueueW2lTemplateUploadJob
|
||||
}
|
||||
w2lTemplateUploadJobs={this.state.w2lTemplateUploadJobs}
|
||||
enqueueVocoderUploadJob={this.enqueueVocoderUploadJob}
|
||||
vocoderUploadJobs={this.state.vocoderUploadJobs}
|
||||
enqueueVoiceConversionModelUploadJob={
|
||||
this.enqueueVoiceConversionModelUploadJob
|
||||
}
|
||||
voiceConversionModelUploadJobs={
|
||||
this.state.voiceConversionModelUploadJobs
|
||||
}
|
||||
textBuffer={this.state.textBuffer}
|
||||
setTextBuffer={this.setTextBuffer}
|
||||
clearTextBuffer={this.clearTextBuffer}
|
||||
ttsModels={this.props.allTtsModels}
|
||||
setTtsModels={this.props.setAllTtsModels}
|
||||
allTtsCategories={this.props.allTtsCategories}
|
||||
setAllTtsCategories={this.props.setAllTtsCategories}
|
||||
computedTtsCategoryAssignments={
|
||||
this.props.computedTtsCategoryAssignments
|
||||
}
|
||||
setComputedTtsCategoryAssignments={
|
||||
this.props.setComputedTtsCategoryAssignments
|
||||
}
|
||||
allTtsCategoriesByTokenMap={
|
||||
this.props.allTtsCategoriesByTokenMap
|
||||
}
|
||||
allTtsModelsByTokenMap={this.props.allTtsModelsByTokenMap}
|
||||
ttsModelsByCategoryToken={
|
||||
this.props.ttsModelsByCategoryToken
|
||||
}
|
||||
dropdownCategories={this.props.dropdownCategories}
|
||||
setDropdownCategories={this.props.setDropdownCategories}
|
||||
selectedCategories={this.props.selectedCategories}
|
||||
setSelectedCategories={this.props.setSelectedCategories}
|
||||
maybeSelectedTtsModel={this.props.maybeSelectedTtsModel}
|
||||
setMaybeSelectedTtsModel={
|
||||
this.props.setMaybeSelectedTtsModel
|
||||
}
|
||||
selectedTtsLanguageScope={
|
||||
this.props.selectedTtsLanguageScope
|
||||
}
|
||||
setSelectedTtsLanguageScope={
|
||||
this.props.setSelectedTtsLanguageScope
|
||||
}
|
||||
voiceConversionModels={this.state.voiceConversionModels}
|
||||
setVoiceConversionModels={this.setAllVoiceConversionModels}
|
||||
maybeSelectedVoiceConversionModel={
|
||||
this.state.maybeSelectedVoiceConversionModel
|
||||
}
|
||||
setMaybeSelectedVoiceConversionModel={
|
||||
this.setMaybeSelectedVoiceConversionModel
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
</SessionProvider>
|
||||
</InferenceJobs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
iconFlip?: boolean;
|
||||
download?: boolean | string;
|
||||
disabled?: boolean;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
export default function Button({
|
||||
@@ -36,6 +37,7 @@ export default function Button({
|
||||
iconFlip = false,
|
||||
download,
|
||||
disabled,
|
||||
isLoading = false,
|
||||
...rest
|
||||
}: ButtonProps) {
|
||||
let iconMarginClass = !square && label ? (iconFlip ? "ms-2" : "me-2") : "";
|
||||
@@ -45,13 +47,22 @@ export default function Button({
|
||||
|
||||
let LabelComponent = !square ? label : null;
|
||||
|
||||
const SpinnerComponent = isLoading ? (
|
||||
<div
|
||||
className="spinner-border spinner-border-sm text-white ms-2"
|
||||
role="status"
|
||||
>
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const externalClass = rest.className || "";
|
||||
|
||||
const commonProps = {
|
||||
className: `${externalClass} button ${small ? "button-small" : ""} ${
|
||||
square ? (small ? "button-square-small" : "button-square") : ""
|
||||
} button-${variant} ${full ? "w-100" : ""}`,
|
||||
disabled: disabled,
|
||||
disabled: disabled || isLoading,
|
||||
};
|
||||
|
||||
delete rest.className;
|
||||
@@ -60,11 +71,13 @@ export default function Button({
|
||||
<>
|
||||
{LabelComponent}
|
||||
{IconComponent}
|
||||
{SpinnerComponent}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{IconComponent}
|
||||
{LabelComponent}
|
||||
{SpinnerComponent}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -4,14 +4,18 @@ import { faTrashAlt } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useSpring, a } from "@react-spring/web";
|
||||
|
||||
|
||||
|
||||
interface ModalProps {
|
||||
show: boolean;
|
||||
handleClose: () => void;
|
||||
onCancel?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||
onConfirm?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||
title: string;
|
||||
content: React.ReactNode;
|
||||
}
|
||||
|
||||
const Modal: React.FC<ModalProps> = ({ show, handleClose, title, content }) => {
|
||||
const Modal: React.FC<ModalProps> = ({ show, handleClose, onCancel: cancelEvent, onConfirm: confirmEvent, title, content }) => {
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const fadeIn = useSpring({
|
||||
@@ -22,6 +26,16 @@ const Modal: React.FC<ModalProps> = ({ show, handleClose, title, content }) => {
|
||||
},
|
||||
});
|
||||
|
||||
const onCancel = (e: React.MouseEvent<HTMLElement>) => {
|
||||
if (cancelEvent) cancelEvent(e);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const onConfirm = (e: React.MouseEvent<HTMLElement>) => {
|
||||
if (confirmEvent) confirmEvent(e);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
@@ -70,9 +84,9 @@ const Modal: React.FC<ModalProps> = ({ show, handleClose, title, content }) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
label="Cancel"
|
||||
onClick={handleClose}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
<Button variant="danger" label="Delete" onClick={handleClose} />
|
||||
<Button variant="danger" label="Delete" onClick={onConfirm} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,9 +8,12 @@ import FileDetails from "./FileDetails";
|
||||
import FileWrapper from "./FileWrapper";
|
||||
import FileLabel from "./FileLabel";
|
||||
import ImageInput from "./ImageInput";
|
||||
import Input from "./Input";
|
||||
import NumberSlider from "./NumberSlider";
|
||||
import Panel from "./Panel";
|
||||
import Radio from "./Radio";
|
||||
import SegmentButtons from "./SegmentButtons";
|
||||
import Select from "./Select";
|
||||
import Slider from "./Slider";
|
||||
import Spinner from "./Spinner";
|
||||
|
||||
@@ -25,9 +28,12 @@ export {
|
||||
FileWrapper,
|
||||
FileLabel,
|
||||
ImageInput,
|
||||
Input,
|
||||
NumberSlider,
|
||||
Panel,
|
||||
Radio,
|
||||
SegmentButtons,
|
||||
Select,
|
||||
Slider,
|
||||
Spinner
|
||||
};
|
||||
@@ -5,7 +5,13 @@ import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import Button from "components/common/Button";
|
||||
import BackButton from "components/common/BackButton";
|
||||
|
||||
interface BackConfig {
|
||||
label: string;
|
||||
to: string;
|
||||
};
|
||||
|
||||
interface PageHeaderProps {
|
||||
back?: BackConfig;
|
||||
titleIcon?: IconDefinition;
|
||||
title: string | React.ReactNode;
|
||||
subText: string | React.ReactNode;
|
||||
@@ -25,6 +31,7 @@ interface PageHeaderProps {
|
||||
}
|
||||
|
||||
export default function PageHeader({
|
||||
back,
|
||||
titleIcon,
|
||||
title,
|
||||
subText,
|
||||
@@ -50,13 +57,17 @@ export default function PageHeader({
|
||||
return (
|
||||
<div className="py-3 py-lg-4">
|
||||
<Panel clear={true}>
|
||||
{showBackButton && (
|
||||
{( back || showBackButton) && (
|
||||
<div className="d-flex mb-2 mb-lg-3">
|
||||
<BackButton label={backbuttonLabel} to={backbuttonTo} />
|
||||
<BackButton label={back ? back.label : backbuttonLabel} to={ back ? back.to : backbuttonTo } />
|
||||
</div>
|
||||
)}
|
||||
<div className="row">
|
||||
<div className="d-flex flex-column col-lg-7 justify-content-center gap-4 py-3">
|
||||
<div
|
||||
className={`d-flex flex-column ${
|
||||
imageUrl ? "col-lg-7" : "col-12"
|
||||
} justify-content-center gap-4 py-2`}
|
||||
>
|
||||
<div>
|
||||
<h1 className="fw-bold">
|
||||
{/* {icon} */}
|
||||
@@ -76,7 +87,11 @@ export default function PageHeader({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="d-none d-lg-block col-lg-5">
|
||||
<div
|
||||
className={`d-none col-lg-5 ${
|
||||
imageUrl ? "d-lg-block" : "d-lg-none"
|
||||
}`}
|
||||
>
|
||||
{imageUrl && (
|
||||
<img
|
||||
src={imageUrl}
|
||||
|
||||
@@ -6,9 +6,10 @@ import { InferenceJobsContext } from 'context';
|
||||
interface Props {
|
||||
children?: any;
|
||||
byCategory?: any;
|
||||
sessionWrapper?: any;
|
||||
}
|
||||
|
||||
export default function InferenceJobs({ children, byCategory }: Props) {
|
||||
export default function InferenceJobs({ children, byCategory, sessionWrapper }: Props) {
|
||||
const processStatus = (job: InferenceJob) => {
|
||||
switch (job.jobState) {
|
||||
case JobState.PENDING:
|
||||
@@ -21,7 +22,7 @@ export default function InferenceJobs({ children, byCategory }: Props) {
|
||||
default: return -1;
|
||||
}
|
||||
};
|
||||
return <InferenceJobsContext.Provider {...{ value: { byCategory, processStatus } }}>
|
||||
return <InferenceJobsContext.Provider {...{ value: { byCategory, processStatus, sessionWrapper } }}>
|
||||
{ children }
|
||||
</InferenceJobsContext.Provider>
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { SessionContext } from 'context';
|
||||
|
||||
interface Props {
|
||||
children?: any;
|
||||
sessionWrapper?: any;
|
||||
}
|
||||
|
||||
export default function Session({ children, sessionWrapper }: Props) {
|
||||
return <SessionContext.Provider {...{ value: { sessionWrapper } }}>
|
||||
{ children }
|
||||
</SessionContext.Provider>
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './Session';
|
||||
@@ -1,3 +1,4 @@
|
||||
import InferenceJobs from "./InferenceJobs";
|
||||
import Session from "./Session";
|
||||
|
||||
export { InferenceJobs };
|
||||
export { InferenceJobs, Session };
|
||||
@@ -0,0 +1,7 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
interface SessionContextType {
|
||||
sessionWrapper?: any
|
||||
}
|
||||
|
||||
export default createContext<SessionContextType>({});
|
||||
@@ -1,3 +1,4 @@
|
||||
import InferenceJobsContext from "./InferenceJobsContext";
|
||||
import SessionContext from "./SessionContext";
|
||||
|
||||
export { InferenceJobsContext };
|
||||
export { InferenceJobsContext, SessionContext };
|
||||
@@ -4,6 +4,7 @@ import useId from './useId';
|
||||
import useIdempotency from './useIdempotency';
|
||||
import useInferenceJobs from './useInferenceJobs';
|
||||
import useLocalize from './useLocalize';
|
||||
import useSession from './useSession';
|
||||
import useVideo from './useVideo';
|
||||
|
||||
export { useFile, useHover, useId, useIdempotency, useInferenceJobs, useLocalize, useVideo };
|
||||
export { useFile, useHover, useId, useIdempotency, useInferenceJobs, useLocalize, useSession, useVideo };
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './useSession';
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
import { SessionContext } from 'context';
|
||||
|
||||
export default function useInferenceJobs() {
|
||||
const { sessionWrapper } = useContext(SessionContext);
|
||||
return sessionWrapper?.sessionStateResponse || {};
|
||||
};
|
||||
@@ -82,6 +82,7 @@ import { VoiceConversionModelListItem } from "@storyteller/components/src/api/vo
|
||||
import { CommunityCommissionsPage } from "./pages/contest/CommunityCommissionsPage";
|
||||
import { ProductUsageInfoPage } from "./pages/product_usage_info/ProductUsageInfoPage";
|
||||
import { VoiceDesignerFormPage } from "./pages/voice_designer/VoiceDesignerFormPage";
|
||||
import DatasetEditor from "./pages/voice_designer/DatasetEditor";
|
||||
import { GenerateSpeechPage } from "./pages/generate_speech/GenerateSpeechPage";
|
||||
import VcModelViewPage from "./pages/vc/vc_model_view/VcModelViewPage";
|
||||
import VcModelEditPage from "./pages/vc/vc_model_edit/VcModelEditPage";
|
||||
@@ -94,6 +95,9 @@ import MediaPage from "./pages/media/MediaPage";
|
||||
import { VoiceDesignerMainPage } from "./pages/voice_designer/VoiceDesignerMainPage";
|
||||
import { VoiceDesignerVoiceEditPage } from "./pages/voice_designer/VoiceDesignerVoiceEditPage";
|
||||
import VoiceDesignerUseVoicePage from "./pages/voice_designer/VoiceDesignerUseVoicePage";
|
||||
import { PasswordResetEmailPage } from "./pages/password_reset/PasswordResetEmailPage";
|
||||
import { PasswordResetVerificationPage } from "./pages/password_reset/PasswordResetVerificationPage";
|
||||
import { PasswordResetChangePage } from "./pages/password_reset/PasswordResetChangePage";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
@@ -270,6 +274,24 @@ class PageContainer extends React.Component<
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/password-reset/new">
|
||||
<PasswordResetChangePage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/password-reset/verify">
|
||||
<PasswordResetVerificationPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/password-reset">
|
||||
<PasswordResetEmailPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/profile/:username/edit">
|
||||
<ProfileEditFc sessionWrapper={this.props.sessionWrapper} />
|
||||
</Route>
|
||||
@@ -631,7 +653,10 @@ class PageContainer extends React.Component<
|
||||
<Route
|
||||
exact
|
||||
path="/voice-designer/dataset/:dataset_token/edit"
|
||||
component={VoiceDesignerFormPage}
|
||||
{...{
|
||||
sessionWrapper: this.props.sessionWrapper
|
||||
}}
|
||||
component={DatasetEditor}
|
||||
/>
|
||||
|
||||
{/* Route for handling dataset token for uploading samples */}
|
||||
@@ -655,7 +680,7 @@ class PageContainer extends React.Component<
|
||||
</Route>
|
||||
|
||||
<Route path="/voice-designer">
|
||||
<VoiceDesignerMainPage />
|
||||
<VoiceDesignerMainPage/>
|
||||
</Route>
|
||||
|
||||
<Route path="/generate-speech">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { faMicrophone, faVolume } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faVolume } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
|
||||
import { PageHeaderWithImage } from "v2/view/_common/PageHeaderWithImage";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import { WebUrl } from "../../../../common/WebUrl";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import {
|
||||
@@ -13,6 +12,9 @@ import { faUser, faKey } from "@fortawesome/free-solid-svg-icons";
|
||||
import { Analytics } from "../../../../common/Analytics";
|
||||
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
|
||||
import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient";
|
||||
import Container from "components/common/Container";
|
||||
import Panel from "components/common/Panel";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
@@ -88,62 +90,59 @@ function LoginPage(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">Login</h1>
|
||||
<div className="py-6">
|
||||
{errorWarning}
|
||||
<Container type="panel" className="login-panel">
|
||||
<PageHeader
|
||||
title="Login"
|
||||
subText="Log into your account."
|
||||
panel={false}
|
||||
/>
|
||||
<Panel padding={true}>
|
||||
{errorWarning}
|
||||
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<div>
|
||||
<label className="sub-title">Username or Email</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faUser} />
|
||||
</span>
|
||||
<input
|
||||
className="form-control"
|
||||
type="text"
|
||||
placeholder="Username or Email"
|
||||
value={usernameOrEmail}
|
||||
onChange={handleUsernameOrEmailChange}
|
||||
/>
|
||||
</div>
|
||||
{/*<p className="help"></p>*/}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="sub-title">Password</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faKey} />
|
||||
</span>
|
||||
<input
|
||||
className="form-control"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={handlePasswordChange}
|
||||
/>
|
||||
</div>
|
||||
{/*<p className="help"></p>*/}
|
||||
</div>
|
||||
|
||||
<button className="btn btn-primary w-100 mt-2">Login</button>
|
||||
<p>
|
||||
Don’t have an account?
|
||||
<Link to={WebUrl.signupPage()} className="text-link">
|
||||
Create an account now.
|
||||
</Link>
|
||||
</p>
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<div>
|
||||
<label className="sub-title">Username or Email</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faUser} />
|
||||
</span>
|
||||
<input
|
||||
className="form-control"
|
||||
type="text"
|
||||
placeholder="Username or Email"
|
||||
value={usernameOrEmail}
|
||||
onChange={handleUsernameOrEmailChange}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
{/*<p className="help"></p>*/}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="sub-title">Password</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={faKey} />
|
||||
</span>
|
||||
<input
|
||||
className="form-control"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={handlePasswordChange}
|
||||
/>
|
||||
</div>
|
||||
<p className="d-flex flex-column flex-lg-row gap-2">
|
||||
<Link to="/password-reset" className="text-link form-text">
|
||||
Forgot your password?
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button className="btn btn-primary w-100 mt-4">Login</button>
|
||||
</form>
|
||||
</Panel>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import React, { useState } from "react";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
|
||||
import Container from "components/common/Container";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
import Input from "components/common/Input";
|
||||
import { Button } from "components/common";
|
||||
import Panel from "components/common/Panel";
|
||||
import { faKey } from "@fortawesome/pro-solid-svg-icons";
|
||||
import BackButton from "components/common/BackButton";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
function PasswordResetChangePage(props: Props) {
|
||||
let history = useHistory();
|
||||
const [isPasswordChanged, setIsPasswordChanged] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
if (props.sessionWrapper.isLoggedIn()) {
|
||||
history.push("/");
|
||||
}
|
||||
|
||||
usePrefixedDocumentTitle("Reset Password");
|
||||
|
||||
const handlePasswordChange = async () => {
|
||||
// Simulate a delay using a timer (dummy timer)
|
||||
setIsLoading(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsPasswordChanged(true);
|
||||
setIsLoading(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<Container type="panel" className="login-panel">
|
||||
<PageHeader
|
||||
title="Change Password"
|
||||
subText="Set your new password."
|
||||
panel={false}
|
||||
/>
|
||||
|
||||
<Panel padding={true}>
|
||||
{isPasswordChanged ? (
|
||||
<div className="d-flex flex-column gap-3 align-items-start">
|
||||
<p>Your password has been successfully changed!</p>
|
||||
<BackButton label="Back to Login" to="/login" />
|
||||
</div>
|
||||
) : (
|
||||
<form>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<Input
|
||||
label="New Password"
|
||||
icon={faKey}
|
||||
placeholder="Enter new password"
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Confirm New Password"
|
||||
icon={faKey}
|
||||
placeholder="Confirm new password"
|
||||
/>
|
||||
|
||||
<Button
|
||||
label="Confirm Change"
|
||||
onClick={handlePasswordChange}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</Panel>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export { PasswordResetChangePage };
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
|
||||
import Container from "components/common/Container";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
import Input from "components/common/Input";
|
||||
import { Button } from "components/common";
|
||||
import Panel from "components/common/Panel";
|
||||
import { faEnvelope } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
function PasswordResetEmailPage(props: Props) {
|
||||
let history = useHistory();
|
||||
|
||||
if (props.sessionWrapper.isLoggedIn()) {
|
||||
history.push("/");
|
||||
}
|
||||
|
||||
usePrefixedDocumentTitle("Reset Password");
|
||||
|
||||
return (
|
||||
<Container type="panel" className="login-panel">
|
||||
<PageHeader
|
||||
title="Reset Password"
|
||||
subText="Enter your account's email address you'd like your password
|
||||
reset information sent to."
|
||||
panel={false}
|
||||
showBackButton={true}
|
||||
/>
|
||||
|
||||
<Panel padding={true}>
|
||||
<form>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<Input
|
||||
label="Enter Email"
|
||||
icon={faEnvelope}
|
||||
placeholder="Email address"
|
||||
/>
|
||||
|
||||
<Button label="Reset Password" onClick={() => {}} />
|
||||
</div>
|
||||
</form>
|
||||
</Panel>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export { PasswordResetEmailPage };
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import React, { useState } from "react";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle";
|
||||
import Container from "components/common/Container";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
import Input from "components/common/Input";
|
||||
import { Button } from "components/common";
|
||||
import Panel from "components/common/Panel";
|
||||
import { faLock } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
function PasswordResetVerificationPage(props: Props) {
|
||||
let history = useHistory();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
if (props.sessionWrapper.isLoggedIn()) {
|
||||
history.push("/");
|
||||
}
|
||||
|
||||
usePrefixedDocumentTitle("Code Verification");
|
||||
|
||||
const handleVerifyCode = async () => {
|
||||
// Simulate a delay using a timer (dummy timer)
|
||||
setIsLoading(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<Container type="panel" className="login-panel">
|
||||
<PageHeader
|
||||
title="Code Verification"
|
||||
subText="Enter the 6-digit code sent to your email address."
|
||||
panel={false}
|
||||
/>
|
||||
|
||||
<Panel padding={true}>
|
||||
<form>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<Input
|
||||
label="Verification Code"
|
||||
icon={faLock}
|
||||
placeholder="Enter 6-digit code"
|
||||
/>
|
||||
|
||||
<Button
|
||||
label="Verify Code"
|
||||
onClick={handleVerifyCode}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Panel>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export { PasswordResetVerificationPage };
|
||||
@@ -0,0 +1,83 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams } from "react-router-dom";
|
||||
import Container from "components/common/Container";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
// import { Stepper } from "./components/Stepper";
|
||||
// import { StepperControls } from "./components/StepperControls";
|
||||
import { faPencil, faWaveform } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { Input, Panel, Select } from "components/common";
|
||||
import useVoiceRequests from "./useVoiceRequests";
|
||||
|
||||
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
|
||||
interface Props {
|
||||
value?: any;
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
interface RouteParams {
|
||||
dataset_token?: string;
|
||||
}
|
||||
|
||||
export default function DatasetEditor({ sessionWrapper, value }: Props) {
|
||||
const { datasets, datasetByToken } = useVoiceRequests();
|
||||
const [title, titleSet] = useState("");
|
||||
const [language,languageSet] = useState({ value: "en", label: "English"});
|
||||
const [visibility,visibilitySet] = useState("");
|
||||
const [ready,readySet] = useState(false);
|
||||
// const [currentStep, currentStepSet] = useState(0);
|
||||
const { dataset_token: token } = useParams<RouteParams>();
|
||||
const pageTitle = token ? `Edit` : "Create Dataset";
|
||||
const subText = token ?
|
||||
"Edit your dataset by uploading more samples to create a new voice" :
|
||||
"Add voice details and upload audio samples to clone your voice!";
|
||||
const titleIcon = token ? faPencil : faWaveform;
|
||||
const back = {
|
||||
label: "Back to Voice Designer",
|
||||
to: token ? "/voice-designer/datasets" : "/voice-designer/voices"
|
||||
};
|
||||
// const steps = ["A","B"];
|
||||
|
||||
useEffect(() => {
|
||||
if (token && datasets && datasets.length && !ready) {
|
||||
const { creator_set_visibility, ietf_language_tag, title: resTitle } = datasetByToken(token);
|
||||
console.log("☔️",ietf_language_tag);
|
||||
readySet(true);
|
||||
titleSet(resTitle);
|
||||
languageSet({ value: ietf_language_tag, label: "asss" });
|
||||
visibilitySet(creator_set_visibility);
|
||||
}
|
||||
},[datasetByToken,datasets,ready,token]);
|
||||
|
||||
const inputCtrl = (todo: any) => ({ target }: { target: any}) => todo(target.value);
|
||||
|
||||
const selectChange = ({ value }: any) => { // react-select doesn't format events like events, doesn't pass name prop, ew
|
||||
console.log("🍕",value);
|
||||
// languageSet(value);
|
||||
}
|
||||
|
||||
const options = [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "es", label: "Spanish" },
|
||||
{ value: "fr", label: "French" },
|
||||
];
|
||||
|
||||
const [abc,abcSet] = useState("123");
|
||||
|
||||
console.log("🚜", visibility, language, title);
|
||||
return <Container type="panel">
|
||||
<PageHeader {...{ back, subText, title: pageTitle, titleIcon }} panel={false} />
|
||||
<Panel>
|
||||
<div className="p-3 py-4 p-md-4">
|
||||
<Input {...{ label: "Title", onChange: inputCtrl(titleSet), value: title }}/>
|
||||
<Select {...{ label: "Language", name: "language", onChange: selectChange, options, value: language }}/>
|
||||
<select {...{ name: 'abc', value: abc, onChange: (e:any) => console.log("🌸",e.target.value) }}>
|
||||
<option value="123">123</option>
|
||||
<option value="456">456</option>
|
||||
<option value="789">789</option>
|
||||
</select>
|
||||
</div>
|
||||
</Panel>
|
||||
</Container>;
|
||||
};
|
||||
@@ -1,15 +1,25 @@
|
||||
import React, { useState } from "react";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { faPlus, faWaveform } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
|
||||
// import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
|
||||
import Panel from "components/common/Panel";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
import Container from "components/common/Container";
|
||||
import { Route, NavLink, Switch, Redirect } from "react-router-dom";
|
||||
import ListItems from "./components/ListItems/ListItems";
|
||||
import ListItems from "./components/NewList";
|
||||
import Modal from "components/common/Modal";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
// import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { faMicrophone } from "@fortawesome/pro-solid-svg-icons";
|
||||
import useVoiceRequests from "./useVoiceRequests";
|
||||
|
||||
// interface Props {
|
||||
// sessionWrapper: SessionWrapper;
|
||||
// }
|
||||
|
||||
function VoiceDesignerMainPage() {
|
||||
usePrefixedDocumentTitle("Voice Designer");
|
||||
|
||||
const { datasets, deleteDataSet } = useVoiceRequests();
|
||||
|
||||
const [isDeleteVoiceModalOpen, setIsDeleteVoiceModalOpen] = useState(false);
|
||||
const [isDeleteDatasetModalOpen, setIsDeleteDatasetModalOpen] =
|
||||
@@ -31,24 +41,51 @@ function VoiceDesignerMainPage() {
|
||||
setIsDeleteDatasetModalOpen(false);
|
||||
};
|
||||
|
||||
const voicesList = [
|
||||
const DataBadge = () => <span className="dataset-badge mb-0">Dataset</span>;
|
||||
const VoiceBadge = () => <FontAwesomeIcon icon={faMicrophone} className="me-2 me-lg-3" />;
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
const navToEdit = (token: string) => { history.push(`/voice-designer/dataset/${token}/edit`) }
|
||||
|
||||
const rowClick = (todo: any) => ({ target }: { target: any }) => {
|
||||
let datasetToken = datasets[target.name.split(",")[0].split(":")[1]].dataset_token;
|
||||
todo(datasetToken);
|
||||
};
|
||||
|
||||
const actionDataSets = datasets.map((dataset,i) => {
|
||||
return {
|
||||
...dataset,
|
||||
badge: DataBadge,
|
||||
buttons: [{
|
||||
label: "Edit",
|
||||
small: true,
|
||||
variant: "secondary",
|
||||
onClick: rowClick(navToEdit)
|
||||
},{
|
||||
label: "Delete",
|
||||
small: true,
|
||||
variant: "danger",
|
||||
onClick: rowClick(deleteDataSet)
|
||||
}],
|
||||
name: dataset.title
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
const voicesList = [
|
||||
{
|
||||
badge: VoiceBadge,
|
||||
name: "Donald Trump (45th US President)",
|
||||
modelToken: "TM:z7x37sbvb8vc",
|
||||
isCreating: true,
|
||||
},
|
||||
{
|
||||
badge: VoiceBadge,
|
||||
name: "Spongebob Squarepants (Season 1)",
|
||||
modelToken: "TM:z7x37sbvb8vc",
|
||||
},
|
||||
];
|
||||
const datasetList = [
|
||||
{
|
||||
name: "Donald Trump (45th US President)",
|
||||
modelToken: "dummyToken",
|
||||
},
|
||||
];
|
||||
// const emptyList = [] as any[];
|
||||
|
||||
function MyVoices() {
|
||||
return (
|
||||
@@ -64,7 +101,8 @@ function VoiceDesignerMainPage() {
|
||||
return (
|
||||
<ListItems
|
||||
type="dataset"
|
||||
data={datasetList}
|
||||
data={actionDataSets}
|
||||
//data={datasetList}
|
||||
handleDeleteDataset={openDeleteDatasetModal}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import Button from "components/common/Button";
|
||||
import { faPlus } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import "./ListItems/ListItems.scss";
|
||||
|
||||
|
||||
interface ListItemsProps {
|
||||
data: any[];
|
||||
type: "voice" | "dataset";
|
||||
handleDeleteVoice?: () => void;
|
||||
handleDeleteDataset?: () => void;
|
||||
}
|
||||
|
||||
interface ListItem {
|
||||
badge?: any;
|
||||
buttons: any;
|
||||
index: number;
|
||||
isCreating?: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const Working = () => <div className="d-flex align-items-center gap-2 py-1">
|
||||
<p className="fw-medium opacity-75">
|
||||
Voice is being created...
|
||||
</p>
|
||||
<div
|
||||
className="spinner-border spinner-border-sm text-light"
|
||||
role="status"
|
||||
>
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
const ItemRow = ({ badge: Badge, buttons, index, isCreating, name, ...rest }: ListItem) => {
|
||||
return <div className="d-flex flex-column flex-lg-row gap-3 list-items p-3 align-items-lg-center">
|
||||
<div className="d-inline-flex flex-wrap align-items-center flex-grow-1 gap-2">
|
||||
<h5 className="fw-semibold mb-0">
|
||||
{ Badge ? <Badge /> : null }
|
||||
{ name }
|
||||
</h5>
|
||||
</div>
|
||||
<div className="d-flex">
|
||||
{ isCreating ? <Working /> :
|
||||
<div className="d-flex gap-2">
|
||||
{ buttons && buttons.length ? buttons.map((action: any, key: number) => {
|
||||
return <Button { ...{
|
||||
...action,
|
||||
key,
|
||||
name: `item-row:${index},button:${key}`,
|
||||
} }/>;
|
||||
}) : null }
|
||||
</div> }
|
||||
</div>
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default function NewList({ data, handleDeleteDataset, type, handleDeleteVoice, }: ListItemsProps) {
|
||||
|
||||
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.
|
||||
</h5>
|
||||
<Button
|
||||
icon={faPlus} // 1
|
||||
label="Create New Voice" // 2
|
||||
small={true}
|
||||
to="/voice-designer/create" // 3
|
||||
/>
|
||||
</div>;
|
||||
return data.length ? <div className="d-flex flex-column gap-3">
|
||||
{ data.map((item, key) => <ItemRow {...{ key, ...item, index: key }}/>) }
|
||||
</div> : <DataPlaceholder />;
|
||||
};
|
||||
+8
-6
@@ -8,11 +8,11 @@ import React, { useState } from "react";
|
||||
import { FileUploader } from "react-drag-drop-files";
|
||||
import { InputVcAudioPlayer } from "v2/view/_common/InputVcAudioPlayer";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import {
|
||||
UploadAudio,
|
||||
UploadAudioIsOk,
|
||||
UploadAudioRequest,
|
||||
} from "@storyteller/components/src/api/upload/UploadAudio";
|
||||
// import {
|
||||
// UploadAudio,
|
||||
// UploadAudioIsOk,
|
||||
// UploadAudioRequest,
|
||||
// } from "@storyteller/components/src/api/upload/UploadAudio";
|
||||
import useUploadedFiles from "hooks/useUploadedFiles";
|
||||
|
||||
const FILE_TYPES = ["MP3", "WAV", "FLAC", "OGG"];
|
||||
@@ -34,6 +34,7 @@ function VoiceDesignerUploadComponent(props: Props) {
|
||||
const setAudioLinks = useUploadedFiles((state: any) => state.setAudioLinks);
|
||||
|
||||
// Auto generated
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [idempotencyToken, setIdempotencyToken] = useState(uuidv4());
|
||||
|
||||
const handleChange = (file: any) => {
|
||||
@@ -68,6 +69,7 @@ function VoiceDesignerUploadComponent(props: Props) {
|
||||
? `${Math.floor(file.size / 1024)} KB`
|
||||
: null;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const uploadBtnClass = isUploadDisabled
|
||||
? "btn btn-uploaded w-100 disabled"
|
||||
: "btn btn-primary w-100";
|
||||
@@ -154,7 +156,7 @@ function VoiceDesignerUploadComponent(props: Props) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const LoadingIcon: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
+3
@@ -3,6 +3,7 @@ import VoiceDesignerUploadComponent from "../VoiceDesignerUploadComponent";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
function UploadSamples() {
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
const [convertLoading, setConvertLoading] = useState(false);
|
||||
const [canConvert, setCanConvert] = useState(false);
|
||||
const [mediaUploadToken, setMediaUploadToken] = useState<string | undefined>(
|
||||
@@ -11,6 +12,8 @@ function UploadSamples() {
|
||||
const [convertIdempotencyToken, setConvertIdempotencyToken] = useState(
|
||||
uuidv4()
|
||||
);
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
const [formIsCleared, setFormIsCleared] = useState(false);
|
||||
|
||||
const changeConvertIdempotencyToken = () => {
|
||||
|
||||
+3
-3
@@ -4,8 +4,8 @@ import {
|
||||
// faMicrophoneLines,
|
||||
// faTags,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Button } from "components/common";
|
||||
// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
// import { Button } from "components/common";
|
||||
import Input from "components/common/Input";
|
||||
import useVoiceDetailsStore from "hooks/useVoiceDetailsStore/";
|
||||
import React from "react";
|
||||
@@ -27,7 +27,7 @@ export const SearchFieldClass = {
|
||||
};
|
||||
|
||||
function VoiceDetails() {
|
||||
const { name, visibility, setName, setVisibility } = useVoiceDetailsStore();
|
||||
const { visibility, setVisibility } = useVoiceDetailsStore();
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-column gap-4">
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
// import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { ListDatasetsByUser, Dataset } from "@storyteller/components/src/api/voice_designer/voice_datasets/ListDatasetsByUser";
|
||||
import { DeleteDataset } from "@storyteller/components/src/api/voice_designer/voice_datasets/DeleteDataset";
|
||||
import { useSession } from "hooks";
|
||||
|
||||
export default function useVoiceRequests() {
|
||||
const [datasets, datasetsSet] = useState<Dataset[]>([]);
|
||||
const { user } = useSession();
|
||||
|
||||
const deleteDataSet = (voiceToken: string) => DeleteDataset(voiceToken,{
|
||||
set_delete: true,
|
||||
as_mod: false
|
||||
}).then(res => {
|
||||
console.log("🏧",res);
|
||||
});
|
||||
|
||||
const datasetByToken = (datasetToken?: string) => datasets.filter(({ dataset_token },i) => datasetToken === dataset_token)[0];
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (!datasets.length && user && user.username) {
|
||||
ListDatasetsByUser(user.username,{}).then(res => {
|
||||
if (res.datasets) datasetsSet(res.datasets);
|
||||
});
|
||||
}
|
||||
|
||||
},[user, datasets]);
|
||||
|
||||
return { datasetByToken, datasets, deleteDataSet };
|
||||
};
|
||||
@@ -4574,6 +4574,7 @@ __metadata:
|
||||
craco: ^0.0.3
|
||||
cross-env: ^7.0.3
|
||||
lerna: ^4.0.0
|
||||
universal-cookie: ^6.1.1
|
||||
yarn: ^1.22.19
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -5103,6 +5104,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/cookie@npm:^0.5.1":
|
||||
version: 0.5.3
|
||||
resolution: "@types/cookie@npm:0.5.3"
|
||||
checksum: b785618f6b2fdceb6a20a17e1dfe99651b1e5c2c079f486de76dfb21b508f09d91913755e4a6dbdfe628882ea32466bb2e9318b114ce34efa5e624356494fcab
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/debounce@npm:*":
|
||||
version: 1.2.1
|
||||
resolution: "@types/debounce@npm:1.2.1"
|
||||
@@ -8400,6 +8408,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cookie@npm:^0.5.0":
|
||||
version: 0.5.0
|
||||
resolution: "cookie@npm:0.5.0"
|
||||
checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"copy-concurrently@npm:^1.0.0":
|
||||
version: 1.0.5
|
||||
resolution: "copy-concurrently@npm:1.0.5"
|
||||
@@ -20366,6 +20381,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"universal-cookie@npm:^6.1.1":
|
||||
version: 6.1.1
|
||||
resolution: "universal-cookie@npm:6.1.1"
|
||||
dependencies:
|
||||
"@types/cookie": ^0.5.1
|
||||
cookie: ^0.5.0
|
||||
checksum: 5c4d83f5879c5f133a51dadd080bf7a7a5c3a4b38695bb52ae560564884b84156d3c872abb1ade54a0ee00baf3870a59abc9907a4fa5bcf7a8d2d63af581c8e1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"universal-user-agent@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "universal-user-agent@npm:6.0.0"
|
||||
|
||||
Reference in new issue
Block a user