"use client"; import { useEffect, useMemo, useRef, useState } from "react"; import { useRouter } from "next/navigation"; import { BackButton } from "@/app/_components"; import { Checkbox, MobileShell } from "@/app/_components/core"; import { AppConstants } from "@/core/app_constants"; import { PendingPaymentOrderStorage } from "@/data/storage"; import { usePaymentDispatch, usePaymentState, } from "@/stores/payment/payment-context"; import { useUserDispatch } from "@/stores/user/user-context"; import { ROUTES } from "@/router/routes"; import { SubscriptionCheckoutButton, SubscriptionCoinsOfferSection, SubscriptionPaymentMethod, SubscriptionPaymentSuccessDialog, SubscriptionVipOfferSection, } from "./components"; import styles from "./components/subscription-screen.module.css"; import { isCreditPlan, isVipPlan, toCoinsOfferPlanView, toVipOfferPlanView, type SubscriptionType, } from "./subscription-screen.helpers"; export type { SubscriptionType } from "./subscription-screen.helpers"; export interface SubscriptionScreenProps { subscriptionType?: SubscriptionType; shouldResumePendingOrder?: boolean; returnTo?: "chat" | null; } export function SubscriptionScreen({ subscriptionType = "vip", shouldResumePendingOrder = false, returnTo = null, }: SubscriptionScreenProps) { const router = useRouter(); const userDispatch = useUserDispatch(); const payment = usePaymentState(); const paymentDispatch = usePaymentDispatch(); const refreshedPaidOrderRef = useRef(null); const resumedPendingOrderRef = useRef(null); const successDialogShownOrderRef = useRef(null); const [showPaymentSuccessDialog, setShowPaymentSuccessDialog] = useState(false); const canSubscribeVip = subscriptionType === "vip"; useEffect(() => { if (payment.status === "idle") { paymentDispatch({ type: "PaymentInit" }); } }, [payment.status, paymentDispatch]); useEffect(() => { if (!payment.isPaid || !payment.currentOrderId) return; if (refreshedPaidOrderRef.current === payment.currentOrderId) return; refreshedPaidOrderRef.current = payment.currentOrderId; userDispatch({ type: "UserFetch" }); }, [payment.currentOrderId, payment.isPaid, userDispatch]); useEffect(() => { if (!payment.isPaid || !payment.currentOrderId) return; if (successDialogShownOrderRef.current === payment.currentOrderId) return; successDialogShownOrderRef.current = payment.currentOrderId; setShowPaymentSuccessDialog(true); }, [payment.currentOrderId, payment.isPaid]); useEffect(() => { const canInspectPendingOrder = payment.status === "ready" || (!shouldResumePendingOrder && (payment.isPollingOrder || payment.isPaid || payment.status === "failed")); if (!canInspectPendingOrder) return; let cancelled = false; const handlePendingOrder = async () => { const result = await PendingPaymentOrderStorage.getPendingOrderForType( subscriptionType, ); if (cancelled || !result.success || result.data === null) return; if (!shouldResumePendingOrder) { await PendingPaymentOrderStorage.clearPendingOrder(); if ( payment.currentOrderId === result.data.orderId && (payment.isPollingOrder || payment.isPaid || payment.status === "failed") ) { paymentDispatch({ type: "PaymentReset" }); } return; } if (payment.currentOrderId === result.data.orderId) return; if (resumedPendingOrderRef.current === result.data.orderId) return; resumedPendingOrderRef.current = result.data.orderId; paymentDispatch({ type: "PaymentReturned", orderId: result.data.orderId, createdAt: result.data.createdAt, }); }; void handlePendingOrder(); return () => { cancelled = true; }; }, [ payment.currentOrderId, payment.isPaid, payment.isPollingOrder, payment.status, paymentDispatch, shouldResumePendingOrder, subscriptionType, ]); useEffect(() => { if (!payment.currentOrderId) return; if (!payment.isPaid && payment.status !== "failed") return; void PendingPaymentOrderStorage.clearPendingOrder(); }, [payment.currentOrderId, payment.isPaid, payment.status]); const vipOfferPlans = useMemo( () => payment.plans .filter(isVipPlan) .slice(0, 3) .map(toVipOfferPlanView), [payment.plans], ); const directCoinsPlans = useMemo( () => payment.plans.filter(isCreditPlan).map(toCoinsOfferPlanView), [payment.plans], ); const selectedPlan = (canSubscribeVip ? [...vipOfferPlans, ...directCoinsPlans].find( (plan) => plan.id === payment.selectedPlanId, ) : directCoinsPlans.find((plan) => plan.id === payment.selectedPlanId)) ?? null; const isPaymentBusy = payment.isCreatingOrder || payment.isPollingOrder; const canActivate = selectedPlan !== null && payment.agreed && !isPaymentBusy; const backHref = returnTo === "chat" ? ROUTES.chat : ROUTES.sidebar; const finishPaymentSuccessClose = () => { setShowPaymentSuccessDialog(false); paymentDispatch({ type: "PaymentReset" }); if (returnTo === "chat") { router.replace(ROUTES.chat); } }; const handlePaymentSuccessClose = () => { finishPaymentSuccessClose(); }; useEffect(() => { if (canSubscribeVip) { const firstVipPlanId = vipOfferPlans[0]?.id ?? ""; const hasSelectedVipPlan = vipOfferPlans.some( (plan) => plan.id === payment.selectedPlanId, ); const hasSelectedCoinsPlan = directCoinsPlans.some( (plan) => plan.id === payment.selectedPlanId, ); const canSelectPlan = firstVipPlanId.length > 0 && (payment.status === "ready" || payment.status === "paid" || payment.status === "failed"); if (!canSelectPlan || hasSelectedVipPlan || hasSelectedCoinsPlan) return; paymentDispatch({ type: "PaymentPlanSelected", planId: firstVipPlanId, }); return; } if (payment.isLoadingPlans || directCoinsPlans.length === 0) return; if (selectedPlan !== null) return; paymentDispatch({ type: "PaymentPlanSelected", planId: directCoinsPlans[0]?.id ?? "", }); }, [ canSubscribeVip, directCoinsPlans, payment.isLoadingPlans, paymentDispatch, payment.selectedPlanId, payment.status, selectedPlan, vipOfferPlans, ]); return (
{canSubscribeVip ? ( paymentDispatch({ type: "PaymentPlanSelected", planId, }) } /> ) : null} paymentDispatch({ type: "PaymentPlanSelected", planId, }) } />
paymentDispatch({ type: "PaymentPayChannelChanged", payChannel, }) } />
paymentDispatch({ type: "PaymentAgreementChanged", agreed, }) } label={ I agree to the{" "} VIP Membership Benefits Agreement {" "} and{" "} Automatic renewal Agreement } />
); }