mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 17:58:55 +00:00
resolve conflict
This commit is contained in:
commit
e31d22ec7f
56 files changed
+1500
-121
No files matched your search
@@ -0,0 +1,16 @@
|
||||
import MakeRequest from "../MakeRequest";
|
||||
|
||||
export interface EnqueueEngineCompositingRequest {
|
||||
uuid_idempotency_token: string,
|
||||
video_source: string
|
||||
}
|
||||
|
||||
export interface EnqueueEngineCompositingResponse {
|
||||
success: boolean,
|
||||
inference_job_token?: string
|
||||
}
|
||||
|
||||
export const EnqueueEngineCompositing = MakeRequest<string, EnqueueEngineCompositingRequest, EnqueueEngineCompositingResponse,{}>({
|
||||
method: "POST",
|
||||
routingFunction: () => `/v1/compositor/enqueue_engine_compositing`,
|
||||
});
|
||||
@@ -11,6 +11,7 @@ export enum FrontendInferenceJobType {
|
||||
ImageGeneration,
|
||||
VideoMotionCapture,
|
||||
ConvertFbxtoGltf,
|
||||
EngineComposition
|
||||
}
|
||||
|
||||
// NB: Many of these fields are optional despite the response payload containing them
|
||||
|
||||
@@ -60,7 +60,11 @@ 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, SessionProvider } from "components/providers";
|
||||
import {
|
||||
InferenceJobs,
|
||||
ModalProvider,
|
||||
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.
|
||||
@@ -832,8 +836,10 @@ class App extends React.Component<Props, State> {
|
||||
*/}
|
||||
|
||||
<div className="migrationComponentWrapper">
|
||||
<ModalProvider>
|
||||
<InferenceJobs
|
||||
{...{
|
||||
enqueue: this.enqueueInferenceJob,
|
||||
byCategory: this.state.inferenceJobsByCategory,
|
||||
}}
|
||||
>
|
||||
@@ -954,6 +960,7 @@ class App extends React.Component<Props, State> {
|
||||
</Switch>
|
||||
</SessionProvider>
|
||||
</InferenceJobs>
|
||||
</ModalProvider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -329,6 +329,19 @@ $headings-font-family: "Fira Sans", sans-serif;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.to-top-button-off-screen {
|
||||
position: fixed;
|
||||
width: fit-content;
|
||||
white-space: nowrap;
|
||||
top: 5rem;
|
||||
margin: 0 auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
|
||||
box-shadow: 0px 5px 20px rgba($black, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 740px) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import Tippy from "@tippyjs/react";
|
||||
import "tippy.js/dist/tippy.css";
|
||||
|
||||
export default function Button({
|
||||
buttonRef,
|
||||
label,
|
||||
icon,
|
||||
small,
|
||||
@@ -81,7 +82,7 @@ export default function Button({
|
||||
{ButtonContent}
|
||||
</a>
|
||||
) : (
|
||||
<button type="button" {...commonProps} {...rest}>
|
||||
<button {...{ ...commonProps, ...rest, type: "button", ref: buttonRef }}>
|
||||
{ButtonContent}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ButtonHTMLAttributes } from "react";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
export default interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
buttonRef?: any;
|
||||
label?: string | null;
|
||||
icon?: IconDefinition;
|
||||
small?: boolean;
|
||||
|
||||
@@ -25,6 +25,7 @@ interface AudioCardProps {
|
||||
type: "media" | "weights";
|
||||
inSelectModal?: boolean;
|
||||
onResultSelect?: (data:{token: string, title:string}) => void;
|
||||
onClick?: (e:any) => any;
|
||||
}
|
||||
|
||||
export default function AudioCard({
|
||||
@@ -36,6 +37,7 @@ export default function AudioCard({
|
||||
source = "",
|
||||
type,
|
||||
inSelectModal = false,
|
||||
onClick: inClick,
|
||||
onResultSelect,
|
||||
}: AudioCardProps) {
|
||||
// const { setToken, setWeightTitle } = useToken();
|
||||
@@ -166,7 +168,7 @@ export default function AudioCard({
|
||||
<div className="d-flex flex-grow-1">
|
||||
<Badge label={weightBadgeLabel} color={weightBadgeColor} />
|
||||
</div>
|
||||
{inSelectModal ? (
|
||||
{inClick ? (
|
||||
<Button
|
||||
icon={faArrowRight}
|
||||
iconFlip={true}
|
||||
@@ -260,7 +262,7 @@ export default function AudioCard({
|
||||
|
||||
return (
|
||||
<>
|
||||
{inSelectModal ? (
|
||||
{inClick ? (
|
||||
<>{card}</>
|
||||
) : (
|
||||
<Link
|
||||
|
||||
@@ -13,6 +13,7 @@ import useMediaFileTypeInfo from "hooks/useMediaFileTypeInfo";
|
||||
interface BVHCardProps {
|
||||
bookmarks: any;
|
||||
data: any;
|
||||
onClick?: (e:any) => any;
|
||||
ratings: any;
|
||||
showCreator?: boolean;
|
||||
source?: string;
|
||||
@@ -22,6 +23,7 @@ interface BVHCardProps {
|
||||
export default function BVHCard({
|
||||
bookmarks,
|
||||
data,
|
||||
onClick: inClick,
|
||||
showCreator,
|
||||
source = "",
|
||||
ratings,
|
||||
@@ -40,12 +42,12 @@ export default function BVHCard({
|
||||
data.media_type || data.details?.maybe_media_data?.media_type
|
||||
);
|
||||
|
||||
const Wrapper = ({ children }: { children: any }) => inClick ? <div {...{ onClick: () => { inClick(data) } }}>{ children }</div> : <Link {...{ to: linkUrl }}>{ children }</Link>;
|
||||
|
||||
// const bucketConfig = new BucketConfig();
|
||||
|
||||
return (
|
||||
<Link
|
||||
{...{
|
||||
to: linkUrl,
|
||||
}}
|
||||
>
|
||||
<Wrapper>
|
||||
<Card padding={false} canHover={true}>
|
||||
<div className="card-img d-flex align-items-center justify-content-center">
|
||||
<FontAwesomeIcon icon={faPersonWalking} className="card-img-icon" />
|
||||
@@ -92,21 +94,18 @@ export default function BVHCard({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<LikeButton
|
||||
{...{
|
||||
...ratings.makeProps({
|
||||
entityToken: data.token,
|
||||
entityType: "media_file",
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<LikeButton
|
||||
{...{
|
||||
...ratings.makeProps({
|
||||
entityToken: data.token,
|
||||
entityType: "media_file",
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
@import "scss/colours.scss";
|
||||
|
||||
.fy-uploader {
|
||||
.fy-uploader, .fy-uploader-no-style {
|
||||
[type="file"] {
|
||||
border: 0;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
@@ -13,12 +13,6 @@
|
||||
|
||||
+ label {
|
||||
cursor: pointer;
|
||||
border: dashed 2px rgba(255, 255, 255, 0.12) !important;
|
||||
border-radius: 0.5rem;
|
||||
transition: 0.1s all;
|
||||
&:hover {
|
||||
background-color: lighten($panel, 6%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.fy-uploader-layout {
|
||||
@@ -32,3 +26,17 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fy-uploader {
|
||||
[type="file"] {
|
||||
|
||||
+ label {
|
||||
border: dashed 2px rgba(255, 255, 255, 0.12) !important;
|
||||
border-radius: 0.5rem;
|
||||
transition: 0.1s all;
|
||||
&:hover {
|
||||
background-color: lighten($panel, 6%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,13 @@ interface Props {
|
||||
containerClass?: string;
|
||||
fileTypes?: string[];
|
||||
inputRef: any;
|
||||
noStyle?: boolean;
|
||||
onChange: (file?: any) => void;
|
||||
panelClass?: string;
|
||||
[x:string]: any;
|
||||
};
|
||||
|
||||
export default function FileWrapper({ children, containerClass, fileTypes = [], inputRef, onChange, panelClass, ...rest }: Props) {
|
||||
export default function FileWrapper({ children, containerClass, fileTypes = [], inputRef, noStyle, onChange, panelClass, ...rest }: Props) {
|
||||
const id = 'file-input-' + useId();
|
||||
const accept = fileTypes.map((type,i) => `.${ type.toLowerCase() }`).join(",");
|
||||
|
||||
@@ -32,9 +33,12 @@ export default function FileWrapper({ children, containerClass, fileTypes = [],
|
||||
}
|
||||
};
|
||||
|
||||
return <div {...{ className: `fy-uploader${ containerClass ? " " + containerClass : "" }`, onDragLeave: onDragEvent(1), onDragOver: onDragEvent(0), onDrop }}>
|
||||
const containerBaseClass = noStyle ? "-no-style" : "";
|
||||
const panelBaseClass = noStyle ? "" : "panel panel-inner d-flex align-items-center";
|
||||
|
||||
return <div {...{ className: `fy-uploader${ containerBaseClass }${ containerClass ? " " + containerClass : "" }`, onDragLeave: onDragEvent(1), onDragOver: onDragEvent(0), onDrop }}>
|
||||
<input { ...{ accept, onChange: fileChange, type: "file", id, ref: inputRef, ...rest }} />
|
||||
<label {...{ className: `panel panel-inner d-flex align-items-center${ panelClass ? " " + panelClass : "" }`, htmlFor: id }} >
|
||||
<label {...{ className: `${ panelBaseClass }${ panelClass ? " " + panelClass : "" }`, htmlFor: id }} >
|
||||
{ children }
|
||||
</label>
|
||||
</div>;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
@import "scss/colours.scss";
|
||||
@import "scss/site-refresh.scss";
|
||||
|
||||
|
||||
.fy-slide-frame {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fy-mocap-input {
|
||||
@include panel;
|
||||
height: 300px;
|
||||
border-radius: .5rem;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
// background-color: $panel;
|
||||
}
|
||||
|
||||
.fy-media-input-square {
|
||||
aspect-ratio: 1/1;
|
||||
|
||||
.fy-media-picker-empty-controls {}
|
||||
}
|
||||
|
||||
.fy-media-input-landscape {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.fy-media-picker-empty, .fy-media-picker-full {
|
||||
// position: relative;
|
||||
// overflow: hidden;
|
||||
|
||||
.fy-media-picker-icon {
|
||||
position: absolute;
|
||||
height: 80%;
|
||||
opacity: 75%;
|
||||
// background-color: blue;
|
||||
margin: auto;
|
||||
inset: 0;
|
||||
|
||||
path { fill: $panel-inner-border}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.fy-media-picker-label-icon {
|
||||
margin-right: .5rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.fy-media-picker-mocap-preview {
|
||||
position: absolute;
|
||||
border-radius: .5rem;
|
||||
border: 1px solid rgba(#fff,.2);
|
||||
display: block;
|
||||
vertical-align: bottom;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.fy-media-picker-full-controls {
|
||||
position: absolute;
|
||||
background-color: rgba(#000, .5);
|
||||
padding: 1rem;
|
||||
// backdrop-filter: blur(2px);
|
||||
overflow: hidden;
|
||||
border-radius: .5rem .5rem 0 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fy-media-picker-empty-controls {
|
||||
// background-color: red;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
|
||||
.fy-media-picker-row {
|
||||
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fy-media-picker-button {
|
||||
background-color: rgba(#000,.15);
|
||||
border-radius: .5rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
transition: .25s all ease-in-out;
|
||||
color: rgba(#fff, .66);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(#000,.1);
|
||||
color: rgba(#fff, 1);
|
||||
}
|
||||
|
||||
label { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import React, { useState } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { a, useTransition } from "@react-spring/web";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import Iframe from "react-iframe";
|
||||
import { MediaBrowser } from "components/modals";
|
||||
import { FileWrapper, Label, Spinner } from "components/common";
|
||||
import { MediaFilterProp } from "components/entities/EntityTypes";
|
||||
import { useFile, useMedia, useModal, useSession } from "hooks";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
import { faFileArrowUp, faGrid, faPersonWalking } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon as Icon } from "@fortawesome/react-fontawesome";
|
||||
import { UploadMedia, UploadMediaResponse } from "@storyteller/components/src/api/media_files/UploadMedia";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import "./MocapInput.scss"
|
||||
|
||||
interface Props {
|
||||
label?: string,
|
||||
onChange?: any,
|
||||
type?: MediaFilterProp,
|
||||
aspectRatio?: "square" | "landscape" | "portrait"
|
||||
}
|
||||
|
||||
interface SlideProps {
|
||||
media?: MediaFile
|
||||
};
|
||||
|
||||
interface EmptySlideProps extends SlideProps {
|
||||
inputProps?: any,
|
||||
onSelect: any,
|
||||
open: any,
|
||||
type: MediaFilterProp,
|
||||
user: any
|
||||
};
|
||||
|
||||
interface AniProps {
|
||||
animating: boolean,
|
||||
className: string,
|
||||
isLeaving: boolean,
|
||||
render: any,
|
||||
style: any
|
||||
}
|
||||
|
||||
const MediaBusy = () => {
|
||||
return <Spinner/>;
|
||||
};
|
||||
|
||||
const MocapInputFull = ({ media }: SlideProps) => {
|
||||
const bucketConfig = new BucketConfig();
|
||||
const bvhUrl = media?.public_bucket_path ? bucketConfig.getGcsUrl(media.public_bucket_path) : "";
|
||||
|
||||
return <>
|
||||
<Iframe
|
||||
{...{
|
||||
url: `https://engine.fakeyou.com?mode=viewer&bvh=${bvhUrl}`,
|
||||
className: "fy-media-picker-mocap-preview",
|
||||
}}
|
||||
/>
|
||||
<div {...{ className: "fy-media-picker-full-controls" }}>
|
||||
Your file
|
||||
</div>
|
||||
</>;
|
||||
};
|
||||
|
||||
const MediaPickerEmpty = ({ media, onSelect, open, inputProps, type, user }: EmptySlideProps) => {
|
||||
const browserClick = () => open({
|
||||
component: MediaBrowser,
|
||||
props: { onSelect, type, username: user?.username || "" }
|
||||
});
|
||||
|
||||
return <>
|
||||
<Icon {...{ className: "fy-media-picker-icon", icon: faPersonWalking }}/>
|
||||
<div {...{ className: "fy-media-picker-empty-controls" }}>
|
||||
<FileWrapper {...{ containerClass: "fy-media-picker-row", panelClass: "fy-media-picker-button", noStyle: true, ...inputProps }}>
|
||||
<>
|
||||
<Icon {...{ className: "fy-media-picker-label-icon", icon: faFileArrowUp }}/>
|
||||
Upload, click or drag here
|
||||
</>
|
||||
</FileWrapper>
|
||||
<div {...{ className: "fy-media-picker-row fy-media-picker-button", onClick: browserClick }}>
|
||||
<Icon {...{ className: "fy-media-picker-label-icon", icon: faGrid }}/>
|
||||
Choose from your media
|
||||
</div>
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
|
||||
const AniMod = ({ animating, className, isLeaving, render: Render, style, ...rest }: AniProps) => <a.div {...{
|
||||
className: `fy-slide-frame${ className ? " " + className : "" }`,
|
||||
style
|
||||
}}>
|
||||
<Render {...{ ...rest, animating }} />
|
||||
</a.div>;
|
||||
|
||||
export default function MocapInput({ aspectRatio = "square", label, onChange, type }: Props) {
|
||||
const { search } = useLocation();
|
||||
const presetToken = search ? new URLSearchParams(search).get("preset_token") : "";
|
||||
const [mediaToken,mediaTokenSet] = useState(presetToken || "");
|
||||
const { media, mediaSet } = useMedia({ mediaToken });
|
||||
const { user } = useSession();
|
||||
const { open } = useModal();
|
||||
const { clear, inputProps } = useFile({
|
||||
onChange: (inputFile: any) => {
|
||||
if (inputFile) UploadMedia({
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
file: inputFile,
|
||||
source: "file",
|
||||
})
|
||||
.then((res: UploadMediaResponse) => {
|
||||
if ("media_file_token" in res) {
|
||||
clear();
|
||||
mediaTokenSet(res.media_file_token);
|
||||
}
|
||||
});
|
||||
else console.log("🥺 no file");
|
||||
}
|
||||
});
|
||||
const onSelect = (data: MediaFile) => {
|
||||
mediaSet(data)
|
||||
onChange({ target: { name: "temp", value: data.token } });
|
||||
}
|
||||
const busy = false;
|
||||
const index = busy ? 0 : media ? 1 : 2;
|
||||
const [animating,animatingSet] = useState(false);
|
||||
|
||||
const transitions = useTransition(index, {
|
||||
config: { mass: 1, tension: 80, friction: 10 },
|
||||
from: { opacity: 0, transform: `translateX(${ 5 }rem)` },
|
||||
enter: { opacity: 1, transform: `translateX(0)` },
|
||||
leave: { opacity: 0, transform: `translateX(${ 5 }rem)` },
|
||||
onRest: () => animatingSet(false),
|
||||
onStart: () => animatingSet(true)
|
||||
});
|
||||
|
||||
|
||||
return <>
|
||||
<Label {...{ label }}/>
|
||||
<div {...{ className: `fy-mocap-input panel-inner${ aspectRatio ? " fy-media-input-" + aspectRatio : "" }`, }}>
|
||||
{
|
||||
// media ? <MocapInputFull {...{ media }}/> : <MediaPickerEmpty {...{ inputProps, media, onSelect, open, user }}/>
|
||||
transitions((style: any, i: number, state: any) => {
|
||||
let isLeaving = state.phase === "leave";
|
||||
let sharedProps = { animating, isLeaving, media, style };
|
||||
|
||||
return [
|
||||
<AniMod {...{ render: MediaBusy, className: "fy-media-picker-busy", ...sharedProps }}/>,
|
||||
<AniMod {...{ render: MocapInputFull, className: "fy-media-picker-full", ...sharedProps }}/>,
|
||||
<AniMod {...{ render: MediaPickerEmpty, className: "fy-media-picker-empty", inputProps, onSelect, open, type, user, ...sharedProps }}/>
|
||||
][i];
|
||||
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</>;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./MocapInput";
|
||||
@@ -63,3 +63,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fy-core-modal {
|
||||
.modal-content {
|
||||
width: 85vw;
|
||||
height: 85vh;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Button from "../Button";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useSpring, a } from "@react-spring/web";
|
||||
@@ -6,11 +6,13 @@ import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import "./Modal.scss";
|
||||
|
||||
interface ModalProps {
|
||||
content: React.ElementType;
|
||||
className?: string;
|
||||
content?: React.ElementType | null;
|
||||
contentProps?: any;
|
||||
show: boolean;
|
||||
handleClose: () => void;
|
||||
noHeader?: boolean;
|
||||
omitBody?: boolean;
|
||||
onCancel?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||
onConfirm?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||
title?: string;
|
||||
@@ -23,12 +25,18 @@ interface ModalProps {
|
||||
mobileFullscreen?: boolean;
|
||||
}
|
||||
|
||||
const ModalBody = ({ children, omitBody, padding }: { children: any, omitBody?: boolean, padding?: boolean }) => omitBody ? children : <div {...{ className: `modal-body ${padding ? "p-3" : ""}` }}>
|
||||
{ children }
|
||||
</div>;
|
||||
|
||||
const Modal: React.FC<ModalProps> = ({
|
||||
autoWidth,
|
||||
className,
|
||||
content: Content,
|
||||
contentProps,
|
||||
handleClose,
|
||||
noHeader,
|
||||
omitBody,
|
||||
onCancel: cancelEvent,
|
||||
onConfirm: confirmEvent,
|
||||
icon,
|
||||
@@ -44,6 +52,7 @@ const Modal: React.FC<ModalProps> = ({
|
||||
opacity: show ? 1 : 0,
|
||||
config: { duration: 80, easing: t => t },
|
||||
});
|
||||
const [loaded,loadedSet] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
@@ -53,6 +62,10 @@ const Modal: React.FC<ModalProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
if (!loaded) {
|
||||
loadedSet(true);
|
||||
}
|
||||
|
||||
if (show) {
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
}
|
||||
@@ -60,7 +73,7 @@ const Modal: React.FC<ModalProps> = ({
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, [show, handleClose]);
|
||||
}, [show, handleClose, loaded]);
|
||||
|
||||
const onCancel = (e: React.MouseEvent<HTMLElement>) => {
|
||||
if (cancelEvent) cancelEvent(e);
|
||||
@@ -78,7 +91,7 @@ const Modal: React.FC<ModalProps> = ({
|
||||
|
||||
return (
|
||||
<a.div style={fadeIn} className="modal-backdrop">
|
||||
<div className="modal" role="dialog">
|
||||
<div {...{ className: `modal${ className ? " " + className : "" }`, role: "dialog" }}>
|
||||
<div
|
||||
className={`modal-dialog ${
|
||||
position === "center" ? "modal-dialog-centered" : ""
|
||||
@@ -101,9 +114,9 @@ const Modal: React.FC<ModalProps> = ({
|
||||
/>
|
||||
</header>
|
||||
)}
|
||||
<div {...{ className: `modal-body ${padding ? "p-3" : ""}` }}>
|
||||
{Content && <Content {...{ handleClose }} />}
|
||||
</div>
|
||||
<ModalBody {...{ omitBody, padding }}>
|
||||
{Content && <Content {...{ ...contentProps, handleClose }} />}
|
||||
</ModalBody>
|
||||
{showButtons && (
|
||||
<div className="modal-footer">
|
||||
<Button variant="secondary" label="Cancel" onClick={onCancel} />
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
.fy-select {
|
||||
margin-bottom: 1rem !important;
|
||||
|
||||
&:only-child,
|
||||
&:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.d-flex:not(.flex-column) > .fy-select {
|
||||
margin-bottom: 0 !important;
|
||||
margin-right: 0.5rem;
|
||||
|
||||
&:only-child,
|
||||
&:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.gap-1 > .fy-select,
|
||||
.gap-2 > .fy-select,
|
||||
.gap-3 > .fy-select,
|
||||
.gap-4 > .fy-select,
|
||||
.gap-5 > .fy-select {
|
||||
margin-bottom: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.fy-temp-select { // NO !IMPORTANT
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import "../Select/Select.scss";
|
||||
import ReactSelect, { Props as ReactSelectProps } from "react-select";
|
||||
import "./Select.scss";
|
||||
import "./TempSelect.scss";
|
||||
|
||||
export interface Option {
|
||||
value: string | number | boolean;
|
||||
@@ -54,7 +53,7 @@ export default function Select({
|
||||
|
||||
return (
|
||||
// Changed fragment to div here just so that it can be laid out with bootstrap easily using d-flex, flex-column and responsive gaps which requires grouping.
|
||||
<div className="fy-select">
|
||||
<div className="fy-temp-select">
|
||||
{label && (
|
||||
<label className={`sub-title ${required ? "required" : ""}`}>
|
||||
{label}
|
||||
|
||||
@@ -14,6 +14,7 @@ import FileLabel from "./FileLabel";
|
||||
import ImageInput from "./ImageInput";
|
||||
import Input from "./Input";
|
||||
import Label from "./Label";
|
||||
import MocapInput from "./MocapInput";
|
||||
import Modal from "./Modal";
|
||||
import NumberSlider from "./NumberSlider";
|
||||
import NumberSliderV2 from "./NumberSliderV2";
|
||||
@@ -53,6 +54,7 @@ export {
|
||||
ImageInput,
|
||||
Input,
|
||||
Label,
|
||||
MocapInput,
|
||||
Modal,
|
||||
NumberSlider,
|
||||
NumberSliderV2,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
// import { CardWrapper } from "components/entities";
|
||||
|
||||
interface Props {
|
||||
value?: any;
|
||||
}
|
||||
|
||||
export default function AudioCard({ value }: Props) {
|
||||
return {};
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
import React from "react";
|
||||
import useTimeAgo from "hooks/useTimeAgo";
|
||||
import { faPersonWalking } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon as Icon } from "@fortawesome/react-fontawesome";
|
||||
import CreatorName from "components/common/Card/CreatorName";
|
||||
import Badge from "components/common/Badge";
|
||||
|
||||
interface Props {
|
||||
data: any,
|
||||
showCreator?: boolean
|
||||
}
|
||||
|
||||
|
||||
export default function BvhCard({ data, showCreator }: Props) {
|
||||
const timeAgo = useTimeAgo(data.created_at);
|
||||
const handleInnerClick = (event: any) => event.stopPropagation();
|
||||
return <>
|
||||
<Icon {...{ className: "card-img", icon: faPersonWalking }}/>
|
||||
<div className="card-img-overlay">
|
||||
<div className="card-img-gradient" />
|
||||
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="d-flex flex-grow-1">
|
||||
<Badge label="BVH" color="pink" overlay={true} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-img-overlay-text">
|
||||
<div>
|
||||
<p className="fs-7 opacity-75 mb-0">{timeAgo}</p>
|
||||
</div>
|
||||
|
||||
<hr className="my-2" />
|
||||
|
||||
<div
|
||||
className="d-flex align-items-center gap-2"
|
||||
onClick={handleInnerClick}
|
||||
>
|
||||
{showCreator && (
|
||||
<div className="flex-grow-1">
|
||||
<CreatorName
|
||||
displayName={
|
||||
data.maybe_creator?.display_name || "Anonymous"
|
||||
}
|
||||
gravatarHash={data.maybe_creator?.gravatar_hash || null}
|
||||
avatarIndex={
|
||||
data.maybe_creator?.default_avatar.image_index || 0
|
||||
}
|
||||
backgroundIndex={
|
||||
data.maybe_creator?.default_avatar.color_index || 0
|
||||
}
|
||||
username={data.maybe_creator?.username || "anonymous"}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
// <div>
|
||||
// <LikeButton
|
||||
// {...{
|
||||
// ...ratings.makeProps({
|
||||
// entityToken: data.token,
|
||||
// entityType: "media_file",
|
||||
// }),
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./BvhCard";
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import getCardUrl from "components/common/Card/getCardUrl";
|
||||
|
||||
interface Props {
|
||||
canHover?: boolean,
|
||||
card: any,
|
||||
data: any,
|
||||
onClick?: (e:any) => any,
|
||||
padding?: boolean,
|
||||
source?: string,
|
||||
type: "media" | "weights"
|
||||
}
|
||||
|
||||
export default function CardWrapper({ canHover, card: Card, data, onClick, padding, source = "", type, ...rest }: Props) {
|
||||
const linkUrl = getCardUrl(data,source,type);
|
||||
const cardProps = { data, source, type, ...rest }
|
||||
const className = `card ${ padding ? "p-3" : "" }${ onClick || canHover ? " card-clickable" : "" }`;
|
||||
|
||||
return onClick ?
|
||||
<div {...{ className, onClick }}>
|
||||
<Card { ...cardProps }/>
|
||||
</div> :
|
||||
<Link {...{ className, to: linkUrl }}>
|
||||
<Card {...cardProps}/>
|
||||
</Link>;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./CardWrapper";
|
||||
@@ -0,0 +1,18 @@
|
||||
export enum MediaFilters {
|
||||
all,
|
||||
audio,
|
||||
image,
|
||||
video,
|
||||
bvh,
|
||||
gltf,
|
||||
fbx
|
||||
}
|
||||
|
||||
export type MediaFilterProp = keyof typeof MediaFilters;
|
||||
|
||||
export const MediaFilterOptions = (t = (v:string) => v) => Object.values(MediaFilters)
|
||||
.filter(val => isNaN(Number(val)))
|
||||
.map((value) => {
|
||||
if (typeof value === "string") return { value, label: t(value) }
|
||||
return { label: "", value: "" };
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
import React, { useRef } from "react";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import MasonryGrid from "components/common/MasonryGrid/MasonryGrid";
|
||||
// import MediaCards from "components/common/Card/MediaCards";
|
||||
import AudioCard from "components/common/Card/AudioCard";
|
||||
import ImageCard from "components/common/Card/ImageCard";
|
||||
import VideoCard from "components/common/Card/VideoCard";
|
||||
import { BvhCard, CardWrapper } from "components/entities";
|
||||
|
||||
interface MediaCardsProps {
|
||||
props: any,
|
||||
type: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
list: MediaFile[],
|
||||
success?: boolean
|
||||
}
|
||||
|
||||
const MediaCards = ({ props, type }: MediaCardsProps) => {
|
||||
switch (type) {
|
||||
case "audio":
|
||||
return <AudioCard {...props} />;
|
||||
case "image":
|
||||
return <ImageCard {...props} />;
|
||||
case "video":
|
||||
return <VideoCard {...props} />;
|
||||
case "bvh":
|
||||
return <CardWrapper {...{ ...props, card: BvhCard }}/>
|
||||
default:
|
||||
return <div>Unsupported media type</div>;
|
||||
}
|
||||
};
|
||||
|
||||
export default function MediaList({ list, success, ...rest }: Props) {
|
||||
const gridRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
return list.length === 0 && success ?
|
||||
<div className="text-center mt-4 opacity-75">
|
||||
No media created yet.
|
||||
</div> : <MasonryGrid {...{ gridRef }}>
|
||||
{ list.map((data: MediaFile, key: number) => {
|
||||
let props = { data, type: "media", ...rest };
|
||||
return <div {...{
|
||||
className: "col-12 col-sm-6 col-xl-4 grid-item",
|
||||
key,
|
||||
}}>
|
||||
<MediaCards {...{ type: data.media_type, props }} />
|
||||
</div>;
|
||||
}) }
|
||||
</MasonryGrid>;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./MediaList";
|
||||
@@ -0,0 +1,5 @@
|
||||
import BvhCard from "./BvhCard";
|
||||
import CardWrapper from "./CardWrapper";
|
||||
import MediaList from "./MediaList";
|
||||
|
||||
export { BvhCard, CardWrapper, MediaList };
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from "react";
|
||||
|
||||
interface Props {
|
||||
byQueue: any,
|
||||
t: any,
|
||||
ttsQueuedCount: number
|
||||
}
|
||||
|
||||
export default function SideNavJobs({ byQueue, t, ttsQueuedCount }: Props) {
|
||||
return <div className="d-none d-lg-block">
|
||||
<div className="sidebar-heading">Jobs Queue</div>
|
||||
<div className="ps-4 mb-3">
|
||||
<div>
|
||||
{t("queueTts")}:{" "}
|
||||
<span className="text-red">{ ttsQueuedCount }</span>
|
||||
</div>
|
||||
<div>
|
||||
{t("queueRvc")}:{" "}
|
||||
<span className="text-red">
|
||||
{ byQueue.pending_rvc_jobs }
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
{t("queueSvc")}:{" "}
|
||||
<span className="text-red">
|
||||
{ byQueue.pending_svc_jobs }
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
{t("queueFaceAnimator")}:{" "}
|
||||
<span className="text-red">
|
||||
{ byQueue.pending_face_animation_jobs }
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
Voice Designer:{" "}
|
||||
<span className="text-red">
|
||||
{ byQueue.pending_voice_designer }
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
.fy-media-browser-tools {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
|
||||
.pagination {
|
||||
margin: auto 0 auto auto;
|
||||
}
|
||||
|
||||
.fy-temp-select { margin-bottom: 0 }
|
||||
}
|
||||
|
||||
.fy-media-browser-header{
|
||||
h3 { margin-bottom: 0 }
|
||||
> * {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.icon-close-button {
|
||||
cursor: pointer;
|
||||
height: 1.5rem;
|
||||
opacity: .33;
|
||||
// transition: .25s all ease-in-out;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.fy-media-browser-list {
|
||||
flex-grow: 1;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.fy-media-browser-footer {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import React, { useState } from "react";
|
||||
import { MediaList } from "components/entities";
|
||||
import { MediaFilterOptions, MediaFilterProp } from "components/entities/EntityTypes";
|
||||
import { TempSelect } from "components/common";
|
||||
import AudioPlayerProvider from "components/common/AudioPlayer/AudioPlayerContext";
|
||||
import SkeletonCard from "components/common/Card/SkeletonCard";
|
||||
import Pagination from "components/common/Pagination";
|
||||
import { GetMediaByUser } from "@storyteller/components/src/api/media_files/GetMediaByUser";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import { useListContent, useRatings } from "hooks";
|
||||
import { FontAwesomeIcon as Icon } from "@fortawesome/react-fontawesome";
|
||||
import { faArrowDownWideShort, faFilter, faXmark } from "@fortawesome/pro-solid-svg-icons";
|
||||
import prepFilter from "resources/prepFilter";
|
||||
import "./MediaBrowser.scss";
|
||||
|
||||
interface Props {
|
||||
handleClose: any,
|
||||
mediaToken: string,
|
||||
onSelect?: any,
|
||||
type?: MediaFilterProp,
|
||||
username: string,
|
||||
}
|
||||
|
||||
export default function MediaBrowser({ mediaToken, handleClose = () => {}, onSelect, type, username }: Props) {
|
||||
const ratings = useRatings();
|
||||
const [showMasonryGrid, setShowMasonryGrid] = useState(true);
|
||||
const [mediaType, mediaTypeSet] = useState(type || "all");
|
||||
const [list, listSet] = useState<MediaFile[]>([]);
|
||||
const media = useListContent({
|
||||
addQueries: {
|
||||
page_size: 24,
|
||||
...prepFilter(mediaType, "filter_media_type"),
|
||||
},
|
||||
addSetters: { mediaTypeSet },
|
||||
// debug: "profile media",
|
||||
fetcher: GetMediaByUser,
|
||||
list,
|
||||
listSet,
|
||||
onInputChange: () => setShowMasonryGrid(false),
|
||||
onSuccess: (res) => {
|
||||
// bookmarks.gather({ res, key: "token" });
|
||||
ratings.gather({ res, key: "token" });
|
||||
setShowMasonryGrid(true);
|
||||
},
|
||||
requestList: true,
|
||||
urlParam: username,
|
||||
urlUpdate: false
|
||||
});
|
||||
|
||||
const handlePageClick = (selectedItem: { selected: number }) => {
|
||||
media.pageChange(selectedItem.selected);
|
||||
};
|
||||
|
||||
const paginationProps = {
|
||||
onPageChange: handlePageClick,
|
||||
pageCount: media.pageCount,
|
||||
currentPage: media.page,
|
||||
};
|
||||
|
||||
const sortOptions = [
|
||||
{ value: false, label: "Newest" },
|
||||
{ value: true, label: "Oldest" },
|
||||
// { value: "mostliked", label: "Most Liked" },
|
||||
];
|
||||
|
||||
const onClick = (data: any) => {
|
||||
onSelect(data);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return <>
|
||||
<header {...{ className: "fy-media-browser-header" }}>
|
||||
<div {...{ className: "fy-media-browser-tools" }}>
|
||||
<h3>Media</h3>
|
||||
<Icon {...{ className: "icon-close-button", icon: faXmark, onClick: () => handleClose() }}/>
|
||||
</div>
|
||||
<div {...{ className: "fy-media-browser-tools" }}>
|
||||
<TempSelect
|
||||
{...{
|
||||
icon: faArrowDownWideShort,
|
||||
options: sortOptions,
|
||||
name: "sort",
|
||||
onChange: media.onChange,
|
||||
value: media.sort,
|
||||
}}
|
||||
/>
|
||||
{ (!type || type === "all") && <TempSelect {...{
|
||||
icon: faFilter,
|
||||
options: MediaFilterOptions(),
|
||||
name: "mediaType",
|
||||
onChange: media.onChange,
|
||||
value: mediaType,
|
||||
}}/> }
|
||||
<Pagination {...paginationProps} />
|
||||
</div>
|
||||
</header>
|
||||
<AudioPlayerProvider>
|
||||
{ media.isLoading ? (
|
||||
<div {...{ className: "row gx-3 gy-3" }}>
|
||||
{Array.from({ length: 12 }).map((_, index) => (
|
||||
<SkeletonCard key={index} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{ showMasonryGrid && (
|
||||
<div {...{ className: "fy-media-browser-list" }}>
|
||||
<MediaList {...{ list: media.list, success: media.status === 3, onClick }}/>
|
||||
</div>
|
||||
) }
|
||||
</>
|
||||
)}
|
||||
</AudioPlayerProvider>
|
||||
|
||||
<footer {...{ className: "fy-media-browser-footer fy-media-browser-tools" }}>
|
||||
<Pagination {...paginationProps} />
|
||||
</footer>
|
||||
</>;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./MediaBrowser";
|
||||
@@ -0,0 +1,3 @@
|
||||
import MediaBrowser from "./MediaBrowser";
|
||||
|
||||
export { MediaBrowser };
|
||||
@@ -3,11 +3,12 @@ import { InferenceJobsContext } from 'context';
|
||||
|
||||
interface Props {
|
||||
children?: any;
|
||||
enqueue?: any;
|
||||
byCategory?: any;
|
||||
}
|
||||
|
||||
export default function InferenceJobs({ children, byCategory }: Props) {
|
||||
return <InferenceJobsContext.Provider {...{ value: { byCategory } }}>
|
||||
export default function InferenceJobs({ byCategory, children, enqueue }: Props) {
|
||||
return <InferenceJobsContext.Provider {...{ value: { byCategory, enqueue } }}>
|
||||
{ children }
|
||||
</InferenceJobsContext.Provider>
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import React, { useState } from "react";
|
||||
import { ModalContext } from "context";
|
||||
import { Modal } from "components/common";
|
||||
|
||||
interface Props {
|
||||
children?: any,
|
||||
}
|
||||
|
||||
interface ModalConfig {
|
||||
component: React.ElementType,
|
||||
props?: any
|
||||
}
|
||||
|
||||
export default function ModalProvider({ children }: Props) {
|
||||
const [modalState,modalStateSet] = useState<ModalConfig | null>(null);
|
||||
const open = (cfg: ModalConfig) => {
|
||||
console.log("🪬",cfg);
|
||||
modalStateSet(cfg);
|
||||
};
|
||||
const close = () => modalStateSet(null);
|
||||
|
||||
return <ModalContext.Provider {...{ value: { close, open, modalState } }}>
|
||||
{ children }
|
||||
{
|
||||
<Modal {...{
|
||||
autoWidth: true,
|
||||
className: "fy-core-modal",
|
||||
content: modalState?.component,
|
||||
contentProps: modalState?.props,
|
||||
handleClose: close,
|
||||
noHeader: true,
|
||||
omitBody: true,
|
||||
show: !!modalState?.component,
|
||||
showButtons: false,
|
||||
}}/>
|
||||
}
|
||||
</ModalContext.Provider>
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./ModalProvider";
|
||||
@@ -1,5 +1,6 @@
|
||||
import AccountModalProvider from "./AccountModalProvider";
|
||||
import InferenceJobs from "./InferenceJobs";
|
||||
import ModalProvider from "./ModalProvider";
|
||||
import SessionProvider from "./SessionProvider";
|
||||
|
||||
export { AccountModalProvider, InferenceJobs, SessionProvider };
|
||||
export { AccountModalProvider, InferenceJobs, ModalProvider, SessionProvider };
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
interface InferenceJobsContext { byCategory?: any }
|
||||
interface InferenceJobsContext { byCategory?: any, enqueue?: any }
|
||||
|
||||
export default createContext<InferenceJobsContext>({ });
|
||||
@@ -0,0 +1,20 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export enum ModalView {
|
||||
Closed,
|
||||
Signup,
|
||||
Login
|
||||
}
|
||||
|
||||
interface ModalConfig {
|
||||
component: React.ElementType,
|
||||
props?: any
|
||||
}
|
||||
|
||||
interface AccountModalContext { close: () => void, modalState: ModalConfig | null, open: (comp: any) => void }
|
||||
|
||||
export default createContext<AccountModalContext>({
|
||||
close: () => {},
|
||||
open: () => {},
|
||||
modalState: null,
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import AccountModalContext from "./AccountModalContext";
|
||||
import InferenceJobsContext from "./InferenceJobsContext";
|
||||
import ModalContext from "./ModalContext";
|
||||
import SessionContext from "./SessionContext";
|
||||
|
||||
export { AccountModalContext, InferenceJobsContext, SessionContext };
|
||||
export { AccountModalContext, InferenceJobsContext, ModalContext, SessionContext };
|
||||
@@ -12,6 +12,8 @@ import useLazyLists from './useLazyLists';
|
||||
import useListContent from './useListContent';
|
||||
import useLocalize from './useLocalize';
|
||||
import useMedia from './useMedia';
|
||||
import useModal from './useModal';
|
||||
import useOnScreen from "./useOnScreen";
|
||||
import useRatings from './useRatings';
|
||||
import useSdUpload from './useSdUpload';
|
||||
import useSession from './useSession';
|
||||
@@ -33,6 +35,8 @@ export {
|
||||
useListContent,
|
||||
useLocalize,
|
||||
useMedia,
|
||||
useModal,
|
||||
useOnScreen,
|
||||
useRatings,
|
||||
useSdUpload,
|
||||
useSession,
|
||||
|
||||
@@ -6,10 +6,9 @@ interface Props {
|
||||
debug?: any;
|
||||
onChange?: (file: any) => void;
|
||||
onClear?: (x?: any) => void;
|
||||
onSubmit?: (file: any) => Promise<boolean | undefined> | Promise<void>;
|
||||
}
|
||||
|
||||
export default function useFile({ debug, onChange = n, onClear = n, onSubmit = n }: Props) {
|
||||
export default function useFile({ debug, onChange = n, onClear = n }: Props) {
|
||||
const [file, fileSet] = useState<any>(undefined);
|
||||
const [blob, blobSet] = useState<string>();
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -4,8 +4,9 @@ import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/Infer
|
||||
import { InferenceJobsContext } from 'context';
|
||||
|
||||
export default function useInferenceJobs(jobType: FrontendInferenceJobType) {
|
||||
const { byCategory } = useContext(InferenceJobsContext);
|
||||
const { byCategory, enqueue } = useContext(InferenceJobsContext);
|
||||
return {
|
||||
enqueue,
|
||||
inferenceJobs: (byCategory?.get(jobType) || []),
|
||||
jobStatusDescription: (jobState: JobState) => Object.keys(JobState).filter(key => isNaN(Number(key)))[jobState]
|
||||
};
|
||||
|
||||
@@ -45,6 +45,9 @@ export default function useLazyLists({
|
||||
status === FetchStatus.ready || status === FetchStatus.in_progress;
|
||||
const fetchError = status === FetchStatus.error;
|
||||
|
||||
const [goingtToTop,goingtToTopSet] = useState(false);
|
||||
const [y,ySet] = useState(0);
|
||||
|
||||
const getMore = () => {
|
||||
if (next) statusSet(1);
|
||||
};
|
||||
@@ -67,6 +70,8 @@ export default function useLazyLists({
|
||||
nextSet("");
|
||||
previousSet("");
|
||||
statusSet(FetchStatus.ready);
|
||||
window.scrollTo(0,0);
|
||||
goingtToTopSet(true);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -76,7 +81,15 @@ export default function useLazyLists({
|
||||
...(sort ? { sort_ascending: true } : {}),
|
||||
};
|
||||
|
||||
if (status === FetchStatus.ready) {
|
||||
const adjustY = () => ySet(window.pageYOffset);
|
||||
|
||||
if (goingtToTop) {
|
||||
window.addEventListener('scroll', adjustY, { passive: true });
|
||||
if (y === 0) setTimeout(() => goingtToTopSet(false),500);
|
||||
}
|
||||
else window.removeEventListener('scroll',adjustY);
|
||||
|
||||
if (status === FetchStatus.ready && !goingtToTop) {
|
||||
let search = new URLSearchParams(queries).toString();
|
||||
statusSet(FetchStatus.in_progress);
|
||||
!disableUrlQueries && history.replace({ pathname, search });
|
||||
@@ -114,6 +127,7 @@ export default function useLazyLists({
|
||||
addQueries,
|
||||
debug,
|
||||
fetcher,
|
||||
goingtToTop,
|
||||
history,
|
||||
listKeys,
|
||||
listSet,
|
||||
@@ -124,6 +138,7 @@ export default function useLazyLists({
|
||||
status,
|
||||
totalKeys,
|
||||
disableUrlQueries,
|
||||
y
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -14,6 +14,7 @@ interface Props {
|
||||
pagePreset?: number;
|
||||
requestList?: boolean;
|
||||
urlParam: string;
|
||||
urlUpdate?: boolean;
|
||||
}
|
||||
|
||||
const n = () => {};
|
||||
@@ -30,6 +31,7 @@ export default function useListContent({
|
||||
pagePreset = 0,
|
||||
requestList = false,
|
||||
urlParam = "",
|
||||
urlUpdate = true
|
||||
}: Props) {
|
||||
const { pathname, search: locSearch } = useLocation();
|
||||
const history = useHistory();
|
||||
@@ -76,7 +78,7 @@ export default function useListContent({
|
||||
if (status === FetchStatus.ready) {
|
||||
let search = new URLSearchParams(queries).toString();
|
||||
statusSet(FetchStatus.in_progress);
|
||||
history.replace({ pathname, search });
|
||||
if (urlUpdate) history.replace({ pathname, search });
|
||||
fetcher(urlParam,{},queries).then((res: any) => {
|
||||
if (debug)
|
||||
console.log(`🪲 useListContent success debug at: ${debug}`, res);
|
||||
@@ -101,6 +103,7 @@ export default function useListContent({
|
||||
sort,
|
||||
status,
|
||||
urlParam,
|
||||
urlUpdate
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function useMedia({ mediaToken = "", onSuccess = (res:MediaFile)
|
||||
const [media,mediaSet] = useState<MediaFile | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
if (mediaToken && status === FetchStatus.ready) {
|
||||
if (!media && mediaToken && status === FetchStatus.ready) {
|
||||
statusSet(FetchStatus.in_progress);
|
||||
GetMedia(mediaToken,{})
|
||||
.then((res) => {
|
||||
@@ -22,6 +22,6 @@ export default function useMedia({ mediaToken = "", onSuccess = (res:MediaFile)
|
||||
});
|
||||
}
|
||||
|
||||
},[mediaToken, onSuccess, status, statusSet]);
|
||||
return { media, status };
|
||||
},[media, mediaToken, onSuccess, status, statusSet]);
|
||||
return { media, mediaSet, status };
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./useModal";
|
||||
@@ -0,0 +1,6 @@
|
||||
import { useContext } from 'react';
|
||||
import { ModalContext } from 'context';
|
||||
|
||||
export default function useInferenceJobs() {
|
||||
return useContext(ModalContext);
|
||||
};
|
||||
@@ -3,14 +3,4 @@ import { SessionContext } from 'context';
|
||||
|
||||
export default function useInferenceJobs() {
|
||||
return useContext(SessionContext);
|
||||
// const { querySession, querySubscriptions, sessionFetched, sessionWrapper } = useContext(SessionContext);
|
||||
// const { logged_in: loggedIn, user } = sessionWrapper?.sessionStateResponse || { logged_in: false };
|
||||
|
||||
// return {
|
||||
// loggedIn,
|
||||
// querySession,
|
||||
// querySubscriptions,
|
||||
// sessionFetched,
|
||||
// user
|
||||
// };
|
||||
};
|
||||
@@ -11,6 +11,23 @@ styles with important tags:
|
||||
//
|
||||
// Once the rollout is complete, this outer wrapper class can be removed.
|
||||
|
||||
@mixin panel {
|
||||
background-color: $panel;
|
||||
border-color: #ffffff12 !important;
|
||||
border: 1px solid;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-radius: 0px;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(0 0 0 / 16%),
|
||||
0 70px 65px rgb(0 0 0 / 8%),
|
||||
0 30px 30px rgb(0 0 0 / 7%),
|
||||
0 15px 15px rgb(0 0 0 / 5%),
|
||||
0 10px 8px rgb(0 0 0 / 3%),
|
||||
0 4px 4px rgb(0 0 0 / 2%),
|
||||
0 2px 2px rgb(0 0 0 / 1%) !important;
|
||||
}
|
||||
|
||||
.fakeyou-refresh {
|
||||
//- BS5 classnames -//
|
||||
.object-fit-cover {
|
||||
@@ -315,22 +332,9 @@ styles with important tags:
|
||||
}
|
||||
|
||||
.panel {
|
||||
@include panel;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background-color: $panel;
|
||||
border-color: #ffffff12 !important;
|
||||
border: 1px solid;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-radius: 0px;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(0 0 0 / 16%),
|
||||
0 70px 65px rgb(0 0 0 / 8%),
|
||||
0 30px 30px rgb(0 0 0 / 7%),
|
||||
0 15px 15px rgb(0 0 0 / 5%),
|
||||
0 10px 8px rgb(0 0 0 / 3%),
|
||||
0 4px 4px rgb(0 0 0 / 2%),
|
||||
0 2px 2px rgb(0 0 0 / 1%);
|
||||
|
||||
.video-bg {
|
||||
background-color: $black;
|
||||
|
||||
@@ -97,6 +97,7 @@ import VoiceDesignerUseVoicePage from "./pages/voice_designer/VoiceDesignerUseVo
|
||||
import { PasswordResetEmailPage } from "./pages/password_reset/PasswordResetEmailPage";
|
||||
import { PasswordResetVerificationPage } from "./pages/password_reset/PasswordResetVerificationPage";
|
||||
import DevUpload from "./pages/dev_upload/DevUpload";
|
||||
import EngineCompositor from "./pages/EngineCompositor";
|
||||
import { NewProfilePage } from "./pages/profile/profile_view/NewProfilePage";
|
||||
import { ModerationJobControlPage } from "./pages/moderation/job_control/ModerationJobControlPage";
|
||||
import WeightPage from "./pages/weight/WeightPage";
|
||||
@@ -212,8 +213,6 @@ class PageContainer extends React.Component<
|
||||
> {
|
||||
constructor(props: Props & RouteComponentProps) {
|
||||
super(props);
|
||||
|
||||
console.log("💰");
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
@@ -963,14 +962,485 @@ class PageContainer extends React.Component<
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/" exact={true}>
|
||||
<LandingPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/tts/:token">
|
||||
<TtsModelViewPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
enqueueTtsJob={this.props.enqueueTtsJob}
|
||||
inferenceJobs={this.props.inferenceJobs}
|
||||
ttsInferenceJobs={this.props.ttsInferenceJobs}
|
||||
textBuffer={this.props.textBuffer}
|
||||
setTextBuffer={this.props.setTextBuffer}
|
||||
clearTextBuffer={this.props.clearTextBuffer}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/w2l/result/:token/edit">
|
||||
<W2lResultEditPage sessionWrapper={this.props.sessionWrapper} />
|
||||
</Route>
|
||||
|
||||
<Route path="/w2l/result/:token/delete">
|
||||
<W2lResultDeletePage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/w2l/result/:token">
|
||||
<W2lResultViewPage sessionWrapper={this.props.sessionWrapper} />
|
||||
</Route>
|
||||
|
||||
<Route path="/w2l/:templateToken/edit">
|
||||
<W2lTemplateEditPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/w2l/:templateToken/approval">
|
||||
<W2lTemplateApprovePage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/w2l/:templateToken/delete">
|
||||
<W2lTemplateDeletePage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/w2l/:templateSlug">
|
||||
<W2lTemplateViewPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
enqueueW2lJob={this.props.enqueueW2lJob}
|
||||
w2lInferenceJobs={this.props.w2lInferenceJobs}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/video">
|
||||
<W2lTemplateListPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/upload/w2l_photo">
|
||||
<UploadW2lPhotoPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
w2lTemplateUploadJobs={this.props.w2lTemplateUploadJobs}
|
||||
enqueueW2lTemplateUploadJob={
|
||||
this.props.enqueueW2lTemplateUploadJob
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/upload/w2l_video">
|
||||
<UploadW2lVideoPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
w2lTemplateUploadJobs={this.props.w2lTemplateUploadJobs}
|
||||
enqueueW2lTemplateUploadJob={
|
||||
this.props.enqueueW2lTemplateUploadJob
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/upload/tts">
|
||||
<UploadTtsModelPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
ttsModelUploadJobs={this.props.ttsModelUploadJobs}
|
||||
enqueueTtsModelUploadJob={this.props.enqueueTtsModelUploadJob}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/upload/voice_conversion">
|
||||
<UploadVoiceConversionModel
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
voiceConversionModelUploadJobs={
|
||||
this.props.voiceConversionModelUploadJobs
|
||||
}
|
||||
enqueueVoiceConversionModelUploadJob={
|
||||
this.props.enqueueVoiceConversionModelUploadJob
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/upload/vocoder" exact={true}>
|
||||
<UploadVocoderPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
vocoderUploadJobs={this.props.vocoderUploadJobs}
|
||||
enqueueVocoderUploadJob={this.props.enqueueVocoderUploadJob}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/contribute">
|
||||
<ContributeIndexPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/category/create">
|
||||
<CreateCategoryPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/user/list">
|
||||
<ModerationUserListFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/ip_bans/:ipAddress">
|
||||
<ModerationViewIpBanFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/ip_bans">
|
||||
<ModerationIpBanListFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/voice_stats">
|
||||
<ModerationVoiceStatsFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/job_stats">
|
||||
<ModerationJobStatsFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/job_control">
|
||||
<ModerationJobControlPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/tts_category/list">
|
||||
<ModerationTtsCategoryListPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/tts_category/edit/:token">
|
||||
<ModerationTtsCategoryEditPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/category/delete/:token">
|
||||
<ModerationCategoryDeletePage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation/approve/w2l_templates">
|
||||
<ModerationPendingW2lTemplatesFc
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation">
|
||||
<ModerationFc sessionWrapper={this.props.sessionWrapper} />
|
||||
</Route>
|
||||
|
||||
<Route exact={true} path="/clone">
|
||||
<VoiceCloneRequestPage />
|
||||
</Route>
|
||||
|
||||
<Route path="/patrons">
|
||||
<PatronPage sessionWrapper={this.props.sessionWrapper} />
|
||||
</Route>
|
||||
|
||||
<Route path="/product-usage">
|
||||
<ProductUsageInfoPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/voice-conversion/:token/delete">
|
||||
<VcModelDeletePage sessionWrapper={this.props.sessionWrapper} />
|
||||
</Route>
|
||||
|
||||
<Route path="/voice-conversion/:token/edit">
|
||||
<VcModelEditPage sessionWrapper={this.props.sessionWrapper} />
|
||||
</Route>
|
||||
|
||||
<Route path="/voice-conversion/:token">
|
||||
<VcModelViewPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
inferenceJobsByCategory={this.props.inferenceJobsByCategory}
|
||||
setMaybeSelectedInferenceJob={
|
||||
this.props.maybeSelectedVoiceConversionModel
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/voice-conversion">
|
||||
<VcModelListPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
voiceConversionModels={this.props.voiceConversionModels}
|
||||
setVoiceConversionModels={this.props.setVoiceConversionModels}
|
||||
maybeSelectedVoiceConversionModel={
|
||||
this.props.maybeSelectedVoiceConversionModel
|
||||
}
|
||||
setMaybeSelectedVoiceConversionModel={
|
||||
this.props.setMaybeSelectedVoiceConversionModel
|
||||
}
|
||||
enqueueInferenceJob={this.props.enqueueInferenceJob}
|
||||
inferenceJobs={this.props.inferenceJobs}
|
||||
inferenceJobsByCategory={this.props.inferenceJobsByCategory}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/about">
|
||||
<AboutPage />
|
||||
</Route>
|
||||
|
||||
<Route path="/face-animator/:mediaToken?">
|
||||
<FaceAnimator
|
||||
{...{
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper,
|
||||
inferenceJobs: this.props.inferenceJobs,
|
||||
inferenceJobsByCategory: this.props.inferenceJobsByCategory,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/studio">
|
||||
<StorytellerStudioListPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/commissions">
|
||||
<CommunityCommissionsPage />
|
||||
</Route>
|
||||
|
||||
<Route path="/terms">
|
||||
<TermsPage />
|
||||
</Route>
|
||||
|
||||
<Route path="/privacy">
|
||||
<PrivacyPage />
|
||||
</Route>
|
||||
|
||||
<Route path="/guide">
|
||||
<GuidePage />
|
||||
</Route>
|
||||
|
||||
<Route path="/old">
|
||||
<VocodesPage />
|
||||
</Route>
|
||||
|
||||
<Route path="/dev-upload">
|
||||
<DevUpload />
|
||||
</Route>
|
||||
|
||||
{/* Route for initial voice creation */}
|
||||
<Route exact path="/voice-designer/create">
|
||||
<VoiceDesignerFormPage
|
||||
{...{
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
{/* Route for editing the dataset details */}
|
||||
<Route exact path="/voice-designer/dataset/:dataset_token/edit">
|
||||
<VoiceDesignerFormPage
|
||||
{...{
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
{/* Route for handling dataset token for uploading samples */}
|
||||
<Route exact path="/voice-designer/dataset/:dataset_token/upload">
|
||||
<VoiceDesignerFormPage
|
||||
{...{
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/voice-designer/voice/:voice_token/edit">
|
||||
<VoiceDesignerVoiceEditPage />
|
||||
</Route>
|
||||
|
||||
<Route path="/voice-designer/voice/:voice_token">
|
||||
<VoiceDesignerUseVoicePage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
inferenceJobs={this.props.inferenceJobs}
|
||||
enqueueInferenceJob={this.props.enqueueInferenceJob}
|
||||
inferenceJobsByCategory={this.props.inferenceJobsByCategory}
|
||||
ttsInferenceJobs={this.props.ttsInferenceJobs}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/engine-compositor">
|
||||
<EngineCompositor />
|
||||
</Route>
|
||||
|
||||
<Route path="/voice-designer">
|
||||
<VoiceDesignerMainPage />
|
||||
</Route>
|
||||
|
||||
<Route path="/generate-speech">
|
||||
<GenerateSpeechPage />
|
||||
</Route>
|
||||
|
||||
<Route path="/video-mocap/:mediaToken?">
|
||||
<VideoMocap
|
||||
{...{
|
||||
enqueueInferenceJob: this.props.enqueueInferenceJob,
|
||||
sessionSubscriptionsWrapper:
|
||||
this.props.sessionSubscriptionsWrapper,
|
||||
inferenceJobs: this.props.inferenceJobs,
|
||||
inferenceJobsByCategory: this.props.inferenceJobsByCategory,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/character/donald-trump">
|
||||
<TrumpTtsPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
enqueueTtsJob={this.props.enqueueTtsJob}
|
||||
inferenceJobs={this.props.inferenceJobs}
|
||||
ttsInferenceJobs={this.props.ttsInferenceJobs}
|
||||
ttsModelUploadJobs={this.props.ttsModelUploadJobs}
|
||||
w2lInferenceJobs={this.props.w2lInferenceJobs}
|
||||
w2lTemplateUploadJobs={this.props.w2lTemplateUploadJobs}
|
||||
textBuffer={this.props.textBuffer}
|
||||
setTextBuffer={this.props.setTextBuffer}
|
||||
clearTextBuffer={this.props.clearTextBuffer}
|
||||
ttsModels={this.props.ttsModels}
|
||||
setTtsModels={this.props.setTtsModels}
|
||||
allTtsCategories={this.props.allTtsCategories}
|
||||
setAllTtsCategories={this.props.setAllTtsCategories}
|
||||
allTtsCategoriesByTokenMap={
|
||||
this.props.allTtsCategoriesByTokenMap
|
||||
}
|
||||
computedTtsCategoryAssignments={
|
||||
this.props.computedTtsCategoryAssignments
|
||||
}
|
||||
setComputedTtsCategoryAssignments={
|
||||
this.props.setComputedTtsCategoryAssignments
|
||||
}
|
||||
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
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/tts" exact={true}>
|
||||
<TtsModelListPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
isShowingVocodesNotice={this.props.isShowingVocodesNotice}
|
||||
clearVocodesNotice={this.props.clearVocodesNotice}
|
||||
isShowingLanguageNotice={this.props.isShowingLangaugeNotice}
|
||||
clearLanguageNotice={this.props.clearLanguageNotice}
|
||||
displayLanguage={this.props.displayLanguage}
|
||||
isShowingTwitchTtsNotice={this.props.isShowingTwitchTtsNotice}
|
||||
clearTwitchTtsNotice={this.props.clearTwitchTtsNotice}
|
||||
isShowingPleaseFollowNotice={
|
||||
this.props.isShowingPleaseFollowNotice
|
||||
}
|
||||
clearPleaseFollowNotice={this.props.clearPleaseFollowNotice}
|
||||
isShowingBootstrapLanguageNotice={
|
||||
this.props.isShowingBootstrapLanguageNotice
|
||||
}
|
||||
clearBootstrapLanguageNotice={
|
||||
this.props.clearBootstrapLanguageNotice
|
||||
}
|
||||
enqueueInferenceJob={this.props.enqueueInferenceJob}
|
||||
inferenceJobs={this.props.inferenceJobs}
|
||||
inferenceJobsByCategory={this.props.inferenceJobsByCategory}
|
||||
enqueueTtsJob={this.props.enqueueTtsJob}
|
||||
ttsInferenceJobs={this.props.ttsInferenceJobs}
|
||||
ttsModelUploadJobs={this.props.ttsModelUploadJobs}
|
||||
w2lInferenceJobs={this.props.w2lInferenceJobs}
|
||||
w2lTemplateUploadJobs={this.props.w2lTemplateUploadJobs}
|
||||
textBuffer={this.props.textBuffer}
|
||||
setTextBuffer={this.props.setTextBuffer}
|
||||
clearTextBuffer={this.props.clearTextBuffer}
|
||||
ttsModels={this.props.ttsModels}
|
||||
setTtsModels={this.props.setTtsModels}
|
||||
allTtsCategories={this.props.allTtsCategories}
|
||||
setAllTtsCategories={this.props.setAllTtsCategories}
|
||||
allTtsCategoriesByTokenMap={
|
||||
this.props.allTtsCategoriesByTokenMap
|
||||
}
|
||||
computedTtsCategoryAssignments={
|
||||
this.props.computedTtsCategoryAssignments
|
||||
}
|
||||
setComputedTtsCategoryAssignments={
|
||||
this.props.setComputedTtsCategoryAssignments
|
||||
}
|
||||
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
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/" exact={true}>
|
||||
<LandingPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
</div>
|
||||
<FooterNav sessionWrapper={this.props.sessionWrapper} />
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
.fy-engine-compositor {
|
||||
// margin: 1rem;
|
||||
padding: 1rem;
|
||||
|
||||
.engine-compositor-container {
|
||||
padding: 1rem;
|
||||
|
||||
header {
|
||||
// background: blue;
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h2 { margin-bottom: 0; }
|
||||
}
|
||||
.compositor-content {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
// padding: 1rem;
|
||||
|
||||
> * {
|
||||
width: 100%;
|
||||
// background: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button, MocapInput } from "components/common";
|
||||
import { useInferenceJobs } from "hooks";
|
||||
import InferenceJobsList from "components/layout/InferenceJobsList";
|
||||
import { EnqueueEngineCompositing } from "@storyteller/components/src/api/engine_compositor/EnqueueEngineCompositing";
|
||||
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import "./EngineCompositor.scss"
|
||||
|
||||
interface Props {
|
||||
value?: any;
|
||||
}
|
||||
|
||||
export default function EngineCompositor({ value }: Props) {
|
||||
const [mediaToken,mediaTokenSet] = useState();
|
||||
const onChange = ({ target }: any) => mediaTokenSet(target.value);
|
||||
const inferenceJobs = useInferenceJobs(FrontendInferenceJobType.EngineComposition);
|
||||
|
||||
const onClick = () => {
|
||||
EnqueueEngineCompositing("",{
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
video_source: mediaToken || "",
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res && res.success) {
|
||||
console.log("🚛 success!",{ mediaToken, res });
|
||||
inferenceJobs.enqueue( // I need to pass this
|
||||
res.inference_job_token,
|
||||
FrontendInferenceJobType.EngineComposition
|
||||
);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const failures = (fail = "") => {
|
||||
switch (fail) {
|
||||
default:
|
||||
return "Uknown failure";
|
||||
}
|
||||
};
|
||||
|
||||
return <div {...{ className: "fy-engine-compositor"}}>
|
||||
<div {...{ className: "panel engine-compositor-container" }}>
|
||||
<header>
|
||||
<h2>Engine Compositor</h2>
|
||||
<Button {...{ label: "Enqueue", onClick, variant: "primary" }}/>
|
||||
</header>
|
||||
<MocapInput {...{ aspectRatio: "landscape", label: "Choose 3D data", onChange, type: "bvh" }}/>
|
||||
</div>
|
||||
<InferenceJobsList
|
||||
{...{
|
||||
failures,
|
||||
jobType: FrontendInferenceJobType.EngineComposition,
|
||||
}}
|
||||
/>
|
||||
</div>;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./EngineCompositor";
|
||||
@@ -11,7 +11,7 @@ import AudioPlayerProvider from "components/common/AudioPlayer/AudioPlayerContex
|
||||
import SkeletonCard from "components/common/Card/SkeletonCard";
|
||||
import { ListMediaFiles } from "@storyteller/components/src/api/media_files/ListMediaFiles";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import { useBookmarks, useLazyLists, useRatings } from "hooks";
|
||||
import { useBookmarks, useLazyLists, useOnScreen, useRatings } from "hooks";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import prepFilter from "resources/prepFilter";
|
||||
|
||||
@@ -20,6 +20,9 @@ export default function MediaTab() {
|
||||
const urlQueries = new URLSearchParams(search);
|
||||
const bookmarks = useBookmarks();
|
||||
const ratings = useRatings();
|
||||
const toTopBtnRef = useRef<HTMLDivElement | null>(null);
|
||||
const onScreen = useOnScreen(toTopBtnRef,"0px");
|
||||
|
||||
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [mediaType, mediaTypeSet] = useState(
|
||||
urlQueries.get("filter_media_type") || "all"
|
||||
@@ -59,8 +62,6 @@ export default function MediaTab() {
|
||||
// { value: "mostliked", label: "Most Liked" },
|
||||
];
|
||||
|
||||
console.log("💎",media.urlCursor);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="d-flex flex-wrap gap-3 mb-3">
|
||||
@@ -84,8 +85,20 @@ export default function MediaTab() {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{ media.urlCursor ? <Button {...{ label: "Back to top", onClick: () => media.reset() }}/> : null }
|
||||
{ media.urlCursor ?
|
||||
<Button {...{
|
||||
className: `to-top-button`,
|
||||
buttonRef: toTopBtnRef,
|
||||
label: "Back to top",
|
||||
onClick: () => media.reset(),
|
||||
}}/> : null }
|
||||
</div>
|
||||
{ media.urlCursor && !onScreen ?
|
||||
<Button {...{
|
||||
className: `to-top-button-off-screen`,
|
||||
label: "Back to top",
|
||||
onClick: () => media.reset(),
|
||||
}}/> : null }
|
||||
<AudioPlayerProvider>
|
||||
{media.isLoading && !media.list.length ? (
|
||||
<div className="row gx-3 gy-3">
|
||||
@@ -94,6 +107,7 @@ export default function MediaTab() {
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
|
||||
<InfiniteScroll
|
||||
dataLength={media.list.length}
|
||||
next={media.getMore}
|
||||
|
||||
@@ -12,7 +12,7 @@ import SkeletonCard from "components/common/Card/SkeletonCard";
|
||||
import { ListWeights } from "@storyteller/components/src/api/weights/ListWeights";
|
||||
import { Weight } from "@storyteller/components/src/api/weights/GetWeight";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import { useBookmarks, useLazyLists, useRatings } from "hooks";
|
||||
import { useBookmarks, useLazyLists, useOnScreen, useRatings } from "hooks";
|
||||
import prepFilter from "resources/prepFilter";
|
||||
|
||||
export default function WeightsTab() {
|
||||
@@ -25,6 +25,8 @@ export default function WeightsTab() {
|
||||
const [weightCategory, weightCategorySet] = useState(urlQueries.get("maybe_scoped_weight_category") || "all");
|
||||
const [showMasonryGrid, setShowMasonryGrid] = useState(true);
|
||||
const [list, listSet] = useState<Weight[]>([]);
|
||||
const toTopBtnRef = useRef<HTMLDivElement | null>(null);
|
||||
const onScreen = useOnScreen(toTopBtnRef,"0px");
|
||||
const weights = useLazyLists({
|
||||
addQueries: {
|
||||
page_size: 24,
|
||||
@@ -145,8 +147,20 @@ export default function WeightsTab() {
|
||||
/>
|
||||
)} */}
|
||||
</div>
|
||||
{ weights.urlCursor ? <Button {...{ label: "Back to top", onClick: () => weights.reset() }}/> : null }
|
||||
{ weights.urlCursor ?
|
||||
<Button {...{
|
||||
className: `to-top-button`,
|
||||
buttonRef: toTopBtnRef,
|
||||
label: "Back to top",
|
||||
onClick: () => weights.reset(),
|
||||
}}/> : null }
|
||||
</div>
|
||||
{ weights.urlCursor && !onScreen ?
|
||||
<Button {...{
|
||||
className: `to-top-button-off-screen`,
|
||||
label: "Back to top",
|
||||
onClick: () => weights.reset(),
|
||||
}}/> : null }
|
||||
<AudioPlayerProvider>
|
||||
{weights.isLoading && !weights.list.length ? (
|
||||
<div className="row gx-3 gy-3">
|
||||
|
||||
Reference in new issue
Block a user