Merge branch 'feature/mvp' into bflat-fy-dynamic-storyteller-fakeyou

This commit is contained in:
BFlat authored and GitHub committed 2024-02-07 09:13:38 +07:00
commit 2e931840fb
25 files changed
+427 -94

No files matched your search

@@ -5,7 +5,7 @@ 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 { EntityType, 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";
@@ -65,7 +65,7 @@ const MocapInputFull = ({ media }: SlideProps) => {
const MediaPickerEmpty = ({ media, onSelect, open, inputProps, type, user }: EmptySlideProps) => {
const browserClick = () => open({
component: MediaBrowser,
props: { onSelect, type, username: user?.username || "" }
props: { entityType: EntityType.media, onSelect, type, username: user?.username || "" }
});
return <>
@@ -1,5 +1,6 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import Input from "../Input";
import { TempInput as Input } from 'components/common';
// import Input from "../Input";
import { faSearch } from "@fortawesome/pro-solid-svg-icons";
import MasonryGrid from "../MasonryGrid/MasonryGrid";
import "./Searcher.scss";
@@ -106,7 +107,7 @@ export default function Searcher({
placeholder="Search..."
value={searchTerm[searcherKey]}
onChange={handleInputChange}
className="mb-3"
type="text"
/>
<div
className={`searcher-container ${
@@ -5,6 +5,7 @@ import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import "./Input.scss";
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
className?: string;
icon?: IconDefinition;
invalidReason?: string;
label?: string;
@@ -13,6 +14,7 @@ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
}
export default function TempInput({
className,
label,
icon,
invalidReason,
@@ -28,7 +30,7 @@ export default function TempInput({
{icon && (
<FontAwesomeIcon icon={icon} className="form-control-feedback" />
)}
<input className="form-control" {...rest} />
<input {...{ className: `form-control${ className ? " " + className : "" }`, ...rest }} />
</div>
</div>
);
@@ -0,0 +1,109 @@
@import "scss/colours.scss";
@import "scss/site-refresh.scss";
.fy-slide-frame {
position: absolute;
height: 100%;
width: 100%;
}
.fy-entity-input {
@include panel;
border-radius: .5rem;
overflow: hidden;
position: relative;
// background-color: $panel;
}
.fy-entity-input-square {
aspect-ratio: 1/1;
height: 300px;
.fy-entity-input-empty-controls {}
}
.fy-entity-input-landscape {
width: 100%;
aspect-ratio: 4/3;
max-height: 66vh;
}
.fy-entity-input-empty, .fy-entity-input-full {
// position: relative;
// overflow: hidden;
.fy-entity-input-icon {
position: absolute;
height: 80%;
opacity: 75%;
// background-color: blue;
margin: auto;
inset: 0;
path { fill: $panel-inner-border}
}
}
.fy-entity-input-label-icon {
margin-right: .5rem;
height: 1.25rem;
}
.fy-entity-input-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-entity-input-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-entity-input-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-entity-input-row {
height: 100%;
}
.fy-entity-input-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,159 @@
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 { EntityType, MediaFilterProp, WeightFilterProp } 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 "./EntityInput.scss";
interface Props {
label?: string,
onChange?: any,
mediaType?: MediaFilterProp,
aspectRatio?: "square" | "landscape" | "portrait",
weightType?: WeightFilterProp,
}
interface SlideProps {
media?: MediaFile
};
interface EmptySlideProps extends SlideProps {
entityType: EntityType,
filterType: MediaFilterProp | WeightFilterProp,
inputProps?: any,
onSelect: any,
open: any,
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-entity-input-mocap-preview",
}}
/>
<div {...{ className: "fy-entity-input-full-controls" }}>
Your file
</div>
</>;
};
const MediaPickerEmpty = ({ entityType, filterType, media, onSelect, open, inputProps, user }: EmptySlideProps) => {
const browserClick = () => open({
component: MediaBrowser,
props: { entityType, filterType, onSelect, username: user?.username || "" }
});
return <>
<Icon {...{ className: "fy-entity-input-icon", icon: faPersonWalking }}/>
<div {...{ className: "fy-entity-input-empty-controls" }}>
<FileWrapper {...{ containerClass: "fy-entity-input-row", panelClass: "fy-entity-input-button", noStyle: true, ...inputProps }}>
<>
<Icon {...{ className: "fy-entity-input-label-icon", icon: faFileArrowUp }}/>
Upload, click or drag here
</>
</FileWrapper>
<div {...{ className: "fy-entity-input-row fy-entity-input-button", onClick: browserClick }}>
<Icon {...{ className: "fy-entity-input-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 EntityInput({ aspectRatio = "square", label, onChange, mediaType, weightType }: Props) {
const entityType = mediaType ? EntityType.media : weightType ? EntityType.weights : EntityType.unknown;
const filterType = mediaType || weightType || "all";
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-entity-input panel-inner${ aspectRatio ? " fy-entity-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-entity-input-busy", ...sharedProps }}/>,
<AniMod {...{ render: MocapInputFull, className: "fy-entity-input-full", ...sharedProps }}/>,
<AniMod {...{ render: MediaPickerEmpty, className: "fy-entity-input-empty", entityType, filterType, inputProps, onSelect, open, user, ...sharedProps }}/>
][i];
})
}
</div>
</>;
};
@@ -0,0 +1 @@
export { default } from "./EntityInput";
@@ -8,11 +8,33 @@ export enum MediaFilters {
fbx
}
export type MediaFilterProp = keyof typeof MediaFilters;
export enum WeightsFilters {
all,
hifigan_tt2,
sd_1,
"sd_1.5",
sdxl,
so_vits_svc,
tt2,
loRA,
vall_e
}
export const MediaFilterOptions = (t = (v:string) => v) => Object.values(MediaFilters)
export type MediaFilterProp = keyof typeof MediaFilters;
export type WeightFilterProp = keyof typeof WeightsFilters;
export enum EntityType {
unknown,
media,
weights
}
export const EntityFilterOptions = ( mode: EntityType, t = (v:string) => v) => {
const filters = [{},MediaFilters,WeightsFilters][mode];
return mode ? Object.values(filters)
.filter(val => isNaN(Number(val)))
.map((value) => {
if (typeof value === "string") return { value, label: t(value) }
return { label: "", value: "" };
});
}) : [];
};
@@ -1,10 +1,9 @@
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 { ImagePreview, MocapPreview, VideoPreview } from '../CardPreviews';
import { AudioCard, OverlayCard, CardWrapper } from "components/entities";
import { EntityType } from "components/entities/EntityTypes";
interface MediaCardsProps {
props: any,
@@ -12,6 +11,7 @@ interface MediaCardsProps {
}
interface Props {
entityType: EntityType,
list: MediaFile[],
success?: boolean
}
@@ -31,20 +31,32 @@ const MediaCards = ({ props, type }: MediaCardsProps) => {
}
};
export default function MediaList({ list, success, ...rest }: Props) {
const WeightsCards = ({ props, type }: MediaCardsProps) => {
switch (type) {
default:
return <div>Unsupported media type</div>;
}
};
export default function MediaList({ entityType, 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) => {
{ list.map((data: any, 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 }} />
{
[ null,
<MediaCards {...{ type: data.media_type, props }} />,
<WeightsCards {...{ type: data.weight_type, props }} />
][entityType]
}
</div>;
}) }
</MasonryGrid>;
@@ -1,6 +1,7 @@
import AudioCard from "./AudioCard";
import CardWrapper from "./CardWrapper";
import EntityInput from "./EntityInput";
import MediaList from "./MediaList";
import OverlayCard from "./OverlayCard";
export { AudioCard, CardWrapper, MediaList, OverlayCard };
export { AudioCard, CardWrapper, EntityInput, MediaList, OverlayCard };
@@ -24,9 +24,6 @@ export default function InferenceJobsList({
useInferenceJobs(jobType);
const { t } = useLocalize("InferenceJobs");
// let height = 0;
// const transitions = useTransition(inferenceJobs,{ not right now });
if (inferenceJobs.length) {
return (
<Panel {...{ className: "fy-inference-jobs-list", padding: true }}>
@@ -1,25 +1,27 @@
import { faDiscord } from "@fortawesome/free-brands-svg-icons";
import {
faBookOpen,
faMessageDots,
faTrophy,
faWaveformLines,
faStar,
faFaceViewfinder,
faCloudUpload,
faWandMagicSparkles,
faHome,
faCompass,
faUser,
faSignOutAlt,
faCameraMovie,
faPersonRays,
faMessageImage,
faPhotoFilm,
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, { useEffect, useState } from "react";
import { NavLink, useHistory } from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faDiscord } from "@fortawesome/free-brands-svg-icons";
import {
faArrowsTurnToDots,
faBookOpen,
faCameraMovie,
faCompass,
faCloudUpload,
faFaceViewfinder,
faHome,
faMessageDots,
faMessageImage,
faPersonRays,
faSignOutAlt,
faStar,
faTrophy,
faUser,
faWandMagicSparkles,
faWaveformLines,
faTransporter
} from "@fortawesome/pro-solid-svg-icons";
import {
GetQueueStats,
GetQueueStatsIsOk,
@@ -386,15 +388,15 @@ export default function SideNav({
<li>
<NavLink
to="/storyteller-filter"
to="/video-workflow"
activeClassName="active-link"
onClick={handleNavLinkClick}
>
<FontAwesomeIcon
icon={faPhotoFilm}
icon={faArrowsTurnToDots}
className="sidebar-heading-icon"
/>
{t("storytellerFilter")}
{t("videoWorkflow")}
</NavLink>
</li>
@@ -412,6 +414,20 @@ export default function SideNav({
{/* {t("videoStorytellerStudio")} */}
</NavLink>
</li>
<li>
<NavLink
to="/engine-compositor"
activeClassName="active-link"
onClick={handleNavLinkClick}
>
<FontAwesomeIcon
icon={faTransporter}
className="sidebar-heading-icon"
/>
Engine Compositor
{/* {t("videoStorytellerStudio")} */}
</NavLink>
</li>
<hr className="mb-3 mt-3" />
<li className="sidebar-heading">Image Generation</li>
<li>
@@ -1,12 +1,14 @@
import React, { useState } from "react";
import { MediaList } from "components/entities";
import { MediaFilterOptions, MediaFilterProp } from "components/entities/EntityTypes";
import { EntityType, EntityFilterOptions, MediaFilterProp, WeightFilterProp } 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 { GetWeightsByUser } from "@storyteller/components/src/api/weights/GetWeightsByUser";
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
import { Weight } from "@storyteller/components/src/api/weights/GetWeight";
import { useListContent, useRatings } from "hooks";
import { FontAwesomeIcon as Icon } from "@fortawesome/react-fontawesome";
import { faArrowDownWideShort, faFilter, faXmark } from "@fortawesome/pro-solid-svg-icons";
@@ -14,26 +16,27 @@ import prepFilter from "resources/prepFilter";
import "./MediaBrowser.scss";
interface Props {
entityType: EntityType,
handleClose: any,
mediaToken: string,
onSelect?: any,
type?: MediaFilterProp,
filterType?: MediaFilterProp | WeightFilterProp,
username: string,
}
export default function MediaBrowser({ mediaToken, handleClose = () => {}, onSelect, type, username }: Props) {
export default function MediaBrowser({ entityType, filterType: inputFilter, mediaToken, handleClose = () => {}, onSelect, username }: Props) {
console.log("😎",entityType);
const ratings = useRatings();
const [showMasonryGrid, setShowMasonryGrid] = useState(true);
const [mediaType, mediaTypeSet] = useState(type || "all");
const [list, listSet] = useState<MediaFile[]>([]);
const media = useListContent({
const [filterType, filterTypeSet] = useState(inputFilter || "all");
const [list, listSet] = useState<MediaFile | Weight[]>([]);
const entities = useListContent({
addQueries: {
page_size: 24,
...prepFilter(mediaType, "filter_media_type"),
...prepFilter(filterType, ["","filter_media_type","maybe_scoped_weight_type"][entityType]),
},
addSetters: { mediaTypeSet },
// debug: "profile media",
fetcher: GetMediaByUser,
addSetters: { filterTypeSet },
fetcher: [() => {},GetMediaByUser,GetWeightsByUser][entityType] || GetMediaByUser,
list,
listSet,
onInputChange: () => setShowMasonryGrid(false),
@@ -43,18 +46,18 @@ export default function MediaBrowser({ mediaToken, handleClose = () => {}, onSel
setShowMasonryGrid(true);
},
requestList: true,
urlParam: username,
urlParam: "echelon",
urlUpdate: false
});
const handlePageClick = (selectedItem: { selected: number }) => {
media.pageChange(selectedItem.selected);
entities.pageChange(selectedItem.selected);
};
const paginationProps = {
onPageChange: handlePageClick,
pageCount: media.pageCount,
currentPage: media.page,
pageCount: entities.pageCount,
currentPage: entities.page,
};
const sortOptions = [
@@ -71,7 +74,7 @@ export default function MediaBrowser({ mediaToken, handleClose = () => {}, onSel
return <>
<header {...{ className: "fy-media-browser-header" }}>
<div {...{ className: "fy-media-browser-tools" }}>
<h3>Media</h3>
<h3>{ ["","Media","Weights"][entityType] }</h3>
<Icon {...{ className: "icon-close-button", icon: faXmark, onClick: () => handleClose() }}/>
</div>
<div {...{ className: "fy-media-browser-tools" }}>
@@ -80,22 +83,22 @@ export default function MediaBrowser({ mediaToken, handleClose = () => {}, onSel
icon: faArrowDownWideShort,
options: sortOptions,
name: "sort",
onChange: media.onChange,
value: media.sort,
onChange: entities.onChange,
value: entities.sort,
}}
/>
{ (!type || type === "all") && <TempSelect {...{
{ (!inputFilter || inputFilter === "all") && <TempSelect {...{
icon: faFilter,
options: MediaFilterOptions(),
name: "mediaType",
onChange: media.onChange,
value: mediaType,
options: EntityFilterOptions(entityType),
name: "filterType",
onChange: entities.onChange,
value: filterType,
}}/> }
<Pagination {...paginationProps} />
</div>
</header>
<AudioPlayerProvider>
{ media.isLoading ? (
{ entities.isLoading ? (
<div {...{ className: "row gx-3 gy-3" }}>
{Array.from({ length: 12 }).map((_, index) => (
<SkeletonCard key={index} />
@@ -105,7 +108,7 @@ export default function MediaBrowser({ mediaToken, handleClose = () => {}, onSel
<>
{ showMasonryGrid && (
<div {...{ className: "fy-media-browser-list" }}>
<MediaList {...{ list: media.list, success: media.status === 3, onClick }}/>
<MediaList {...{ entityType, list: entities.list, success: entities.status === 3, onClick }}/>
</div>
) }
</>
@@ -33,6 +33,7 @@ export default function useListContent({
urlParam = "",
urlUpdate = true
}: Props) {
console.log("💎",fetcher);
const { pathname, search: locSearch } = useLocation();
const history = useHistory();
const urlQueries = new URLSearchParams(locSearch);
@@ -108,7 +108,7 @@ import UploadSdWeightPage from "./pages/upload/UploadSdWeightPage";
import { FooterNav } from "./nav/FooterNav";
import FbxToGltfPage from "./pages/fbx_to_gltf/FbxToGltfPage";
import UploadLoraWeightPage from "./pages/upload/UploadLoraWeightPage";
import StorytellerFilterPage from "./pages/storyteller_filter/StorytellerFilter";
import VideoWorkflowPage from "./pages/video_workflow";
import ScrollToTop from "./_common/ScrollToTop";
import TextToImagePage from "./pages/text_to_image/TextToImagePage";
import DomainConfigProvider from "context/DomainConfigContext";
@@ -1,5 +1,6 @@
import React, { useState } from "react";
import { Button, MocapInput } from "components/common";
import { EntityInput } from "components/entities";
import { Button } from "components/common";
// import { useInferenceJobs } from "hooks";
// import InferenceJobsList from "components/layout/InferenceJobsList";
// import { EnqueueEngineCompositing } from "@storyteller/components/src/api/engine_compositor/EnqueueEngineCompositing";
@@ -28,7 +29,7 @@ export default function DevMediaInput({ value }: Props) {
<h2>{ [526,187].map((num = 0) => String.fromCodePoint(128000 + num)) }</h2>
<Button {...{ label: "Enqueue", variant: "primary" }}/>
</header>
<MocapInput {...{ aspectRatio: "landscape", label: "Choose Media File", onChange, type: "all" }}/>
<EntityInput {...{ aspectRatio: "landscape", label: "Choose Media File", onChange, weightType: "sd_1.5" }}/>
<div>
{ mediaToken }
</div>
@@ -1 +0,0 @@
export { default } from './StorytellerFilter';
@@ -6,8 +6,8 @@ import { FrontendInferenceJobType, InferenceJob } from '@storyteller/components/
import { Container } from "components/common";
import PageHeader from "components/layout/PageHeader";
import { states, reducer } from "./storytellerFilterReducer";
import SubRoutes from "./storytellerFilterRoutes";
import { states, reducer } from "./videoWorkflowReducer";
import SubRoutes from "./videoWorkflowRoutes";
export default function StorytellerFilter(props:{
enqueueInferenceJob: (
@@ -1,9 +1,9 @@
import React from 'react'
import React, { memo } from 'react'
import { Analytics } from "common/Analytics";
import InferenceJobsList from "components/layout/InferenceJobsList";
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
import { states, Action, State } from "../storytellerFilterReducer";
import { states, Action, State } from "../videoWorkflowReducer";
import { Spinner } from 'components/common';
function StorytellerJobList(){
@@ -25,7 +25,7 @@ function StorytellerJobList(){
);
}
export default function PageJobList({
export default memo (function PageJobList({
t, pageState, dispatchPageState
}: {
debug?: boolean;
@@ -46,4 +46,4 @@ export default function PageJobList({
<StorytellerJobList />
</>
);
}
});
@@ -2,14 +2,15 @@ import React from "react";
import { BasicVideo, Panel, Tabs } from "components/common";
import { State, Action } from "../storytellerFilterReducer";
import { State, Action } from "../videoWorkflowReducer";
import TabContentUpload from "./tabContentUpload";
import TabContentLibrary from "./tabContentLibrary";
export default function PageVideoProvision({
debug=false, t, pageState, dispatchPageState
debug=false, parentPath, t, pageState, dispatchPageState
}: {
debug?: boolean;
parentPath: string;
t: Function;
pageState: State;
dispatchPageState: (action: Action) => void;
@@ -21,7 +22,7 @@ export default function PageVideoProvision({
content: <TabContentUpload {...{
debug, t, pageState, dispatchPageState
}} />,
to: "/storyteller-filter/upload",
to: `${parentPath}/upload`,
padding: true,
},
{
@@ -29,7 +30,7 @@ export default function PageVideoProvision({
content: <TabContentLibrary {...{
debug, t, pageState, dispatchPageState
}} />,
to: "/storyteller-filter/select-media",
to: `${parentPath}/select-media`,
padding: true,
},
];
@@ -7,7 +7,7 @@ import { useMedia } from "hooks";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
import { EnqueueStorytellerFilter } from "@storyteller/components/src/api/storyteller_filter";
import { states, Action, State } from "../storytellerFilterReducer";
import { states, Action, State } from "../videoWorkflowReducer";
import {
Accordion,
Button,
@@ -1,6 +1,6 @@
import React from "react";
import { NavLink } from "react-router-dom";
import { states, Action, State } from "../storytellerFilterReducer";
import { states, Action, State } from "../videoWorkflowReducer";
import { Button } from "components/common";
import SelectModal from "components/common/SelectModal/SelectModal";
@@ -21,7 +21,8 @@ export default function TabContentLibrary({
});
};
return (
<div>
<div className="row g-3">
<div className="col-12">
<SelectModal
modalTitle="Select a Video"
label="Select a Video"
@@ -36,13 +37,16 @@ export default function TabContentLibrary({
},
]}
/>
</div>
{pageState.status === states.FILE_SELECTED &&
<NavLink to={`load/${pageState.mediaFileToken}`}>
<Button
label={t("button.proceed")}
onClick={handleProceed}
/>
</NavLink>
<div className="col-12 d-flex justify-content-center mt-5">
<NavLink to={`load/${pageState.mediaFileToken}`}>
<Button
label={t("button.proceed")}
onClick={handleProceed}
/>
</NavLink>
</div>
}
</div>
);
@@ -5,7 +5,7 @@ import { v4 as uuidv4 } from "uuid";
import { UploadMedia } from "@storyteller/components/src/api/media_files/UploadMedia";
import { useFile } from "hooks";
import { ErrorMessage, VideoInput, Spinner } from "components/common";
import { states, Action, State } from "../storytellerFilterReducer";
import { states, Action, State } from "../videoWorkflowReducer";
export default function TabContentUpload({
debug=false, t, pageState, dispatchPageState
@@ -0,0 +1 @@
export { default } from './VideoWorkflow';
@@ -1,9 +1,9 @@
import React from "react";
import {Switch, Route, useRouteMatch, Redirect} from "react-router-dom";
import PageFilterControls from "./components/pageFilterControls";
import PageWorkflowControls from "./components/pageWorkflowControls";
import PageVideoProvision from "./components/pageVideoProvision";
import PageJobList from "./components/pageJobList";
import {State,Action} from "./storytellerFilterReducer";
import {State,Action} from "./videoWorkflowReducer";
export default function SubRoutes ({
debug, t, pageState, dispatchPageState
@@ -14,20 +14,23 @@ export default function SubRoutes ({
dispatchPageState: (action: Action) => void
}){
const { path } = useRouteMatch();
const commonPageProps = {
debug, parentPath: path, t, pageState, dispatchPageState
};
return(
<Switch>
<Route exact path={`${path}/load/:mediaToken`} >
<PageFilterControls {...{debug, t, pageState, dispatchPageState}}/>
<PageWorkflowControls {...commonPageProps}/>
</Route>
<Route exact path={`${path}/upload`}>
<PageVideoProvision {...{debug, t, pageState, dispatchPageState}}/>
<PageVideoProvision {...commonPageProps}/>
</Route>
<Route exact path={`${path}/select-media`}>
<PageVideoProvision {...{debug, t, pageState, dispatchPageState}}/>
<PageVideoProvision {...commonPageProps}/>
</Route>
<Route exact path={`${path}/jobs`}>
<PageJobList {...{debug, t, pageState, dispatchPageState}}/>
<PageJobList {...commonPageProps}/>
</Route>
<Route path={`${path}`}>
<Redirect to={`${path}/upload`} />