w-aura/src/components/CompatibilityV2/pages/ScannedPhoto/index.tsx
2025-04-07 07:03:12 +00:00

368 lines
12 KiB
TypeScript

import { useSelector } from "react-redux";
import styles from "./styles.module.scss";
import { selectors } from "@/store";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { IPalmistryLine } from "@/api/resources/Palmistry";
import Title from "@/components/Title";
import { ICompatibilityV2FingerLocal } from "@/store/compatibilityV2";
import { useNavigate, useSearchParams } from "react-router-dom";
import routes from "@/routes";
import ScannedPhotoElement from "@/components/palmistry/scanned-photo/scanned-photo";
import { useTranslations } from "@/hooks/translations";
import { ELocalesPlacement } from "@/locales";
import ProgressBarLine from "@/components/ui/ProgressBarLine";
import Modal from "@/components/Modal";
import { useAuthentication } from "@/hooks/authentication/use-authentication";
import { ESourceAuthorization } from "@/api/resources/User";
import { getRandomArbitrary } from "@/services/random-value";
import { usePreloadImages } from "@/hooks/preload/images";
const drawElementChangeDelay = 1500;
const startDelay = 500;
interface IPoint {
title1: string;
title2: string;
modal: {
title: string;
description: string;
answerLeft: string;
answerRight: string;
}
}
function ScannedPhoto() {
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
const navigate = useNavigate();
const photo = useSelector(selectors.selectCompatibilityV2Photo);
const fingers = useSelector(selectors.selectCompatibilityV2Fingers);
const lines = useSelector(selectors.selectCompatibilityV2Lines);
const changeTitleTimeOut = useRef<NodeJS.Timeout>();
const [currentElementIndex, setCurrentElementIndex] = useState(0);
const [title, setTitle] = useState("");
const [shouldDisplayPalmLines, setShouldDisplayPalmLines] = useState(false);
const [smallPhotoState, setSmallPhotoState] = useState(false);
const [isDecorationShown, setIsDecorationShown] = useState(true);
const [searchParams] = useSearchParams();
const isFromScanHand = searchParams.get("fromScanHand") === "true";
const [classNameScannedPhoto, setClassNameScannedPhoto] = useState(isFromScanHand ? styles.scannedPhotoFromScanHand : "");
const feature = useSelector(selectors.selectFeature);
const isIOSPath = useMemo(() => feature?.toLowerCase()?.includes("ios"), [feature]);
const authCode = useSelector(selectors.selectAuthCode);
const { authorization } = useAuthentication();
usePreloadImages([
"/v2/compatibility/download-app.png",
"/v2/compatibility/love-line.svg",
"/v2/compatibility/head-line.svg",
"/v2/compatibility/life-line.svg",
"/v2/compatibility/fate-line.svg",
"/v2/compatibility/hand-little-finger.svg",
"/v2/compatibility/partners.png"
])
const drawElements = useMemo(() => [...fingers, ...lines], [fingers, lines]);
const { relationshipStatus } = useSelector(selectors.selectCompatibilityV2Answers)
const loadingProfilePoints: IPoint[] = useMemo(() => {
const prefix = relationshipStatus === "single" ? "without-partner" : "with-partner";
return [
{
title1: `/scanned-photo.${prefix}.loaders.title-1-1`,
title2: `/scanned-photo.${prefix}.loaders.title-1-2`,
modal: {
title: `/scanned-photo.${prefix}.modals.title-1`,
description: `/scanned-photo.${prefix}.modals.description-1`,
answerLeft: `/scanned-photo.${prefix}.modals.answer-1-left`,
answerRight: `/scanned-photo.${prefix}.modals.answer-1-right`,
}
},
{
title1: `/scanned-photo.${prefix}.loaders.title-2-1`,
title2: `/scanned-photo.${prefix}.loaders.title-2-2`,
modal: {
title: `/scanned-photo.${prefix}.modals.title-2`,
description: `/scanned-photo.${prefix}.modals.description-2`,
answerLeft: `/scanned-photo.${prefix}.modals.answer-2-left`,
answerRight: `/scanned-photo.${prefix}.modals.answer-2-right`,
}
},
{
title1: `/scanned-photo.${prefix}.loaders.title-3-1`,
title2: `/scanned-photo.${prefix}.loaders.title-3-2`,
modal: {
title: `/scanned-photo.${prefix}.modals.title-3`,
description: `/scanned-photo.${prefix}.modals.description-3`,
answerLeft: `/scanned-photo.${prefix}.modals.answer-3-left`,
answerRight: `/scanned-photo.${prefix}.modals.answer-3-right`,
}
},
]
}, []);
useEffect(() => {
if (isIOSPath) {
(async () => {
await authorization("", ESourceAuthorization["aura.compatibility.v2"], true);
})();
}
}, [isIOSPath, authorization])
useEffect(() => {
if (!drawElements[currentElementIndex]) return;
changeTitleTimeOut.current = setTimeout(() => {
const title =
(drawElements[currentElementIndex] as ICompatibilityV2FingerLocal)
.fingerName || drawElements[currentElementIndex].name;
setTitle(title);
if (currentElementIndex < drawElements.length - 1) {
setCurrentElementIndex((prevState) => prevState + 1);
}
}, drawElementChangeDelay);
return () => {
if (changeTitleTimeOut.current) {
clearTimeout(changeTitleTimeOut.current);
}
};
}, [currentElementIndex, drawElements]);
useEffect(() => {
if (currentElementIndex < drawElements.length - 1) return;
const timer = setTimeout(() => {
setTitle("");
}, drawElementChangeDelay * 2)
return () => {
if (timer) {
clearTimeout(timer);
}
}
}, [currentElementIndex, drawElements])
useEffect(() => {
setShouldDisplayPalmLines(
lines.includes(drawElements[currentElementIndex] as IPalmistryLine)
);
}, [currentElementIndex, drawElements, lines]);
useEffect(() => {
if (currentElementIndex < drawElements.length - 1) return;
const timer = setTimeout(() => {
setSmallPhotoState(true);
}, drawElementChangeDelay * 2);
const goNextTimer = setTimeout(
() => {
// navigate(routes.client.compatibilityV2Email())
setIsDecorationShown(false);
setClassNameScannedPhoto(styles.scannedPhotoSmall);
},
drawElementChangeDelay * drawElements.length + 8000
);
return () => {
if (timer) {
clearTimeout(timer);
}
if (goNextTimer) {
clearTimeout(goNextTimer);
}
};
}, [currentElementIndex, drawElements.length, navigate]);
// useEffect(() => {
// if (currentElementIndex < drawElements.length) return;
// const timer = setTimeout(() => {
// // navigate(routes.client.compatibilityV2Email());
// }, drawElementChangeDelay + 1000);
// return () => clearTimeout(timer);
// }, [currentElementIndex, drawElements.length, navigate]);
const [progress, setProgress] = useState(0);
const [isPause, setIsPause] = useState(false);
const interval = useRef<NodeJS.Timeout>();
const loadingPointSettings = useMemo(() => {
const currentPointIndex = Math.floor(progress / 100);
const endFastLoading = getRandomArbitrary(15, 30);
const startFastLoading = getRandomArbitrary(70, 85);
const pauseLoading = getRandomArbitrary(40, 60);
return { endFastLoading, pauseLoading, startFastLoading, currentPointIndex };
}, [Math.floor(progress / 100)])
const getProgressValue = useCallback(
(index: number) => {
const integerDivision = Math.floor(progress / 100);
if (integerDivision > index) {
return 100;
}
if (integerDivision === index) {
return progress % 100;
}
return 0;
},
[progress]
);
const onEndLoading = useCallback(() => {
const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
if (isIOSPath && !!authCode && isIOS) {
return navigate(routes.client.compatibilityV2TryApp());
}
navigate(routes.client.compatibilityV2Email());
}, [isIOSPath, authCode, navigate]);
useEffect(() => {
if (progress === 75) {
setIsPause(true);
}
if (progress !== 0 && (progress / 50) % 2 === 1 && progress > 100) {
return setIsPause(true);
}
}, [progress]);
const getLoadingTime = useCallback((progress: number) => {
const progressOfCurrentPoint = progress % 100;
if (progressOfCurrentPoint < loadingPointSettings.endFastLoading) {
return 40;
}
if (progressOfCurrentPoint > loadingPointSettings.startFastLoading) {
return 40;
}
if (progressOfCurrentPoint === loadingPointSettings.pauseLoading) {
return 3000;
}
return 100
}, [Math.floor(progress / 100)]);
useEffect(() => {
const loadingTime = getLoadingTime(progress);
if (progress >= loadingProfilePoints.length * 100) {
return onEndLoading();
}
interval.current = setTimeout(() => {
setProgress((prevProgress) => {
if (!isPause && !isDecorationShown) return prevProgress + 1;
return prevProgress;
});
}, loadingTime);
return () => {
clearTimeout(interval.current);
};
}, [progress, onEndLoading, isPause, isDecorationShown]);
const getCurrentIndex = () => {
return Math.floor(progress / 100);
};
return (
<section className={`${styles.page} palmistry-container_type_scan-photo`}>
<Title variant="h2" className={styles.title}>
{title}
</Title>
<ScannedPhotoElement
photo={photo}
small={smallPhotoState}
drawElementChangeDelay={drawElementChangeDelay}
startDelay={startDelay}
displayLines={shouldDisplayPalmLines}
lines={lines}
fingers={fingers}
drawElements={drawElements}
className={classNameScannedPhoto}
isDecorationShown={isDecorationShown}
/>
<h2
className={`palmistry-container__waiting-title ${styles.waitingTitle} ${!isDecorationShown ? styles.hidden : ""}`}
style={{
animationDelay: `${drawElementChangeDelay * drawElements.length + 2500
}ms`,
}}
>
{translate("/scanned-photo.title")}
</h2>
<h3
className={`palmistry-container__waiting-description ${styles.waitingDescription} ${!isDecorationShown ? styles.hidden : ""}`}
style={{
animationDelay: `${drawElementChangeDelay * drawElements.length + 3000
}ms`,
}}
>
{translate("/scanned-photo.text")}
</h3>
{isPause && (
<Modal
isCloseButtonVisible={false}
open={!!isPause}
onClose={() => setIsPause(false)}
className={styles.modal}
containerClassName={styles["modal-container"]}
>
<Title variant="h4" className={styles["modal-title"]}>
{translate(loadingProfilePoints[getCurrentIndex()].modal.title)}
</Title>
<p className={styles["modal-description"]}>
{translate(loadingProfilePoints[getCurrentIndex()].modal.description)}
</p>
<div className={styles["modal-answers"]}>
<div className={styles["modal-answer"]} onClick={() => setIsPause(false)}>
<p className={styles["modal-answer-text"]}>
{translate(loadingProfilePoints[getCurrentIndex()].modal.answerLeft)}
</p>
</div>
<div className={styles["modal-answer"]} onClick={() => setIsPause(false)}>
<p className={styles["modal-answer-text"]}>
{translate(loadingProfilePoints[getCurrentIndex()].modal.answerRight)}
</p>
</div>
</div>
</Modal>
)}
{!isDecorationShown && <div className={styles["points-container"]}>
{loadingProfilePoints.map(({ title1, title2 }, index) => (
<div
className={`${styles["point"]} ${getCurrentIndex() === index && styles["active"]
}`}
// ref={(el) => (pointsRef.current[index] = el as HTMLDivElement)}
key={`point-${index}`}
>
<div className={styles["point__text-container"]}>
<Title variant="h2" className={styles["point__title"]}>
{translate(getProgressValue(index) > 50 ? title2 : title1)}
</Title>
<ProgressBarLine
containerClassName={styles["progress-bar__container"]}
lineClassName={styles["progress-bar__line"]}
lineColor={"#275DA7"}
value={getProgressValue(index)}
delay={50}
/>
</div>
<p
className={styles["point__percentage"]}
>
{getProgressValue(index)}%
</p>
</div>
))}
</div>}
</section >
);
}
export default ScannedPhoto;