diff --git a/packages/components/src/jobs/InferenceJob.ts b/packages/components/src/jobs/InferenceJob.ts index eee50ef525..dc099f2b94 100644 --- a/packages/components/src/jobs/InferenceJob.ts +++ b/packages/components/src/jobs/InferenceJob.ts @@ -16,6 +16,10 @@ export enum FrontendInferenceJobType { VideoStyleTransfer, } +export enum AllInferenceJobs { All } + +export type JobListOptions = FrontendInferenceJobType | AllInferenceJobs; + // NB: Many of these fields are optional despite the response payload containing them // This is because we create temporary placeholder objects with just the token. export class InferenceJob { diff --git a/packages/fakeyou.com/public/images/landing/onboarding/room-scene-thumbnail.webp b/packages/fakeyou.com/public/images/landing/onboarding/room-scene-thumbnail.webp new file mode 100644 index 0000000000..7c2f9f44b5 Binary files /dev/null and b/packages/fakeyou.com/public/images/landing/onboarding/room-scene-thumbnail.webp differ diff --git a/packages/fakeyou.com/public/images/landing/onboarding/styles/style-2d-anime.webp b/packages/fakeyou.com/public/images/landing/onboarding/styles/style-2d-anime.webp new file mode 100644 index 0000000000..ee7117b0fc Binary files /dev/null and b/packages/fakeyou.com/public/images/landing/onboarding/styles/style-2d-anime.webp differ diff --git a/packages/fakeyou.com/public/images/landing/onboarding/styles/style-3d-cartoon.webp b/packages/fakeyou.com/public/images/landing/onboarding/styles/style-3d-cartoon.webp new file mode 100644 index 0000000000..823a8dc322 Binary files /dev/null and b/packages/fakeyou.com/public/images/landing/onboarding/styles/style-3d-cartoon.webp differ diff --git a/packages/fakeyou.com/public/images/landing/onboarding/styles/style-ink-bw.webp b/packages/fakeyou.com/public/images/landing/onboarding/styles/style-ink-bw.webp new file mode 100644 index 0000000000..1ddc373609 Binary files /dev/null and b/packages/fakeyou.com/public/images/landing/onboarding/styles/style-ink-bw.webp differ diff --git a/packages/fakeyou.com/public/images/landing/onboarding/styles/style-origami.webp b/packages/fakeyou.com/public/images/landing/onboarding/styles/style-origami.webp new file mode 100644 index 0000000000..edc2050967 Binary files /dev/null and b/packages/fakeyou.com/public/images/landing/onboarding/styles/style-origami.webp differ diff --git a/packages/fakeyou.com/public/videos/landing/vst/1-original.jpg b/packages/fakeyou.com/public/videos/landing/vst/1-original.jpg new file mode 100644 index 0000000000..30486e43b9 Binary files /dev/null and b/packages/fakeyou.com/public/videos/landing/vst/1-original.jpg differ diff --git a/packages/fakeyou.com/public/videos/landing/vst/2-original.jpg b/packages/fakeyou.com/public/videos/landing/vst/2-original.jpg new file mode 100644 index 0000000000..354b0b04d1 Binary files /dev/null and b/packages/fakeyou.com/public/videos/landing/vst/2-original.jpg differ diff --git a/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.tsx b/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.tsx index 1cc71914df..520f518c40 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.tsx +++ b/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.tsx @@ -26,6 +26,12 @@ interface Scene3DProps { // CSS classes to apply to the engine className?: string; + // Callback when the scene is first loaded + // Not always 100% true. Sometimes it prematurely fires. + onSceneReadyCallback?: () => void; + + // Callback called when the scene is saved. + // It will receive the argument of the new scene media token. onSceneSavedCallback?: (mediaToken: string) => void; } @@ -35,6 +41,7 @@ export default function Scene3D({ skybox, fullScreen = false, className, + onSceneReadyCallback, onSceneSavedCallback, }: Scene3DProps) { @@ -54,6 +61,10 @@ export default function Scene3D({ console.log("studio-ready message (2)"); + if (onSceneReadyCallback !== undefined) { + onSceneReadyCallback(); + } + // NB: Example of how to call the API in the other direction: // studio.postMessage("save-scene", engineBaseUrl); } else if ( @@ -70,7 +81,7 @@ export default function Scene3D({ } else if (event.data === "scene-save-failed") { console.error("Failed to save the scene!"); } - }, [onSceneSavedCallback]); + }, [onSceneReadyCallback, onSceneSavedCallback]); useEffect(() => { diff --git a/packages/fakeyou.com/src/components/common/SelectionBubblesV2/SelectionBubblesV2.scss b/packages/fakeyou.com/src/components/common/SelectionBubblesV2/SelectionBubblesV2.scss index f5c1fbdeeb..aa3af4de85 100644 --- a/packages/fakeyou.com/src/components/common/SelectionBubblesV2/SelectionBubblesV2.scss +++ b/packages/fakeyou.com/src/components/common/SelectionBubblesV2/SelectionBubblesV2.scss @@ -129,6 +129,7 @@ bottom: 0; left: 0; font-size: 14px; + text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5); } &::after { @@ -140,14 +141,12 @@ height: 50%; background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent); z-index: 2; + height: 35%; } &.selected::after { - background: linear-gradient( - to top, - rgba(desaturate(darken($primary, 20%), 30%), 70%), - transparent - ); + background: linear-gradient(to top, rgba(#632323, 90%), transparent); + height: 35%; } } diff --git a/packages/fakeyou.com/src/components/layout/InferenceJobsList/InferenceJobsList.tsx b/packages/fakeyou.com/src/components/layout/InferenceJobsList/InferenceJobsList.tsx index 3e8da898f9..c7f31f7255 100644 --- a/packages/fakeyou.com/src/components/layout/InferenceJobsList/InferenceJobsList.tsx +++ b/packages/fakeyou.com/src/components/layout/InferenceJobsList/InferenceJobsList.tsx @@ -3,7 +3,6 @@ import { FrontendInferenceJobType, InferenceJob } from "@storyteller/components/ // import { useTransition } from "@react-spring/web"; import JobItem from "./JobItem"; import { useInferenceJobs, useLocalize, useSession } from "hooks"; -import { JobListTypes } from "hooks/useInferenceJobs/useInferenceJobs"; import "./InferenceJobsList.scss"; import { Button, Panel, JobQueueTicker } from "components/common"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -12,7 +11,6 @@ import { faClipboardList } from "@fortawesome/pro-solid-svg-icons"; interface JobsListProps { failures: (fail: string) => string; jobType?: FrontendInferenceJobType; - value?: JobListTypes; onSelect?: (e: any) => any; panel?: boolean; showJobQueue?: boolean; @@ -34,21 +32,15 @@ const resultPaths = { export default function InferenceJobsList({ failures, jobType, - value, onSelect, panel = true, showHeader = true, showJobQueue = false, showNoJobs = false }: JobsListProps) { - const jobValue = value !== undefined ? value : jobType !== undefined ? (jobType || 0) + 1 : 0; - // undefined specified here to allow 0. - // jobType + 1 because the difference between FrontendInferenceJobType and JobListTypes is an "all" option - const { sessionSubscriptions } = useSession(); const hasPaidFeatures = sessionSubscriptions?.hasPaidFeatures(); - const { inferenceJobs = [], jobStatusDescription } = useInferenceJobs(jobValue); - + const { inferenceJobs = [], jobStatusDescription } = useInferenceJobs(jobType); const { t } = useLocalize("InferenceJobs"); const jobContent = ( diff --git a/packages/fakeyou.com/src/components/modals/InferenceJobsModal/InferenceJobsModal.tsx b/packages/fakeyou.com/src/components/modals/InferenceJobsModal/InferenceJobsModal.tsx index cea502693c..ae754acc12 100644 --- a/packages/fakeyou.com/src/components/modals/InferenceJobsModal/InferenceJobsModal.tsx +++ b/packages/fakeyou.com/src/components/modals/InferenceJobsModal/InferenceJobsModal.tsx @@ -1,10 +1,10 @@ -import React from "react"; // useState +import React, { useState } from "react"; // useState import InferenceJobsList from "components/layout/InferenceJobsList"; import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob"; -// import { TempSelect as Select } from "components/common"; -// import { enumToKeyArr } from "resources"; +import { TempSelect as Select } from "components/common"; +import { enumToKeyArr } from "resources"; import ModalHeader from "../ModalHeader"; -import { useLocalize } from "hooks"; +import { useLocalize, useSession } from "hooks"; interface Props { handleClose?: any; @@ -18,18 +18,17 @@ export default function InferenceJobsModal({ showModalHeader = true, ...rest }: Props) { - // const presetFilter = enumToKeyArr(FrontendInferenceJobType)[inJobType]; - // const [jobType,jobTypeSet] = useState(inJobType > -1 ? presetFilter : "All"); - // const typeObj = ["All", ...Object.values(FrontendInferenceJobType)]; - - // const options = typeObj.filter(val => isNaN(Number(val))) - // .map((value) => { - // if (typeof value === "string") return { value, label: value } - // return { label: "", value: "" }; - // }); - + const { canAccessStudio } = useSession(); + const presetFilter = enumToKeyArr(FrontendInferenceJobType)[inJobType]; + const [jobType,jobTypeSet] = useState(inJobType > -1 ? presetFilter : "All"); + const typeObj = ["All", ...Object.values(FrontendInferenceJobType)]; + const options = typeObj.filter(val => isNaN(Number(val))) + .map((value) => { + if (typeof value === "string") return { value, label: value } + return { label: "", value: "" }; + }); const { t } = useLocalize("InferenceJobs"); - + const selectedType = typeObj.indexOf(jobType) - 1; const failures = (fail = "") => { switch (fail) { default: @@ -40,8 +39,8 @@ export default function InferenceJobsModal({ return ( <> { showModalHeader && } - { - // jobTypeSet(target.value), options, value: jobType }} /> } { if (handleClose) handleClose(); }, - value: 0, // fixed for now - // value: typeObj.indexOf(jobType), + ...selectedType > -1 ? { jobType: selectedType } : {}, showHeader: false, showJobQueue: true, showNoJobs: true, diff --git a/packages/fakeyou.com/src/components/providers/CoreServicesProvider/ServerStatusChecker.tsx b/packages/fakeyou.com/src/components/providers/CoreServicesProvider/ServerStatusChecker.tsx index 42556a5a31..8b0e400c44 100644 --- a/packages/fakeyou.com/src/components/providers/CoreServicesProvider/ServerStatusChecker.tsx +++ b/packages/fakeyou.com/src/components/providers/CoreServicesProvider/ServerStatusChecker.tsx @@ -6,6 +6,5 @@ interface Props { export default function ServerStatusChecker({ value }: Props) { useStatusPoll(); - console.log("🤬",); return null; }; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/providers/SessionProvider/SessionProvider.tsx b/packages/fakeyou.com/src/components/providers/SessionProvider/SessionProvider.tsx index 526c8f7558..c7b12e9035 100644 --- a/packages/fakeyou.com/src/components/providers/SessionProvider/SessionProvider.tsx +++ b/packages/fakeyou.com/src/components/providers/SessionProvider/SessionProvider.tsx @@ -1,19 +1,70 @@ -import React, { useState } from "react"; -import { SessionContext } from "context"; +import React, { createContext, useState } from "react"; +// import { SessionContext } from "context"; import { Modal } from "components/common"; import AccountModal from "components/layout/AccountModal"; -import { ModalView } from "context/SessionContext"; +// import { ModalView } from "context/SessionContext"; import { StudioNotAvailable } from "v2/view/_common/StudioNotAvailable"; +import { StudioRolloutHostnameAllowed } from "@storyteller/components/src/utils/StudioRolloutHostnameAllowed"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper"; + +export enum ModalView { // ignore this modal stuff for now -V + Closed, + Signup, + Login, +} + +interface ModalProps { // this too + close: () => void; + open: () => void; + view: ModalView; +} + +interface SessionContextType { + canAccessStudio: () => boolean; + canEditTtsModel: (token: string) => boolean; + canBanUsers: () => boolean; + check: () => boolean; + loggedIn: boolean; + modal: ModalProps; + querySession?: any; + querySubscriptions?: any; + sessionFetched: boolean; + sessionSubscriptions?: any; + studioAccessCheck: (x:any) => any, + user?: any; + userTokenMatch: (token: string) => boolean; +} interface Props { children?: any; - querySession: any; - querySubscriptions: any; + querySession: () => void; + querySubscriptions: () => void; sessionFetched: boolean; - sessionSubscriptions: any; - sessionWrapper?: any; + sessionSubscriptions: SessionSubscriptionsWrapper; + sessionWrapper: SessionWrapper; } +// Functions are initially No-ops/dummies so that they are never undefined and never have to be called conditionally. +// These functions will never actually fire because they are immediately redefined in the provider below, +// as they must be as they utilize the provider's state. + +export const SessionContext = createContext({ + canAccessStudio: () => false, + canEditTtsModel: () => false, + canBanUsers: () => false, + check: () => false, + loggedIn: false, + sessionFetched: false, + studioAccessCheck: () => null, + modal: { + close: () => {}, + open: () => {}, + view: ModalView.Closed, + }, + userTokenMatch: () => false, +}); + export default function SessionProvider({ children, querySession, @@ -22,19 +73,12 @@ export default function SessionProvider({ sessionSubscriptions, sessionWrapper, }: Props) { - const sessionResponse = sessionWrapper?.sessionStateResponse || { - logged_in: false, - }; + const sessionResponse = sessionWrapper?.sessionStateResponse || { logged_in: false, user: null }; const { logged_in: loggedIn, user } = sessionResponse; const [view, viewSet] = useState(ModalView.Closed); const open = () => viewSet(ModalView.Signup); - const close = () => { - viewSet(ModalView.Closed); - }; - const viewSwitch = () => - view === ModalView.Signup - ? viewSet(ModalView.Login) - : viewSet(ModalView.Signup); + const close = () => { viewSet(ModalView.Closed); }; + const viewSwitch = () => viewSet(view === ModalView.Signup ? ModalView.Login : ModalView.Signup); const check = () => { if (user) { return true; @@ -43,23 +87,31 @@ export default function SessionProvider({ return false; } }; - const userTokenMatch = (otherUserToken: string) => !otherUserToken || !user?.user_token ? false : user.user_token === otherUserToken; const canEditTtsModel = (userToken: string) => - user?.canEditOtherUsersTtsModels || userTokenMatch(userToken); + user?.can_delete_other_users_tts_models || userTokenMatch(userToken); const canBanUsers = () => user?.can_ban_users || false; - const studioAccessCheck = (content: React.ElementType) => !sessionWrapper?.canAccessStudio() ? : content; + const canAccessStudio = () => { + const hostnameAllowed = StudioRolloutHostnameAllowed(); + const userAllowed = !!user?.can_access_studio || !!user?.maybe_feature_flags.includes("studio"); + return hostnameAllowed && userAllowed; + } + + const studioAccessCheck = (content: React.ElementType) => canAccessStudio() ? content : ; + + const modal = { close, open, view }; return ( open({ component: InferenceJobsModal, props: { jobType } }); @@ -20,7 +16,8 @@ export default function useInferenceJobs(jobType: JobListTypes = 0, debug = fals if (!noModalPls) { openJobListModal(); } enqueue(jobToken,jobType); }, - inferenceJobs: jobType === JobListAll.All ? inferenceJobs : (byCategory?.get(jobType - 1) || []), + inferenceJobs: jobType === undefined ? inferenceJobs : (byCategory?.get(jobType) || []), + // inferenceJobs: jobType === AllInferenceJobs.All ? inferenceJobs : (byCategory?.get(jobType) || []), jobStatusDescription: (jobState: JobState) => Object.keys(JobState).filter(key => isNaN(Number(key)))[jobState], queueStats }; diff --git a/packages/fakeyou.com/src/hooks/useSession/useSession.tsx b/packages/fakeyou.com/src/hooks/useSession/useSession.tsx index c3bf71f497..ba2ac40373 100644 --- a/packages/fakeyou.com/src/hooks/useSession/useSession.tsx +++ b/packages/fakeyou.com/src/hooks/useSession/useSession.tsx @@ -1,5 +1,5 @@ -import { useContext } from 'react'; -import { SessionContext } from 'context'; +import { useContext } from "react"; +import { SessionContext } from "components/providers/SessionProvider"; export default function useInferenceJobs() { return useContext(SessionContext); diff --git a/packages/fakeyou.com/src/v2/view/pages/EngineCompositor/EngineCompositor.tsx b/packages/fakeyou.com/src/v2/view/pages/EngineCompositor/EngineCompositor.tsx index 0b39267b3c..926a910703 100644 --- a/packages/fakeyou.com/src/v2/view/pages/EngineCompositor/EngineCompositor.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/EngineCompositor/EngineCompositor.tsx @@ -20,9 +20,7 @@ interface Props { export default function EngineCompositor({ value, sessionWrapper }: Props) { const [mediaToken, mediaTokenSet] = useState(); const onChange = ({ target }: any) => mediaTokenSet(target.value); - const inferenceJobs = useInferenceJobs( - FrontendInferenceJobType.EngineComposition - ); + const inferenceJobs = useInferenceJobs(FrontendInferenceJobType.EngineComposition); const onClick = () => { EnqueueEngineCompositing("", { diff --git a/packages/fakeyou.com/src/v2/view/pages/fbx_to_gltf/FbxToGltfPage.tsx b/packages/fakeyou.com/src/v2/view/pages/fbx_to_gltf/FbxToGltfPage.tsx index 2ec760b9ce..05455ada9b 100644 --- a/packages/fakeyou.com/src/v2/view/pages/fbx_to_gltf/FbxToGltfPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/fbx_to_gltf/FbxToGltfPage.tsx @@ -43,7 +43,6 @@ export default function FbxToGltfPage({ const { inferenceJobs } = useInferenceJobs( FrontendInferenceJobType.ConvertFbxtoGltf ); - const hasConversionJobs = inferenceJobs && inferenceJobs.length > 0; console.log("inferenceJobs", inferenceJobs); @@ -151,11 +150,9 @@ export default function FbxToGltfPage({ imageUrl="/images/header/fbx-to-gltf.png" /> - {hasConversionJobs && (
- )}
diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/LandingPage.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/LandingPage.tsx index eafc84f8f3..037f31a146 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/LandingPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/landing/LandingPage.tsx @@ -43,6 +43,8 @@ function LandingPage(props: Props) { const isLoggedIn = props.sessionWrapper.isLoggedIn(); + const canAccessStudio = props.sessionWrapper.canAccessStudio(); + return ( <> {domain.website === Website.StorytellerAi && !isLoggedIn && ( @@ -79,7 +81,7 @@ function LandingPage(props: Props) { {/* STORYTELLER,AI */} {!isLoggedIn ? ( <> - + {!canAccessStudio && } (null); const [activeIndex, setActiveIndex] = useState(0); - const [isPlaying, setIsPlaying] = useState(true); + // const [isPlaying, setIsPlaying] = useState(true); // const togglePlayback = () => { // setIsPlaying(!isPlaying); // }; - const handleVideoPlay = () => { - if (!isPlaying) { - setIsPlaying(true); - } - }; + // const handleVideoPlay = () => { + // if (!isPlaying) { + // setIsPlaying(true); + // } + // }; - const handleVideoPause = () => { - if (isPlaying) { - setIsPlaying(false); - } - }; + // const handleVideoPause = () => { + // if (isPlaying) { + // setIsPlaying(false); + // } + // }; - const videoRef = useRef(null); + // const videoRef = useRef(null); - useEffect(() => { - if (videoRef.current) { - if (isPlaying) { - videoRef.current.play(); - } else { - videoRef.current.pause(); - } - } - }, [isPlaying]); + // useEffect(() => { + // if (videoRef.current) { + // if (isPlaying) { + // videoRef.current.play(); + // } else { + // videoRef.current.pause(); + // } + // } + // }, [isPlaying]); const videoSet = [ { src: "/videos/landing/vst/1-original.mp4", + thumbnail: "/videos/landing/vst/1-original.jpg", styledVideos: [ { src: "/videos/landing/vst/1-2d_flat_anime.mp4", @@ -80,6 +81,7 @@ export default function VstSectionV2(props: VstSectionV2Props) { }, { src: "/videos/landing/vst/2-original.mp4", + thumbnail: "/videos/landing/vst/2-original.jpg", styledVideos: [ { src: "/videos/landing/vst/2-2d_flat_anime.mp4", @@ -157,16 +159,17 @@ export default function VstSectionV2(props: VstSectionV2Props) { muted playsInline controls={false} + preload="auto" key={`${index}-${activeIndex}`} - onPlay={handleVideoPlay} - onPause={handleVideoPause} - ref={videoRef => { - if (videoRef && isPlaying) { - videoRef.play(); - } else if (videoRef) { - videoRef.pause(); - } - }} + // onPlay={handleVideoPlay} + // onPause={handleVideoPause} + // ref={videoRef => { + // if (videoRef && isPlaying) { + // videoRef.play(); + // } else if (videoRef) { + // videoRef.pause(); + // } + // }} > Your browser does not support the video tag. @@ -186,10 +189,10 @@ export default function VstSectionV2(props: VstSectionV2Props) { {videoSet.map((video, index) => (
- + Demo Original Video Thumbnail
))} @@ -218,16 +221,17 @@ export default function VstSectionV2(props: VstSectionV2Props) { muted playsInline controls={false} + preload="auto" key={styledVideo.src} - onPlay={handleVideoPlay} - onPause={handleVideoPause} - ref={videoRef => { - if (videoRef && isPlaying) { - videoRef.play(); - } else if (videoRef) { - videoRef.pause(); - } - }} + // onPlay={handleVideoPlay} + // onPause={handleVideoPause} + // ref={videoRef => { + // if (videoRef && isPlaying) { + // videoRef.play(); + // } else if (videoRef) { + // videoRef.pause(); + // } + // }} > Your browser does not support the video tag. diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/OnboardingSelection.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/OnboardingSelection.tsx index 7620cc1ded..dfa84bb460 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/OnboardingSelection.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/OnboardingSelection.tsx @@ -10,31 +10,31 @@ export default function OnboardingSelection(props: OnboardingSelectionProps) { const cards = [ { - text: "Scene 1", - backgroundImage: "", + text: "Room Scene", + backgroundImage: "/images/landing/onboarding/room-scene-thumbnail.webp", onClick: () => { - history.push("/studio-intro/"); + history.push("/studio-intro/m_ejhs95fc5aybp36h4a79k7523ds6an"); }, }, { text: "Scene 2", backgroundImage: "", onClick: () => { - history.push("/studio-intro/"); + history.push("/studio-intro"); }, }, { text: "Scene 3", backgroundImage: "", onClick: () => { - history.push("/studio-intro/"); + history.push("/studio-intro"); }, }, { text: "Scene 4", backgroundImage: "", onClick: () => { - history.push("/studio-intro/"); + history.push("/studio-intro"); }, }, ]; diff --git a/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioIntroPage.tsx b/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioIntroPage.tsx index 360a8360c9..de7662cf56 100644 --- a/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioIntroPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioIntroPage.tsx @@ -19,7 +19,6 @@ interface Props { } function StudioIntroPage(props: Props) { - const [camera,cameraSet] = useState("rotation"); const { mediaToken } = useParams<{ mediaToken: string }>(); const history = useHistory(); @@ -32,11 +31,28 @@ function StudioIntroPage(props: Props) { // for subsequent steps of this flow. const [savedMediaToken, setSavedMediaToken] = useState(mediaToken); + // We don't show the "next step" buttons until the engine loads. + // Unfortunately the engine sometimes fires this twice, with one instance + // being called before the scene loads. Until this is fixed, we'll count the + // number of event fires and assume we must have two calls. + const [sceneIsLoadedCount, sceneIsLoadedCountSet] = useState(0); + + // If the scene is saved, we know the user must have interacted. + // This can serve as a second optional gate for enabling the next steps. + const [sceneIsSaved, sceneIsSavedSet] = useState(false); + + const [camera, cameraSet] = useState("rotation"); + usePrefixedDocumentTitle("Storyteller Studio"); const onSaveCallback = useCallback((sceneMediaToken: string) => { setSavedMediaToken(sceneMediaToken); - }, [setSavedMediaToken]); + sceneIsSavedSet(true); // Just in case we missed the "scene loaded" event. + }, [setSavedMediaToken, sceneIsSavedSet]); + + const onSceneReadyCallback = useCallback(() => { + sceneIsLoadedCountSet(sceneIsLoadedCount + 1); + }, [sceneIsLoadedCount, sceneIsLoadedCountSet]) if (!props.sessionWrapper.canAccessStudio()) { return ; @@ -84,6 +100,30 @@ function StudioIntroPage(props: Props) { value: "static" }]; + const sceneIsLoaded = sceneIsLoadedCount > 1 || sceneIsSaved; + + let progressButtons = <>; + + if (sceneIsLoaded) { + progressButtons = ( + <> +
+
+ + Add camera motion to render + +
+
+ { + cameraSet(target.value) + }, options: cameraOpts, value: camera }}/> +
+
+ + ) + } + return (
-
-
- - Add camera motion to render - -
-
- { - cameraSet(target.value) - }, options: cameraOpts, value: camera }}/> -
-
+ + {progressButtons}
); } diff --git a/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioVST/StudioVSTPage.tsx b/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioVST/StudioVSTPage.tsx index 88f65b8da2..7a87fb65b2 100644 --- a/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioVST/StudioVSTPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioVST/StudioVSTPage.tsx @@ -75,23 +75,23 @@ export default function PageVSTApp() { const styleOptions = [ { - label: "Anime", - imageUrl: "/images/dummy-image.jpg", + label: "2D Anime", + imageUrl: "/images/landing/onboarding/styles/style-2d-anime.webp", token: "weight_yqexh77ntqyawzgh9fzash798", }, { - label: "Pixel", - imageUrl: "/images/dummy-image-2.jpg", + label: "3D Cartoon", + imageUrl: "/images/landing/onboarding/styles/style-3d-cartoon.webp", token: "weight_yqexh77ntqyawzgh9fzash798", }, { - label: "Painting", - imageUrl: "/images/dummy-image-3.jpg", + label: "Ink B&W", + imageUrl: "/images/landing/onboarding/styles/style-ink-bw.webp", token: "weight_yqexh77ntqyawzgh9fzash798", }, { - label: "Ink", - imageUrl: "/images/dummy-image-4.jpg", + label: "Origami", + imageUrl: "/images/landing/onboarding/styles/style-origami.webp", token: "weight_yqexh77ntqyawzgh9fzash798", }, ];