Merge branch 'feature/mvp' of https://github.com/storytold/storyteller-frontend into feature/mvp

This commit is contained in:
Victor committed 2024-02-01 22:38:04 -08:00
commit e9bbac2ec0
30 files changed
+510 -187

No files matched your search

@@ -11,6 +11,7 @@ import { faChevronDown } from "@fortawesome/pro-solid-svg-icons";
import { animated, useSpring } from "@react-spring/web";
interface AccordionProps {
className?: string;
children: React.ReactNode;
}
@@ -96,7 +97,7 @@ function AccordionItem({
);
}
function Accordion({ children }: AccordionProps) {
function Accordion({ children, className }: AccordionProps) {
// Extract titles of items that should be initially open
const defaultOpenTitles = React.Children.toArray(children)
.filter((child: any) => child.props.defaultOpen)
@@ -115,7 +116,9 @@ function Accordion({ children }: AccordionProps) {
return (
<AccordionContext.Provider value={{ openItems, toggleItem }}>
<div className="d-flex flex-column gap-3">{children}</div>
<div className={`d-flex flex-column gap-3 ${className}`}>
{children}
</div>
</AccordionContext.Provider>
);
}
@@ -12,8 +12,8 @@ import Button from "components/common/Button";
import useWeightTypeInfo from "hooks/useWeightTypeInfo/useWeightTypeInfo";
import WeightCoverImage from "components/common/WeightCoverImage";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
import useToken from "hooks/useToken";
import getCardUrl from "../getCardUrl";
// import getCardUrl from "../getCardUrl";
interface AudioCardProps {
bookmarks?: any;
@@ -24,8 +24,8 @@ interface AudioCardProps {
source?: string;
type: "media" | "weights";
inSelectModal?: boolean;
onClick?: (e:any) => any;
onResultSelect?: () => void;
onResultSelect?: (data: { token: string; title: string }) => void;
// onClick?: (e:any) => any;
}
export default function AudioCard({
@@ -37,11 +37,10 @@ export default function AudioCard({
source = "",
type,
inSelectModal = false,
onClick: inClick,
// onClick: inClick,
onResultSelect,
}: AudioCardProps) {
const { setToken, setWeightTitle } = useToken();
const linkUrl = getCardUrl(data,source,type);
const linkUrl = getCardUrl(data, source, type);
const history = useHistory();
const handleInnerClick = (event: any) => {
@@ -49,10 +48,11 @@ export default function AudioCard({
};
const handleSelectModalResultSelect = () => {
if (inSelectModal) {
setToken(data.weight_token);
setWeightTitle && setWeightTitle(data.title);
onResultSelect && onResultSelect();
if (inSelectModal && onResultSelect) {
onResultSelect({
token: data.weight_token,
title: data.title,
});
}
};
@@ -85,10 +85,6 @@ export default function AudioCard({
}
}
// const onClick = () => {
// if (inClick) inClick(data);
// };
const card = (
<Card
padding={true}
@@ -169,14 +165,16 @@ export default function AudioCard({
<div className="d-flex flex-grow-1">
<Badge label={weightBadgeLabel} color={weightBadgeColor} />
</div>
{inClick ? (
{inSelectModal ? (
<Button
icon={faArrowRight}
iconFlip={true}
variant="link"
label="Select"
className="fs-7"
onClick={handleSelectModalResultSelect}
onClick={() => {
history.push(linkUrl);
}}
/>
) : (
<Button
@@ -185,9 +183,7 @@ export default function AudioCard({
variant="link"
label="Use"
className="fs-7"
onClick={() => {
history.push(linkUrl);
}}
onClick={handleSelectModalResultSelect}
/>
)}
</div>
@@ -263,12 +259,12 @@ export default function AudioCard({
return (
<>
{inClick ? (
{inSelectModal ? (
<>{card}</>
) : (
<Link
{...{
to: linkUrl
to: linkUrl,
}}
>
{card}
@@ -10,7 +10,7 @@ import Button from "components/common/Button";
import { faArrowRight } from "@fortawesome/pro-solid-svg-icons";
import useWeightTypeInfo from "hooks/useWeightTypeInfo/useWeightTypeInfo";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
import useToken from "hooks/useToken";
// import useToken from "hooks/useToken";
import getCardUrl from "../getCardUrl";
interface ImageCardProps {
@@ -21,7 +21,7 @@ interface ImageCardProps {
source?: string;
type: "media" | "weights";
inSelectModal?: boolean;
onResultSelect?: () => void;
onResultSelect?: (data:{token: string, title:string}) => void;
}
export default function ImageCard({
@@ -35,18 +35,22 @@ export default function ImageCard({
onResultSelect,
}: ImageCardProps) {
const history = useHistory();
const { setToken, setWeightTitle } = useToken();
// const { setToken, setWeightTitle } = useToken();
const linkUrl = getCardUrl(data,source,type);
const handleInnerClick = (event: any) => {
console.log("handleInnderClick")
event.stopPropagation();
};
const handleSelectModalResultSelect = () => {
console.log("handleSelectModalResultSelect")
if (inSelectModal) {
setToken(data.weight_token);
setWeightTitle && setWeightTitle(data.title);
onResultSelect && onResultSelect();
onResultSelect && onResultSelect({
token: data.weight_token,
title: data.title
});
}
};
@@ -9,7 +9,6 @@ import Button from "components/common/Button";
import CreatorName from "../CreatorName";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
import { Link } from "react-router-dom";
import useToken from "hooks/useToken";
import getCardUrl from "../getCardUrl";
interface VideoCardProps {
@@ -19,7 +18,7 @@ interface VideoCardProps {
source?: string;
type: "media" | "weights";
inSelectModal?: boolean;
onResultSelect?: () => void;
onResultSelect?: (data:{token: string, title:string}) => void;
}
export default function VideoCard({
@@ -31,7 +30,6 @@ export default function VideoCard({
inSelectModal = false,
onResultSelect,
}: VideoCardProps) {
const { setToken, setWeightTitle } = useToken();
const linkUrl = getCardUrl(data,source,type);
const handleInnerClick = (event: any) => {
@@ -39,10 +37,8 @@ export default function VideoCard({
};
const handleSelectModalResultSelect = () => {
if (inSelectModal) {
setToken(data.weight_token || data.token);
setWeightTitle && setWeightTitle(data.title);
onResultSelect && onResultSelect();
if (inSelectModal && onResultSelect) {
onResultSelect(data);
}
};
@@ -137,55 +133,6 @@ export default function VideoCard({
</div>
</>
)}
{/* {type === "weights" && (
<>
<img
src={data.public_bucket_path}
alt={data.weight_name}
className="card-video"
/>
<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="Video" color="purple" overlay={true} />
</div>
<Button
icon={faArrowRight}
iconFlip={true}
variant="link"
label="Use"
className="fs-7"
onClick={() => {
history.push(linkUrl);
}}
/>
</div>
<FontAwesomeIcon icon={faPlayCircle} className="card-video-play" />
<div className="card-img-overlay-text">
<div>
<h6 className="fw-semibold text-white mb-1">
{data.weight_name}
</h6>
<p className="fs-7 opacity-75">{timeAgo}</p>
<div>
<LikeButton
{...{
overlay: true,
...ratings.makeProps({
entityToken: data.weight_token,
entityType: "model_weight",
}),
}}
/>
</div>
</div>
</div>
</div>
</>
)} */}
</Card>
);
@@ -13,7 +13,7 @@ export default function WeightsCards({ props, type }: Props) {
case WeightCategory.TTS:
case WeightCategory.VC:
case WeightCategory.ZS:
return <AudioCard {...{ ...props, showCover: true }} />;
return <AudioCard { ...props} showCover />;
case WeightCategory.SD:
return <ImageCard {...props} />;
default:
@@ -8,12 +8,16 @@ interface Props {
label?: string;
max?: number;
min?: number;
name?: string;
onChange?: any;
required?: boolean;
step?: number;
thumbTip?: string;
// name, value and setter for onChange has to be in this particular format
// name = state variable's name as a stringh
// value = the state variable's reference
// onChange = the statevariable's setter function's reference
name?: string;
value?: number;
onChange?: any;
}
const renderTrack = ({ props: { style, ...props }, children }: any) => (
@@ -0,0 +1,42 @@
@import "scss/colours.scss";
.fy-number-slider {
display: flex;
gap: 0.5rem;
align-items: center;
.fy-number-slider-range {
flex-grow: 1;
}
.fy-number-slider-value {
text-align: center;
width: fit-content;
padding: 6px;
border-radius: 8px;
background-color: $secondary;
border-width: 0px;
border-color: none;
color: rgba(#fff, 0.85);
font-weight: 500;
flex-shrink: 0;
}
.fy-number-slider-track {
height: 6px;
margin: 0 0 0 0.6875rem; // margin half width of thumb, so that thumb does not overlap margins at minimum position
background-color: #4f4f66;
border-radius: 0.25rem;
}
.fy-number-slider-thumb {
height: 16px;
width: 16px;
background-color: #e66462;
border-radius: 50%;
box-shadow: 0px 0px 10px rgba(#000, 0.15);
&:hover {
background-color: #fff;
}
}
}
@@ -0,0 +1,93 @@
import React from "react";
import { Range } from "react-range";
import Tippy from "@tippyjs/react";
import { Label } from "components/common";
import "./NumberSliderV2.scss";
interface Props {
label?: string;
thumbTip?: string;
min: number;
max: number;
step?: number;
initialValue?: number;
onChange?: (x:number) => void;
required?: boolean;
}
function roundToStep(x:number, step:number){
return Math.round(x/step) * step;
}
const renderTrack = ({ props: { style, ...props }, children }: any) => (
<div {...{ ...props, className: "fy-number-slider-track", style }}>
{children}
</div>
);
const thumb =
(thumbTip = "") =>
({ props: { style, ...props } }: any) => {
return (
<Tippy
{...{
arrow: false,
content: thumbTip,
placement: "bottom",
theme: "range-slider",
}}
>
<div
{...{ ...props, className: "fy-number-slider-thumb", style }}
></div>
</Tippy>
);
};
export default function NumberSlider({
label,
thumbTip,
min,
max,
step: stepProps,
onChange: onChangeCallback,
required,
initialValue: initialValueProps,
}: Props) {
const step = stepProps && stepProps <= max-min ? stepProps
: max-min >= 1 ? 1 : (max-min) / 10;
const initialValue = initialValueProps && initialValueProps <= max && initialValueProps >= min ? initialValueProps
: max-min === step ? min : roundToStep((max+min)/2, step);
function handleInputOnChange(e: React.ChangeEvent<HTMLInputElement>){
if(onChangeCallback)onChangeCallback(Number.parseInt(e.target.value))
}
function handleRangeOnChange(rangeValue: number[]){
if(onChangeCallback)onChangeCallback(rangeValue[0])
}
return (
<div>
<Label {...{ label, required }} />
<div className="fy-number-slider">
<input
className="fy-number-slider-value"
type="number"
{...{ min, max, step, value:initialValue}}
onChange={handleInputOnChange}
/>
<div className="fy-number-slider-range">
<Range
{...{min, max, step,
onChange: handleRangeOnChange,
renderThumb: thumb(thumbTip),
renderTrack,
thumbTip,
values: [initialValue],
}}
/>
</div>
</div>
</div>
);
}
@@ -0,0 +1 @@
export { default } from './NumberSliderV2';
@@ -17,7 +17,7 @@ interface SearcherProps {
type?: "page" | "modal";
dataType?: "media" | "weights";
weightType?: string;
onResultSelect?: () => void;
onResultSelect?: (data:{token:string, title:string}) => void;
searcherKey: string;
}
@@ -10,7 +10,7 @@ import { ListMediaFiles } from "@storyteller/components/src/api/media_files/List
interface SelectMediaListProps {
mediaType: string;
listKey: string;
onResultSelect?: () => void;
onResultSelect?: (data:{token:string, title:string}) => void;
}
export default function SelectMediaList({
@@ -4,7 +4,6 @@ import Modal from "../Modal";
import NonRouteTabs from "../Tabs/NonRouteTabs";
import Input from "../Input";
import Button from "../Button";
import useToken from "hooks/useToken";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import SelectMediaList from "./SelectMediaList";
import SelectWeightsList from "./SelectWeightsList";
@@ -21,7 +20,10 @@ interface SelectModalProps {
label?: string;
tabs: TabConfig[];
modalTitle?: string;
onSelect?: (token: string) => void;
onSelect?: (data:{
token: string,
title: string,
}) => void;
required?: boolean;
}
@@ -34,8 +36,8 @@ const SelectModal = memo(
required,
}: SelectModalProps) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const { token, setToken, weightTitle, setWeightTitle } = useToken();
const [selectedValue, setSelectedValue] = useState("");
const initialValue = {token:"", title:""};
const [selectedValue, setSelectedValue] = useState(initialValue);
const [activeTab, setActiveTab] = useState(tabs[0].tabKey);
const [mediaType, setMediaType] = useState(
tabs[0].mediaTypeFilter || "all"
@@ -51,19 +53,6 @@ const SelectModal = memo(
setWeightType(currentTab?.weightTypeFilter || "all");
}, [activeTab, tabs]);
useEffect(() => {
if (token && token !== selectedValue && onSelect) {
setSelectedValue(token);
onSelect(token);
} else if (weightTitle && weightTitle !== selectedValue && onSelect) {
setSelectedValue(weightTitle);
onSelect(weightTitle);
}
console.log("token", token, "weightTitle", weightTitle);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [token, weightTitle]);
const openModal = () => {
setIsModalOpen(true);
};
@@ -73,16 +62,21 @@ const SelectModal = memo(
};
const handleRemove = () => {
setWeightTitle && setWeightTitle("");
setToken("");
setSelectedValue(initialValue);
};
const handleOnSelect = (data:{token:string, title:string}) => {
setSelectedValue({token: data.token, title: data.title || ""});
if (onSelect) onSelect(data);
closeModal();
}
const searchTabs = tabs.map(tab => ({
label: tab.label,
content: tab.searcher ? (
<Searcher
type="modal"
onResultSelect={closeModal}
onResultSelect={handleOnSelect}
searcherKey={tab.tabKey}
weightType={tab.weightTypeFilter}
/>
@@ -92,14 +86,14 @@ const SelectModal = memo(
<SelectMediaList
mediaType={mediaType}
listKey={tab.tabKey}
onResultSelect={closeModal}
onResultSelect={handleOnSelect}
/>
)}
{tab.type === "weights" && (
<SelectWeightsList
weightType={weightType}
listKey={tab.tabKey}
onResultSelect={closeModal}
onResultSelect={handleOnSelect}
/>
)}
</>
@@ -122,10 +116,12 @@ const SelectModal = memo(
disabled={true}
className="w-100"
placeholder="None selected"
value={weightTitle ? weightTitle : token || ""}
value={selectedValue.title !==""
? selectedValue.title
: selectedValue.token || ""}
/>
<Button label={token ? "Change" : "Select"} onClick={openModal} />
{token && (
<Button label={selectedValue.token !== "" ? "Change" : "Select"} onClick={openModal} />
{selectedValue.token && (
<Button
square={true}
variant="danger"
@@ -153,4 +149,4 @@ const SelectModal = memo(
}
);
export default SelectModal;
export default SelectModal;
@@ -10,7 +10,7 @@ import { Weight } from "@storyteller/components/src/api/weights/GetWeight";
interface SelectWeightsListProps {
weightType: string;
listKey: string;
onResultSelect?: () => void;
onResultSelect?: (data:{token:string, title:string}) => void;
}
export default function SelectWeightsList({
@@ -0,0 +1 @@
export { default } from "./SelectModal";
@@ -0,0 +1,27 @@
.fy-textarea {
margin-bottom: 1rem;
&:only-child,
&:last-child {
margin-bottom: 0;
}
}
.d-flex:not(.flex-column) > .fy-textarea {
margin-bottom: 0;
margin-right: 0.5rem;
&:only-child,
&:last-child {
margin-bottom: 0;
}
}
.gap-1 > .fy-textarea,
.gap-2 > .fy-textarea,
.gap-3 > .fy-textarea,
.gap-4 > .fy-textarea,
.gap-5 > .fy-textarea {
margin-bottom: 0;
margin-right: 0;
}
@@ -0,0 +1,39 @@
import React from "react";
import "./TextAreaV2.scss";
interface TextAreaProps {
label?: string;
required?: boolean;
placeholder?: string;
initialValue?: string;
value?: string;
onChange?: (newText:string) => void
}
export default function TextArea({
label,
required,
value,
placeholder,
onChange: onChangeCallback,
}: TextAreaProps) {
const handleOnChange = (e:React.ChangeEvent<HTMLTextAreaElement>)=>{
if(onChangeCallback) onChangeCallback (e.target.value);
}
return (
<div>
{label &&
<label className={`sub-title ${required ? "required" : ""}`}>
{label}
</label>
}
<div className="form-group">
<textarea
className="form-control"
onChange={handleOnChange}
{...{value, placeholder}}
/>
</div>
</div>
);
}
@@ -0,0 +1 @@
export { default } from "./TextAreaV2";
@@ -1,3 +1,4 @@
import Accordion from "./Accordion";
import AudioBlobPreview from "./AudioBlobPreview";
import AudioInput from "./AudioInput";
import BasicVideo from "./BasicVideo";
@@ -16,10 +17,12 @@ import Label from "./Label";
import MocapInput from "./MocapInput";
import Modal from "./Modal";
import NumberSlider from "./NumberSlider";
import NumberSliderV2 from "./NumberSliderV2";
import Panel from "./Panel";
import Radio from "./Radio";
import SegmentButtons from "./SegmentButtons";
import Select from "./Select";
import SelectModal from "./SelectModal";
import Skeleton from "./Skeleton";
import Slider from "./Slider";
import Spinner from "./Spinner";
@@ -29,10 +32,13 @@ import TempAudioPlayer from "./TempAudioPlayer";
import TempInput from "./TempInput";
import TempSelect from "./TempSelect";
import TempTextArea from "./TempTextArea";
import TextArea from "./TextArea";
import TextAreaV2 from "./TextAreaV2";
import TintSpinner from "./TintSpinner";
import VideoInput from "./VideoInput";
export {
Accordion,
AudioBlobPreview,
AudioInput,
BasicVideo,
@@ -51,10 +57,12 @@ export {
MocapInput,
Modal,
NumberSlider,
NumberSliderV2,
Panel,
Radio,
SegmentButtons,
Select,
SelectModal,
Skeleton,
Slider,
Spinner,
@@ -64,6 +72,8 @@ export {
TempInput,
TempSelect,
TempTextArea,
TextArea,
TextAreaV2,
TintSpinner,
VideoInput
};
@@ -1,6 +1,6 @@
import React from "react";
import useTimeAgo from "hooks/useTimeAgo";
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
// import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
import { MakeRatingsProps } from "hooks/useRatings";
import { faPersonWalking } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon as Icon } from "@fortawesome/react-fontawesome";
@@ -15,6 +15,7 @@ import {
faCameraMovie,
faPersonRays,
faMessageImage,
faPhotoFilm,
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, { useEffect, useState } from "react";
@@ -371,6 +372,20 @@ export default function SideNav({
</NavLink>
</li>
<li>
<NavLink
to="/storyteller-filter"
activeClassName="active-link"
onClick={handleNavLinkClick}
>
<FontAwesomeIcon
icon={faPhotoFilm}
className="sidebar-heading-icon"
/>
{t("storytellerFilter")}
</NavLink>
</li>
<li>
<NavLink
to="/studio"
@@ -821,7 +821,7 @@ class PageContainer extends React.Component<
/>
</Route>
<Route path="/storyteller-filter/:mediaToken?">
<Route path="/storyteller-filter">
<StorytellerFilterPage
{...{
enqueueInferenceJob: this.props.enqueueInferenceJob,
@@ -3,13 +3,11 @@ import React, {useReducer } from 'react';
import { useLocalize } from "hooks";
import { FrontendInferenceJobType, InferenceJob } from '@storyteller/components/src/jobs/InferenceJob';
import { Container, ErrorMessage } from "components/common";
import { Container } from "components/common";
import PageHeader from "components/layout/PageHeader";
import PageVideoProvision from './components/pageVideoProvision';
import PageFilterControls from './components/pageFilterControls';
import { states, reducer } from "./storytellerFilterReducer";
import SubRoutes from "./storytellerFilterRoutes";
export default function StorytellerFilter(props:{
enqueueInferenceJob: (
@@ -20,37 +18,20 @@ export default function StorytellerFilter(props:{
inferenceJobsByCategory: Map<FrontendInferenceJobType, Array<InferenceJob>>;
}){
const debug=true;
const { t } = useLocalize("StorytellerFilter");
const { NO_FILE, FILE_STAGED, FILE_UPLOADING, FILE_UPLOADED, FILE_SELECTED, FILE_LOADING, FILE_LOADED } = states;
const {t} = useLocalize("StorytellerFilter");
const { NO_FILE } = states;
const [pageState, dispatchPageState] = useReducer(reducer, {
status: NO_FILE,
});
const pagePicker = () => {
switch (pageState.status){
case NO_FILE:
case FILE_STAGED:
case FILE_UPLOADING:
case FILE_SELECTED:
return <PageVideoProvision {...{debug, t, pageState, dispatchPageState}}/>
case FILE_UPLOADED:
case FILE_LOADING:
case FILE_LOADED:
return <PageFilterControls {...{debug, t, pageState, dispatchPageState}}/>
default:
return <ErrorMessage/>
}
}
const currPage = pagePicker();
return(
<Container type="panel" className="mb-5">
<PageHeader
title={t("headings.title")}
subText={t("headings.subtitle")}
// imageUrl="/images/header/video-mocap.png"
/>
{currPage}
<SubRoutes {...{debug, t, pageState, dispatchPageState}}/>
</Container>
);
}
@@ -1,8 +1,22 @@
import React from "react";
import React, {useState, useEffect} from "react";
import { useParams } from "react-router-dom";
import { useMedia } from "hooks";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
import { states, Action, State } from "../storytellerFilterReducer";
import { BasicVideo, ErrorMessage, Panel, Spinner } from "components/common";
import {
Accordion,
BasicVideo,
ErrorMessage,
NumberSliderV2,
Panel,
SelectModal,
Spinner,
TextAreaV2
} from "components/common";
import { faChevronRight } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
export default function PageFilterControls({
debug=false, t, pageState, dispatchPageState
@@ -12,11 +26,12 @@ export default function PageFilterControls({
pageState: State;
dispatchPageState: (action: Action) => void;
}) {
const { mediaToken } = useParams<any>();
useMedia({
mediaToken: pageState.mediaFileToken,
mediaToken: pageState.mediaFileToken || mediaToken,
onSuccess: (res: any) => {
// ratings.gather({ res, key: "token" });
console.log(res)
dispatchPageState({
type: 'loadFileSuccess',
payload: {mediaFile: res}
@@ -24,22 +39,135 @@ export default function PageFilterControls({
},
});
const [filterState, setFilterState] = useState({
sdModelToken: "",
loraModelToken: "",
posPrompt: "",
negPrompt: "",
firstPass: 15,
upScalePass: 15
});
useEffect(()=>{
if(debug) console.log(filterState)
},[filterState, debug])
const handleOnChange = (key: string, newValue:any,) => {
setFilterState((curr)=>({...curr, [key]: newValue}));
}
if (pageState.mediaFile){
const mediaLink = new BucketConfig().getGcsUrl(pageState.mediaFile.public_bucket_path);
if (mediaLink)
return(
<Panel>
<div className="row g-3 p-3">
{/* <h1>{t("message.fileUploaded")}</h1> */}
{debug && <p>{`File Token: ${pageState.mediaFileToken}`}</p> }
<div className="col-6">
<BasicVideo src={mediaLink} />
<>
<Panel>
<div className="row g-3 p-3">
{debug && <p>{`File Token: ${pageState.mediaFileToken}`}</p> }
<div className="col-5">
<BasicVideo src={mediaLink} />
</div>
<div className="col-1 d-flex align-items-center justify-items-center">
<FontAwesomeIcon icon={faChevronRight} className="fa-6x"/>
</div>
<div className="col-6">
<BasicVideo src={mediaLink} />
</div>
</div>
<div className="col-6">
<BasicVideo src={mediaLink} />
</div>
</div>
</Panel>
</Panel>
<Accordion className="mt-4">
<Accordion.Item title={t("heading1")}>
<div className="row g-3 p-3">
<SelectModal
modalTitle="Select a Stable Diffusion Weight"
label="Select a Stable Diffusion Weight"
onSelect={({token})=>{
handleOnChange("sdModelToken", token);
}}
tabs={[
{
label: "All Weights",
tabKey: "allWeights",
weightTypeFilter: "sdxl",
searcher: true,
type: "weights",
},
{
label: "Bookmarked",
tabKey: "bookmarkedWeights",
weightTypeFilter: "sdxl",
searcher: false,
type: "weights",
},
]}
/>
<SelectModal
modalTitle="Select a LoRA Weight"
label="Additional LoRA Weight"
onSelect={({token})=>{
console.log(`calling from select modal result select ${token}`);
handleOnChange("loraModelToken", token);
}}
tabs={[
{
label: "All LoRA Weights",
tabKey: "allLoraWeights",
weightTypeFilter: "rvc_v2",
searcher: true,
type: "weights",
},
{
label: "Bookmarked",
tabKey: "bookmarkedLoraWeights",
weightTypeFilter: "rvc_v2",
searcher: false,
type: "weights",
},
]}
/>
<TextAreaV2
{...{
label: "Prompt",
placeholder: "Enter a prompt",
onChange: (val:string)=>handleOnChange("posPrompt", val),
value: filterState.posPrompt,
required: false,
}}
/>
<TextAreaV2
{...{
label: "Negative Prompt",
placeholder: "Enter Negative Prompt",
onChange: (val:string)=>handleOnChange("negPrompt", val),
value: filterState.negPrompt,
required: false,
}}
/>
</div>
</Accordion.Item>
<Accordion.Item title={t("headings2")}>
<div className="row g-3 p-3">
<NumberSliderV2 {...{
min: 1,
max: 30,
step: 0.5,
initialValue: filterState.firstPass,
label: " Denoise First Pass",
thumbTip: "Denoise First Pass",
onChange: (val)=>{handleOnChange("firstPass",val)}
}}/>
<NumberSliderV2 {...{
min: 1,
max: 30,
initialValue: filterState.upScalePass,
label: "Denoise Upscale Pass",
thumbTip: "Denoise Upscale Pass",
onChange: (val)=>{handleOnChange("upScalePass", val)}
}}/>
<br />
</div>
</Accordion.Item>
</Accordion>
</>
);
}else if (pageState.status <= states.FILE_LOADING){
return (
@@ -1,9 +1,8 @@
import React from "react";
import { Redirect, useLocation } from "react-router-dom";
import { BasicVideo, Panel, Tabs } from "components/common";
import { Action, State } from "../storytellerFilterReducer";
import { State, Action } from "../storytellerFilterReducer";
import TabContentUpload from "./tabContentUpload";
import TabContentLibrary from "./tabContentLibrary";
@@ -15,6 +14,7 @@ export default function PageVideoProvision({
pageState: State;
dispatchPageState: (action: Action) => void;
}) {
const tabs = [
{
label: t("tabTitle.upload"),
@@ -34,12 +34,6 @@ export default function PageVideoProvision({
},
];
//pick default tab using pathname
const { pathname } = useLocation();
if (pathname === "/storyteller-filter" || pathname === "/storyteller-filter/") {
return <Redirect to={"/storyteller-filter/upload"} />;
}
return(
<Panel>
<div className="row g-0">
@@ -1,7 +1,9 @@
import SelectModal from "components/common/SelectModal/SelectModal";
import React from "react";
import { NavLink } from "react-router-dom";
import { states, Action, State } from "../storytellerFilterReducer";
import { Button } from "components/common";
import SelectModal from "components/common/SelectModal/SelectModal";
export default function TabContentLibrary({
t, pageState, dispatchPageState
}: {
@@ -9,14 +11,13 @@ export default function TabContentLibrary({
pageState: State;
dispatchPageState: (action: Action) => void;
}){
const handleProceed = ()=>{
dispatchPageState({type: "loadFile"})
}
const handleOnSelect = (token:string)=>{
const handleOnSelect = (data:{token:string, title:string})=>{
dispatchPageState({
type: "selectedFile",
payload: {mediaFileToken: token}
payload: {mediaFileToken: data.token}
});
};
return (
@@ -35,11 +36,13 @@ export default function TabContentLibrary({
},
]}
/>
{pageState.status === states.FILE_SELECTED &&
<Button
label={t("button.proceed")}
onClick={handleProceed}
/>
{pageState.status === states.FILE_SELECTED &&
<NavLink to={`load/${pageState.mediaFileToken}`}>
<Button
label={t("button.proceed")}
onClick={handleProceed}
/>
</NavLink>
}
</div>
);
@@ -1,4 +1,5 @@
import React from "react";
import { Redirect } from "react-router-dom";
import { v4 as uuidv4 } from "uuid";
import { UploadMedia } from "@storyteller/components/src/api/media_files/UploadMedia";
@@ -15,7 +16,7 @@ export default function TabContentUpload({
dispatchPageState: (action: Action) => void;
}) {
const videoProps = useFile({});
const {NO_FILE, FILE_STAGED, FILE_UPLOADING} = states;
const {NO_FILE, FILE_STAGED, FILE_UPLOADING, FILE_UPLOADED} = states;
const makeVideoUploadRequest = () => ({
uuid_idempotency_token: uuidv4(),
@@ -85,6 +86,10 @@ export default function TabContentUpload({
</div>
</div>
);
}else if (pageState.status === FILE_UPLOADED){
return(
<Redirect to={`load/${pageState.mediaFileToken}`}/>
)
}
return <ErrorMessage />;
}
@@ -28,7 +28,7 @@ export type Action =
export function reducer (state: State, action: Action): State {
console.log(action);
switch(action.type){
case 'stagedFile':
return {...state, status: states.FILE_STAGED}
@@ -0,0 +1,34 @@
import React from "react";
import {Switch, Route, useRouteMatch, Redirect} from "react-router-dom";
import PageFilterControls from "./components/pageFilterControls";
import PageVideoProvision from "./components/pageVideoProvision";
import {State,Action} from "./storytellerFilterReducer";
export default function SubRoutes ({
debug, t, pageState, dispatchPageState
}:{
debug?:boolean,
t:Function,
pageState:State,
dispatchPageState: (action: Action) => void
}){
const { path } = useRouteMatch();
return(
<Switch>
<Route exact path={`${path}/load/:mediaToken`} >
<PageFilterControls {...{debug, t, pageState, dispatchPageState}}/>
</Route>
<Route exact path={`${path}/upload`}>
<PageVideoProvision {...{debug, t, pageState, dispatchPageState}}/>
</Route>
<Route exact path={`${path}/select-media`}>
<PageVideoProvision {...{debug, t, pageState, dispatchPageState}}/>
</Route>
<Route path={`${path}`}>
<Redirect to={`${path}/upload`} />
</Route>
</Switch>
)
}
@@ -20,8 +20,8 @@ export default function TabContentLibrary({
payload: { mediaFileToken: token },
});
};
const handleOnSelect = (token: string) => {
setToken(token);
const handleOnSelect = (data:{token:string,title:string}) => {
setToken(data.token);
};
return (
@@ -1,16 +1,17 @@
import React, { useRef, useState, memo } from "react";
import {
Accordion,
Button,
Input,
Label,
NumberSlider,
Panel,
SegmentButtons,
SelectModal,
TempSelect,
TempTextArea,
} from "components/common";
import { onChanger } from "resources";
import Accordion from "components/common/Accordion";
import {
faRectangleLandscape,
faRectanglePortrait,
@@ -23,7 +24,6 @@ import {
EnqueueImageGenIsError,
} from "@storyteller/components/src/api/image_generation/EnqueueImageGen";
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
import SelectModal from "components/common/SelectModal/SelectModal";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import PremiumLock from "components/PremiumLock";
@@ -46,12 +46,11 @@ function SdInferencePanel({
const [loraToken, setLoraToken] = useState<string | null>(null);
const [weightToken, setWeightToken] = useState(sd_model_token);
const handleOnWeightSelect = (token: string) => {
setWeightToken(token);
const handleOnWeightSelect = (data:{token:string,title:string}) => {
setWeightToken(data.token);
};
const handleOnSelect = (token: string) => {
setLoraToken(token);
const handleOnSelect = (data:{token:string,title:string}) => {
setLoraToken(data.token);
};
const [isEnqueuing, setIsEnqueuing] = useState(false);