AW-119-personal-video
show generated personal video on /v1/trial-payment page
This commit is contained in:
parent
93896b9b19
commit
1430b67f31
@ -6,3 +6,4 @@ AURA_PREFIX=api/v1
|
||||
AURA_OPEN_AI_HOST=https://api.openai.com
|
||||
AURA_OPEN_AI_PREFIX=v1
|
||||
AURA_YANDEX_COUNTER_NUMBER=95799066
|
||||
AURA_PERSONAL_VIDEO_TIME_LIMIT=180
|
||||
@ -6,3 +6,4 @@ AURA_PREFIX=api/v1
|
||||
AURA_OPEN_AI_HOST=https://api.openai.com
|
||||
AURA_OPEN_AI_PREFIX=v1
|
||||
AURA_YANDEX_COUNTER_NUMBER=95799066
|
||||
AURA_PERSONAL_VIDEO_TIME_LIMIT=120
|
||||
71
package-lock.json
generated
71
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -29,6 +29,7 @@ import {
|
||||
Palmistry,
|
||||
Paywall,
|
||||
Payment,
|
||||
UserVideos,
|
||||
} from './resources'
|
||||
|
||||
const api = {
|
||||
@ -80,6 +81,8 @@ const api = {
|
||||
getPaywallByPlacementKey: createMethod<Paywall.PayloadGet, Paywall.ResponseGet>(Paywall.createRequestGet),
|
||||
// Payment
|
||||
makePayment: createMethod<Payment.PayloadPost, Payment.ResponsePost>(Payment.createRequestPost),
|
||||
// User videos
|
||||
getUserVideos: createMethod<UserVideos.PayloadGet, UserVideos.ResponseGet>(UserVideos.createRequest),
|
||||
}
|
||||
|
||||
export type ApiContextValue = typeof api
|
||||
|
||||
@ -178,6 +178,8 @@ export interface ICreateAuthorizePayload {
|
||||
export interface ICreateAuthorizeResponse {
|
||||
token: string;
|
||||
userId?: string;
|
||||
generatingVideo?: boolean;
|
||||
videoId?: string;
|
||||
}
|
||||
|
||||
export const createAuthorizeRequest = (data: ICreateAuthorizePayload): Request => {
|
||||
|
||||
23
src/api/resources/UserVideos.ts
Normal file
23
src/api/resources/UserVideos.ts
Normal file
@ -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) });
|
||||
};
|
||||
@ -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";
|
||||
|
||||
@ -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<NodeJS.Timeout>();
|
||||
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 (
|
||||
<section className={`${styles.page} page`}>
|
||||
|
||||
@ -15,8 +15,6 @@ function RelationshipAlmostTherePage() {
|
||||
preloadKey: ELottieKeys.sun,
|
||||
});
|
||||
|
||||
console.log("animationData", animationData);
|
||||
|
||||
const handleBack = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
@ -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<IPersonalVideoProps>(({ url, gender }) => {
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [isError, setIsError] = useState(false);
|
||||
|
||||
const onError = (error: unknown) => {
|
||||
if (!error) return;
|
||||
setIsError(true);
|
||||
setIsPlaying(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.container}
|
||||
style={{
|
||||
backgroundColor: gender === "male" ? "#85B6FF" : "#D1ACF2",
|
||||
}}
|
||||
>
|
||||
{!isPlaying && !isError && <Loader className={styles.loader} />}
|
||||
{isError && (
|
||||
<PlayButton
|
||||
backgroundFill={gender === "male" ? "#85B6FF" : "#D1ACF2"}
|
||||
className={styles["play-button"]}
|
||||
onClick={() => {
|
||||
setIsError(false);
|
||||
setIsPlaying(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<ReactPlayer
|
||||
url={url}
|
||||
controls={false}
|
||||
light={false}
|
||||
muted={false}
|
||||
playing={isPlaying}
|
||||
stopOnUnmount={true}
|
||||
width="100%"
|
||||
onReady={() => setIsPlaying(true)}
|
||||
onError={onError}
|
||||
playsinline={true}
|
||||
height={"auto"}
|
||||
style={{
|
||||
aspectRatio: "16 / 9",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default PersonalVideo;
|
||||
@ -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%);
|
||||
}
|
||||
@ -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() {
|
||||
/>
|
||||
<Header className={styles.header} />
|
||||
<TrialPaymentHeader buttonClick={openStripeModal} />
|
||||
{!!videoUrl.length && <PersonalVideo gender={gender} url={videoUrl} />}
|
||||
{singleOrWithPartner === "partner" && (
|
||||
<WithPartnerInformation
|
||||
zodiacSign={zodiacSign}
|
||||
|
||||
52
src/components/pages/ABDesign/v1/ui/PlayButton/index.tsx
Normal file
52
src/components/pages/ABDesign/v1/ui/PlayButton/index.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import { SVGProps } from "react";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
interface IPlayButtonProps {
|
||||
backgroundFill?: string;
|
||||
}
|
||||
|
||||
function PlayButton({
|
||||
backgroundFill = "#32BEA6",
|
||||
className = "",
|
||||
...props
|
||||
}: IPlayButtonProps & SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<>
|
||||
<svg
|
||||
height="48px"
|
||||
width="48px"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="-4.96 -4.96 506.08 506.08"
|
||||
xmlSpace="preserve"
|
||||
fill="#000000"
|
||||
stroke="#000000"
|
||||
strokeWidth="7.938528000000001"
|
||||
className={`${styles.svg} ${className}`}
|
||||
{...props}
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
|
||||
<g
|
||||
id="SVGRepo_tracerCarrier"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
{" "}
|
||||
<path
|
||||
style={{ fill: backgroundFill }}
|
||||
d="M496.158,248.085c0-137.021-111.07-248.082-248.076-248.082C111.07,0.002,0,111.062,0,248.085 c0,137.002,111.07,248.071,248.083,248.071C385.088,496.155,496.158,385.086,496.158,248.085z"
|
||||
></path>{" "}
|
||||
<path
|
||||
style={{ fill: "#FFFFFF" }}
|
||||
d="M370.805,235.242L195.856,127.818c-4.776-2.934-11.061-3.061-15.951-0.322 c-4.979,2.785-8.071,8.059-8.071,13.762v214c0,5.693,3.083,10.963,8.046,13.752c2.353,1.32,5.024,2.02,7.725,2.02 c2.897,0,5.734-0.797,8.205-2.303l174.947-106.576c4.657-2.836,7.556-7.986,7.565-13.44 C378.332,243.258,375.452,238.096,370.805,235.242z"
|
||||
></path>{" "}
|
||||
</g>
|
||||
</svg>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlayButton;
|
||||
@ -0,0 +1,4 @@
|
||||
.svg {
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
}
|
||||
3
src/env.d.ts
vendored
3
src/env.d.ts
vendored
@ -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
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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])
|
||||
|
||||
57
src/hooks/personalVideo/usePersonalVideo.ts
Normal file
57
src/hooks/personalVideo/usePersonalVideo.ts
Normal file
@ -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<NodeJS.Timeout>();
|
||||
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])
|
||||
}
|
||||
@ -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("/"),
|
||||
|
||||
@ -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<typeof reducer>;
|
||||
|
||||
37
src/store/personalVideo.ts
Normal file
37
src/store/personalVideo.ts
Normal file
@ -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<Omit<IPersonalVideo, 'createdDate' | 'videoUrl'>>) {
|
||||
return { ...state, ...action.payload, createdDate: new Date().getTime() }
|
||||
},
|
||||
updateUrl(state, action: PayloadAction<string>) {
|
||||
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
|
||||
Loading…
Reference in New Issue
Block a user