This commit is contained in:
Daniil Chemerkin 2024-11-25 22:32:25 +00:00
parent 2700408741
commit 6c4acb9588
6 changed files with 283 additions and 28 deletions

View File

@ -1,12 +1,69 @@
import Title from "@/components/Title";
import styles from "./styles.module.scss";
import { palmistryV1Prefix } from "@/routes";
import PointRing from "./PointRing";
import { ELocalesPlacement } from "@/locales";
import { useTranslations } from "@/hooks/translations";
import PhotoReady from "../PhotoReady";
import { useSelector } from "react-redux";
import { selectors } from "@/store";
import { useMemo } from "react";
import { palmistryV1Prefix } from "@/routes";
import { IPalmistryLine } from "@/api/resources/Palmistry";
import { IPalmistryFingerLocal } from "@/store/palmistry";
import { useMetricABFlags } from "@/services/metric/metricService";
function PalmsSayAbout() {
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
const lines = useSelector(selectors.selectPalmistryLines);
const fingers = useSelector(selectors.selectPalmistryFingers);
const { flags } = useMetricABFlags();
const isReal = flags?.palmOnPayment?.[0] === "real";
const filterByName = (
array: Array<IPalmistryLine | IPalmistryFingerLocal>,
name: string
) => {
return array.filter((value: IPalmistryLine | IPalmistryFingerLocal) => value.name === name);
};
const love = useMemo(() => {
return {
lines: filterByName(lines, "heart") as IPalmistryLine[],
fingers: filterByName(fingers, "thumb") as IPalmistryFingerLocal[],
};
}, [fingers, lines]);
const head = useMemo(() => {
return {
lines: filterByName(lines, "head") as IPalmistryLine[],
fingers: filterByName(fingers, "index_finger") as IPalmistryFingerLocal[],
};
}, [fingers, lines]);
const life = useMemo(() => {
return {
lines: filterByName(lines, "life") as IPalmistryLine[],
fingers: filterByName(
fingers,
"middle_finger"
) as IPalmistryFingerLocal[],
};
}, [fingers, lines]);
const fate = useMemo(() => {
return {
lines: filterByName(lines, "fate") as IPalmistryLine[],
fingers: filterByName(fingers, "ring_finger") as IPalmistryFingerLocal[],
};
}, [fingers, lines]);
const pinky = useMemo(() => {
return {
lines: [],
fingers: filterByName(fingers, "pinky") as IPalmistryFingerLocal[],
};
}, [fingers]);
return (
<div className={styles.container}>
@ -15,10 +72,19 @@ function PalmsSayAbout() {
</Title>
<div className={styles.rows}>
<div className={styles.row}>
<img
src={`${palmistryV1Prefix}/love-line.svg`}
alt="Hand with love line"
/>
{!!love.lines.length && !!love.fingers.length && isReal && (
<PhotoReady
className={styles["photo-ready"]}
lines={love.lines}
fingers={love.fingers}
/>
)}
{(!love.lines.length || !love.fingers.length || !isReal) && (
<img
src={`${palmistryV1Prefix}/love-line.svg`}
alt="Hand with love line"
/>
)}
<div className={styles.points}>
<div className={styles.point}>
<PointRing />
@ -41,10 +107,19 @@ function PalmsSayAbout() {
</div>
</div>
<div className={styles.row}>
<img
src={`${palmistryV1Prefix}/head-line.svg`}
alt="Hand with head line"
/>
{!!head.lines.length && !!head.fingers.length && isReal && (
<PhotoReady
className={styles["photo-ready"]}
lines={head.lines}
fingers={head.fingers}
/>
)}
{(!head.lines.length || !head.fingers.length || !isReal) && (
<img
src={`${palmistryV1Prefix}/head-line.svg`}
alt="Hand with head line"
/>
)}
<div className={styles.points}>
<div className={styles.point}>
<PointRing gradientColor={["#34DF3D", "#27A72D"]} />
@ -67,10 +142,19 @@ function PalmsSayAbout() {
</div>
</div>
<div className={styles.row}>
<img
src={`${palmistryV1Prefix}/life-line.svg`}
alt="Hand with life line"
/>
{!!life.lines.length && !!life.fingers.length && isReal && (
<PhotoReady
className={styles["photo-ready"]}
lines={life.lines}
fingers={life.fingers}
/>
)}
{(!life.lines.length || !life.fingers.length || !isReal) && (
<img
src={`${palmistryV1Prefix}/life-line.svg`}
alt="Hand with life line"
/>
)}
<div className={styles.points}>
<div className={styles.point}>
<PointRing gradientColor={["#363AB0", "#282C84"]} />
@ -93,10 +177,19 @@ function PalmsSayAbout() {
</div>
</div>
<div className={styles.row}>
<img
src={`${palmistryV1Prefix}/fate-line.svg`}
alt="Hand with fate line"
/>
{!!fate.lines.length && !!fate.fingers.length && isReal && (
<PhotoReady
className={styles["photo-ready"]}
lines={fate.lines}
fingers={fate.fingers}
/>
)}
{(!fate.lines.length || !fate.fingers.length || !isReal) && (
<img
src={`${palmistryV1Prefix}/fate-line.svg`}
alt="Hand with fate line"
/>
)}
<div className={styles.points}>
<div className={styles.point}>
<PointRing gradientColor={["#DF38E4", "#922595"]} />
@ -119,10 +212,19 @@ function PalmsSayAbout() {
</div>
</div>
<div className={styles.row}>
<img
src={`${palmistryV1Prefix}/hand-little-finger.svg`}
alt="Hand with little finger"
/>
{!!pinky.fingers.length && isReal && (
<PhotoReady
className={styles["photo-ready"]}
lines={[]}
fingers={pinky.fingers}
/>
)}
{(!pinky.fingers.length || !isReal) && (
<img
src={`${palmistryV1Prefix}/hand-little-finger.svg`}
alt="Hand with little finger"
/>
)}
<div className={styles.points}>
<div className={styles.point}>
<PointRing gradientColor={["#E9453A", "#922B25"]} />

View File

@ -52,3 +52,7 @@
}
}
}
.photo-ready {
max-width: 90px;
}

View File

@ -0,0 +1,105 @@
import { useCallback, useEffect, useRef, useState } from "react";
import styles from "./styles.module.scss";
import { useSelector } from "react-redux";
import { selectors } from "@/store";
import { IPalmistryLine, IPalmistryPoint } from "@/api/resources/Palmistry";
import { IPalmistryFingerLocal } from "@/store/palmistry";
interface IPhotoReadyProps {
className?: string;
lines: IPalmistryLine[];
fingers: IPalmistryFingerLocal[];
}
function PhotoReady({ className = "", lines, fingers }: IPhotoReadyProps) {
const photo = useSelector(selectors.selectPalmistryPhoto);
const imageRef = useRef<HTMLImageElement>(null);
const [isImageLoaded, setIsImageLoaded] = useState(false);
const [imageWidth, setImageWidth] = useState(0);
const [imageHeight, setImageHeight] = useState(0);
const linesRef = useRef<SVGPathElement[]>([]);
useEffect(() => {
if (isImageLoaded && imageRef.current) {
setImageWidth(imageRef.current.width || 0);
setImageHeight(imageRef.current.height || 0);
}
}, [isImageLoaded]);
const getCoordinatesString = useCallback(
(points: IPalmistryPoint[]) => {
const coordinatesString = `M ${points[0]?.x * imageWidth} ${
points[0]?.y * imageHeight
}`;
return points.reduce(
(acc, point) =>
`${acc} L ${point?.x * imageWidth} ${point?.y * imageHeight}`,
coordinatesString
);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[lines, isImageLoaded, imageWidth, imageHeight]
);
return (
<div className={`${styles.container} ${className}`}>
<img
className="scanned-photo__image"
alt="PalmIcon"
src={photo}
ref={imageRef}
onLoad={() => setIsImageLoaded(true)}
/>
{!!imageHeight && !!imageWidth && (
<svg
viewBox={`0 0 ${imageWidth} ${imageHeight}`}
className={styles["svg-objects"]}
>
{!!fingers.length &&
fingers?.map((finger, index) => {
return (
<svg
x={finger.point.x * imageWidth - 12}
y={finger.point.y * imageHeight - 12}
height="24px"
width="24px"
key={index}
>
{/* <circle
cx="50%"
cy="50%"
r="11"
fill="white"
opacity="0.3"
className="scanned-photo__finger-point"
/> */}
<circle
cx="50%"
cy="50%"
r="4"
fill="#066FDE"
stroke="white"
strokeWidth="0.3"
/>
</svg>
);
})}
<>
{lines.map((line, index) => (
<path
key={index}
className={`${styles.line} ${styles[`line-${line?.name}`]}`}
d={getCoordinatesString(line?.points)}
ref={(el) => (linesRef.current[index] = el as SVGPathElement)}
/>
))}
</>
</svg>
)}
</div>
);
}
export default PhotoReady;

View File

@ -0,0 +1,43 @@
.container {
position: relative;
}
.svg-objects {
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
.line {
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2px;
fill-rule: evenodd;
clip-rule: evenodd;
stroke-miterlimit: 1.5;
stroke-dasharray: 500;
stroke: #fff;
fill: none;
stroke-dashoffset: 0;
}
.line-heart {
stroke: #f8d90f;
/* animation-delay: 4.5s; */
}
.line-life {
stroke: #e51c39;
}
.line-head {
stroke: #00d114;
/* animation-delay: 1.5s; */
}
.line-fate {
stroke: #05ced8;
/* animation-delay: 3s; */
}

View File

@ -2,7 +2,7 @@ import { useDispatch, useSelector } from "react-redux";
import EmailSubstrate from "../../components/EmailSubstrate";
import styles from "./styles.module.scss";
import { actions, selectors } from "@/store";
import Title from "@/components/Title";
// import Title from "@/components/Title";
import PriceList from "@/components/pages/ABDesign/v1/components/PriceList";
import { usePaywall } from "@/hooks/paywall/usePaywall";
import { EPlacementKeys } from "@/api/resources/Paywall";
@ -17,7 +17,7 @@ import routes from "@/routes";
import { useNavigate } from "react-router-dom";
import Loader from "@/components/Loader";
import { useTranslations } from "@/hooks/translations";
import { useMetricABFlags } from "@/services/metric/metricService";
// import { useMetricABFlags } from "@/services/metric/metricService";
import { getLongText } from "./abText";
function TrialChoice() {
@ -31,8 +31,8 @@ function TrialChoice() {
const locale = getDefaultLocaleByLanguage(language);
const { flags } = useMetricABFlags();
const isLongText = flags?.text?.[0] === "on";
// const { flags } = useMetricABFlags();
// const isLongText = flags?.text?.[0] === "on";
const [isDisabled, setIsDisabled] = useState(true);
@ -61,12 +61,12 @@ function TrialChoice() {
{!isLoading && (
<>
<EmailSubstrate className={styles["email-substrate"]} email={email} />
{!isLongText && (
{/* {!isLongText && (
<Title className={styles.title} variant="h2">
{getText("text.0")}
</Title>
)}
{isLongText && <p className={styles.text}>{getLongText(locale)}</p>}
)} */}
<p className={styles.text}>{getLongText(locale)}</p>
<div className={styles["price-container"]}>
<PriceList

View File

@ -150,6 +150,7 @@ type TABFlags = {
auraVideoTrial: "on";
auraPalmistry: "on";
esFlag: "hiCopy" | "standard";
palmOnPayment: "graphical" | "real"
}
export const useMetricABFlags = () => {