diff --git a/environments/.env.develop b/environments/.env.develop index 8f4f3be..3ad3ffa 100644 --- a/environments/.env.develop +++ b/environments/.env.develop @@ -5,4 +5,5 @@ AURA_SITE_HOST=https://aura.wit.life AURA_PREFIX=api/v1 AURA_OPEN_AI_HOST=https://api.openai.com AURA_OPEN_AI_PREFIX=v1 -AURA_YANDEX_COUNTER_NUMBER=95799066 \ No newline at end of file +AURA_YANDEX_COUNTER_NUMBER=95799066 +AURA_PERSONAL_VIDEO_TIME_LIMIT=180 \ No newline at end of file diff --git a/environments/.env.production b/environments/.env.production index 6d73dbc..6136a84 100644 --- a/environments/.env.production +++ b/environments/.env.production @@ -5,4 +5,5 @@ AURA_SITE_HOST=https://aura.wit.life AURA_PREFIX=api/v1 AURA_OPEN_AI_HOST=https://api.openai.com AURA_OPEN_AI_PREFIX=v1 -AURA_YANDEX_COUNTER_NUMBER=95799066 \ No newline at end of file +AURA_YANDEX_COUNTER_NUMBER=95799066 +AURA_PERSONAL_VIDEO_TIME_LIMIT=120 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1ad5bea..a7c4997 100755 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "react-dom": "^18.2.0", "react-ga4": "^2.1.0", "react-i18next": "^12.3.1", + "react-player": "^2.16.0", "react-redux": "^8.0.5", "react-router-dom": "^6.11.2", "react-slick": "^0.30.2", @@ -1678,6 +1679,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2685,6 +2694,11 @@ "node": ">= 0.8.0" } }, + "node_modules/load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2736,6 +2750,11 @@ "resolved": "https://registry.npmjs.org/md5-es/-/md5-es-1.8.2.tgz", "integrity": "sha512-LKq5jmKMhJYhsBFUh2w+J3C4bMiC5uQie/UYJ429UATmMnFr6iANO2uQq5HXAZSIupGp0WO2mH3sNfxR4XO40Q==" }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3069,6 +3088,11 @@ "react": "^18.2.0" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, "node_modules/react-ga4": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/react-ga4/-/react-ga4-2.1.0.tgz", @@ -3100,6 +3124,21 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/react-player": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz", + "integrity": "sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==", + "dependencies": { + "deepmerge": "^4.0.0", + "load-script": "^1.0.0", + "memoize-one": "^5.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.0.1" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, "node_modules/react-property": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz", @@ -4766,6 +4805,11 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -5508,6 +5552,11 @@ "type-check": "~0.4.0" } }, + "load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==" + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5550,6 +5599,11 @@ "resolved": "https://registry.npmjs.org/md5-es/-/md5-es-1.8.2.tgz", "integrity": "sha512-LKq5jmKMhJYhsBFUh2w+J3C4bMiC5uQie/UYJ429UATmMnFr6iANO2uQq5HXAZSIupGp0WO2mH3sNfxR4XO40Q==" }, + "memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5774,6 +5828,11 @@ "scheduler": "^0.23.0" } }, + "react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, "react-ga4": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/react-ga4/-/react-ga4-2.1.0.tgz", @@ -5793,6 +5852,18 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "react-player": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz", + "integrity": "sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==", + "requires": { + "deepmerge": "^4.0.0", + "load-script": "^1.0.0", + "memoize-one": "^5.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.0.1" + } + }, "react-property": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz", diff --git a/package.json b/package.json index 1bce495..ec34560 100755 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "react-dom": "^18.2.0", "react-ga4": "^2.1.0", "react-i18next": "^12.3.1", + "react-player": "^2.16.0", "react-redux": "^8.0.5", "react-router-dom": "^6.11.2", "react-slick": "^0.30.2", diff --git a/src/api/api.ts b/src/api/api.ts index 4acb49b..dc78628 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -29,6 +29,7 @@ import { Palmistry, Paywall, Payment, + UserVideos, } from './resources' const api = { @@ -80,6 +81,8 @@ const api = { getPaywallByPlacementKey: createMethod(Paywall.createRequestGet), // Payment makePayment: createMethod(Payment.createRequestPost), + // User videos + getUserVideos: createMethod(UserVideos.createRequest), } export type ApiContextValue = typeof api diff --git a/src/api/resources/User.ts b/src/api/resources/User.ts index f0a1209..3cca74a 100644 --- a/src/api/resources/User.ts +++ b/src/api/resources/User.ts @@ -178,6 +178,8 @@ export interface ICreateAuthorizePayload { export interface ICreateAuthorizeResponse { token: string; userId?: string; + generatingVideo?: boolean; + videoId?: string; } export const createAuthorizeRequest = (data: ICreateAuthorizePayload): Request => { diff --git a/src/api/resources/UserVideos.ts b/src/api/resources/UserVideos.ts new file mode 100644 index 0000000..3ab52a0 --- /dev/null +++ b/src/api/resources/UserVideos.ts @@ -0,0 +1,23 @@ +import routes from "@/routes"; +import { getAuthHeaders } from "../utils"; + +interface Payload { + token: string; +} + +export type PayloadGet = Payload; + +export interface IUserVideo { + id: string, + videoUrl: string, + createdAt: string +} + +type ResponseGetSuccess = IUserVideo[]; + +export type ResponseGet = ResponseGetSuccess; + +export const createRequest = ({ token }: PayloadGet): Request => { + const url = new URL(routes.server.getUserVideos()); + return new Request(url, { method: "GET", headers: getAuthHeaders(token) }); +}; diff --git a/src/api/resources/index.ts b/src/api/resources/index.ts index 4123777..fb33f6a 100644 --- a/src/api/resources/index.ts +++ b/src/api/resources/index.ts @@ -27,3 +27,4 @@ export * as Products from "./Products"; export * as Palmistry from "./Palmistry"; export * as Paywall from "./Paywall"; export * as Payment from "./Payment"; +export * as UserVideos from "./UserVideos"; diff --git a/src/components/pages/ABDesign/v1/pages/Onboarding/index.tsx b/src/components/pages/ABDesign/v1/pages/Onboarding/index.tsx index 5508905..850b461 100644 --- a/src/components/pages/ABDesign/v1/pages/Onboarding/index.tsx +++ b/src/components/pages/ABDesign/v1/pages/Onboarding/index.tsx @@ -7,6 +7,9 @@ import { onboardingTitles } from "../../data/onboarding"; import ProgressBarLine from "@/components/ui/ProgressBarLine"; import { usePaywall } from "@/hooks/paywall/usePaywall"; import { EPlacementKeys } from "@/api/resources/Paywall"; +import { usePersonalVideo } from "@/hooks/personalVideo/usePersonalVideo"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; function OnboardingPage() { const navigate = useNavigate(); @@ -17,21 +20,29 @@ function OnboardingPage() { const [progress, setProgress] = useState(0); const progressInterval = useRef(); usePaywall({ placementKey: EPlacementKeys["aura.placement.redesign.main"] }); + const { isVideoReady } = usePersonalVideo(); + const { createdDate } = useSelector(selectors.selectPersonalVideo); const handleNext = useCallback(() => { navigate(routes.client.trialChoiceV1()); }, [navigate]); + useEffect(() => { + if (isVideoReady && progress >= 100) { + handleNext(); + } + }, [activeIndexTitle, handleNext, isVideoReady, progress]); + useEffect(() => { setPeriodClassName("to-nontransparent"); - classNameTimeOut.current = setTimeout(() => { - setPeriodClassName("to-transparent"); - }, 4000); + if (activeIndexTitle < onboardingTitles.length - 1) { + classNameTimeOut.current = setTimeout(() => { + setPeriodClassName("to-transparent"); + }, 4000); + } titleInterval.current = setTimeout(() => { if (activeIndexTitle < onboardingTitles.length - 1) { setIndexTitle((prev) => prev + 1); - } else { - handleNext(); } }, 5000); return () => { @@ -40,17 +51,28 @@ function OnboardingPage() { }; }, [activeIndexTitle, handleNext]); + const getProgressIntervalTiming = useCallback(() => { + const generateTimeLimit = import.meta.env.AURA_PERSONAL_VIDEO_TIME_LIMIT; + if (progress < 95 || isVideoReady) { + return (onboardingTitles.length * 5000) / 100; + } + return ( + (Number(generateTimeLimit) * 1000 - new Date().getTime() + createdDate) / + 4 + ); + }, [createdDate, isVideoReady, progress]); + useEffect(() => { progressInterval.current = setInterval(() => { setProgress((prev) => { if (prev >= 100) return prev; return prev + 1; }); - }, (onboardingTitles.length * 5000) / 100); + }, getProgressIntervalTiming()); return () => { if (progressInterval.current) clearInterval(progressInterval.current); }; - }, []); + }, [getProgressIntervalTiming]); return (
diff --git a/src/components/pages/ABDesign/v1/pages/RelationshipAlmostThere/index.tsx b/src/components/pages/ABDesign/v1/pages/RelationshipAlmostThere/index.tsx index 9b90d94..5510cf2 100644 --- a/src/components/pages/ABDesign/v1/pages/RelationshipAlmostThere/index.tsx +++ b/src/components/pages/ABDesign/v1/pages/RelationshipAlmostThere/index.tsx @@ -15,8 +15,6 @@ function RelationshipAlmostTherePage() { preloadKey: ELottieKeys.sun, }); - console.log("animationData", animationData); - const handleBack = () => { navigate(-1); }; diff --git a/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PersonalVideo/index.tsx b/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PersonalVideo/index.tsx new file mode 100644 index 0000000..90e2d54 --- /dev/null +++ b/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PersonalVideo/index.tsx @@ -0,0 +1,60 @@ +import React, { useState } from "react"; +import ReactPlayer from "react-player"; +import styles from "./styles.module.css"; +import Loader from "@/components/Loader"; +import PlayButton from "../../../../ui/PlayButton"; + +interface IPersonalVideoProps { + gender: string; + url: string; +} + +const PersonalVideo = React.memo(({ url, gender }) => { + const [isPlaying, setIsPlaying] = useState(false); + const [isError, setIsError] = useState(false); + + const onError = (error: unknown) => { + if (!error) return; + setIsError(true); + setIsPlaying(false); + }; + + return ( +
+ {!isPlaying && !isError && } + {isError && ( + { + setIsError(false); + setIsPlaying(true); + }} + /> + )} + setIsPlaying(true)} + onError={onError} + playsinline={true} + height={"auto"} + style={{ + aspectRatio: "16 / 9", + }} + /> +
+ ); +}); + +export default PersonalVideo; diff --git a/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PersonalVideo/styles.module.css b/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PersonalVideo/styles.module.css new file mode 100644 index 0000000..8099361 --- /dev/null +++ b/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PersonalVideo/styles.module.css @@ -0,0 +1,14 @@ +.container { + width: 100%; + margin: 24px 0 16px; + position: relative; + overflow: hidden; + border-radius: 10px; +} + +.play-button, .loader { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} \ No newline at end of file diff --git a/src/components/pages/ABDesign/v1/pages/TrialPayment/index.tsx b/src/components/pages/ABDesign/v1/pages/TrialPayment/index.tsx index 59f265b..68ffd74 100644 --- a/src/components/pages/ABDesign/v1/pages/TrialPayment/index.tsx +++ b/src/components/pages/ABDesign/v1/pages/TrialPayment/index.tsx @@ -24,6 +24,7 @@ import { useDynamicSize } from "@/hooks/useDynamicSize"; import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall"; import { usePaywall } from "@/hooks/paywall/usePaywall"; import PaymentModal from "@/components/PaymentModal"; +import PersonalVideo from "./components/PersonalVideo"; function TrialPaymentPage() { const dispatch = useDispatch(); @@ -53,6 +54,7 @@ function TrialPaymentPage() { "single" | "partner" >("single"); const { subPlan } = useParams(); + const { videoUrl } = useSelector(selectors.selectPersonalVideo); useEffect(() => { if (subPlan) { @@ -131,6 +133,7 @@ function TrialPaymentPage() { />
+ {!!videoUrl.length && } {singleOrWithPartner === "partner" && ( ) { + return ( + <> + + + + + {" "} + {" "} + {" "} + + + + ); +} + +export default PlayButton; diff --git a/src/components/pages/ABDesign/v1/ui/PlayButton/styles.module.css b/src/components/pages/ABDesign/v1/ui/PlayButton/styles.module.css new file mode 100644 index 0000000..1906ee9 --- /dev/null +++ b/src/components/pages/ABDesign/v1/ui/PlayButton/styles.module.css @@ -0,0 +1,4 @@ +.svg { + cursor: pointer; + z-index: 10; +} \ No newline at end of file diff --git a/src/env.d.ts b/src/env.d.ts index 155ff67..e22e3a1 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -6,5 +6,6 @@ interface ImportMetaEnv { AURA_PREFIX: string, AURA_OPEN_AI_HOST: string, AURA_OPEN_AI_PREFIX: string, - AURA_YANDEX_COUNTER_NUMBER: string + AURA_YANDEX_COUNTER_NUMBER: string, + AURA_PERSONAL_VIDEO_TIME_LIMIT: string } \ No newline at end of file diff --git a/src/hooks/authentication/use-authentication.ts b/src/hooks/authentication/use-authentication.ts index 55868ec..4c6d889 100644 --- a/src/hooks/authentication/use-authentication.ts +++ b/src/hooks/authentication/use-authentication.ts @@ -120,7 +120,7 @@ export const useAuthentication = () => { setIsLoading(true); setError(null) const payload = getAuthorizationPayload(email, source); - const { token, userId } = await api.authorization(payload); + const { token, userId, generatingVideo, videoId } = await api.authorization(payload); const { user } = await api.getUser({ token }); if (userId?.length && !!window.ym && typeof window.ym === 'function') { window.ym(95799066, 'userParams', { @@ -131,6 +131,8 @@ export const useAuthentication = () => { } signUp(token, user); setToken(token); + + dispatch(actions.personalVideo.updateStatus({ generatingVideo: generatingVideo || false, videoId: videoId || "" })); dispatch(actions.status.update("registred")); } catch (error) { setError((error as Error).message); diff --git a/src/hooks/lottie/useLottie.ts b/src/hooks/lottie/useLottie.ts index b921341..04fea96 100644 --- a/src/hooks/lottie/useLottie.ts +++ b/src/hooks/lottie/useLottie.ts @@ -75,12 +75,9 @@ export const useLottie = ({ preloadKey, loadKey }: IUseLottieProps) => { useEffect(() => { if (preloadKey) { - console.log("preload"); - preload(preloadKey); } if (loadKey) { - console.log("load"); load(loadKey); } }, [load, loadKey, preload, preloadKey]) diff --git a/src/hooks/personalVideo/usePersonalVideo.ts b/src/hooks/personalVideo/usePersonalVideo.ts new file mode 100644 index 0000000..1655bc5 --- /dev/null +++ b/src/hooks/personalVideo/usePersonalVideo.ts @@ -0,0 +1,57 @@ +import { useApi } from "@/api"; +import { actions, selectors } from "@/store"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; + + +export const usePersonalVideo = () => { + const api = useApi(); + const dispatch = useDispatch(); + const token = useSelector(selectors.selectToken); + const { generatingVideo, videoId, createdDate } = useSelector( + selectors.selectPersonalVideo + ); + + const personalVideoTimeLimit = useMemo(() => import.meta.env.AURA_PERSONAL_VIDEO_TIME_LIMIT, []) + + const requestVideoInterval = useRef(); + const [isVideoReady, setIsVideoReady] = useState(false); + + const clearPersonalVideoInterval = useCallback(() => { + if (!requestVideoInterval.current) return; + if (isVideoReady) return clearInterval(requestVideoInterval.current); + }, [isVideoReady]) + + const getTargetUserVideo = useCallback(async () => { + const videos = await api.getUserVideos({ + token, + }); + return videos.find((video) => video.id === videoId); + }, [api, token, videoId]); + + useEffect(() => { + clearPersonalVideoInterval(); + return () => { + clearPersonalVideoInterval(); + }; + }, [clearPersonalVideoInterval]) + + useEffect(() => { + if (!generatingVideo) return setIsVideoReady(true); + + requestVideoInterval.current = setInterval(async () => { + const video = await getTargetUserVideo(); + if (!video) return setIsVideoReady(true); + if (video.videoUrl.length) { + dispatch(actions.personalVideo.updateUrl(video.videoUrl)); + return setIsVideoReady(true); + } + if (new Date().getTime() - createdDate > Number(personalVideoTimeLimit) * 1000) { + return setIsVideoReady(true); + } + }, 5000); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return useMemo(() => ({ isVideoReady }), [isVideoReady]) +} \ No newline at end of file diff --git a/src/routes.ts b/src/routes.ts index 8bf1ec5..31e08d0 100755 --- a/src/routes.ts +++ b/src/routes.ts @@ -274,6 +274,10 @@ const routes = { makePayment: () => [dApiHost, dApiPrefix, "payment", "checkout"].join("/"), + // User videos + getUserVideos: () => + [dApiHost, "users", "videos", "combined"].join("/"), + }, openAi: { createThread: () => [openAIHost, openAiPrefix, "threads"].join("/"), diff --git a/src/store/index.ts b/src/store/index.ts index ca67c17..5abbbf3 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -72,6 +72,7 @@ import palmistry, { } from "./palmistry"; import { selectPaywallsIsMustUpdate, selectPaywalls } from "./paywalls"; import privacyPolicy, { actions as privacyPolicyActions, selectPrivacyPolicy } from "./privacyPolicy"; +import personalVideo, { actions as personalVideoActions, selectPersonalVideo } from "./personalVideo"; const preloadedState = loadStore(); export const actions = { @@ -92,6 +93,7 @@ export const actions = { userConfig: userConfigActions, palmistry: palmistryActions, privacyPolicy: privacyPolicyActions, + personalVideo: personalVideoActions, reset: createAction("reset"), }; export const selectors = { @@ -127,6 +129,7 @@ export const selectors = { selectPaywallsIsMustUpdate, selectPrivacyPolicy, selectStripeButton, + selectPersonalVideo, ...formSelectors, }; @@ -147,7 +150,8 @@ export const reducer = combineReducers({ userConfig, palmistry, paywalls, - privacyPolicy + privacyPolicy, + personalVideo }); export type RootState = ReturnType; diff --git a/src/store/personalVideo.ts b/src/store/personalVideo.ts new file mode 100644 index 0000000..0da1402 --- /dev/null +++ b/src/store/personalVideo.ts @@ -0,0 +1,37 @@ +import { createSlice, createSelector } from '@reduxjs/toolkit' +import type { PayloadAction } from '@reduxjs/toolkit' + +interface IPersonalVideo { + generatingVideo: boolean + videoId: string + videoUrl: string + createdDate: number +} + +const initialState: IPersonalVideo = { + generatingVideo: false, + videoId: "", + videoUrl: "", + createdDate: 0 +} + +const personalVideoSlice = createSlice({ + name: 'personalVideo', + initialState, + reducers: { + updateStatus(state, action: PayloadAction>) { + return { ...state, ...action.payload, createdDate: new Date().getTime() } + }, + updateUrl(state, action: PayloadAction) { + return { ...state, videoUrl: action.payload } + } + }, + extraReducers: (builder) => builder.addCase('reset', () => initialState), +}) + +export const { actions } = personalVideoSlice +export const selectPersonalVideo = createSelector( + (state: { personalVideo: IPersonalVideo }) => state.personalVideo, + (personalVideo) => personalVideo +) +export default personalVideoSlice.reducer