Develop
This commit is contained in:
parent
498ccdddea
commit
9d99d6f90e
@ -4,10 +4,19 @@ export interface Payload {
|
|||||||
formData: FormData;
|
formData: FormData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Response = IPalmistryLine[];
|
export type Response = {
|
||||||
|
fingers: IPalmistryFinger[];
|
||||||
|
lines: IPalmistryLine[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IPalmistryFinger {
|
||||||
|
name: string;
|
||||||
|
point: IPalmistryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IPalmistryLine {
|
export interface IPalmistryLine {
|
||||||
line: string;
|
name: string;
|
||||||
|
line: number;
|
||||||
points: IPalmistryPoint[];
|
points: IPalmistryPoint[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ interface ModalProps {
|
|||||||
isCloseButtonVisible?: boolean;
|
isCloseButtonVisible?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
containerClassName?: string;
|
containerClassName?: string;
|
||||||
|
type?: "hidden" | "normal";
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ function Modal({
|
|||||||
isCloseButtonVisible = true,
|
isCloseButtonVisible = true,
|
||||||
className = "",
|
className = "",
|
||||||
containerClassName = "",
|
containerClassName = "",
|
||||||
|
type = "normal",
|
||||||
onClose,
|
onClose,
|
||||||
}: ModalProps): JSX.Element {
|
}: ModalProps): JSX.Element {
|
||||||
const handleClose = (event: React.MouseEvent) => {
|
const handleClose = (event: React.MouseEvent) => {
|
||||||
@ -34,9 +36,14 @@ function Modal({
|
|||||||
};
|
};
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
if (!open) return <></>;
|
if (!open && type === "normal") return <></>;
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.modal} ${className}`} onClick={handleClose}>
|
<div
|
||||||
|
className={`${styles.modal} ${className} ${
|
||||||
|
type === "hidden" && !open ? styles.hidden : ""
|
||||||
|
}`}
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
<div className={`${styles["modal-content"]} ${containerClassName}`}>
|
<div className={`${styles["modal-content"]} ${containerClassName}`}>
|
||||||
{isCloseButtonVisible && (
|
{isCloseButtonVisible && (
|
||||||
<button className={styles["modal-close-btn"]} onClick={handleClose} />
|
<button className={styles["modal-close-btn"]} onClick={handleClose} />
|
||||||
|
|||||||
@ -12,6 +12,11 @@
|
|||||||
z-index: 2000;
|
z-index: 2000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
height: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import Title from "@/components/Title";
|
import Title from "@/components/Title";
|
||||||
import styles from "./styles.module.css";
|
import styles from "./styles.module.css";
|
||||||
import PaymentMethodsChoice from "../PaymentMethodsChoice";
|
import PaymentMethodsChoice from "../pages/TrialPayment/components/PaymentMethodsChoice";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods";
|
import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods";
|
||||||
import { Elements } from "@stripe/react-stripe-js";
|
import { Elements } from "@stripe/react-stripe-js";
|
||||||
@ -9,7 +9,7 @@ import { AvailablePaymentMethods, Stripe, loadStripe } from "@stripe/stripe-js";
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectors } from "@/store";
|
import { selectors } from "@/store";
|
||||||
import Loader from "@/components/Loader";
|
import Loader from "@/components/Loader";
|
||||||
import SecurityPayments from "../SecurityPayments";
|
import SecurityPayments from "../pages/TrialPayment/components/SecurityPayments";
|
||||||
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
|
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
|
||||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||||
import { useMakePayment } from "@/hooks/payment/useMakePayment";
|
import { useMakePayment } from "@/hooks/payment/useMakePayment";
|
||||||
@ -21,7 +21,7 @@ interface IPaymentModalProps {
|
|||||||
activeProduct?: IPaywallProduct;
|
activeProduct?: IPaywallProduct;
|
||||||
noTrial?: boolean;
|
noTrial?: boolean;
|
||||||
returnUrl?: string;
|
returnUrl?: string;
|
||||||
placementKey?: EPlacementKeys;
|
placementKey: EPlacementKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPrice = (product: IPaywallProduct | null) => {
|
const getPrice = (product: IPaywallProduct | null) => {
|
||||||
@ -35,7 +35,7 @@ function PaymentModal({
|
|||||||
activeProduct,
|
activeProduct,
|
||||||
noTrial,
|
noTrial,
|
||||||
returnUrl,
|
returnUrl,
|
||||||
placementKey = EPlacementKeys["aura.placement.main"],
|
placementKey,
|
||||||
}: IPaymentModalProps) {
|
}: IPaymentModalProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [stripePromise, setStripePromise] =
|
const [stripePromise, setStripePromise] =
|
||||||
@ -40,7 +40,7 @@ function EmailEnterPage({
|
|||||||
const [isAuth, setIsAuth] = useState(false);
|
const [isAuth, setIsAuth] = useState(false);
|
||||||
const { subPlan } = useParams();
|
const { subPlan } = useParams();
|
||||||
const { width: pageWidth, elementRef: pageRef } = useDynamicSize({});
|
const { width: pageWidth, elementRef: pageRef } = useDynamicSize({});
|
||||||
const { error, isLoading, authorization } = useAuthentication();
|
const { error, isLoading, token, user, authorization } = useAuthentication();
|
||||||
const { gender } = useSelector(selectors.selectQuestionnaire);
|
const { gender } = useSelector(selectors.selectQuestionnaire);
|
||||||
const activeProductFromStore = useSelector(selectors.selectActiveProduct);
|
const activeProductFromStore = useSelector(selectors.selectActiveProduct);
|
||||||
const { products } = usePaywall({
|
const { products } = usePaywall({
|
||||||
@ -106,17 +106,34 @@ function EmailEnterPage({
|
|||||||
source = ESourceAuthorization["aura.moons"];
|
source = ESourceAuthorization["aura.moons"];
|
||||||
}
|
}
|
||||||
await authorization(email, source);
|
await authorization(email, source);
|
||||||
dispatch(
|
|
||||||
actions.payment.update({
|
|
||||||
activeProduct,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setIsAuth(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
navigate(redirectUrl);
|
|
||||||
}, 1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user && token?.length && !isLoading && !error) {
|
||||||
|
dispatch(
|
||||||
|
actions.payment.update({
|
||||||
|
activeProduct,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setIsAuth(true);
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
navigate(redirectUrl);
|
||||||
|
}, 1000);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
activeProduct,
|
||||||
|
dispatch,
|
||||||
|
error,
|
||||||
|
isLoading,
|
||||||
|
navigate,
|
||||||
|
redirectUrl,
|
||||||
|
token?.length,
|
||||||
|
user,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`${styles.page} page`}
|
className={`${styles.page} page`}
|
||||||
@ -134,11 +151,11 @@ function EmailEnterPage({
|
|||||||
</Title>
|
</Title>
|
||||||
<p className={styles["not-share"]}>{t("we_dont_share")}</p>
|
<p className={styles["not-share"]}>{t("we_dont_share")}</p>
|
||||||
<EmailInput
|
<EmailInput
|
||||||
name="email"
|
name="email"
|
||||||
value={email}
|
value={email}
|
||||||
placeholder={t("your_email")}
|
placeholder={t("your_email")}
|
||||||
onValid={handleValidEmail}
|
onValid={handleValidEmail}
|
||||||
onInvalid={() => setIsValidEmail(false)}
|
onInvalid={() => setIsValidEmail(false)}
|
||||||
/>
|
/>
|
||||||
<NameInput
|
<NameInput
|
||||||
value={name}
|
value={name}
|
||||||
|
|||||||
@ -1,197 +0,0 @@
|
|||||||
import Title from "@/components/Title";
|
|
||||||
import styles from "./styles.module.css";
|
|
||||||
import PaymentMethodsChoice from "../PaymentMethodsChoice";
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
|
||||||
import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods";
|
|
||||||
import { Elements } from "@stripe/react-stripe-js";
|
|
||||||
import CheckoutForm from "@/components/PaymentPage/methods/CheckoutForm";
|
|
||||||
import { AvailablePaymentMethods, Stripe, loadStripe } from "@stripe/stripe-js";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { selectors } from "@/store";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import routes from "@/routes";
|
|
||||||
import Loader from "@/components/Loader";
|
|
||||||
import SecurityPayments from "../SecurityPayments";
|
|
||||||
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
|
|
||||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
|
||||||
import { useMakePayment } from "@/hooks/payment/useMakePayment";
|
|
||||||
import ExpressCheckoutStripe from "@/components/PaymentPage/methods/ExpressCheckoutStripe";
|
|
||||||
|
|
||||||
interface IPaymentModalProps {
|
|
||||||
activeProduct?: IPaywallProduct;
|
|
||||||
noTrial?: boolean;
|
|
||||||
returnUrl?: string;
|
|
||||||
placementKey?: EPlacementKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getPrice = (product: IPaywallProduct) => {
|
|
||||||
return (product.trialPrice || 0) / 100;
|
|
||||||
};
|
|
||||||
|
|
||||||
function PaymentModal({
|
|
||||||
activeProduct,
|
|
||||||
noTrial,
|
|
||||||
returnUrl,
|
|
||||||
placementKey = EPlacementKeys["aura.placement.redesign.main"],
|
|
||||||
}: IPaymentModalProps) {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [stripePromise, setStripePromise] =
|
|
||||||
useState<Promise<Stripe | null> | null>(null);
|
|
||||||
|
|
||||||
const activeProductFromStore = useSelector(selectors.selectActiveProduct);
|
|
||||||
const _activeProduct = activeProduct ? activeProduct : activeProductFromStore;
|
|
||||||
const { products, paywallId, placementId } = usePaywall({ placementKey });
|
|
||||||
|
|
||||||
const {
|
|
||||||
paymentIntentId,
|
|
||||||
clientSecret,
|
|
||||||
returnUrl: checkoutUrl,
|
|
||||||
paymentType,
|
|
||||||
publicKey,
|
|
||||||
isLoading: isLoadingPayment,
|
|
||||||
error,
|
|
||||||
} = useMakePayment({
|
|
||||||
productId: _activeProduct?._id || "",
|
|
||||||
paywallId,
|
|
||||||
placementId,
|
|
||||||
returnPaidUrl: returnUrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [availableMethods, setAvailableMethods] = useState<
|
|
||||||
AvailablePaymentMethods | undefined
|
|
||||||
>();
|
|
||||||
|
|
||||||
const [isLoadingExpressCheckout, setIsLoadingExpressCheckout] =
|
|
||||||
useState(true);
|
|
||||||
|
|
||||||
const isLoading = useMemo(() => {
|
|
||||||
return isLoadingPayment || isLoadingExpressCheckout;
|
|
||||||
}, [isLoadingPayment, isLoadingExpressCheckout]);
|
|
||||||
|
|
||||||
if (checkoutUrl?.length) {
|
|
||||||
window.location.href = checkoutUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
const paymentMethodsButtons = useMemo(() => {
|
|
||||||
return paymentMethods(availableMethods || null);
|
|
||||||
}, [availableMethods]);
|
|
||||||
|
|
||||||
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(
|
|
||||||
paymentMethodsButtons[0].id
|
|
||||||
);
|
|
||||||
|
|
||||||
const onSelectPaymentMethod = (method: EPaymentMethod) => {
|
|
||||||
setSelectedPaymentMethod(method);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
if (!products?.length || !publicKey) return;
|
|
||||||
setStripePromise(loadStripe(publicKey));
|
|
||||||
const isActiveProduct = products.find(
|
|
||||||
(product) => product._id === _activeProduct?._id
|
|
||||||
);
|
|
||||||
if (!_activeProduct || !isActiveProduct) {
|
|
||||||
navigate(routes.client.trialChoiceV1());
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, [_activeProduct, navigate, products, publicKey]);
|
|
||||||
|
|
||||||
const onAvailableExpressCheckout = (
|
|
||||||
isAvailable: boolean,
|
|
||||||
availableMethods: AvailablePaymentMethods | undefined
|
|
||||||
) => {
|
|
||||||
if (isAvailable && availableMethods) {
|
|
||||||
setAvailableMethods(availableMethods);
|
|
||||||
return setSelectedPaymentMethod(EPaymentMethod.PAYMENT_BUTTONS);
|
|
||||||
}
|
|
||||||
return setAvailableMethods(undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (error?.length) {
|
|
||||||
return (
|
|
||||||
<div className={styles["payment-modal"]}>
|
|
||||||
<Title variant="h3" className={styles.title}>
|
|
||||||
Something went wrong
|
|
||||||
</Title>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isLoading && (
|
|
||||||
<div className={styles["payment-modal"]}>
|
|
||||||
<div className={styles["payment-loader"]}>
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className={`${styles["payment-modal"]} ${isLoading ? styles.hide : ""}`}
|
|
||||||
>
|
|
||||||
<Title variant="h3" className={styles.title}>
|
|
||||||
Choose payment method
|
|
||||||
</Title>
|
|
||||||
<PaymentMethodsChoice
|
|
||||||
paymentMethods={paymentMethodsButtons}
|
|
||||||
selectedPaymentMethod={selectedPaymentMethod}
|
|
||||||
onSelectPaymentMethod={onSelectPaymentMethod}
|
|
||||||
/>
|
|
||||||
{_activeProduct && (
|
|
||||||
<div>
|
|
||||||
{!noTrial && (
|
|
||||||
<>
|
|
||||||
<p className={styles["sub-plan-description"]}>
|
|
||||||
You will be charged only{" "}
|
|
||||||
<b>${getPrice(_activeProduct)} for your 3-day trial.</b>
|
|
||||||
</p>
|
|
||||||
<p className={styles["sub-plan-description"]}>
|
|
||||||
We`ll <b>email you a reminder</b> before your trial period
|
|
||||||
ends.
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<p className={styles["sub-plan-description"]}>
|
|
||||||
Cancel anytime. The charge will appear on your bill as witapps.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={styles["payment-method-container"]}>
|
|
||||||
{stripePromise && clientSecret && (
|
|
||||||
<>
|
|
||||||
<Elements stripe={stripePromise} options={{ clientSecret }}>
|
|
||||||
<ExpressCheckoutStripe
|
|
||||||
clientSecret={clientSecret}
|
|
||||||
returnUrl={returnUrl}
|
|
||||||
isHide={
|
|
||||||
selectedPaymentMethod !== EPaymentMethod.PAYMENT_BUTTONS
|
|
||||||
}
|
|
||||||
onAvailable={(_isAvailable, _availableMethods) =>
|
|
||||||
onAvailableExpressCheckout(_isAvailable, _availableMethods)
|
|
||||||
}
|
|
||||||
onChangeLoading={(isLoading) =>
|
|
||||||
setIsLoadingExpressCheckout(isLoading)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Elements>
|
|
||||||
<Elements stripe={stripePromise} options={{ clientSecret }}>
|
|
||||||
<CheckoutForm
|
|
||||||
confirmType={paymentType}
|
|
||||||
subscriptionReceiptId={paymentIntentId}
|
|
||||||
returnUrl={returnUrl}
|
|
||||||
isHide={selectedPaymentMethod !== EPaymentMethod.CREDIT_CARD}
|
|
||||||
/>
|
|
||||||
</Elements>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<SecurityPayments />
|
|
||||||
<p className={styles.address}>1123 Rimer Dr Moraga, California 94556</p>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PaymentModal;
|
|
||||||
@ -15,7 +15,6 @@ import OftenAsk from "./components/OftenAsk";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import WithPartnerInformation from "./components/WithPartnerInformation";
|
import WithPartnerInformation from "./components/WithPartnerInformation";
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import PaymentModal from "./components/PaymentModal";
|
|
||||||
import { trialPaymentPointsList } from "@/data/pointsLists";
|
import { trialPaymentPointsList } from "@/data/pointsLists";
|
||||||
import { trialPaymentReviews } from "@/data/reviews";
|
import { trialPaymentReviews } from "@/data/reviews";
|
||||||
import TrialPaymentHeader from "./components/Header";
|
import TrialPaymentHeader from "./components/Header";
|
||||||
@ -24,6 +23,7 @@ import BackgroundTopBlob from "../../ui/BackgroundTopBlob";
|
|||||||
import { useDynamicSize } from "@/hooks/useDynamicSize";
|
import { useDynamicSize } from "@/hooks/useDynamicSize";
|
||||||
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
|
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
|
||||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||||
|
import PaymentModal from "@/components/PaymentModal";
|
||||||
|
|
||||||
function TrialPaymentPage() {
|
function TrialPaymentPage() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -118,8 +118,11 @@ function TrialPaymentPage() {
|
|||||||
containerClassName={styles.modal}
|
containerClassName={styles.modal}
|
||||||
open={isOpenPaymentModal}
|
open={isOpenPaymentModal}
|
||||||
onClose={handleDiscount}
|
onClose={handleDiscount}
|
||||||
|
type="hidden"
|
||||||
>
|
>
|
||||||
<PaymentModal />
|
<PaymentModal
|
||||||
|
placementKey={EPlacementKeys["aura.placement.redesign.main"]}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
<BackgroundTopBlob
|
<BackgroundTopBlob
|
||||||
width={pageWidth}
|
width={pageWidth}
|
||||||
|
|||||||
@ -3,12 +3,12 @@ import styles from "./styles.module.css";
|
|||||||
import MainButton from "@/components/MainButton";
|
import MainButton from "@/components/MainButton";
|
||||||
import PaymentDiscountTable from "./PaymentDiscountTable";
|
import PaymentDiscountTable from "./PaymentDiscountTable";
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import PaymentModal from "../TrialPayment/components/PaymentModal";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { actions, selectors } from "@/store";
|
import { actions, selectors } from "@/store";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||||
|
import PaymentModal from "@/components/PaymentModal";
|
||||||
|
|
||||||
function TrialPaymentWithDiscount() {
|
function TrialPaymentWithDiscount() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -38,7 +38,7 @@ function TrialPaymentWithDiscount() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`${styles.page} page`}>
|
<section className={`${styles.page} page`}>
|
||||||
<Modal open={isOpenPaymentModal} onClose={handleClose}>
|
<Modal open={isOpenPaymentModal} onClose={handleClose} type="hidden">
|
||||||
<PaymentModal
|
<PaymentModal
|
||||||
placementKey={EPlacementKeys["aura.placement.secret.discount"]}
|
placementKey={EPlacementKeys["aura.placement.secret.discount"]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import styles from "./styles.module.css";
|
|||||||
import ReservedTimer from "./components/ReservedTimer";
|
import ReservedTimer from "./components/ReservedTimer";
|
||||||
import MainButton from "@/components/MainButton";
|
import MainButton from "@/components/MainButton";
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import PaymentModal from "../../TrialPayment/components/PaymentModal";
|
import PaymentModal from "@/components/PaymentModal";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||||
@ -37,6 +37,7 @@ function MarketingTrialPayment() {
|
|||||||
containerClassName={styles.modal}
|
containerClassName={styles.modal}
|
||||||
open={isOpenPaymentModal}
|
open={isOpenPaymentModal}
|
||||||
onClose={handleCloseModal}
|
onClose={handleCloseModal}
|
||||||
|
type="hidden"
|
||||||
>
|
>
|
||||||
<PaymentModal
|
<PaymentModal
|
||||||
placementKey={EPlacementKeys["aura.placement.email.marketing"]}
|
placementKey={EPlacementKeys["aura.placement.email.marketing"]}
|
||||||
@ -52,7 +53,9 @@ function MarketingTrialPayment() {
|
|||||||
<p className={styles.description}>No pressure. Cancel anytime</p>
|
<p className={styles.description}>No pressure. Cancel anytime</p>
|
||||||
<div className={styles["total-today"]}>
|
<div className={styles["total-today"]}>
|
||||||
<p className={styles.description}>Total today:</p>
|
<p className={styles.description}>Total today:</p>
|
||||||
<p className={styles.value}>${(products[0]?.trialPrice / 100).toFixed(2) || 0}</p>
|
<p className={styles.value}>
|
||||||
|
${(products[0]?.trialPrice / 100).toFixed(2) || 0}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.line} />
|
<div className={styles.line} />
|
||||||
<div className={styles["code-container"]}>
|
<div className={styles["code-container"]}>
|
||||||
@ -71,7 +74,11 @@ function MarketingTrialPayment() {
|
|||||||
<p className={styles["sale-description"]}>Save $10 every period</p>
|
<p className={styles["sale-description"]}>Save $10 every period</p>
|
||||||
<div className={styles.line} />
|
<div className={styles.line} />
|
||||||
<p className={styles["text-description"]}>
|
<p className={styles["text-description"]}>
|
||||||
You will be charged only <b>${(products[0]?.trialPrice / 100).toFixed(2) || 0} for your 7-day trial.</b>{" "}
|
You will be charged only{" "}
|
||||||
|
<b>
|
||||||
|
${(products[0]?.trialPrice / 100).toFixed(2) || 0} for your 7-day
|
||||||
|
trial.
|
||||||
|
</b>{" "}
|
||||||
Subscription <b>renews automatically</b> until cancelled. You{" "}
|
Subscription <b>renews automatically</b> until cancelled. You{" "}
|
||||||
<b>can cancel at any time</b> before the end of the trial.
|
<b>can cancel at any time</b> before the end of the trial.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
.payment-modal {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-height: 250px;
|
|
||||||
gap: 25px;
|
|
||||||
color: #2f2e37;
|
|
||||||
}
|
|
||||||
|
|
||||||
.payment-modal.hide {
|
|
||||||
min-height: 0;
|
|
||||||
height: 0;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-plan-description {
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 150%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.payment-method-container {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.address {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.payment-method {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.address {
|
|
||||||
color: gray;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
@ -16,7 +16,7 @@ import OftenAsk from "./components/OftenAsk";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import WithPartnerInformation from "./components/WithPartnerInformation";
|
import WithPartnerInformation from "./components/WithPartnerInformation";
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import PaymentModal from "./components/PaymentModal";
|
import PaymentModal from "@/components/PaymentModal";
|
||||||
import { trialPaymentPointsList } from "@/data/pointsLists";
|
import { trialPaymentPointsList } from "@/data/pointsLists";
|
||||||
import { trialPaymentReviews } from "@/data/reviews";
|
import { trialPaymentReviews } from "@/data/reviews";
|
||||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||||
@ -111,8 +111,9 @@ function TrialPaymentPage() {
|
|||||||
containerClassName={styles.modal}
|
containerClassName={styles.modal}
|
||||||
open={isOpenPaymentModal}
|
open={isOpenPaymentModal}
|
||||||
onClose={handleDiscount}
|
onClose={handleDiscount}
|
||||||
|
type="hidden"
|
||||||
>
|
>
|
||||||
<PaymentModal />
|
<PaymentModal placementKey={EPlacementKeys["aura.placement.main"]} />
|
||||||
</Modal>
|
</Modal>
|
||||||
<Header buttonClick={openStripeModal} />
|
<Header buttonClick={openStripeModal} />
|
||||||
{singleOrWithPartner === "partner" && (
|
{singleOrWithPartner === "partner" && (
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import styles from "./styles.module.css";
|
|||||||
import MainButton from "@/components/MainButton";
|
import MainButton from "@/components/MainButton";
|
||||||
import PaymentDiscountTable from "./PaymentDiscountTable";
|
import PaymentDiscountTable from "./PaymentDiscountTable";
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import PaymentModal from "../TrialPayment/components/PaymentModal";
|
import PaymentModal from "@/components/PaymentModal";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||||
@ -39,7 +39,7 @@ function TrialPaymentWithDiscount() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`${styles.page} page`}>
|
<section className={`${styles.page} page`}>
|
||||||
<Modal open={isOpenPaymentModal} onClose={handleClose}>
|
<Modal open={isOpenPaymentModal} onClose={handleClose} type="hidden">
|
||||||
<PaymentModal
|
<PaymentModal
|
||||||
placementKey={EPlacementKeys["aura.placement.secret.discount"]}
|
placementKey={EPlacementKeys["aura.placement.secret.discount"]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import "./payment-screen.css";
|
|||||||
import useSteps, { Step } from "@/hooks/palmistry/use-steps";
|
import useSteps, { Step } from "@/hooks/palmistry/use-steps";
|
||||||
import useTimer from "@/hooks/palmistry/use-timer";
|
import useTimer from "@/hooks/palmistry/use-timer";
|
||||||
import HeaderLogo from "@/components/palmistry/header-logo/header-logo";
|
import HeaderLogo from "@/components/palmistry/header-logo/header-logo";
|
||||||
import PaymentModal from "@/components/pages/TrialPayment/components/PaymentModal";
|
import PaymentModal from "@/components/PaymentModal";
|
||||||
import { selectors } from "@/store";
|
import { selectors } from "@/store";
|
||||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||||
import { useSearchParams } from "react-router-dom";
|
import { useSearchParams } from "react-router-dom";
|
||||||
|
|||||||
@ -51,26 +51,6 @@
|
|||||||
transform-origin: center center;
|
transform-origin: center center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scanned-photo__finger-point_thumb {
|
|
||||||
animation-delay: 0s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scanned-photo__finger-point_index {
|
|
||||||
animation-delay: 1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scanned-photo__finger-point_middle {
|
|
||||||
animation-delay: 2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scanned-photo__finger-point_ring {
|
|
||||||
animation-delay: 3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scanned-photo__finger-point_little {
|
|
||||||
animation-delay: 4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scanned-photo__line {
|
.scanned-photo__line {
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
stroke-linejoin: round;
|
stroke-linejoin: round;
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
import { IPalmistryLine, IPalmistryPoint } from "@/api/resources/Palmistry";
|
import {
|
||||||
|
IPalmistryFinger,
|
||||||
|
IPalmistryLine,
|
||||||
|
IPalmistryPoint,
|
||||||
|
} from "@/api/resources/Palmistry";
|
||||||
import "./scanned-photo.css";
|
import "./scanned-photo.css";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
@ -7,13 +11,15 @@ type Props = {
|
|||||||
small: boolean;
|
small: boolean;
|
||||||
displayLines: boolean;
|
displayLines: boolean;
|
||||||
lines: IPalmistryLine[];
|
lines: IPalmistryLine[];
|
||||||
lineChangeDelay: number;
|
fingers: IPalmistryFinger[];
|
||||||
|
drawElementChangeDelay: number;
|
||||||
startDelay: number;
|
startDelay: number;
|
||||||
|
drawElements: Array<IPalmistryLine | IPalmistryFinger>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function StepScanPhoto(props: Props) {
|
export default function StepScanPhoto(props: Props) {
|
||||||
const className = ["scanned-photo"];
|
const className = ["scanned-photo"];
|
||||||
const { lines, lineChangeDelay } = props;
|
const { lines, drawElementChangeDelay, fingers, drawElements } = props;
|
||||||
const imageRef = useRef<HTMLImageElement>(null);
|
const imageRef = useRef<HTMLImageElement>(null);
|
||||||
const linesRef = useRef<SVGPathElement[]>([]);
|
const linesRef = useRef<SVGPathElement[]>([]);
|
||||||
const [isImageLoaded, setIsImageLoaded] = useState(false);
|
const [isImageLoaded, setIsImageLoaded] = useState(false);
|
||||||
@ -51,7 +57,7 @@ export default function StepScanPhoto(props: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// const getAnimationDelayOfLine = (index: number) => {
|
// const getAnimationDelayOfLine = (index: number) => {
|
||||||
// return `${lineChangeDelay * index + startDelay}ms`;
|
// return `${drawElementChangeDelay * index + startDelay}ms`;
|
||||||
// };
|
// };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -65,7 +71,9 @@ export default function StepScanPhoto(props: Props) {
|
|||||||
<div
|
<div
|
||||||
className="scanned-photo__stick"
|
className="scanned-photo__stick"
|
||||||
style={{
|
style={{
|
||||||
animationDelay: `${lineChangeDelay * lines.length + 2500}ms`,
|
animationDelay: `${
|
||||||
|
drawElementChangeDelay * drawElements?.length + 2500
|
||||||
|
}ms`,
|
||||||
maxWidth: `${imageWidth}px`,
|
maxWidth: `${imageWidth}px`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -84,112 +92,52 @@ export default function StepScanPhoto(props: Props) {
|
|||||||
viewBox={`0 0 ${imageWidth} ${imageHeight}`}
|
viewBox={`0 0 ${imageWidth} ${imageHeight}`}
|
||||||
className="scanned-photo__svg-objects"
|
className="scanned-photo__svg-objects"
|
||||||
>
|
>
|
||||||
{/* <svg x="235" y="211" height="24px" width="24px">
|
{fingers.map((finger, index) => {
|
||||||
<circle
|
return (
|
||||||
cx="50%"
|
<svg
|
||||||
cy="50%"
|
x={finger.point.x * imageWidth - 12}
|
||||||
r="11"
|
y={finger.point.y * imageHeight - 12}
|
||||||
fill="white"
|
height="24px"
|
||||||
opacity="0.3"
|
width="24px"
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_thumb"
|
key={index}
|
||||||
/>
|
>
|
||||||
<circle
|
<circle
|
||||||
cx="50%"
|
cx="50%"
|
||||||
cy="50%"
|
cy="50%"
|
||||||
r="5"
|
r="11"
|
||||||
fill="#066FDE"
|
fill="white"
|
||||||
stroke="white"
|
opacity="0.3"
|
||||||
strokeWidth="0.3"
|
className="scanned-photo__finger-point"
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_thumb"
|
style={{
|
||||||
/>
|
animationDelay: `${
|
||||||
</svg>
|
drawElementChangeDelay * (index + 1)
|
||||||
|
}ms`,
|
||||||
<svg x="172" y="38" height="24px" width="24px">
|
}}
|
||||||
<circle
|
/>
|
||||||
cx="50%"
|
<circle
|
||||||
cy="50%"
|
cx="50%"
|
||||||
r="11"
|
cy="50%"
|
||||||
fill="white"
|
r="5"
|
||||||
opacity="0.3"
|
fill="#066FDE"
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_index"
|
stroke="white"
|
||||||
/>
|
strokeWidth="0.3"
|
||||||
<circle
|
className="scanned-photo__finger-point"
|
||||||
cx="50%"
|
style={{
|
||||||
cy="50%"
|
animationDelay: `${
|
||||||
r="5"
|
drawElementChangeDelay * (index + 1)
|
||||||
fill="#066FDE"
|
}ms`,
|
||||||
stroke="white"
|
}}
|
||||||
strokeWidth="0.3"
|
/>
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_index"
|
</svg>
|
||||||
/>
|
);
|
||||||
</svg>
|
})}
|
||||||
|
|
||||||
<svg x="125" y="10" height="24px" width="24px">
|
|
||||||
<circle
|
|
||||||
cx="50%"
|
|
||||||
cy="50%"
|
|
||||||
r="11"
|
|
||||||
fill="white"
|
|
||||||
opacity="0.3"
|
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_middle"
|
|
||||||
/>
|
|
||||||
<circle
|
|
||||||
cx="50%"
|
|
||||||
cy="50%"
|
|
||||||
r="5"
|
|
||||||
fill="#066FDE"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="0.3"
|
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_middle"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<svg x="81" y="41" height="24px" width="24px">
|
|
||||||
<circle
|
|
||||||
cx="50%"
|
|
||||||
cy="50%"
|
|
||||||
r="11"
|
|
||||||
fill="white"
|
|
||||||
opacity="0.3"
|
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_ring"
|
|
||||||
/>
|
|
||||||
<circle
|
|
||||||
cx="50%"
|
|
||||||
cy="50%"
|
|
||||||
r="5"
|
|
||||||
fill="#066FDE"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="0.3"
|
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_ring"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<svg x="32" y="113" height="24px" width="24px">
|
|
||||||
<circle
|
|
||||||
cx="50%"
|
|
||||||
cy="50%"
|
|
||||||
r="11"
|
|
||||||
fill="white"
|
|
||||||
opacity="0.3"
|
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_little"
|
|
||||||
/>
|
|
||||||
<circle
|
|
||||||
cx="50%"
|
|
||||||
cy="50%"
|
|
||||||
r="5"
|
|
||||||
fill="#066FDE"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="0.3"
|
|
||||||
className="scanned-photo__finger-point scanned-photo__finger-point_little"
|
|
||||||
/>
|
|
||||||
</svg> */}
|
|
||||||
|
|
||||||
{props.displayLines && (
|
{props.displayLines && (
|
||||||
<>
|
<>
|
||||||
{lines.map((line, index) => (
|
{lines.map((line, index) => (
|
||||||
<path
|
<path
|
||||||
key={index}
|
key={index}
|
||||||
className={`scanned-photo__line scanned-photo__line_${line?.line}`}
|
className={`scanned-photo__line scanned-photo__line_${line?.name}`}
|
||||||
d={getCoordinatesString(line?.points)}
|
d={getCoordinatesString(line?.points)}
|
||||||
ref={(el) =>
|
ref={(el) =>
|
||||||
(linesRef.current[index] = el as SVGPathElement)
|
(linesRef.current[index] = el as SVGPathElement)
|
||||||
@ -199,30 +147,12 @@ export default function StepScanPhoto(props: Props) {
|
|||||||
getLineLength(linesRef.current[index]) || 500,
|
getLineLength(linesRef.current[index]) || 500,
|
||||||
strokeDashoffset:
|
strokeDashoffset:
|
||||||
getLineLength(linesRef.current[index]) || 500,
|
getLineLength(linesRef.current[index]) || 500,
|
||||||
animationDelay: `${lineChangeDelay * (index + 1)}ms`,
|
animationDelay: `${
|
||||||
|
drawElementChangeDelay * (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
|
|
||||||
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 }}
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
className="scanned-photo__line scanned-photo__line_head"
|
|
||||||
d="M 24 316 L 29 316 L 34 315 L 38 315 L 43 314 L 48 313 L 52 312 L 57 312 L 62 311 L 67 310 L 71 309 L 76 307 L 81 305 L 85 303 L 90 301 L 95 298 L 99 296 L 104 294 L 109 292 L 113 289 L 118 287 L 123 284 L 128 280 L 132 276 L 137 271 L 142 265"
|
|
||||||
style={{ strokeDasharray: 132.6, strokeDashoffset: 132.6 }}
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
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>
|
||||||
@ -232,13 +162,15 @@ export default function StepScanPhoto(props: Props) {
|
|||||||
<div
|
<div
|
||||||
className="scanned-photo__decoration"
|
className="scanned-photo__decoration"
|
||||||
style={{
|
style={{
|
||||||
animationDelay: `${lineChangeDelay * lines?.length}ms`,
|
animationDelay: `${drawElementChangeDelay * drawElements?.length}ms`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="scanned-photo__decoration__corners"
|
className="scanned-photo__decoration__corners"
|
||||||
style={{
|
style={{
|
||||||
animationDelay: `${lineChangeDelay * lines?.length + 1500}ms`,
|
animationDelay: `${
|
||||||
|
drawElementChangeDelay * drawElements?.length + 1500
|
||||||
|
}ms`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="scanned-photo__decoration__light-blue-circle" />
|
<div className="scanned-photo__decoration__light-blue-circle" />
|
||||||
@ -270,7 +202,9 @@ export default function StepScanPhoto(props: Props) {
|
|||||||
>
|
>
|
||||||
<animateTransform
|
<animateTransform
|
||||||
attributeName="transform"
|
attributeName="transform"
|
||||||
dur={`${lineChangeDelay * lines?.length + 3000}ms`}
|
dur={`${
|
||||||
|
drawElementChangeDelay * drawElements?.length + 3000
|
||||||
|
}ms`}
|
||||||
type="rotate"
|
type="rotate"
|
||||||
from="0 110 110"
|
from="0 110 110"
|
||||||
to="360 110 110"
|
to="360 110 110"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
import { Step } from "@/hooks/palmistry/use-steps";
|
import { Step } from "@/hooks/palmistry/use-steps";
|
||||||
import useSteps from "@/hooks/palmistry/use-steps";
|
import useSteps from "@/hooks/palmistry/use-steps";
|
||||||
@ -7,8 +7,10 @@ import { useSelector } from "react-redux";
|
|||||||
import { selectors } from "@/store";
|
import { selectors } from "@/store";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
|
import { IPalmistryLine } from "@/api/resources/Palmistry";
|
||||||
|
import { IPalmistryFingerLocal } from "@/store/palmistry";
|
||||||
|
|
||||||
const lineChangeDelay = 1500;
|
const drawElementChangeDelay = 1500;
|
||||||
const startDelay = 500;
|
const startDelay = 500;
|
||||||
// const goNextDelay = 12000;
|
// const goNextDelay = 12000;
|
||||||
|
|
||||||
@ -19,46 +21,64 @@ export default function StepScanPhoto() {
|
|||||||
const storedPhoto = steps.getStoredValue(Step.Upload);
|
const storedPhoto = steps.getStoredValue(Step.Upload);
|
||||||
|
|
||||||
const lines = useSelector(selectors.selectPalmistryLines);
|
const lines = useSelector(selectors.selectPalmistryLines);
|
||||||
|
const fingers = useSelector(selectors.selectPalmistryFingers);
|
||||||
|
const drawElements = useMemo(() => [...fingers, ...lines], [fingers, lines]);
|
||||||
|
|
||||||
const [currentElementIndex, setCurrentElementIndex] = useState(0);
|
const [currentElementIndex, setCurrentElementIndex] = useState(0);
|
||||||
const [smallPhotoState, setSmallPhotoState] = useState(false);
|
const [smallPhotoState, setSmallPhotoState] = useState(false);
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
const [shouldDisplayPalmLines, setShouldDisplayPalmLines] = useState(false);
|
const [shouldDisplayPalmLines, setShouldDisplayPalmLines] = useState(false);
|
||||||
|
const changeTitleTimeOut = useRef<NodeJS.Timeout>();
|
||||||
|
|
||||||
const prevElementIndex = useRef<number | null>(null);
|
const prevElementIndex = useRef<number | null>(null);
|
||||||
|
|
||||||
const goNextElement = (delay: number) => {
|
const goNextElement = (delay: number) => {
|
||||||
setTimeout(() => {
|
changeTitleTimeOut.current = setTimeout(() => {
|
||||||
setTitle(lines[currentElementIndex]?.line);
|
const title =
|
||||||
|
(drawElements[currentElementIndex] as IPalmistryFingerLocal)
|
||||||
|
.fingerName || drawElements[currentElementIndex].name;
|
||||||
|
setTitle(title);
|
||||||
setCurrentElementIndex((prevState) => prevState + 1);
|
setCurrentElementIndex((prevState) => prevState + 1);
|
||||||
}, delay);
|
}, delay);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!lines.length) {
|
if (!drawElements.length) {
|
||||||
return navigate(routes.client.palmistryUpload());
|
return navigate(routes.client.palmistryUpload());
|
||||||
}
|
}
|
||||||
}, [lines, navigate]);
|
}, [drawElements, navigate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// if (currentElementIndex === 0) {
|
// if (currentElementIndex === 0) {
|
||||||
// new Promise((resolve) => setTimeout(resolve, startDelay));
|
// new Promise((resolve) => setTimeout(resolve, startDelay));
|
||||||
// }
|
// }
|
||||||
if (
|
if (
|
||||||
currentElementIndex < lines?.length &&
|
currentElementIndex < drawElements?.length &&
|
||||||
currentElementIndex !== prevElementIndex.current
|
currentElementIndex !== prevElementIndex.current
|
||||||
) {
|
) {
|
||||||
prevElementIndex.current = currentElementIndex;
|
prevElementIndex.current = currentElementIndex;
|
||||||
goNextElement(lineChangeDelay);
|
goNextElement(drawElementChangeDelay);
|
||||||
setShouldDisplayPalmLines(lines?.includes(lines[currentElementIndex]));
|
setShouldDisplayPalmLines(
|
||||||
|
lines?.includes(drawElements[currentElementIndex] as IPalmistryLine)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentElementIndex >= lines?.length) {
|
if (currentElementIndex >= drawElements?.length) {
|
||||||
setTimeout(() => {
|
const toSmallTimeOut = setTimeout(() => {
|
||||||
setSmallPhotoState(true);
|
setSmallPhotoState(true);
|
||||||
}, lineChangeDelay);
|
}, drawElementChangeDelay);
|
||||||
setTimeout(steps.goNext, lineChangeDelay * lines.length + 8000);
|
const goNextTimeOut = setTimeout(
|
||||||
|
steps.goNext,
|
||||||
|
drawElementChangeDelay * drawElements.length + 8000
|
||||||
|
);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(toSmallTimeOut);
|
||||||
|
clearTimeout(goNextTimeOut);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
return () => {
|
||||||
|
if (changeTitleTimeOut.current) clearTimeout(changeTitleTimeOut.current);
|
||||||
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [currentElementIndex]);
|
}, [currentElementIndex]);
|
||||||
|
|
||||||
@ -74,25 +94,29 @@ export default function StepScanPhoto() {
|
|||||||
<h2
|
<h2
|
||||||
className="palmistry-container__title"
|
className="palmistry-container__title"
|
||||||
style={{
|
style={{
|
||||||
animationDelay: `${lineChangeDelay * lines.length}ms`,
|
animationDelay: `${drawElementChangeDelay * drawElements.length}ms`,
|
||||||
|
animationDuration: `${drawElementChangeDelay}ms`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
{/* <pre>{JSON.stringify(lines, null, 2)}</pre> */}
|
|
||||||
<ScannedPhoto
|
<ScannedPhoto
|
||||||
photo={storedPhoto}
|
photo={storedPhoto}
|
||||||
small={smallPhotoState}
|
small={smallPhotoState}
|
||||||
lineChangeDelay={lineChangeDelay}
|
drawElementChangeDelay={drawElementChangeDelay}
|
||||||
startDelay={startDelay}
|
startDelay={startDelay}
|
||||||
displayLines={shouldDisplayPalmLines}
|
displayLines={shouldDisplayPalmLines}
|
||||||
lines={lines}
|
lines={lines}
|
||||||
|
fingers={fingers}
|
||||||
|
drawElements={drawElements}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h2
|
<h2
|
||||||
className="palmistry-container__waiting-title"
|
className="palmistry-container__waiting-title"
|
||||||
style={{
|
style={{
|
||||||
animationDelay: `${lineChangeDelay * lines.length + 2500}ms`,
|
animationDelay: `${
|
||||||
|
drawElementChangeDelay * drawElements.length + 2500
|
||||||
|
}ms`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
We are putting together a comprehensive Palmistry Reading just for you!
|
We are putting together a comprehensive Palmistry Reading just for you!
|
||||||
@ -101,7 +125,9 @@ export default function StepScanPhoto() {
|
|||||||
<h3
|
<h3
|
||||||
className="palmistry-container__waiting-description"
|
className="palmistry-container__waiting-description"
|
||||||
style={{
|
style={{
|
||||||
animationDelay: `${lineChangeDelay * lines.length + 3000}ms`,
|
animationDelay: `${
|
||||||
|
drawElementChangeDelay * drawElements.length + 3000
|
||||||
|
}ms`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Wow, looks like there is a lot we can tell about your ambitious and
|
Wow, looks like there is a lot we can tell about your ambitious and
|
||||||
|
|||||||
@ -10,6 +10,28 @@ import PalmCameraModal from "../palm-camera-modal/palm-camera-modal";
|
|||||||
import { useApi } from "@/api";
|
import { useApi } from "@/api";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { actions } from "@/store";
|
import { actions } from "@/store";
|
||||||
|
import { IPalmistryFinger } from "@/api/resources/Palmistry";
|
||||||
|
import { IPalmistryFingerLocal } from "@/store/palmistry";
|
||||||
|
|
||||||
|
const fingersNames = {
|
||||||
|
thumb: "Thumb finger",
|
||||||
|
index_finger: "Index finger",
|
||||||
|
middle_finger: "Middle finger",
|
||||||
|
ring_finger: "Ring finger",
|
||||||
|
pinky: "Little finger",
|
||||||
|
};
|
||||||
|
|
||||||
|
const setFingersNames = (
|
||||||
|
fingers: IPalmistryFinger[]
|
||||||
|
): IPalmistryFingerLocal[] => {
|
||||||
|
if (!fingers) return [];
|
||||||
|
return fingers.map((finger) => {
|
||||||
|
return {
|
||||||
|
...finger,
|
||||||
|
fingerName: fingersNames[finger.name as keyof typeof fingersNames],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onOpenModal: (isOpen: boolean) => void;
|
onOpenModal: (isOpen: boolean) => void;
|
||||||
@ -40,8 +62,14 @@ export default function StepUpload(props: Props) {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
const result = await api.getPalmistryLines({ formData });
|
const result = await api.getPalmistryLines({ formData });
|
||||||
|
const fingers = setFingersNames(result?.fingers);
|
||||||
|
|
||||||
dispatch(actions.palmistry.update({ lines: result }));
|
dispatch(
|
||||||
|
actions.palmistry.update({
|
||||||
|
lines: result?.lines,
|
||||||
|
fingers,
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSelectFile = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
const onSelectFile = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
@ -49,10 +77,10 @@ export default function StepUpload(props: Props) {
|
|||||||
|
|
||||||
if (!event.target.files || event.target.files.length === 0) return;
|
if (!event.target.files || event.target.files.length === 0) return;
|
||||||
|
|
||||||
await getLines(event.target.files[0]);
|
|
||||||
|
|
||||||
setIsUpladProcessing(true);
|
setIsUpladProcessing(true);
|
||||||
|
|
||||||
|
await getLines(event.target.files[0]);
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onloadend = () => {
|
reader.onloadend = () => {
|
||||||
|
|||||||
@ -118,6 +118,7 @@ export const useAuthentication = () => {
|
|||||||
const authorization = useCallback(async (email: string, source: ESourceAuthorization) => {
|
const authorization = useCallback(async (email: string, source: ESourceAuthorization) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
setError(null)
|
||||||
const payload = getAuthorizationPayload(email, source);
|
const payload = getAuthorizationPayload(email, source);
|
||||||
const { token, userId } = await api.authorization(payload);
|
const { token, userId } = await api.authorization(payload);
|
||||||
const { user } = await api.getUser({ token });
|
const { user } = await api.getUser({ token });
|
||||||
|
|||||||
@ -264,7 +264,7 @@ const routes = {
|
|||||||
[apiHost, prefix, "ai", "assistants", chatId, "chats.json"].join("/"),
|
[apiHost, prefix, "ai", "assistants", chatId, "chats.json"].join("/"),
|
||||||
// Palmistry
|
// Palmistry
|
||||||
getPalmistryLines: () =>
|
getPalmistryLines: () =>
|
||||||
["https://api.aura.witapps.us", "palmistry", "lines"].join("/"),
|
[dApiHost, dApiPrefix, "palmistry", "lines"].join("/"),
|
||||||
|
|
||||||
// Paywall
|
// Paywall
|
||||||
getPaywallByPlacementKey: (placementKey: EPlacementKeys) =>
|
getPaywallByPlacementKey: (placementKey: EPlacementKeys) =>
|
||||||
|
|||||||
@ -67,6 +67,7 @@ import {
|
|||||||
} from "./userCallbacks";
|
} from "./userCallbacks";
|
||||||
import palmistry, {
|
import palmistry, {
|
||||||
actions as palmistryActions,
|
actions as palmistryActions,
|
||||||
|
selectPalmistryFingers,
|
||||||
selectPalmistryLines,
|
selectPalmistryLines,
|
||||||
} from "./palmistry";
|
} from "./palmistry";
|
||||||
import { selectPaywallsIsMustUpdate, selectPaywalls } from "./paywalls";
|
import { selectPaywallsIsMustUpdate, selectPaywalls } from "./paywalls";
|
||||||
@ -121,6 +122,7 @@ export const selectors = {
|
|||||||
selectIsForceShortPath,
|
selectIsForceShortPath,
|
||||||
selectOpenAiToken,
|
selectOpenAiToken,
|
||||||
selectPalmistryLines,
|
selectPalmistryLines,
|
||||||
|
selectPalmistryFingers,
|
||||||
selectPaywalls,
|
selectPaywalls,
|
||||||
selectPaywallsIsMustUpdate,
|
selectPaywallsIsMustUpdate,
|
||||||
selectPrivacyPolicy,
|
selectPrivacyPolicy,
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
import { IPalmistryLine } from "@/api/resources/Palmistry";
|
import { IPalmistryFinger, IPalmistryLine } from "@/api/resources/Palmistry";
|
||||||
import { createSlice, createSelector } from "@reduxjs/toolkit";
|
import { createSlice, createSelector } from "@reduxjs/toolkit";
|
||||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
export type IPalmistryFingerLocal = IPalmistryFinger & { fingerName?: string }
|
||||||
|
|
||||||
interface IPalmistry {
|
interface IPalmistry {
|
||||||
lines: IPalmistryLine[];
|
lines: IPalmistryLine[];
|
||||||
|
fingers: IPalmistryFingerLocal[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: IPalmistry = {
|
const initialState: IPalmistry = {
|
||||||
lines: [],
|
lines: [],
|
||||||
|
fingers: []
|
||||||
};
|
};
|
||||||
|
|
||||||
const palmistrySlice = createSlice({
|
const palmistrySlice = createSlice({
|
||||||
@ -26,4 +30,8 @@ export const selectPalmistryLines = createSelector(
|
|||||||
(state: { palmistry: IPalmistry }) => state.palmistry.lines,
|
(state: { palmistry: IPalmistry }) => state.palmistry.lines,
|
||||||
(palmistry) => palmistry
|
(palmistry) => palmistry
|
||||||
);
|
);
|
||||||
|
export const selectPalmistryFingers = createSelector(
|
||||||
|
(state: { palmistry: IPalmistry }) => state.palmistry.fingers,
|
||||||
|
(palmistry) => palmistry
|
||||||
|
);
|
||||||
export default palmistrySlice.reducer;
|
export default palmistrySlice.reducer;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user