mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 10:11:31 +00:00
Merge branch 'feature/mvp' into feature/comp/playbar-w-trim
This commit is contained in:
commit
dcafbfb537
18 files changed
+445
-375
No files matched your search
@@ -46,7 +46,8 @@ class ApiConfig {
|
||||
break;
|
||||
case "storyteller.ai":
|
||||
case "staging.storyteller.ai":
|
||||
apiHost = "api.storyteller.ai";
|
||||
//apiHost = "api.storyteller.ai";
|
||||
apiHost = "api.fakeyou.com";
|
||||
break;
|
||||
case "storyteller.stream":
|
||||
// Storyteller.stream is deprecated and will be decommissioned in the future.
|
||||
@@ -61,7 +62,8 @@ class ApiConfig {
|
||||
case "devproxy.storyteller.ai":
|
||||
case "devproxy.storyteller.ai:7000":
|
||||
case "devproxy.storyteller.ai:7001":
|
||||
apiHost = "api.storyteller.ai";
|
||||
//apiHost = "api.storyteller.ai";
|
||||
apiHost = "api.fakeyou.com";
|
||||
useSsl = true;
|
||||
break;
|
||||
case "dev.fakeyou.com":
|
||||
|
||||
@@ -15,13 +15,15 @@ export default function GetApiHost() {
|
||||
return hostConfig("api.fakeyou.com");
|
||||
case "storyteller.ai":
|
||||
case "staging.storyteller.ai":
|
||||
return hostConfig("api.storyteller.ai");
|
||||
//return hostConfig("api.storyteller.ai");
|
||||
return hostConfig("api.fakeyou.com");
|
||||
case "storyteller.stream": // Storyteller.stream is deprecated and will be decommissioned in the future.
|
||||
return hostConfig("api.storyteller.stream");
|
||||
case "devproxy.fakeyou.com":
|
||||
return hostConfig("api.fakeyou.com");
|
||||
case "devproxy.storyteller.ai":
|
||||
return hostConfig("api.storyteller.ai");
|
||||
//return hostConfig("api.storyteller.ai");
|
||||
return hostConfig("api.fakeyou.com");
|
||||
case "dev.fakeyou.com":
|
||||
return hostConfig("api.dev.fakeyou.com:12345",false); // false disables SSL
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
export enum MediaFileClass {
|
||||
/// Unknown (default value)
|
||||
/// This will be present until we migrate all old files.
|
||||
Unknown = "unknown",
|
||||
|
||||
/// Audio files: wav, mp3, etc.
|
||||
Audio = "audio",
|
||||
|
||||
/// Image files: png, jpeg, etc.
|
||||
Image = "image",
|
||||
|
||||
/// Video files: mp4, etc.
|
||||
Video = "video",
|
||||
|
||||
/// Engine "animations"
|
||||
Animation = "animation",
|
||||
|
||||
/// Engine "characters"
|
||||
Character = "character",
|
||||
|
||||
/// Engine "prop" items
|
||||
Prop = "prop",
|
||||
|
||||
/// Engine scenes (internal and external scenes)
|
||||
Scene = "scene",
|
||||
}
|
||||
@@ -1,12 +1,35 @@
|
||||
import { useDocumentTitle } from "@storyteller/components/src/hooks/UseDocumentTitle"
|
||||
import { DomainConfig, Website, getCurrentDomainConfig } from "utils/domainConfig";
|
||||
|
||||
const DEFAULT_FOR_EMPTY_TITLE = "FakeYou. Deep Fake Text to Speech.";
|
||||
const FAKEYOU_DEFAULT_TITLE = "FakeYou. Deep Fake Text to Speech.";
|
||||
const FAKEYOU_SUFFIX = "FakeYou";
|
||||
|
||||
const STORYTELLER_DEFAULT_TITLE = "Storyteller.ai - AI Film and Movie Tools";
|
||||
const STORYTELLER_SUFFIX = "Storyteller.ai";
|
||||
|
||||
export function usePrefixedDocumentTitle(title?: string) {
|
||||
const domainConfig : DomainConfig = getCurrentDomainConfig();
|
||||
|
||||
const fixed = title === undefined ? "" : title.trim();
|
||||
|
||||
let defaultTitle;
|
||||
let defaultSuffix;
|
||||
|
||||
switch (domainConfig.website) {
|
||||
case Website.FakeYou:
|
||||
defaultTitle = FAKEYOU_DEFAULT_TITLE;
|
||||
defaultSuffix = FAKEYOU_SUFFIX;
|
||||
break;
|
||||
case Website.StorytellerAi:
|
||||
default:
|
||||
defaultTitle = STORYTELLER_DEFAULT_TITLE;
|
||||
defaultSuffix = STORYTELLER_SUFFIX;
|
||||
break;
|
||||
}
|
||||
|
||||
// NB: Choice of a vertical bar "|" separator is due to conserving pixels, which *might* matter to SEO.
|
||||
// I haven't fully investigated the veracity of this, nor the position of prefixes, suffixes, etc.
|
||||
// https://www.searchenginejournal.com/pipe-or-dash-in-title-tag/378099/#close
|
||||
const outputTitle = fixed.length === 0 ? DEFAULT_FOR_EMPTY_TITLE : `${fixed} | FakeYou`;
|
||||
const outputTitle = fixed.length === 0 ? defaultTitle : `${fixed} | ${defaultSuffix}`;
|
||||
useDocumentTitle(outputTitle);
|
||||
}
|
||||
@@ -4,11 +4,9 @@ import { a, useTransition } from "@react-spring/web";
|
||||
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import Iframe from "react-iframe";
|
||||
import { Label, Spinner } from "components/common";
|
||||
import { AcceptTypes, EntityInputMode, EntityModeProp } from "components/entities/EntityTypes";
|
||||
import { useFile, useMedia, useModal, useSession } from "hooks";
|
||||
import { AcceptTypes, EntityInputMode, EntityModeProp, UploaderResponse } from "components/entities/EntityTypes";
|
||||
import { useMedia, useMediaUploader, useModal, useSession } from "hooks";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
import { UploadMedia, UploadMediaResponse } from "@storyteller/components/src/api/media_files/UploadMedia";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import EntityInputEmpty from "./EntityInputEmpty";
|
||||
import "./EntityInput.scss";
|
||||
|
||||
@@ -77,22 +75,11 @@ export default function EntityInput({ accept: inAccept, aspectRatio = "square",
|
||||
onChange({ target: { name, value: token } });
|
||||
};
|
||||
|
||||
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();
|
||||
selectToken(res.media_file_token);
|
||||
}
|
||||
});
|
||||
else console.log("🥺 no file");
|
||||
}
|
||||
const { inputProps } = useMediaUploader({
|
||||
autoUpload: true,
|
||||
onSuccess: (res: UploaderResponse) => selectToken(res.media_file_token)
|
||||
});
|
||||
|
||||
const onSelect = (data: MediaFile) => {
|
||||
mediaSet(data);
|
||||
selectToken(data.token);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { AcceptTypes, EntityInputMode, MediaFilters } from "components/entities/EntityTypes";
|
||||
import { AcceptTypes, EntityInputMode, isSelectedType, MediaFilters } from "components/entities/EntityTypes";
|
||||
import { SlideProps } from "./EntityInput";
|
||||
import { MediaBrowser } from "components/modals";
|
||||
import { FileWrapper } from "components/common";
|
||||
@@ -23,26 +23,23 @@ interface EmptySlideProps extends SlideProps {
|
||||
user: any
|
||||
};
|
||||
|
||||
const mediaCheck = (value: string) => (value in MediaFilters);
|
||||
|
||||
export default function EntityInputEmpty({ accept, open, inputMode, inputProps, user, ...rest }: EmptySlideProps) {
|
||||
const accepted = accept ? accept : [];
|
||||
const isMedia = !!accepted.find(mediaCheck);
|
||||
const isMedia = isSelectedType(MediaFilters.all,accepted[0]);
|
||||
|
||||
const browserClick = () => open({
|
||||
component: MediaBrowser,
|
||||
props: { accept, inputMode, username: user?.username || "", ...rest }
|
||||
});
|
||||
|
||||
const mediaIcons = () => {
|
||||
switch (accepted[0]) {
|
||||
case "audio": return faWaveform;
|
||||
case "image": return faImage;
|
||||
case "bvh": return faPersonWalking;
|
||||
default: return faFile;
|
||||
}
|
||||
if (isSelectedType(MediaFilters.audio,accepted[0])) return faWaveform;
|
||||
if (isSelectedType(MediaFilters.engine_asset,accepted[0])) return faPersonWalking;
|
||||
if (isSelectedType(MediaFilters.image,accepted[0])) return faImage;
|
||||
if (isSelectedType(MediaFilters.video,accepted[0])) return faFile;
|
||||
return faFile;
|
||||
};
|
||||
|
||||
|
||||
const supported = `${ accepted.length ? accepted.join(", ") : accepted[0] } files supported`;
|
||||
|
||||
return <>
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { enumToKeyArr } from "resources";
|
||||
import { UploadMediaResponse } from "@storyteller/components/src/api/media_files/UploadMedia";
|
||||
import { UploadEngineAssetResponse }from "@storyteller/components/src/api/media_files/UploadEngineAsset";
|
||||
|
||||
export enum EntityInputMode {
|
||||
bookmarks,
|
||||
media,
|
||||
@@ -10,10 +14,7 @@ export enum MediaFilters {
|
||||
audio,
|
||||
image,
|
||||
video,
|
||||
bvh,
|
||||
glb,
|
||||
gltf,
|
||||
fbx
|
||||
engine_asset
|
||||
}
|
||||
|
||||
export enum WeightsFilters {
|
||||
@@ -36,19 +37,47 @@ export enum WeightsCategories {
|
||||
voiceConversion
|
||||
}
|
||||
|
||||
export type EntityModeProp = keyof typeof EntityInputMode;
|
||||
export type MediaFilterProp = keyof typeof MediaFilters;
|
||||
export type WeightFilterProp = keyof typeof WeightsFilters;
|
||||
export type WeightCategoriesProp = keyof typeof WeightsCategories;
|
||||
export type AcceptTypes = MediaFilterProp | WeightFilterProp;
|
||||
export type JobSelection = WeightCategoriesProp | WeightFilterProp;
|
||||
|
||||
export enum EntityType {
|
||||
unknown,
|
||||
media,
|
||||
weights
|
||||
}
|
||||
|
||||
export enum EngineTypes {
|
||||
bvh,
|
||||
fbx,
|
||||
glb,
|
||||
gltf,
|
||||
obj,
|
||||
ron
|
||||
}
|
||||
|
||||
export enum AudioTypes {
|
||||
mp3,
|
||||
wav
|
||||
}
|
||||
|
||||
export enum ImageTypes {
|
||||
jpg,
|
||||
png
|
||||
}
|
||||
|
||||
export enum VideoTypes {
|
||||
mp4,
|
||||
}
|
||||
|
||||
export type EntityModeProp = keyof typeof EntityInputMode;
|
||||
export type MediaFilterProp = keyof typeof MediaFilters;
|
||||
export type WeightFilterProp = keyof typeof WeightsFilters;
|
||||
export type EngineFilterProp = keyof typeof EngineTypes;
|
||||
export type AudioFilterProp = keyof typeof AudioTypes;
|
||||
export type ImageFilterProp = keyof typeof ImageTypes;
|
||||
export type VideoFilterProp = keyof typeof VideoTypes;
|
||||
export type WeightCategoriesProp = keyof typeof WeightsCategories;
|
||||
export type AcceptTypes = EngineFilterProp | AudioFilterProp | ImageFilterProp | VideoFilterProp | WeightFilterProp;
|
||||
export type JobSelection = WeightCategoriesProp | WeightFilterProp;
|
||||
export type UploaderResponse = UploadEngineAssetResponse | UploadMediaResponse;
|
||||
|
||||
export const ListEntityFilters = (mode?: number) => {
|
||||
const bookmarkFilters = Object.keys({ ...MediaFilters, ...WeightsFilters }).filter(val => isNaN(Number(val))).reduce((obj,current) => ({ ...obj, [current]: current }),{});
|
||||
|
||||
@@ -62,4 +91,25 @@ export const EntityFilterOptions = (mode?: EntityInputMode, t = (v:string) => v)
|
||||
if (typeof value === "string") return { value, label: t(value) }
|
||||
return { label: "all", value: "all" };
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const isSelectedType = (mode: MediaFilters, fileExtension: string) => enumToKeyArr([
|
||||
{ ...AudioTypes, ...ImageTypes, ...VideoTypes, ...EngineTypes },
|
||||
AudioTypes,
|
||||
ImageTypes,
|
||||
VideoTypes,
|
||||
EngineTypes
|
||||
][mode]).includes(fileExtension);
|
||||
|
||||
export const getMediaCategory = (fileExtension: string) => {
|
||||
isSelectedType(MediaFilters.image,fileExtension);
|
||||
if (isSelectedType(MediaFilters.engine_asset, fileExtension)) return MediaFilters.engine_asset;
|
||||
if (isSelectedType(MediaFilters.audio, fileExtension)) return MediaFilters.audio;
|
||||
if (isSelectedType(MediaFilters.image, fileExtension)) return MediaFilters.image;
|
||||
if (isSelectedType(MediaFilters.video, fileExtension)) return MediaFilters.video;
|
||||
return MediaFilters.all; // will change to "unknown" eventually
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ import { useInferenceJobs, useLocalize } from "hooks";
|
||||
import { Logout } from "@storyteller/components/src/api/session/Logout";
|
||||
import { Button } from "components/common";
|
||||
import { WebUrl } from "common/WebUrl";
|
||||
import { DomainConfig, Website } from "utils/domainConfig";
|
||||
import { useDomainConfig } from "context/DomainConfigContext";
|
||||
|
||||
interface SideNavProps {
|
||||
sessionWrapper: SessionWrapper;
|
||||
@@ -56,6 +58,7 @@ export default function SideNav({
|
||||
const isOnLoginPage = window.location.pathname.includes("/login");
|
||||
const isOnSignUpPage = window.location.pathname.includes("/signup");
|
||||
const isOnStudioPage = window.location.pathname.includes("/studio");
|
||||
const domain: DomainConfig = useDomainConfig();
|
||||
|
||||
let history = useHistory();
|
||||
const handleNavLinkClick = () => {
|
||||
@@ -107,7 +110,8 @@ export default function SideNav({
|
||||
|
||||
const shouldNotShowSidebar =
|
||||
(!isLoggedIn && (isOnLandingPage || isOnLoginPage || isOnSignUpPage)) ||
|
||||
isOnStudioPage;
|
||||
isOnStudioPage ||
|
||||
(domain.website === Website.StorytellerAi && isOnLandingPage);
|
||||
const shouldShowSidebar = windowWidth >= 992 && !shouldNotShowSidebar;
|
||||
const sidebarClassName = `sidebar ${
|
||||
shouldShowSidebar ? "visible" : ""
|
||||
@@ -333,7 +337,7 @@ export default function SideNav({
|
||||
<li>
|
||||
<NavLink
|
||||
exact={true}
|
||||
to="/"
|
||||
to={domain.website === Website.FakeYou ? "/" : "/dashboard"}
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
@@ -341,7 +345,7 @@ export default function SideNav({
|
||||
icon={faHome}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
Home
|
||||
{domain.website === Website.FakeYou ? "Home" : "Dashboard"}
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -14,6 +14,7 @@ import useLazyLists from './useLazyLists';
|
||||
import useListContent from './useListContent';
|
||||
import useLocalize from './useLocalize';
|
||||
import useMedia from './useMedia';
|
||||
import useMediaUploader from './useMediaUploader';
|
||||
import useModal from './useModal';
|
||||
import useNotifications from './useNotifications';
|
||||
import useOnScreen from "./useOnScreen";
|
||||
@@ -44,6 +45,7 @@ export {
|
||||
useListContent,
|
||||
useLocalize,
|
||||
useMedia,
|
||||
useMediaUploader,
|
||||
useModal,
|
||||
useNotifications,
|
||||
useOnScreen,
|
||||
|
||||
@@ -19,7 +19,7 @@ export default function useFile({ debug, onChange = n, onClear = n }: Props) {
|
||||
blobSet(inputFile ? URL.createObjectURL(inputFile) : "");
|
||||
};
|
||||
const inputChange = ({ target = {} }: { target: any }) => {
|
||||
fileChange(target.value);
|
||||
fileChange(target.files ? target.files[0] : target.value);
|
||||
};
|
||||
const clear = () => {
|
||||
if (inputRef?.current?.value) inputRef.current.value = '';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./useMediaUploader";
|
||||
@@ -0,0 +1,66 @@
|
||||
import { useState } from "react";
|
||||
import { useFile } from "hooks";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { isSelectedType, MediaFilters, UploaderResponse } from "components/entities/EntityTypes";
|
||||
import { UploadMedia } from "@storyteller/components/src/api/media_files/UploadMedia";
|
||||
import { UploadEngineAsset }from "@storyteller/components/src/api/media_files/UploadEngineAsset";
|
||||
import { MediaFileSubtype } from "@storyteller/components/src/api/enums/MediaFileSubtype";
|
||||
import { GetFileTypeByExtension as extension } from "@storyteller/components/src/utils/GetFileTypeByExtension";
|
||||
import { MediaFileClass } from "@storyteller/components/src/api/enums/MediaFileClass";
|
||||
|
||||
interface Props {
|
||||
autoUpload?: boolean,
|
||||
onSuccess?: (res: UploaderResponse) => any;
|
||||
}
|
||||
|
||||
const n = () => {};
|
||||
|
||||
export default function useMediaUploader({ autoUpload, onSuccess = n }: Props) {
|
||||
const [mediaClass,mediaClassSet] = useState<MediaFileClass>(MediaFileClass.Unknown);
|
||||
const [engineSubtype,engineSubtypeSet] = useState<MediaFileSubtype | undefined>();
|
||||
|
||||
const createUpload = (inputFile: File, todo = n ) => {
|
||||
const fileExtension = extension(inputFile.name || "");
|
||||
const isEngineAsset = isSelectedType(MediaFilters.engine_asset, fileExtension);
|
||||
const baseConfig = { uuid_idempotency_token: uuidv4(), file: inputFile };
|
||||
const engineConfig = { ...baseConfig, media_file_subtype: engineSubtype, media_file_class: mediaClass };
|
||||
const mediaConfig = { ...baseConfig, source: "file" };
|
||||
const uploader = isEngineAsset ? UploadEngineAsset(engineConfig) : UploadMedia(mediaConfig);
|
||||
|
||||
if (inputFile) {
|
||||
uploader
|
||||
.then((res: UploaderResponse) => {
|
||||
if ("media_file_token" in res) {
|
||||
onSuccess(res);
|
||||
todo();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const { file, clear, inputProps } = useFile({
|
||||
...autoUpload ? { onChange: (inputFile: File) => createUpload(inputFile,clear) } : {}
|
||||
});
|
||||
|
||||
const upload = () => createUpload(file,clear);
|
||||
|
||||
const engineSubtypeChange = ({ target }: { target: any }) => engineSubtypeSet(target.value);
|
||||
|
||||
const mediaClassChange = ({ target }: { target: any }) => mediaClassSet(target.value);
|
||||
|
||||
return {
|
||||
clear,
|
||||
engineSubtype,
|
||||
engineSubtypeChange,
|
||||
mediaClass,
|
||||
mediaClassChange,
|
||||
file,
|
||||
inputProps,
|
||||
isAudio: isSelectedType(MediaFilters.audio, extension(file?.name || "")),
|
||||
isEngineAsset: isSelectedType(MediaFilters.engine_asset, extension(file?.name || "")),
|
||||
isImage: isSelectedType(MediaFilters.image, extension(file?.name || "")),
|
||||
isVideo: isSelectedType(MediaFilters.video, extension(file?.name || "")),
|
||||
upload
|
||||
}
|
||||
|
||||
};
|
||||
@@ -127,6 +127,7 @@ import DevTTS from "./pages/dev_tts/DevTTS";
|
||||
import { StudioIntroPage } from "./pages/storyteller_studio_intro/StudioIntroPage";
|
||||
import StudioVSTPage from "./pages/storyteller_studio_intro/StudioVST/StudioVSTPage";
|
||||
import { StudioIntroResultPage } from "./pages/storyteller_studio_intro/StudioIntroResultPage";
|
||||
import DashboardPage from "./pages/dashboard/DashboardPage";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
@@ -727,6 +728,10 @@ class PageContainer extends React.Component<
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/dashboard">
|
||||
<DashboardPage sessionWrapper={this.props.sessionWrapper} />
|
||||
</Route>
|
||||
|
||||
<Route path="/about">
|
||||
<AboutPage />
|
||||
</Route>
|
||||
@@ -812,7 +817,7 @@ class PageContainer extends React.Component<
|
||||
<Route path="/dev-tts">
|
||||
<DevTTS />
|
||||
</Route>
|
||||
|
||||
|
||||
{/* Route for initial voice creation */}
|
||||
<Route exact path="/voice-designer/create">
|
||||
<VoiceDesignerFormPage
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
import Dashboard from "../landing/Dashboard";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
|
||||
import { Container } from "components/common";
|
||||
|
||||
interface DashboardPageProps {
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
export default function DashboardPage({ sessionWrapper }: DashboardPageProps) {
|
||||
usePrefixedDocumentTitle("AI Tools");
|
||||
|
||||
return (
|
||||
<Container type="panel">
|
||||
<Dashboard sessionWrapper={sessionWrapper} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,142 +1,68 @@
|
||||
import React, { useState } from "react";
|
||||
import Panel from "components/common/Panel";
|
||||
import Container from "components/common/Container";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
import { useSession } from "hooks";
|
||||
import { Button } from "components/common";
|
||||
import { useMediaUploader, useSession } from "hooks";
|
||||
import { Button, Container, Panel } from "components/common";
|
||||
import { UploaderResponse } from "components/entities/EntityTypes";
|
||||
import { faUpload } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FileType, GetFileTypeByExtension } from "@storyteller/components/src/utils/GetFileTypeByExtension";
|
||||
import { UploadMedia, UploadMediaResponse, } from "@storyteller/components/src/api/media_files/UploadMedia";
|
||||
import { UploadEngineAsset, UploadEngineAssetResponse, } from "@storyteller/components/src/api/media_files/UploadEngineAsset";
|
||||
import { MediaFileSubtype } from "@storyteller/components/src/api/enums/MediaFileSubtype";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { MediaFileClass } from "@storyteller/components/src/api/enums/MediaFileClass";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
enum UploadType {
|
||||
EngineAsset = "engine_asset",
|
||||
Image = "image",
|
||||
Audio = "audio",
|
||||
Video = "video",
|
||||
Unknown = "unknown",
|
||||
}
|
||||
|
||||
interface DevUploadProps {}
|
||||
|
||||
export default function DevUpload(props: DevUploadProps) {
|
||||
const [tokens, tokensSet] = useState<string[]>([]);
|
||||
const { engineSubtype, engineSubtypeChange, mediaClass, mediaClassChange, file, inputProps, isEngineAsset, isVideo, upload } = useMediaUploader({
|
||||
onSuccess: (res: UploaderResponse) => tokensSet([res.media_file_token, ...tokens])
|
||||
});
|
||||
|
||||
const { onChange } = inputProps;
|
||||
const { studioAccessCheck } = useSession();
|
||||
|
||||
const [file, fileSet] = useState<File | null>(null);
|
||||
const [maybeMediaFileSubtype, maybeMediaFileSubtypeSet] = useState<MediaFileSubtype | undefined>(undefined);
|
||||
const [uploadType, uploadTypeSet] = useState<UploadType>(UploadType.Unknown);
|
||||
const [tokens, tokensSet] = useState<string[]>([]);
|
||||
|
||||
const handleFileChange = (event: any) => {
|
||||
const maybeFile = event.target.files[0];
|
||||
const fileType = GetFileTypeByExtension(maybeFile?.name || "");
|
||||
|
||||
let uploadType = UploadType.Unknown;
|
||||
|
||||
switch (fileType) {
|
||||
case FileType.Bvh:
|
||||
case FileType.Fbx:
|
||||
case FileType.Glb:
|
||||
case FileType.Gltf:
|
||||
case FileType.Obj:
|
||||
case FileType.Ron:
|
||||
uploadType = UploadType.EngineAsset;
|
||||
break;
|
||||
case FileType.Jpg:
|
||||
case FileType.Png:
|
||||
uploadType = UploadType.Image;
|
||||
break;
|
||||
case FileType.Mp3:
|
||||
case FileType.Wav:
|
||||
uploadType = UploadType.Audio;
|
||||
break;
|
||||
case FileType.Mp4:
|
||||
uploadType = UploadType.Video;
|
||||
break;
|
||||
case FileType.Unknown:
|
||||
default:
|
||||
uploadType = UploadType.Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
fileSet(maybeFile);
|
||||
uploadTypeSet(uploadType);
|
||||
};
|
||||
|
||||
const handleSubtypeChange = (ev: React.FormEvent<HTMLSelectElement>) => {
|
||||
const value = (ev.target as HTMLSelectElement).value;
|
||||
const maybeSubtype = value as MediaFileSubtype;
|
||||
maybeMediaFileSubtypeSet(maybeSubtype);
|
||||
}
|
||||
|
||||
const handleUpload = () => {
|
||||
if (!file) {
|
||||
console.error("no file specified for upload");
|
||||
return;
|
||||
}
|
||||
|
||||
switch(uploadType) {
|
||||
case UploadType.EngineAsset:
|
||||
UploadEngineAsset({
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
file,
|
||||
media_file_subtype: maybeMediaFileSubtype,
|
||||
})
|
||||
.then((res: UploadEngineAssetResponse) => {
|
||||
if ("media_file_token" in res) {
|
||||
tokensSet([res.media_file_token, ...tokens]);
|
||||
fileSet(null);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
UploadMedia({
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
file,
|
||||
source: "file",
|
||||
})
|
||||
.then((res: UploadMediaResponse) => {
|
||||
if ("media_file_token" in res) {
|
||||
tokensSet([res.media_file_token, ...tokens]);
|
||||
fileSet(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let title = "Upload Generic File";
|
||||
let mediaFileSubtypeForm = <></>;
|
||||
let engineSubForm = <></>;
|
||||
|
||||
switch (uploadType) {
|
||||
case UploadType.Video:
|
||||
title = "Upload Video";
|
||||
break;
|
||||
|
||||
case UploadType.EngineAsset:
|
||||
title = "Upload Engine Asset";
|
||||
mediaFileSubtypeForm = (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="fileSubtypeSelect" className="form-label">Engine Media File Subtype</label>
|
||||
<select
|
||||
onChange={handleSubtypeChange}
|
||||
className="form-select"
|
||||
aria-label="Default select example"
|
||||
id="fileSubtypeSelect"
|
||||
value={maybeMediaFileSubtype}
|
||||
>
|
||||
<option value={MediaFileSubtype.SceneImport}>Scene Import (default)</option>
|
||||
<option value={MediaFileSubtype.Mixamo}>Mixamo Animation</option>
|
||||
<option value={MediaFileSubtype.StorytellerScene}>Storyteller Scene</option>
|
||||
<option value={MediaFileSubtype.AnimationOnly}>Animation Only</option>
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
if (isVideo) { title = "Upload Video"; }
|
||||
if (isEngineAsset) {
|
||||
title = "Upload Engine Asset";
|
||||
engineSubForm = (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="fileClassSelect" className="form-label">Media Class</label>
|
||||
<select
|
||||
onChange={mediaClassChange}
|
||||
className="form-select"
|
||||
aria-label="Default select example"
|
||||
id="fileClassSelect"
|
||||
value={mediaClass}
|
||||
>
|
||||
<option value={MediaFileClass.Unknown}>Unknown</option>
|
||||
<option value={MediaFileClass.Audio}>Audio</option>
|
||||
<option value={MediaFileClass.Image}>Image</option>
|
||||
<option value={MediaFileClass.Video}>Video</option>
|
||||
<option value={MediaFileClass.Animation}>Animation</option>
|
||||
<option value={MediaFileClass.Character}>Character</option>
|
||||
<option value={MediaFileClass.Prop}>Prop</option>
|
||||
<option value={MediaFileClass.Scene}>Scene</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="fileSubtypeSelect" className="form-label">Engine Media File Subtype</label>
|
||||
<select
|
||||
onChange={engineSubtypeChange}
|
||||
className="form-select"
|
||||
aria-label="Default select example"
|
||||
id="fileSubtypeSelect"
|
||||
value={engineSubtype}
|
||||
>
|
||||
<option value={MediaFileSubtype.SceneImport}>Scene Import (default)</option>
|
||||
<option value={MediaFileSubtype.Mixamo}>Mixamo Animation</option>
|
||||
<option value={MediaFileSubtype.StorytellerScene}>Storyteller Scene</option>
|
||||
<option value={MediaFileSubtype.AnimationOnly}>Animation Only</option>
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return studioAccessCheck(
|
||||
@@ -146,7 +72,7 @@ export default function DevUpload(props: DevUploadProps) {
|
||||
subText="Upload files to the server for testing."
|
||||
/>
|
||||
|
||||
{maybeMediaFileSubtype}
|
||||
{engineSubtype}
|
||||
|
||||
<Panel padding={true}>
|
||||
<div className="d-flex flex-column gap-5">
|
||||
@@ -154,22 +80,22 @@ export default function DevUpload(props: DevUploadProps) {
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="formFile" className="form-label">Select File for Upload</label>
|
||||
<input
|
||||
className="form-control form-control-lg"
|
||||
id="formFile"
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
<input {...{
|
||||
onChange,
|
||||
className: "form-control form-control-lg",
|
||||
type: "file",
|
||||
id: "formFile"
|
||||
}}/>
|
||||
</div>
|
||||
|
||||
{mediaFileSubtypeForm}
|
||||
{engineSubForm}
|
||||
|
||||
<div className="d-flex gap-3 justify-content-end">
|
||||
<Button
|
||||
disabled={!file}
|
||||
icon={faUpload}
|
||||
label="Upload Media"
|
||||
onClick={handleUpload}
|
||||
onClick={upload}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
faArrowRightArrowLeft,
|
||||
faFile,
|
||||
faTrashAlt,
|
||||
// faFile,
|
||||
// faTrashAlt,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Button, Container, Panel } from "components/common";
|
||||
import FileInput from "components/common/FileInput";
|
||||
// import FileInput from "components/common/FileInput";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
import { useFile, useInferenceJobs, useMedia } from "hooks";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useHistory, useParams } from "react-router-dom";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { UploadMedia } from "@storyteller/components/src/api/media_files/UploadMedia";
|
||||
// import { useHistory, useParams } from "react-router-dom";
|
||||
// import { v4 as uuidv4 } from "uuid";
|
||||
// import { UploadMedia } from "@storyteller/components/src/api/media_files/UploadMedia";
|
||||
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import FbxToGltfJobList from "./components/FbxToGltfJobList";
|
||||
import {
|
||||
EnqueueFbxToGltf,
|
||||
EnqueueFbxToGltfIsSuccess,
|
||||
EnqueueFbxToGltfIsError,
|
||||
// EnqueueFbxToGltf,
|
||||
// EnqueueFbxToGltfIsSuccess,
|
||||
// EnqueueFbxToGltfIsError,
|
||||
} from "@storyteller/components/src/api/file_conversion/EnqueueFbxToGltf";
|
||||
import { onChanger } from "resources";
|
||||
|
||||
import { EntityInput } from "components/entities";
|
||||
|
||||
interface FbxToGltfPageProps {
|
||||
enqueueInferenceJob: (
|
||||
@@ -30,117 +32,39 @@ interface FbxToGltfPageProps {
|
||||
export default function FbxToGltfPage({
|
||||
enqueueInferenceJob,
|
||||
}: FbxToGltfPageProps) {
|
||||
const history = useHistory();
|
||||
const { mediaToken: mediaTokenParam } = useParams<{ mediaToken: string }>();
|
||||
const [mediaToken, setMediaToken] = useState<string | null>(mediaTokenParam);
|
||||
const { media: presetFile } = useMedia({
|
||||
mediaToken: mediaToken ?? undefined,
|
||||
});
|
||||
const fileProps = useFile({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isWorking, setIsWorking] = useState(false);
|
||||
const [mediaToken,mediaTokenSet] = useState();
|
||||
const onChange = onChanger({ mediaTokenSet });
|
||||
|
||||
const { inferenceJobs } = useInferenceJobs(
|
||||
FrontendInferenceJobType.ConvertFbxtoGltf
|
||||
);
|
||||
// const EnqueueConvert = async ({ upload_token }: any) => {
|
||||
// if (!upload_token) return false;
|
||||
|
||||
console.log("inferenceJobs", inferenceJobs);
|
||||
// try {
|
||||
// let request = {
|
||||
// uuid_idempotency_token: uuidv4(),
|
||||
// file_source: undefined,
|
||||
// media_file_token: upload_token,
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
// If there's a mediaToken, automatically prepare the file for conversion
|
||||
if (mediaToken && presetFile) {
|
||||
}
|
||||
}, [mediaToken, presetFile]);
|
||||
// const response = await EnqueueFbxToGltf(request);
|
||||
|
||||
const clearMediaToken = () => {
|
||||
// Reset the media token state
|
||||
setMediaToken(null);
|
||||
history.push("/fbx-to-gltf");
|
||||
};
|
||||
// if (EnqueueFbxToGltfIsSuccess(response)) {
|
||||
// console.log("Enqueue successful");
|
||||
|
||||
const makeFBXUploadRequest = () => ({
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
file: fileProps.file,
|
||||
source: "file",
|
||||
type: "fbx/model",
|
||||
});
|
||||
|
||||
const handleUploadFBX = async () => {
|
||||
try {
|
||||
const res = await UploadMedia(makeFBXUploadRequest());
|
||||
if (res.success && res.media_file_token) {
|
||||
return { upload_token: res.media_file_token };
|
||||
}
|
||||
console.error("Upload failed:", res);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("Error in upload:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
if (isSubmitting) return;
|
||||
setIsSubmitting(true);
|
||||
setIsWorking(true);
|
||||
try {
|
||||
let uploadToken = mediaToken;
|
||||
|
||||
// If no media token, upload the file first
|
||||
if (!mediaToken && fileProps.file) {
|
||||
const uploadResponse = await handleUploadFBX();
|
||||
if (uploadResponse && uploadResponse.upload_token) {
|
||||
uploadToken = uploadResponse.upload_token;
|
||||
} else {
|
||||
console.error("Failed to upload file. Response:", uploadResponse);
|
||||
throw new Error("File upload failed");
|
||||
}
|
||||
}
|
||||
|
||||
// Enqueue the conversion job
|
||||
if (uploadToken) {
|
||||
await EnqueueConvert({ upload_token: uploadToken });
|
||||
} else {
|
||||
console.error("No upload token available for conversion");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in submit process: ", error);
|
||||
} finally {
|
||||
setIsWorking(false);
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const EnqueueConvert = async ({ upload_token }: any) => {
|
||||
if (!upload_token) return false;
|
||||
|
||||
try {
|
||||
let request = {
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
file_source: undefined,
|
||||
media_file_token: upload_token,
|
||||
};
|
||||
|
||||
const response = await EnqueueFbxToGltf(request);
|
||||
|
||||
if (EnqueueFbxToGltfIsSuccess(response)) {
|
||||
console.log("Enqueue successful");
|
||||
|
||||
if (response && response.inference_job_token) {
|
||||
enqueueInferenceJob(
|
||||
response.inference_job_token,
|
||||
FrontendInferenceJobType.ConvertFbxtoGltf
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} else if (EnqueueFbxToGltfIsError(response)) {
|
||||
throw new Error("Enqueue failed");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in enqueueing conversion: ", error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// if (response && response.inference_job_token) {
|
||||
// enqueueInferenceJob(
|
||||
// response.inference_job_token,
|
||||
// FrontendInferenceJobType.ConvertFbxtoGltf
|
||||
// );
|
||||
// }
|
||||
// return true;
|
||||
// } else if (EnqueueFbxToGltfIsError(response)) {
|
||||
// throw new Error("Enqueue failed");
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error("Error in enqueueing conversion: ", error);
|
||||
// return false;
|
||||
// }
|
||||
// };
|
||||
|
||||
return (
|
||||
<Container type="panel">
|
||||
@@ -156,47 +80,59 @@ export default function FbxToGltfPage({
|
||||
|
||||
<Panel padding={true}>
|
||||
<div className="d-flex flex-column gap-3">
|
||||
{mediaToken && presetFile ? (
|
||||
<div>
|
||||
<label className="sub-title">FBX file from media</label>
|
||||
<Panel className="panel-inner p-3 rounded">
|
||||
<div className="d-flex gap-3 align-items-center flex-wrap">
|
||||
<div className="d-flex gap-3 flex-grow-1 align-items-center">
|
||||
<FontAwesomeIcon icon={faFile} className="display-6" />
|
||||
<div>
|
||||
<h6 className="mb-1">{presetFile.token}</h6>
|
||||
<p className="opacity-75">
|
||||
Created by {presetFile.maybe_creator_user?.display_name}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
icon={faTrashAlt}
|
||||
square={true}
|
||||
onClick={clearMediaToken}
|
||||
variant="danger"
|
||||
small={true}
|
||||
tooltip="Remove file"
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
</div>
|
||||
) : (
|
||||
<FileInput
|
||||
{...fileProps}
|
||||
label="Select FBX File"
|
||||
fileTypes={["FBX"]}
|
||||
mediaToken={mediaToken}
|
||||
/>
|
||||
)}
|
||||
<EntityInput {...{
|
||||
accept: ["fbx"],
|
||||
aspectRatio: "landscape",
|
||||
label: "Choose FBX file",
|
||||
name: "mediaToken",
|
||||
onChange,
|
||||
// owner: "echelon",
|
||||
type: "media"
|
||||
}}/>
|
||||
{
|
||||
|
||||
// mediaToken && presetFile ? (
|
||||
// <div>
|
||||
// <label className="sub-title">FBX file from media</label>
|
||||
// <Panel className="panel-inner p-3 rounded">
|
||||
// <div className="d-flex gap-3 align-items-center flex-wrap">
|
||||
// <div className="d-flex gap-3 flex-grow-1 align-items-center">
|
||||
// <FontAwesomeIcon icon={faFile} className="display-6" />
|
||||
// <div>
|
||||
// <h6 className="mb-1">{presetFile.token}</h6>
|
||||
// <p className="opacity-75">
|
||||
// Created by {presetFile.maybe_creator_user?.display_name}
|
||||
// </p>
|
||||
// </div>
|
||||
// </div>
|
||||
// <Button
|
||||
// icon={faTrashAlt}
|
||||
// square={true}
|
||||
// onClick={clearMediaToken}
|
||||
// variant="danger"
|
||||
// small={true}
|
||||
// tooltip="Remove file"
|
||||
// />
|
||||
// </div>
|
||||
// </Panel>
|
||||
// </div>
|
||||
// ) : (
|
||||
// <FileInput
|
||||
// {...fileProps}
|
||||
// label="Select FBX File"
|
||||
// fileTypes={["FBX"]}
|
||||
// mediaToken={mediaToken}
|
||||
// />
|
||||
// )
|
||||
}
|
||||
|
||||
<div className="d-flex justify-content-end">
|
||||
<Button
|
||||
icon={faArrowRightArrowLeft}
|
||||
label="Convert to glTF"
|
||||
onClick={submit}
|
||||
disabled={!mediaToken && !fileProps.file}
|
||||
isLoading={isWorking}
|
||||
// onClick={submit}
|
||||
disabled={!mediaToken}
|
||||
isLoading={false} // REPLACE
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -35,20 +35,34 @@ interface Props {
|
||||
}
|
||||
|
||||
function LandingPage(props: Props) {
|
||||
usePrefixedDocumentTitle("FakeYou Celebrity Voice Generator");
|
||||
|
||||
PosthogClient.recordPageview();
|
||||
|
||||
const domain: DomainConfig = useDomainConfig();
|
||||
|
||||
const webpageTitle =
|
||||
domain.website === Website.FakeYou
|
||||
? "FakeYou Celebrity Voice Generator"
|
||||
: "";
|
||||
|
||||
usePrefixedDocumentTitle(webpageTitle);
|
||||
|
||||
const isLoggedIn = props.sessionWrapper.isLoggedIn();
|
||||
|
||||
const canAccessStudio = props.sessionWrapper.canAccessStudio();
|
||||
// DO NOT LEAK THIS YET!!
|
||||
let protectedStudioOnboarding = <></>;
|
||||
|
||||
if (props.sessionWrapper.canAccessStudio()) {
|
||||
protectedStudioOnboarding = (
|
||||
<>
|
||||
<OnboardingSelection />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{domain.website === Website.StorytellerAi && !isLoggedIn && (
|
||||
<LandingVideoReel />
|
||||
{domain.website === Website.StorytellerAi && (
|
||||
<LandingVideoReel sessionWrapper={props.sessionWrapper} />
|
||||
)}
|
||||
<Container type="panel">
|
||||
{domain.website === Website.FakeYou ? (
|
||||
@@ -79,27 +93,22 @@ function LandingPage(props: Props) {
|
||||
) : (
|
||||
<>
|
||||
{/* STORYTELLER,AI */}
|
||||
{!isLoggedIn ? (
|
||||
<>
|
||||
{!canAccessStudio && <OnboardingSelection />}
|
||||
<VstSectionV2 />
|
||||
<TtsDemoSection
|
||||
sessionWrapper={props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
props.sessionSubscriptionsWrapper
|
||||
}
|
||||
inferenceJobs={props.inferenceJobs}
|
||||
ttsInferenceJobs={props.ttsInferenceJobs}
|
||||
enqueueInferenceJob={props.enqueueInferenceJob}
|
||||
inferenceJobsByCategory={props.inferenceJobsByCategory}
|
||||
enqueueTtsJob={props.enqueueTtsJob}
|
||||
/>
|
||||
<Dashboard sessionWrapper={props.sessionWrapper} />
|
||||
<FakeYouLandingBody />
|
||||
</>
|
||||
) : (
|
||||
<Dashboard sessionWrapper={props.sessionWrapper} />
|
||||
)}
|
||||
|
||||
{protectedStudioOnboarding}
|
||||
<VstSectionV2 />
|
||||
<TtsDemoSection
|
||||
sessionWrapper={props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={props.sessionSubscriptionsWrapper}
|
||||
inferenceJobs={props.inferenceJobs}
|
||||
ttsInferenceJobs={props.ttsInferenceJobs}
|
||||
enqueueInferenceJob={props.enqueueInferenceJob}
|
||||
inferenceJobsByCategory={props.inferenceJobsByCategory}
|
||||
enqueueTtsJob={props.enqueueTtsJob}
|
||||
/>
|
||||
|
||||
<Dashboard sessionWrapper={props.sessionWrapper} />
|
||||
|
||||
<FakeYouLandingBody />
|
||||
</>
|
||||
)}
|
||||
</Container>
|
||||
|
||||
+21
-7
@@ -1,12 +1,26 @@
|
||||
import { faArrowRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { Button } from "components/common";
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./LandingVideoReel.scss";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
interface LandingVideoReelProps {}
|
||||
interface LandingVideoReelProps {
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
export default function LandingVideoReel(props: LandingVideoReelProps) {
|
||||
const isOnLandingPage = window.location.pathname === "/";
|
||||
export default function LandingVideoReel({
|
||||
sessionWrapper,
|
||||
}: LandingVideoReelProps) {
|
||||
const location = useLocation();
|
||||
const [isOnLandingPage, setIsOnLandingPage] = useState(
|
||||
window.location.pathname === "/"
|
||||
);
|
||||
const isLoggedIn = sessionWrapper.isLoggedIn();
|
||||
|
||||
useEffect(() => {
|
||||
setIsOnLandingPage(location.pathname === "/");
|
||||
}, [location]);
|
||||
|
||||
useEffect(() => {
|
||||
const contentWrapper = document.getElementById("page-content-wrapper");
|
||||
@@ -41,7 +55,7 @@ export default function LandingVideoReel(props: LandingVideoReelProps) {
|
||||
searchBar?.classList.remove("landing-search");
|
||||
contentWrapper?.classList.remove("no-page-padding-top");
|
||||
};
|
||||
}, [isOnLandingPage]);
|
||||
}, [isOnLandingPage, isLoggedIn]);
|
||||
|
||||
return (
|
||||
<div className="storyteller-landing-reel">
|
||||
@@ -62,8 +76,8 @@ export default function LandingVideoReel(props: LandingVideoReelProps) {
|
||||
<Button
|
||||
icon={faArrowRight}
|
||||
iconFlip={true}
|
||||
label="Sign Up Now"
|
||||
to="/signup"
|
||||
label={isLoggedIn ? "Explore AI Tools" : "Sign Up Now"}
|
||||
to={isLoggedIn ? "/dashboard" : "/signup"}
|
||||
className="mt-4"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user