Merge branch 'preview/palmistry' into 'develop'
add lines drawing See merge request witapp/aura-webapp!82
This commit is contained in:
commit
1704df4cc8
@ -28,6 +28,7 @@ import {
|
||||
OpenAI,
|
||||
SinglePayment,
|
||||
Products,
|
||||
Palmistry,
|
||||
} from './resources'
|
||||
|
||||
const api = {
|
||||
@ -72,6 +73,7 @@ const api = {
|
||||
getSinglePaymentProducts: createMethod<SinglePayment.PayloadGet, SinglePayment.ResponseGet[]>(SinglePayment.createRequestGet),
|
||||
createSinglePayment: createMethod<SinglePayment.PayloadPost, SinglePayment.ResponsePost>(SinglePayment.createRequestPost),
|
||||
checkProductPurchased: createMethod<Products.PayloadGet, Products.ResponseGet>(Products.createRequest),
|
||||
getPalmistryLines: createMethod<Palmistry.Payload, Palmistry.Response>(Palmistry.createRequest),
|
||||
}
|
||||
|
||||
export type ApiContextValue = typeof api
|
||||
|
||||
26
src/api/resources/Palmistry.ts
Normal file
26
src/api/resources/Palmistry.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import routes from "@/routes";
|
||||
|
||||
export interface Payload {
|
||||
formData: FormData;
|
||||
}
|
||||
|
||||
export type Response = IPalmistryLine[];
|
||||
|
||||
export interface IPalmistryLine {
|
||||
line: string;
|
||||
points: IPalmistryPoint[];
|
||||
}
|
||||
|
||||
export interface IPalmistryPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export const createRequest = ({ formData }: Payload) => {
|
||||
const url = new URL(routes.server.getPalmistryLines());
|
||||
const body = formData;
|
||||
return new Request(url, {
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
};
|
||||
@ -26,3 +26,4 @@ export * as Assistants from "./Assistants";
|
||||
export * as OpenAI from "./OpenAI";
|
||||
export * as SinglePayment from "./SinglePayment";
|
||||
export * as Products from "./Products";
|
||||
export * as Palmistry from "./Palmistry";
|
||||
|
||||
@ -72,6 +72,7 @@
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.palmistry-container__bottom-content {
|
||||
@ -206,7 +207,8 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.palmistry-container__correct-title, .palmistry-container__wrong-title {
|
||||
.palmistry-container__correct-title,
|
||||
.palmistry-container__wrong-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
@ -359,13 +361,12 @@
|
||||
margin-bottom: 34px;
|
||||
text-align: center;
|
||||
animation: title-opacity 1.5s cubic-bezier(0.37, 0, 0.63, 1);
|
||||
animation-delay: 13s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.palmistry-container_type_scan-photo .palmistry-container__waiting-title {
|
||||
animation: waiting-title 0.5s cubic-bezier(0.37, 0, 0.63, 1);
|
||||
animation-delay: 14.5s;
|
||||
/* animation-delay: 14.5s; */
|
||||
animation-fill-mode: forwards;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
@ -377,7 +378,7 @@
|
||||
|
||||
.palmistry-container_type_scan-photo .palmistry-container__waiting-description {
|
||||
animation: waiting-description 8s cubic-bezier(0.37, 0, 0.63, 1);
|
||||
animation-delay: 15s;
|
||||
/* animation-delay: 15s; */
|
||||
animation-fill-mode: forwards;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
@ -428,7 +429,10 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.palmistry-container_type_email .palmistry-container__input input:not(:placeholder-shown) + .input__placeholder {
|
||||
.palmistry-container_type_email
|
||||
.palmistry-container__input
|
||||
input:not(:placeholder-shown)
|
||||
+ .input__placeholder {
|
||||
font-size: 12px;
|
||||
top: 12px;
|
||||
width: auto;
|
||||
@ -519,16 +523,19 @@
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.palmistry-container_type_subscription-plan .palmistry-container__plan:not(:last-child) {
|
||||
.palmistry-container_type_subscription-plan
|
||||
.palmistry-container__plan:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.palmistry-container_type_subscription-plan .palmistry-container__plan:last-child {
|
||||
.palmistry-container_type_subscription-plan
|
||||
.palmistry-container__plan:last-child {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.palmistry-container_type_subscription-plan .palmistry-container__plan:last-child::after {
|
||||
content: '';
|
||||
.palmistry-container_type_subscription-plan
|
||||
.palmistry-container__plan:last-child::after {
|
||||
content: "";
|
||||
background: var(--pale-gray);
|
||||
bottom: -24px;
|
||||
height: 15px;
|
||||
@ -542,11 +549,13 @@
|
||||
color: var(--black-color-text);
|
||||
}
|
||||
|
||||
.palmistry-container_type_subscription-plan .palmistry-container__plan_active::after {
|
||||
.palmistry-container_type_subscription-plan
|
||||
.palmistry-container__plan_active::after {
|
||||
background: var(--strong-blue) !important;
|
||||
}
|
||||
|
||||
.palmistry-container_type_subscription-plan .palmistry-container__subscription-text {
|
||||
.palmistry-container_type_subscription-plan
|
||||
.palmistry-container__subscription-text {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
@ -554,7 +563,8 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.palmistry-container_type_subscription-plan .palmistry-container__subscription-text_active {
|
||||
.palmistry-container_type_subscription-plan
|
||||
.palmistry-container__subscription-text_active {
|
||||
color: var(--blue-color-text);
|
||||
}
|
||||
|
||||
|
||||
@ -1,35 +1,38 @@
|
||||
.scanned-photo {
|
||||
display: flex;
|
||||
height: 477px;
|
||||
/* height: 477px; */
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 291px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.scanned-photo__container {
|
||||
height: 477px;
|
||||
/* background-color: red; */
|
||||
height: 100%;
|
||||
position: relative;
|
||||
width: 291px;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.scanned-photo__stick {
|
||||
animation: scanned-photo-stick-move 5.5s cubic-bezier(0.37, 0, 0.63, 1);
|
||||
animation-delay: 14.5s;
|
||||
/* animation-delay: 14.5s; */
|
||||
animation-fill-mode: forwards;
|
||||
animation-iteration-count: infinite;
|
||||
background-color: var(--strong-blue-text);
|
||||
height: 2px;
|
||||
left: -2px;
|
||||
/* left: -2px; */
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
width: 96px;
|
||||
width: 100%;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.scanned-photo__image {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
/* height: 100%; */
|
||||
/* object-fit: cover; */
|
||||
object-fit: contain;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -71,7 +74,7 @@
|
||||
.scanned-photo__line {
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 3px;
|
||||
stroke-width: 2px;
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
stroke-miterlimit: 1.5;
|
||||
@ -84,7 +87,7 @@
|
||||
|
||||
.scanned-photo__line_heart {
|
||||
stroke: #f8d90f;
|
||||
animation-delay: 4.5s;
|
||||
/* animation-delay: 4.5s; */
|
||||
}
|
||||
|
||||
.scanned-photo__line_life {
|
||||
@ -93,12 +96,12 @@
|
||||
|
||||
.scanned-photo__line_head {
|
||||
stroke: #00d114;
|
||||
animation-delay: 1.5s;
|
||||
/* animation-delay: 1.5s; */
|
||||
}
|
||||
|
||||
.scanned-photo__line_fate {
|
||||
stroke: #05ced8;
|
||||
animation-delay: 3s;
|
||||
/* animation-delay: 3s; */
|
||||
}
|
||||
|
||||
.scanned-photo__decoration {
|
||||
@ -107,19 +110,35 @@
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
animation: scanned-photo-opacity 1.5s cubic-bezier(0.37, 0, 0.63, 1);
|
||||
animation-delay: 12s;
|
||||
/* animation-delay: 12s; */
|
||||
animation-fill-mode: forwards;
|
||||
height: 220px;
|
||||
margin-top: -10px;
|
||||
position: absolute;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.scanned-photo__decoration__corners {
|
||||
animation: scanned-photo-corners-opacity 1.5s cubic-bezier(0.37, 0, 0.63, 1);
|
||||
animation-delay: 13.5s;
|
||||
/* animation-delay: 13.5s; */
|
||||
animation-fill-mode: forwards;
|
||||
background: linear-gradient(to right, var(--strong-blue-text) 2px, transparent 2px) 0 0, linear-gradient(to right, var(--strong-blue-text) 2px, transparent 2px) 0 100%, linear-gradient(to left, var(--strong-blue-text) 2px, transparent 2px) 100% 0, linear-gradient(to left, var(--strong-blue-text) 2px, transparent 2px) 100% 100%, linear-gradient(to bottom, var(--strong-blue-text) 2px, transparent 2px) 0 0, linear-gradient(to bottom, var(--strong-blue-text) 2px, transparent 2px) 100% 0, linear-gradient(to top, var(--strong-blue-text) 2px, transparent 2px) 0 100%, linear-gradient(to top, var(--strong-blue-text) 2px, transparent 2px) 100% 100%;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
var(--strong-blue-text) 2px,
|
||||
transparent 2px
|
||||
)
|
||||
0 0,
|
||||
linear-gradient(to right, var(--strong-blue-text) 2px, transparent 2px) 0
|
||||
100%,
|
||||
linear-gradient(to left, var(--strong-blue-text) 2px, transparent 2px) 100%
|
||||
0,
|
||||
linear-gradient(to left, var(--strong-blue-text) 2px, transparent 2px) 100%
|
||||
100%,
|
||||
linear-gradient(to bottom, var(--strong-blue-text) 2px, transparent 2px) 0 0,
|
||||
linear-gradient(to bottom, var(--strong-blue-text) 2px, transparent 2px)
|
||||
100% 0,
|
||||
linear-gradient(to top, var(--strong-blue-text) 2px, transparent 2px) 0 100%,
|
||||
linear-gradient(to top, var(--strong-blue-text) 2px, transparent 2px) 100%
|
||||
100%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 15px 15px;
|
||||
height: 100%;
|
||||
@ -156,68 +175,74 @@
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.scanned-photo_small .scanned-photo__image {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@keyframes scanned-photo-resize {
|
||||
100% {
|
||||
height: 207px;
|
||||
}
|
||||
/* align-items: center; */
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scanned-photo-container-resize {
|
||||
100% {
|
||||
border: 3px solid #fff;
|
||||
height: 159px;
|
||||
margin-top: 20px;
|
||||
width: 97px;
|
||||
}
|
||||
/* border: 3px solid #fff; */
|
||||
height: 70.7%;
|
||||
/* margin-top: 20px; */
|
||||
width: auto;
|
||||
/* aspect-ratio: 1 / 1; */
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scanned-photo-opacity {
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scanned-photo-corners-opacity {
|
||||
10% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
20% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
40% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scanned-photo-stick-move {
|
||||
0% {
|
||||
opacity: 1;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
top: 100%;
|
||||
}
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes finger-show {
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes line-show {
|
||||
100% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,27 +1,90 @@
|
||||
import './scanned-photo.css';
|
||||
import { IPalmistryLine, IPalmistryPoint } from "@/api/resources/Palmistry";
|
||||
import "./scanned-photo.css";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
type Props = {
|
||||
photo: string;
|
||||
small: boolean;
|
||||
displayLines: boolean;
|
||||
lines: IPalmistryLine[];
|
||||
lineChangeDelay: number;
|
||||
startDelay: number;
|
||||
};
|
||||
|
||||
export default function StepScanPhoto(props: Props) {
|
||||
const className = ['scanned-photo'];
|
||||
const className = ["scanned-photo"];
|
||||
const { lines, lineChangeDelay } = props;
|
||||
const imageRef = useRef<HTMLImageElement>(null);
|
||||
const linesRef = useRef<SVGPathElement[]>([]);
|
||||
const [isImageLoaded, setIsImageLoaded] = useState(false);
|
||||
const [imageWidth, setImageWidth] = useState(0);
|
||||
const [imageHeight, setImageHeight] = useState(0);
|
||||
|
||||
if (props.small) {
|
||||
className.push('scanned-photo_small');
|
||||
className.push("scanned-photo_small");
|
||||
}
|
||||
|
||||
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]
|
||||
);
|
||||
|
||||
const getLineLength = (line: SVGPathElement) => {
|
||||
return line?.getTotalLength();
|
||||
};
|
||||
|
||||
// const getAnimationDelayOfLine = (index: number) => {
|
||||
// return `${lineChangeDelay * index + startDelay}ms`;
|
||||
// };
|
||||
|
||||
return (
|
||||
<div className={className.join(' ')}>
|
||||
<div
|
||||
className={className.join(" ")}
|
||||
style={{
|
||||
height: imageHeight ? `${imageHeight}px` : "auto",
|
||||
}}
|
||||
>
|
||||
<div className="scanned-photo__container">
|
||||
<div className="scanned-photo__stick" />
|
||||
<div
|
||||
className="scanned-photo__stick"
|
||||
style={{
|
||||
animationDelay: `${lineChangeDelay * lines.length + 2500}ms`,
|
||||
maxWidth: `${imageWidth}px`,
|
||||
}}
|
||||
/>
|
||||
|
||||
<img className="scanned-photo__image" alt="PalmIcon" src={props.photo} width={291} height={477}/>
|
||||
|
||||
<svg viewBox="0 0 291 477" className="scanned-photo__svg-objects">
|
||||
<svg x="235" y="211" height="24px" width="24px">
|
||||
<img
|
||||
className="scanned-photo__image"
|
||||
alt="PalmIcon"
|
||||
src={props.photo}
|
||||
ref={imageRef}
|
||||
onLoad={() => setIsImageLoaded(true)}
|
||||
// width={imageWidth}
|
||||
// height={imageHeight}
|
||||
/>
|
||||
{!!imageHeight && !!imageWidth && (
|
||||
<svg
|
||||
viewBox={`0 0 ${imageWidth} ${imageHeight}`}
|
||||
className="scanned-photo__svg-objects"
|
||||
>
|
||||
{/* <svg x="235" y="211" height="24px" width="24px">
|
||||
<circle
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
@ -119,16 +182,33 @@ export default function StepScanPhoto(props: Props) {
|
||||
strokeWidth="0.3"
|
||||
className="scanned-photo__finger-point scanned-photo__finger-point_little"
|
||||
/>
|
||||
</svg>
|
||||
</svg> */}
|
||||
|
||||
{props.displayLines && (
|
||||
<>
|
||||
<path
|
||||
className="scanned-photo__line scanned-photo__line_heart"
|
||||
{props.displayLines && (
|
||||
<>
|
||||
{lines.map((line, index) => (
|
||||
<path
|
||||
key={index}
|
||||
className={`scanned-photo__line scanned-photo__line_${line?.line}`}
|
||||
d={getCoordinatesString(line?.points)}
|
||||
ref={(el) =>
|
||||
(linesRef.current[index] = el as SVGPathElement)
|
||||
}
|
||||
style={{
|
||||
strokeDasharray:
|
||||
getLineLength(linesRef.current[index]) || 500,
|
||||
strokeDashoffset:
|
||||
getLineLength(linesRef.current[index]) || 500,
|
||||
animationDelay: `${lineChangeDelay * (index + 1)}ms`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{/* <path
|
||||
className={`scanned-photo__line scanned-photo__line_heart`}
|
||||
d="M 95 334 L 99 330 L 104 327 L 109 323 L 113 319 L 118 315 L 123 311 L 128 308 L 132 304 L 137 301 L 142 298 L 146 296 L 151 293 L 156 291 L 160 289 L 165 287 L 170 286 L 174 284 L 179 283 L 184 283 L 189 282 L 193 282 L 198 283 L 203 284 L 207 285"
|
||||
style={{ strokeDasharray: 128.14, strokeDashoffset: 128.14 }}
|
||||
/>
|
||||
<path
|
||||
/> */}
|
||||
{/* <path
|
||||
className="scanned-photo__line scanned-photo__line_life"
|
||||
d="M 205 283 L 193 291 L 181 299 L 170 306 L 160 314 L 153 322 L 147 329 L 143 337 L 139 345 L 136 352 L 133 360 L 130 368 L 128 376 L 126 383 L 125 391 L 125 399 L 126 406 L 128 414 L 132 422 L 137 429 L 143 437 L 149 445 L 156 452"
|
||||
style={{ strokeDasharray: 211.483, strokeDashoffset: 211.483 }}
|
||||
@ -142,14 +222,25 @@ export default function StepScanPhoto(props: Props) {
|
||||
className="scanned-photo__line scanned-photo__line_fate"
|
||||
d="M 134 260 L 129 299 L 125 306 L 122 314 L 120 322 L 118 329 L 116 337 L 115 345 L 114 352"
|
||||
style={{ strokeDasharray: 94.8313, strokeDashoffset: 94.8313 }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</svg>
|
||||
/> */}
|
||||
</>
|
||||
)}
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="scanned-photo__decoration">
|
||||
<div className="scanned-photo__decoration__corners">
|
||||
<div
|
||||
className="scanned-photo__decoration"
|
||||
style={{
|
||||
animationDelay: `${lineChangeDelay * lines?.length}ms`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="scanned-photo__decoration__corners"
|
||||
style={{
|
||||
animationDelay: `${lineChangeDelay * lines?.length + 1500}ms`,
|
||||
}}
|
||||
>
|
||||
<div className="scanned-photo__decoration__light-blue-circle" />
|
||||
|
||||
<svg
|
||||
@ -161,11 +252,25 @@ export default function StepScanPhoto(props: Props) {
|
||||
viewBox="0 0 220 220"
|
||||
enableBackground="new 0 0 0 0"
|
||||
>
|
||||
<circle fill="none" stroke="#EFF2FD" strokeWidth="2" cx="110" cy="110" r="105"/>
|
||||
<circle fill="#066fde" stroke="none" strokeWidth="3" cx="110" cy="215" r="4">
|
||||
<circle
|
||||
fill="none"
|
||||
stroke="#EFF2FD"
|
||||
strokeWidth="2"
|
||||
cx="110"
|
||||
cy="110"
|
||||
r="105"
|
||||
/>
|
||||
<circle
|
||||
fill="#066fde"
|
||||
stroke="none"
|
||||
strokeWidth="3"
|
||||
cx="110"
|
||||
cy="215"
|
||||
r="4"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="15s"
|
||||
dur={`${lineChangeDelay * lines?.length + 3000}ms`}
|
||||
type="rotate"
|
||||
from="0 110 110"
|
||||
to="360 110 110"
|
||||
|
||||
@ -1,96 +1,112 @@
|
||||
import React from 'react';
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { Step } from '@/hooks/palmistry/use-steps';
|
||||
import useSteps from '@/hooks/palmistry/use-steps';
|
||||
import ScannedPhoto from '@/components/palmistry/scanned-photo/scanned-photo';
|
||||
import { Step } from "@/hooks/palmistry/use-steps";
|
||||
import useSteps from "@/hooks/palmistry/use-steps";
|
||||
import ScannedPhoto from "@/components/palmistry/scanned-photo/scanned-photo";
|
||||
import { useSelector } from "react-redux";
|
||||
import { selectors } from "@/store";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
|
||||
enum PalmElement {
|
||||
ThumbFinger = 'Thumb finger',
|
||||
IndexFinger = 'Index finger',
|
||||
MiddleFinger = 'Middle finger',
|
||||
RingFinger = 'Ring finger',
|
||||
LittleFinger = 'Little finger',
|
||||
LifeLine = 'Life line',
|
||||
HeadLine = 'Head line',
|
||||
FateLine = 'Fate line',
|
||||
HeartLine = 'Heart line',
|
||||
}
|
||||
|
||||
const fingers = [
|
||||
PalmElement.ThumbFinger,
|
||||
PalmElement.IndexFinger,
|
||||
PalmElement.MiddleFinger,
|
||||
PalmElement.RingFinger,
|
||||
PalmElement.LittleFinger,
|
||||
];
|
||||
|
||||
const lines = [
|
||||
PalmElement.LifeLine,
|
||||
PalmElement.HeadLine,
|
||||
PalmElement.FateLine,
|
||||
PalmElement.HeartLine,
|
||||
];
|
||||
|
||||
const palmElements = [
|
||||
...fingers,
|
||||
...lines,
|
||||
];
|
||||
|
||||
const fingerChangeDelay = 1000;
|
||||
const lineChangeDelay = 1500;
|
||||
const goNextDelay = 12000;
|
||||
const startDelay = 500;
|
||||
// const goNextDelay = 12000;
|
||||
|
||||
export default function StepScanPhoto() {
|
||||
const steps = useSteps();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const storedPhoto = steps.getStoredValue(Step.Upload);
|
||||
|
||||
const [curentElementIndex, setCurrentElementIndex] = React.useState(0);
|
||||
const [photo, setPhoto] = React.useState('');
|
||||
const [smallPhotoState, setSmallPhotoState] = React.useState(false);
|
||||
const [title, setTitle] = React.useState(palmElements[0]);
|
||||
const [sholdDisplayPalmLines, setSholdDisplayPalmLines] = React.useState(false);
|
||||
const lines = useSelector(selectors.selectPalmistryLines);
|
||||
|
||||
const prevElementIndex = React.useRef<number | null>(null);
|
||||
const [currentElementIndex, setCurrentElementIndex] = useState(0);
|
||||
const [smallPhotoState, setSmallPhotoState] = useState(false);
|
||||
const [title, setTitle] = useState("");
|
||||
const [shouldDisplayPalmLines, setShouldDisplayPalmLines] = useState(false);
|
||||
|
||||
const prevElementIndex = useRef<number | null>(null);
|
||||
|
||||
const goNextElement = (delay: number) => {
|
||||
setTimeout(() => {
|
||||
setTitle(lines[currentElementIndex]?.line);
|
||||
setCurrentElementIndex((prevState) => prevState + 1);
|
||||
}, delay);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (storedPhoto) {
|
||||
setPhoto(storedPhoto);
|
||||
useEffect(() => {
|
||||
if (!lines.length) {
|
||||
return navigate(routes.client.palmistryUpload());
|
||||
}
|
||||
}, [storedPhoto]);
|
||||
}, [lines, navigate]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (curentElementIndex < palmElements.length && curentElementIndex !== prevElementIndex.current) {
|
||||
prevElementIndex.current = curentElementIndex;
|
||||
setTitle(palmElements[curentElementIndex]);
|
||||
goNextElement(fingers.includes(palmElements[curentElementIndex]) ? fingerChangeDelay : lineChangeDelay);
|
||||
setSholdDisplayPalmLines(lines.includes(palmElements[curentElementIndex]));
|
||||
useEffect(() => {
|
||||
// if (currentElementIndex === 0) {
|
||||
// new Promise((resolve) => setTimeout(resolve, startDelay));
|
||||
// }
|
||||
if (
|
||||
currentElementIndex < lines?.length &&
|
||||
currentElementIndex !== prevElementIndex.current
|
||||
) {
|
||||
prevElementIndex.current = currentElementIndex;
|
||||
goNextElement(lineChangeDelay);
|
||||
setShouldDisplayPalmLines(lines?.includes(lines[currentElementIndex]));
|
||||
}
|
||||
|
||||
if (curentElementIndex >= palmElements.length) {
|
||||
setSmallPhotoState(true);
|
||||
|
||||
setTimeout(steps.goNext, goNextDelay);
|
||||
if (currentElementIndex >= lines?.length) {
|
||||
setTimeout(() => {
|
||||
setSmallPhotoState(true);
|
||||
}, lineChangeDelay);
|
||||
setTimeout(steps.goNext, lineChangeDelay * lines.length + 8000);
|
||||
}
|
||||
}, [curentElementIndex]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentElementIndex]);
|
||||
|
||||
if (!photo) return null;
|
||||
// if (!storedPhoto) {
|
||||
// return <Title variant="h4">Upload your photo</Title>;
|
||||
// }
|
||||
|
||||
// console.log(shouldDisplayPalmLines);
|
||||
// return <Title variant="h4">Upload your photo</Title>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="palmistry-container__title">{title}</h2>
|
||||
<h2
|
||||
className="palmistry-container__title"
|
||||
style={{
|
||||
animationDelay: `${lineChangeDelay * lines.length}ms`,
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
{/* <pre>{JSON.stringify(lines, null, 2)}</pre> */}
|
||||
<ScannedPhoto
|
||||
photo={storedPhoto}
|
||||
small={smallPhotoState}
|
||||
lineChangeDelay={lineChangeDelay}
|
||||
startDelay={startDelay}
|
||||
displayLines={shouldDisplayPalmLines}
|
||||
lines={lines}
|
||||
/>
|
||||
|
||||
<ScannedPhoto photo={photo} small={smallPhotoState} displayLines={sholdDisplayPalmLines}/>
|
||||
<h2
|
||||
className="palmistry-container__waiting-title"
|
||||
style={{
|
||||
animationDelay: `${lineChangeDelay * lines.length + 2500}ms`,
|
||||
}}
|
||||
>
|
||||
We are putting together a comprehensive Palmistry Reading just for you!
|
||||
</h2>
|
||||
|
||||
<h2 className="palmistry-container__waiting-title">We are putting together a comprehensive Palmistry Reading just for you!</h2>
|
||||
|
||||
<h3 className="palmistry-container__waiting-description">Wow, looks like there is a lot we can tell about your ambitious and strong self-confident future.</h3>
|
||||
<h3
|
||||
className="palmistry-container__waiting-description"
|
||||
style={{
|
||||
animationDelay: `${lineChangeDelay * lines.length + 3000}ms`,
|
||||
}}
|
||||
>
|
||||
Wow, looks like there is a lot we can tell about your ambitious and
|
||||
strong self-confident future.
|
||||
</h3>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import React from 'react';
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import useSteps from '../../../hooks/palmistry/use-steps';
|
||||
import Button from '../button/button';
|
||||
import BiometricData from '../biometric-data/biometric-data';
|
||||
import UploadModal from '../upload-modal/upload-modal';
|
||||
import ModalOverlay from '../modal-overlay/modal-overlay';
|
||||
import PalmRecognitionErrorModal from '../palm-recognition-error-modal/palm-recognition-error-modal';
|
||||
import PalmCameraModal from '../palm-camera-modal/palm-camera-modal';
|
||||
import useSteps from "../../../hooks/palmistry/use-steps";
|
||||
import Button from "../button/button";
|
||||
import BiometricData from "../biometric-data/biometric-data";
|
||||
import UploadModal from "../upload-modal/upload-modal";
|
||||
import ModalOverlay from "../modal-overlay/modal-overlay";
|
||||
import PalmRecognitionErrorModal from "../palm-recognition-error-modal/palm-recognition-error-modal";
|
||||
import PalmCameraModal from "../palm-camera-modal/palm-camera-modal";
|
||||
import { useApi } from "@/api";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { actions } from "@/store";
|
||||
|
||||
type Props = {
|
||||
onOpenModal: (isOpen: boolean) => void;
|
||||
@ -14,12 +17,15 @@ type Props = {
|
||||
|
||||
export default function StepUpload(props: Props) {
|
||||
const steps = useSteps();
|
||||
const api = useApi();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [uploadMenuModalIsOpen, setUploadMenuModalIsOpen] = React.useState(false);
|
||||
const [isUpladProcessing, setIsUpladProcessing] = React.useState(false);
|
||||
const [recognitionErrorModalIsOpen, setRecognitionErrorModalIsOpen] = React.useState(false);
|
||||
const [palmCameraModalIsOpen, setPalmCameraModalIsOpen] = React.useState(false);
|
||||
const [palmPhoto, setPalmPhoto] = React.useState<string>();
|
||||
const [uploadMenuModalIsOpen, setUploadMenuModalIsOpen] = useState(false);
|
||||
const [isUpladProcessing, setIsUpladProcessing] = useState(false);
|
||||
const [recognitionErrorModalIsOpen, setRecognitionErrorModalIsOpen] =
|
||||
useState(false);
|
||||
const [palmCameraModalIsOpen, setPalmCameraModalIsOpen] = useState(false);
|
||||
const [palmPhoto, setPalmPhoto] = useState<string>();
|
||||
|
||||
// const imitateRequestError = () => {
|
||||
// setTimeout(() => {
|
||||
@ -28,11 +34,21 @@ export default function StepUpload(props: Props) {
|
||||
// }, 2000);
|
||||
// };
|
||||
|
||||
const onSelectFile = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const getLines = async (file: File | Blob) => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
const result = await api.getPalmistryLines({ formData });
|
||||
|
||||
dispatch(actions.palmistry.update({ lines: result }));
|
||||
};
|
||||
|
||||
const onSelectFile = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setUploadMenuModalIsOpen(false);
|
||||
|
||||
if (!event.target.files || event.target.files.length === 0) return;
|
||||
|
||||
await getLines(event.target.files[0]);
|
||||
|
||||
setIsUpladProcessing(true);
|
||||
|
||||
const reader = new FileReader();
|
||||
@ -44,46 +60,70 @@ export default function StepUpload(props: Props) {
|
||||
reader.readAsDataURL(event.target.files[0]);
|
||||
};
|
||||
|
||||
const onTakePhoto = (photo: string) => {
|
||||
const DataURIToBlob = (dataURI: string) => {
|
||||
const splitDataURI = dataURI.split(",");
|
||||
const byteString =
|
||||
splitDataURI[0].indexOf("base64") >= 0
|
||||
? atob(splitDataURI[1])
|
||||
: decodeURI(splitDataURI[1]);
|
||||
const mimeString = splitDataURI[0].split(":")[1].split(";")[0];
|
||||
|
||||
const ia = new Uint8Array(byteString.length);
|
||||
for (let i = 0; i < byteString.length; i++)
|
||||
ia[i] = byteString.charCodeAt(i);
|
||||
|
||||
return new Blob([ia], { type: mimeString });
|
||||
};
|
||||
|
||||
const onTakePhoto = async (photo: string) => {
|
||||
const file = DataURIToBlob(photo);
|
||||
await getLines(file);
|
||||
setPalmPhoto(photo as string);
|
||||
setUploadMenuModalIsOpen(false);
|
||||
setPalmCameraModalIsOpen(false);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (palmPhoto) {
|
||||
fetch(
|
||||
'https://palmistry.hint.app/api/processing',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ image: palmPhoto }),
|
||||
},
|
||||
);
|
||||
fetch("https://palmistry.hint.app/api/processing", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ image: palmPhoto }),
|
||||
});
|
||||
setIsUpladProcessing(false);
|
||||
steps.saveCurrent(palmPhoto);
|
||||
steps.goNext();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [palmPhoto]);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (recognitionErrorModalIsOpen || palmCameraModalIsOpen) {
|
||||
props.onOpenModal(true);
|
||||
} else {
|
||||
props.onOpenModal(false);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [recognitionErrorModalIsOpen, palmCameraModalIsOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="palmistry-container__title">Take your palm picture as instructed</h2>
|
||||
<h2 className="palmistry-container__title">
|
||||
Take your palm picture as instructed
|
||||
</h2>
|
||||
|
||||
<div className="palmistry-container__image-wrapper">
|
||||
<div className="palmistry-container__image-wrapper-container">
|
||||
<p className="palmistry-container__correct-title">Correct</p>
|
||||
|
||||
<div className="palmistry-container__wrapper-correct-palm-image">
|
||||
<svg width="106" height="193" viewBox="0 0 106 193" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="106"
|
||||
height="193"
|
||||
viewBox="0 0 106 193"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13.9463 117.011C14.954 138.058 32.1776 154.86 53.2104 154.86C74.2432 154.86 91.4669 138.051 92.4745 117.011H13.9463Z"
|
||||
fill="#FFFFFF"
|
||||
@ -155,12 +195,23 @@ export default function StepUpload(props: Props) {
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<rect x="3" y="3" width="100" height="187" rx="6" stroke="#9BACD7" strokeWidth="5"></rect>
|
||||
<rect
|
||||
x="3"
|
||||
y="3"
|
||||
width="100"
|
||||
height="187"
|
||||
rx="6"
|
||||
stroke="#9BACD7"
|
||||
strokeWidth="5"
|
||||
></rect>
|
||||
<path
|
||||
d="M3 169H103V184C103 187.314 100.314 190 97 190H9C5.68629 190 3 187.314 3 184V169Z"
|
||||
fill="#9BACD7"
|
||||
></path>
|
||||
<path d="M3 9C3 5.68629 5.68629 3 9 3H97C100.314 3 103 5.68629 103 9V14H3V9Z" fill="#9BACD7"></path>
|
||||
<path
|
||||
d="M3 9C3 5.68629 5.68629 3 9 3H97C100.314 3 103 5.68629 103 9V14H3V9Z"
|
||||
fill="#9BACD7"
|
||||
></path>
|
||||
<circle cx="54" cy="181" r="6" fill="#FFFFFF"></circle>
|
||||
</svg>
|
||||
|
||||
@ -184,7 +235,12 @@ export default function StepUpload(props: Props) {
|
||||
<p className="palmistry-container__wrong-title">Wrong</p>
|
||||
|
||||
<div className="palmistry-container__uncorrect-images">
|
||||
<svg width="59" height="87" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="59"
|
||||
height="87"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.146 54.95c.49 10.243 8.874 18.421 19.111 18.421 10.238 0 18.62-8.181 19.111-18.422H8.146z"
|
||||
fill="#FFFFFF"
|
||||
@ -256,19 +312,52 @@ export default function StepUpload(props: Props) {
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<rect x="3.073" y="2.998" width="48.181" height="81" rx="6" stroke="#9BACD7" strokeWidth="5"></rect>
|
||||
<rect
|
||||
x="3.073"
|
||||
y="2.998"
|
||||
width="48.181"
|
||||
height="81"
|
||||
rx="6"
|
||||
stroke="#9BACD7"
|
||||
strokeWidth="5"
|
||||
></rect>
|
||||
<path
|
||||
d="M3.073 75.27h48.181v2.728a6 6 0 0 1-6 6H9.074a6 6 0 0 1-6-6v-2.729zM3.073 7.537a4.539 4.539 0 0 1 4.539-4.539h39.104a4.539 4.539 0 0 1 4.538 4.539H3.074z"
|
||||
fill="#9BACD7"
|
||||
></path>
|
||||
<circle cx="26.99" cy="80.159" r="2.444" fill="#FFFFFF"></circle>
|
||||
<path fill="#FF5758" d="m39.053 58.875 3.326-3.61L58.62 70.234l-3.326 3.61z"></path>
|
||||
<path fill="#FF5758" d="m54.91 55.01 3.471 3.47-15.617 15.618-3.471-3.47z"></path>
|
||||
<path
|
||||
fill="#FF5758"
|
||||
d="m39.053 58.875 3.326-3.61L58.62 70.234l-3.326 3.61z"
|
||||
></path>
|
||||
<path
|
||||
fill="#FF5758"
|
||||
d="m54.91 55.01 3.471 3.47-15.617 15.618-3.471-3.47z"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
<svg width="58" height="87" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="a" maskUnits="userSpaceOnUse" x="2" y="2" width="49" height="82">
|
||||
<rect x="2.621" y="2.998" width="48.181" height="81" rx="6" fill="#D9D9D9"></rect>
|
||||
<svg
|
||||
width="58"
|
||||
height="87"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<mask
|
||||
id="a"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="2"
|
||||
y="2"
|
||||
width="49"
|
||||
height="82"
|
||||
>
|
||||
<rect
|
||||
x="2.621"
|
||||
y="2.998"
|
||||
width="48.181"
|
||||
height="81"
|
||||
rx="6"
|
||||
fill="#D9D9D9"
|
||||
></rect>
|
||||
</mask>
|
||||
<g mask="url(#a)">
|
||||
<path
|
||||
@ -343,17 +432,36 @@ export default function StepUpload(props: Props) {
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
</g>
|
||||
<rect x="2.621" y="2.998" width="48.181" height="81" rx="6" stroke="#9BACD7" strokeWidth="5"></rect>
|
||||
<rect
|
||||
x="2.621"
|
||||
y="2.998"
|
||||
width="48.181"
|
||||
height="81"
|
||||
rx="6"
|
||||
stroke="#9BACD7"
|
||||
strokeWidth="5"
|
||||
></rect>
|
||||
<path
|
||||
d="M2.621 75.27h48.181v2.728a6 6 0 0 1-6 6H8.622a6 6 0 0 1-6-6v-2.729zM2.621 7.537A4.539 4.539 0 0 1 7.16 2.998h39.103a4.539 4.539 0 0 1 4.54 4.539H2.62z"
|
||||
fill="#9BACD7"
|
||||
></path>
|
||||
<circle cx="26.538" cy="80.159" r="2.444" fill="#FFFFFF"></circle>
|
||||
<path fill="#FF5758" d="m38.148 58.02 3.326-3.61 16.243 14.968-3.326 3.61z"></path>
|
||||
<path fill="#FF5758" d="m54.007 54.154 3.47 3.47L41.86 73.244l-3.47-3.47z"></path>
|
||||
<path
|
||||
fill="#FF5758"
|
||||
d="m38.148 58.02 3.326-3.61 16.243 14.968-3.326 3.61z"
|
||||
></path>
|
||||
<path
|
||||
fill="#FF5758"
|
||||
d="m54.007 54.154 3.47 3.47L41.86 73.244l-3.47-3.47z"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
<svg width="62" height="87" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="62"
|
||||
height="87"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M45.491 28.62c-.49-10.245-8.873-18.423-19.11-18.423-10.238 0-18.621 8.181-19.111 18.422H45.49z"
|
||||
fill="#FFFFFF"
|
||||
@ -425,17 +533,36 @@ export default function StepUpload(props: Props) {
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<rect x="2.717" y="2.998" width="48.181" height="81" rx="6" stroke="#9BACD7" strokeWidth="5"></rect>
|
||||
<rect
|
||||
x="2.717"
|
||||
y="2.998"
|
||||
width="48.181"
|
||||
height="81"
|
||||
rx="6"
|
||||
stroke="#9BACD7"
|
||||
strokeWidth="5"
|
||||
></rect>
|
||||
<path
|
||||
d="M2.717 75.27h48.18v2.728a6 6 0 0 1-6 6H8.718a6 6 0 0 1-6-6v-2.729zM2.717 7.537a4.539 4.539 0 0 1 4.539-4.539h39.103a4.539 4.539 0 0 1 4.539 4.539H2.717z"
|
||||
fill="#9BACD7"
|
||||
></path>
|
||||
<circle cx="26.633" cy="80.159" r="2.444" fill="#FFFFFF"></circle>
|
||||
<path fill="#FF5758" d="m41.645 58.875 3.326-3.61 16.242 14.968-3.326 3.61z"></path>
|
||||
<path fill="#FF5758" d="m57.502 55.01 3.47 3.47-15.617 15.618-3.47-3.47z"></path>
|
||||
<path
|
||||
fill="#FF5758"
|
||||
d="m41.645 58.875 3.326-3.61 16.242 14.968-3.326 3.61z"
|
||||
></path>
|
||||
<path
|
||||
fill="#FF5758"
|
||||
d="m57.502 55.01 3.47 3.47-15.617 15.618-3.47-3.47z"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
<svg width="67" height="111" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="67"
|
||||
height="111"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M.424 79.086c.85 17.745 15.372 31.911 33.105 31.911 17.733 0 32.255-14.172 33.104-31.911H.424z"
|
||||
fill="#FFFFFF"
|
||||
@ -452,14 +579,28 @@ export default function StepUpload(props: Props) {
|
||||
d="M33.545 107.92c-16.064 0-29.333-12.823-30.188-28.858H.332C1.182 96.808 15.812 111 33.545 111s32.532-14.197 33.382-31.936h-3.043c-.837 16.052-14.275 28.857-30.339 28.857z"
|
||||
fill="#DEE5F8"
|
||||
></path>
|
||||
<rect x="8.207" y="14.375" width="48.755" height="81.964" rx="6" stroke="#9BACD7" strokeWidth="5"></rect>
|
||||
<rect
|
||||
x="8.207"
|
||||
y="14.375"
|
||||
width="48.755"
|
||||
height="81.964"
|
||||
rx="6"
|
||||
stroke="#9BACD7"
|
||||
strokeWidth="5"
|
||||
></rect>
|
||||
<path
|
||||
d="M8.207 87.508h48.755v2.832a6 6 0 0 1-6 6H14.207a6 6 0 0 1-6-6v-2.832zM8.207 18.968a4.593 4.593 0 0 1 4.593-4.593h39.569a4.593 4.593 0 0 1 4.593 4.593H8.207z"
|
||||
fill="#9BACD7"
|
||||
></path>
|
||||
<circle cx="32.408" cy="92.454" r="2.473" fill="#FFFFFF"></circle>
|
||||
<path fill="#FF5758" d="m44.629 78.916 3.366-3.653L64.43 90.408l-3.366 3.653z"></path>
|
||||
<path fill="#FF5758" d="m60.676 75.006 3.512 3.512-15.803 15.803-3.512-3.512z"></path>
|
||||
<path
|
||||
fill="#FF5758"
|
||||
d="m44.629 78.916 3.366-3.653L64.43 90.408l-3.366 3.653z"
|
||||
></path>
|
||||
<path
|
||||
fill="#FF5758"
|
||||
d="m60.676 75.006 3.512 3.512-15.803 15.803-3.512-3.512z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@ -473,10 +614,10 @@ export default function StepUpload(props: Props) {
|
||||
onClick={() => setUploadMenuModalIsOpen(true)}
|
||||
isProcessing={isUpladProcessing}
|
||||
>
|
||||
{isUpladProcessing && 'Loading photo' || 'Take a picture now'}
|
||||
{(isUpladProcessing && "Loading photo") || "Take a picture now"}
|
||||
</Button>
|
||||
|
||||
<BiometricData/>
|
||||
<BiometricData />
|
||||
|
||||
{uploadMenuModalIsOpen && (
|
||||
<UploadModal
|
||||
@ -486,10 +627,12 @@ export default function StepUpload(props: Props) {
|
||||
/>
|
||||
)}
|
||||
|
||||
{isUpladProcessing && <ModalOverlay/>}
|
||||
{isUpladProcessing && <ModalOverlay />}
|
||||
|
||||
{recognitionErrorModalIsOpen && (
|
||||
<PalmRecognitionErrorModal onClose={() => setRecognitionErrorModalIsOpen(false)}/>
|
||||
<PalmRecognitionErrorModal
|
||||
onClose={() => setRecognitionErrorModalIsOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{palmCameraModalIsOpen && (
|
||||
|
||||
@ -220,6 +220,9 @@ const routes = {
|
||||
assistants: () => [apiHost, prefix, "ai", "assistants.json"].join("/"),
|
||||
setExternalChatIdAssistants: (chatId: string) =>
|
||||
[apiHost, prefix, "ai", "assistants", chatId, "chats.json"].join("/"),
|
||||
// Palmistry
|
||||
getPalmistryLines: () =>
|
||||
["https://api.aura.witapps.us", "palmistry", "lines"].join("/"),
|
||||
},
|
||||
openAi: {
|
||||
createThread: () => [openAIHost, openAiPrefix, "threads"].join("/"),
|
||||
|
||||
@ -63,6 +63,10 @@ import {
|
||||
selectUserCallbacksDescription,
|
||||
selectUserCallbacksPrevStat,
|
||||
} from "./userCallbacks";
|
||||
import palmistry, {
|
||||
actions as palmistryActions,
|
||||
selectPalmistryLines,
|
||||
} from "./palmistry";
|
||||
|
||||
const preloadedState = loadStore();
|
||||
export const actions = {
|
||||
@ -80,6 +84,7 @@ export const actions = {
|
||||
onboardingConfig: onboardingConfigActions,
|
||||
questionnaire: questionnaireActions,
|
||||
userConfig: userConfigActions,
|
||||
palmistry: palmistryActions,
|
||||
reset: createAction("reset"),
|
||||
};
|
||||
export const selectors = {
|
||||
@ -109,6 +114,7 @@ export const selectors = {
|
||||
selectIsShowTryApp,
|
||||
selectIsForceShortPath,
|
||||
selectOpenAiToken,
|
||||
selectPalmistryLines,
|
||||
...formSelectors,
|
||||
};
|
||||
|
||||
@ -127,6 +133,7 @@ export const reducer = combineReducers({
|
||||
onboardingConfig,
|
||||
questionnaire,
|
||||
userConfig,
|
||||
palmistry,
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof reducer>;
|
||||
|
||||
29
src/store/palmistry.ts
Normal file
29
src/store/palmistry.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { IPalmistryLine } from "@/api/resources/Palmistry";
|
||||
import { createSlice, createSelector } from "@reduxjs/toolkit";
|
||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface IPalmistry {
|
||||
lines: IPalmistryLine[];
|
||||
}
|
||||
|
||||
const initialState: IPalmistry = {
|
||||
lines: [],
|
||||
};
|
||||
|
||||
const palmistrySlice = createSlice({
|
||||
name: "palmistry",
|
||||
initialState,
|
||||
reducers: {
|
||||
update(state, action: PayloadAction<Partial<IPalmistry>>) {
|
||||
return { ...state, ...action.payload };
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => builder.addCase("reset", () => initialState),
|
||||
});
|
||||
|
||||
export const { actions } = palmistrySlice;
|
||||
export const selectPalmistryLines = createSelector(
|
||||
(state: { palmistry: IPalmistry }) => state.palmistry.lines,
|
||||
(palmistry) => palmistry
|
||||
);
|
||||
export default palmistrySlice.reducer;
|
||||
Loading…
Reference in New Issue
Block a user