"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"; 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; } export default function ExternalEntryPersist({ deviceId, asid, psid, avatarUrl, target, character, mode, promotionType, }: ExternalEntryPersistProps) { const router = useRouter(); const authState = useAuthState(); const authDispatch = useAuthDispatch(); const [hasPersisted, setHasPersisted] = useState(false); const hasNavigatedRef = useRef(false); const hasReinitializedForPsidRef = useRef(false); const targetRoute = resolveExternalEntryTarget({ target }); const destination = resolveExternalEntryDestination({ target, character }); const characterId = getCharacterBySlug(character)?.id ?? DEFAULT_CHARACTER_ID; const resolvedPromotionType = resolveExternalEntryPromotionType({ mode, promotionType, }); useEffect(() => { let cancelled = false; void (async () => { const results = await Promise.allSettled([ persistExternalEntryPayload({ deviceId, asid, psid, avatarUrl, }), targetRoute === ROUTES.chat && resolvedPromotionType ? savePendingChatPromotion(resolvedPromotionType, characterId) : clearPendingChatPromotion(), ]); 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, mode, promotionType, psid, resolvedPromotionType, targetRoute, ]); useEffect(() => { if (hasNavigatedRef.current || !hasPersisted) return; // AuthStatusChecker can initialize before this entry has persisted PSID. // Re-run the check so PSID direct login is not skipped by that race. if (hasValue(psid) && !hasReinitializedForPsidRef.current) { if (!authState.hasInitialized || authState.isLoading) return; hasReinitializedForPsidRef.current = 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, psid, router, ]); return (
); } function hasValue(value: string | null): value is string { return typeof value === "string" && value.trim().length > 0; }