175 lines
5.7 KiB
TypeScript
175 lines
5.7 KiB
TypeScript
import Title from "@/components/Title";
|
||
import Header from "../../components/Header";
|
||
import { images } from "../../data";
|
||
import styles from "./styles.module.scss";
|
||
import { useTranslations } from "@/hooks/translations";
|
||
import { ELocalesPlacement } from "@/locales";
|
||
import { useEffect, useState, useRef } from "react";
|
||
import { useDispatch, useSelector } from "react-redux";
|
||
import { actions, selectors } from "@/store";
|
||
import Loader, { LoaderColor } from "@/components/Loader";
|
||
import { useNavigate } from "react-router-dom";
|
||
import routes from "@/routes";
|
||
import { IPalmistryFinger } from "@/api/resources/Palmistry";
|
||
import { ICompatibilityV2FingerLocal } from "@/store/compatibilityV2";
|
||
import { useApi } from "@/api";
|
||
import { DataURIToBlob } from "@/services/data";
|
||
|
||
function ScanHand() {
|
||
const api = useApi();
|
||
const dispatch = useDispatch();
|
||
const navigate = useNavigate();
|
||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||
const [isLoading, setIsLoading] = useState(false);
|
||
const imageRef = useRef<HTMLImageElement>(null);
|
||
const gender = (useSelector(selectors.selectQuestionnaire)?.gender || "female");
|
||
|
||
const handleNext = () => {
|
||
navigate(`${routes.client.compatibilityV2ScannedPhoto()}?fromScanHand=true`)
|
||
}
|
||
|
||
useEffect(() => {
|
||
const timer = setTimeout(() => {
|
||
handleHandOn()
|
||
}, 6000);
|
||
|
||
return () => clearTimeout(timer);
|
||
}, []);
|
||
|
||
const convertImageToBase64 = (img: HTMLImageElement): Promise<string> => {
|
||
return new Promise((resolve, reject) => {
|
||
const canvas = document.createElement('canvas');
|
||
const padding = 100;
|
||
canvas.width = img.width * 1.4 + padding * 2;
|
||
canvas.height = img.height * 1.4;
|
||
const ctx = canvas.getContext('2d');
|
||
|
||
if (!ctx) {
|
||
reject(new Error('Не удалось получить контекст canvas'));
|
||
return;
|
||
}
|
||
|
||
ctx.fillStyle = '#272727';
|
||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||
|
||
ctx.drawImage(img, padding, 0, img.width * 1.4, canvas.height);
|
||
const base64String = canvas.toDataURL('image/png');
|
||
resolve(base64String);
|
||
});
|
||
};
|
||
|
||
const fingersNames = {
|
||
thumb: translate("thumb"),
|
||
index_finger: translate("index_finger"),
|
||
middle_finger: translate("middle_finger"),
|
||
ring_finger: translate("ring_finger"),
|
||
pinky: translate("pinky"),
|
||
};
|
||
|
||
const setFingersNames = (
|
||
fingers: IPalmistryFinger[]
|
||
): ICompatibilityV2FingerLocal[] => {
|
||
if (!fingers) return [];
|
||
return fingers.map((finger) => {
|
||
return {
|
||
...finger,
|
||
fingerName: fingersNames[finger.name as keyof typeof fingersNames],
|
||
};
|
||
});
|
||
};
|
||
|
||
const getLines = async (file: File | Blob) => {
|
||
const formData = new FormData();
|
||
formData.append("file", file);
|
||
try {
|
||
const result = await api.getPalmistryLines({ formData, gender: gender as "male" | "female" });
|
||
const fingers = setFingersNames(result?.fingers);
|
||
|
||
dispatch(
|
||
actions.compatibilityV2.update({
|
||
lines: result?.lines,
|
||
fingers,
|
||
})
|
||
);
|
||
return result;
|
||
} catch (error) {
|
||
dispatch(
|
||
actions.compatibilityV2.update({
|
||
lines: [],
|
||
fingers: [],
|
||
})
|
||
);
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const handleHandOn = async () => {
|
||
if (isLoading || !imageRef.current) return;
|
||
|
||
setIsLoading(true);
|
||
try {
|
||
const photo = await convertImageToBase64(imageRef.current);
|
||
|
||
const file = DataURIToBlob(photo);
|
||
|
||
await getLines(file);
|
||
|
||
URL.revokeObjectURL(URL.createObjectURL(file));
|
||
|
||
dispatch(
|
||
actions.compatibilityV2.update({
|
||
photo,
|
||
})
|
||
);
|
||
handleNext()
|
||
} catch (error) {
|
||
console.error('Ошибка при конвертации изображения:', error);
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div
|
||
className={styles.container}
|
||
>
|
||
<Header
|
||
className={styles.header}
|
||
classNameTitle={styles["header-title"]}
|
||
isBackButtonVisible={true}
|
||
/>
|
||
<div
|
||
className={styles.content}
|
||
onClick={handleHandOn}
|
||
onTouchStart={(e) => {
|
||
e.preventDefault();
|
||
handleHandOn();
|
||
}}
|
||
>
|
||
{!isLoading && <>
|
||
<div className={styles.gradientContainer}>
|
||
<div className={styles.animationContainer}>
|
||
<div className={styles.line} />
|
||
</div>
|
||
</div>
|
||
<img className={styles.imageGif} src={images("scan-hand/Palm-tach-A.gif")} alt="Palm-tach-A" />
|
||
<Title variant="h2" className={styles.title}>
|
||
{translate("/scan-hand.title")}
|
||
</Title>
|
||
</>}
|
||
<img
|
||
ref={imageRef}
|
||
className={styles.imageHand}
|
||
src={images(`scan-hand/${gender}-Palm-A.png`)}
|
||
alt="hand"
|
||
draggable={false}
|
||
/>
|
||
{isLoading &&
|
||
<Loader color={LoaderColor.White} />
|
||
}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default ScanHand |