"use client"; import { useEffect, useRef, useState } from "react"; import { useRouter } from "next/navigation"; import { persistExternalEntryPayload, resolveExternalEntryDestination, resolveExternalEntryPromotionType, resolveExternalEntryTarget, shouldBindExternalFacebookIdentity, } from "@/lib/navigation/external_entry"; import { clearPendingChatPromotion, savePendingChatPromotion, } from "@/lib/navigation/chat_unlock_session"; import { ROUTES } from "@/router/routes"; import { DEFAULT_CHARACTER_ID, getCharacterBySlug, } from "@/data/constants/character"; import { Logger } from "@/utils/logger"; import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context"; import { consumeTopUpHandoff } from "@/lib/auth/top_up_handoff"; import { Result } from "@/utils/result"; import { isFavoriteEntryRequest, persistFavoriteEntryIntent, } from "@/lib/navigation/favorite_entry"; const log = new Logger("ExternalEntryPersist"); interface ExternalEntryPersistProps { deviceId: string | null; asid: string | null; psid: string | null; avatarUrl: string | null; target: string | null; character: string | null; mode: string | null; promotionType: string | null; favorite: string | null; handoffToken: string | null; } export default function ExternalEntryPersist({ deviceId, asid, psid, avatarUrl, target, character, mode, promotionType, favorite, handoffToken, }: ExternalEntryPersistProps) { const router = useRouter(); const authState = useAuthState(); const authDispatch = useAuthDispatch(); const [hasPersisted, setHasPersisted] = useState(false); const [handoffError, setHandoffError] = useState(null); const [handoffCompleted, setHandoffCompleted] = useState(false); const hasNavigatedRef = useRef(false); const handoffStartedRef = useRef(false); const targetRoute = resolveExternalEntryTarget({ target }); const destination = resolveExternalEntryDestination({ target, character }); const characterId = getCharacterBySlug(character)?.id ?? DEFAULT_CHARACTER_ID; const resolvedPromotionType = resolveExternalEntryPromotionType({ mode, promotionType, }); const isTopUpHandoff = targetRoute === ROUTES.subscription; const displayedHandoffError = handoffError ?? (isTopUpHandoff && !hasValue(handoffToken) ? "This top-up link is invalid. Please request a new link in Messenger." : null); useEffect(() => { let cancelled = false; void (async () => { const results = await Promise.allSettled([ persistExternalEntryPayload({ deviceId, asid, psid, avatarUrl, }), targetRoute === ROUTES.chat && resolvedPromotionType ? savePendingChatPromotion(resolvedPromotionType, characterId) : clearPendingChatPromotion(), ]); if (isFavoriteEntryRequest(favorite)) { persistFavoriteEntryIntent(); } for (const result of results) { if (result.status === "rejected") { log.warn( "[ExternalEntryPersist] failed to persist payload", result.reason, ); } } if (!cancelled) setHasPersisted(true); })(); return () => { cancelled = true; }; }, [ avatarUrl, asid, character, characterId, destination, deviceId, favorite, mode, promotionType, psid, resolvedPromotionType, targetRoute, ]); useEffect(() => { if (hasNavigatedRef.current || !hasPersisted) return; if (isTopUpHandoff) { if (!authState.hasInitialized || authState.isLoading) return; if (handoffCompleted) { if ( authState.loginStatus === "notLoggedIn" || authState.loginStatus === "guest" ) { return; } hasNavigatedRef.current = true; router.replace(destination); return; } if (handoffStartedRef.current) return; if (!hasValue(handoffToken)) return; handoffStartedRef.current = true; void (async () => { const result = await consumeTopUpHandoff(handoffToken); if (Result.isErr(result)) { log.warn("[ExternalEntryPersist] top-up handoff failed", result.error); window.history.replaceState( window.history.state, "", "/external-entry?target=topup", ); setHandoffError( "This top-up link is invalid or has expired. Please request a new link in Messenger.", ); return; } setHandoffCompleted(true); authDispatch({ type: "AuthInit" }); })(); return; } if (!authState.hasInitialized || authState.isLoading) return; hasNavigatedRef.current = true; if ( shouldBindExternalFacebookIdentity({ hasInitialized: authState.hasInitialized, isLoading: authState.isLoading, loginStatus: authState.loginStatus, asid, psid, }) ) { authDispatch({ type: "AuthExternalFacebookIdentityBindRequested", asid: asid ?? undefined, psid: psid ?? undefined, }); } router.replace(destination); }, [ asid, authDispatch, authState.hasInitialized, authState.isLoading, authState.loginStatus, destination, hasPersisted, handoffCompleted, handoffToken, isTopUpHandoff, psid, router, ]); return (
{displayedHandoffError ? (

{displayedHandoffError}

) : (
)}
); } function hasValue(value: string | null): value is string { return typeof value === "string" && value.trim().length > 0; }