"use client"; import { useEffect, useMemo, useRef, useState } from "react"; import Image from "next/image"; import { useRouter, useSearchParams } from "next/navigation"; import { CharacterAvatar } from "@/app/_components"; import { AppBottomNav, FavoriteEntryButton, MobileShell, } from "@/app/_components/core"; import { buildGlobalPageUrl } from "@/router/global-route-context"; import { ROUTES } from "@/router/routes"; import { useAppNavigator } from "@/router/use-app-navigator"; import { useActiveCharacter, useActiveCharacterRoutes, } from "@/providers/character-provider"; import { isPrivateAlbumLocked } from "@/lib/private-zone/private_album"; import { behaviorAnalytics } from "@/lib/analytics"; import { recordChatActionEventById } from "@/lib/chat/chat_action_events"; import { useUserSelector } from "@/stores/user/user-context"; import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context"; import { usePrivateZoneDispatch, usePrivateZoneState, } from "@/stores/private-zone"; import { usePrivateZonePostDispatch, usePrivateZonePostState, } from "@/stores/private-zone-posts"; import { PrivateAlbumCard, PrivateAlbumGallery, PrivateZoneVideoPostCard, StatusCard, UnlockConfirmDialog, VideoPostUnlockDialog, } from "./components"; import styles from "./private-zone-screen.module.css"; import { isPrivateZoneAuthRequired, usePrivateZoneBootstrapFlow, usePrivateZoneUnlockPaywallNavigation, usePrivateZoneUnlockSuccessRefresh, } from "./use-private-zone-flow"; import { buildPrivateAlbumGalleryUrl, buildPrivateZoneWithoutGalleryUrl, getPrivateAlbumGalleryState, } from "./private-album-gallery-url"; import { findPrivateAlbumDisplayImage } from "./private-album-images"; export function PrivateZoneScreen() { const router = useRouter(); const searchParams = useSearchParams(); const navigator = useAppNavigator(); const character = useActiveCharacter(); const characterRoutes = useActiveCharacterRoutes(); const authState = useAuthState(); const authDispatch = useAuthDispatch(); const state = usePrivateZoneState(); const dispatch = usePrivateZoneDispatch(); const postState = usePrivateZonePostState(); const postDispatch = usePrivateZonePostDispatch(); const isVip = useUserSelector((user) => user.context.isVip); const [activeTab, setActiveTab] = useState<"moments" | "albums">("moments"); const openedGalleryInPageRef = useRef(false); const previousPostLoginStatusRef = useRef(authState.loginStatus); const galleryState = useMemo( () => getPrivateAlbumGalleryState(searchParams), [searchParams], ); const visibleTab = galleryState ? "albums" : activeTab; usePrivateZoneBootstrapFlow({ authDispatch, hasInitialized: authState.hasInitialized, isAuthLoading: authState.isLoading, loginStatus: authState.loginStatus, roomDispatch: dispatch, roomStatus: state.status, }); usePrivateZoneUnlockPaywallNavigation({ loginStatus: authState.loginStatus, roomDispatch: dispatch, unlockPaywallRequest: state.unlockPaywallRequest, isVip, }); usePrivateZoneUnlockSuccessRefresh(state.unlockSuccessNonce); usePrivateZoneUnlockSuccessRefresh(postState.unlockSuccessNonce); useEffect(() => { if (!authState.hasInitialized || authState.isLoading) return; if (authState.loginStatus === "notLoggedIn") return; const previous = previousPostLoginStatusRef.current; previousPostLoginStatusRef.current = authState.loginStatus; if (postState.status === "idle") { postDispatch({ type: "PrivateZonePostInit" }); return; } if (previous !== authState.loginStatus && postState.status !== "loading") { postDispatch({ type: "PrivateZonePostRefresh" }); } }, [ authState.hasInitialized, authState.isLoading, authState.loginStatus, postDispatch, postState.status, ]); useEffect(() => { const request = postState.unlockPaywallRequest; if (!request) return; if (isPrivateZoneAuthRequired(authState.loginStatus)) { navigator.openAuth(characterRoutes.privateZone); } else { behaviorAnalytics.paywallShown({ entryPoint: "private_zone", triggerReason: "insufficient_credits", }); const guidance = request.paymentGuidance?.characterId === character.id ? request.paymentGuidance : null; if (guidance) { void recordChatActionEventById( guidance.guidanceId, character.id, "opened", ).catch(() => undefined); } navigator.openSubscription({ type: isVip || (guidance && !guidance.purchaseOptions.includes("vip")) ? "topup" : "vip", returnTo: "private-zone", ...(guidance ? { chatActionId: guidance.guidanceId } : {}), analytics: { entryPoint: "private_zone", triggerReason: "insufficient_credits", }, }); } postDispatch({ type: "PrivateZonePostUnlockPaywallConsumed" }); }, [ authState.loginStatus, characterRoutes.privateZone, character.id, isVip, navigator, postDispatch, postState.unlockPaywallRequest, ]); const displayName = character.displayName; const avatarUrl = character.assets.avatar; const title = character.copy.privateZoneTitle; const subtitle = character.copy.privateZoneSubtitle; const pendingAlbum = useMemo( () => state.items.find( (album) => album.albumId === state.pendingConfirmAlbumId, ) ?? null, [state.items, state.pendingConfirmAlbumId], ); const pendingPost = useMemo( () => postState.items.find( (post) => post.postId === postState.pendingConfirmPostId, ) ?? null, [postState.items, postState.pendingConfirmPostId], ); const galleryAlbum = useMemo( () => galleryState ? state.items.find((album) => album.albumId === galleryState.albumId) ?? null : null, [galleryState, state.items], ); const galleryImage = useMemo( () => galleryAlbum && galleryState ? findPrivateAlbumDisplayImage( galleryAlbum, galleryState.imageIndex, ) : null, [galleryAlbum, galleryState], ); useEffect(() => { if (!galleryState || state.isLoading) return; if ( !galleryAlbum || isPrivateAlbumLocked(galleryAlbum) || !galleryImage ) { router.replace( buildPrivateZoneWithoutGalleryUrl( searchParams, characterRoutes.privateZone, ), { scroll: false }, ); } }, [ characterRoutes.privateZone, galleryAlbum, galleryImage, galleryState, router, searchParams, state.isLoading, ]); const handleTopUpClick = () => { if (isPrivateZoneAuthRequired(authState.loginStatus)) { navigator.openAuth(characterRoutes.privateZone); return; } navigator.openSubscription({ type: isVip ? "topup" : "vip", returnTo: "private-zone", analytics: { entryPoint: "private_zone", triggerReason: "vip_cta", }, }); }; const handleOpenGallery = (albumId: string, imageIndex: number) => { openedGalleryInPageRef.current = true; router.push( buildPrivateAlbumGalleryUrl( albumId, imageIndex, characterRoutes.privateZone, ), { scroll: false }, ); }; const handlePostUnlock = (postId: string) => { if (isPrivateZoneAuthRequired(authState.loginStatus)) { navigator.openAuth(characterRoutes.privateZone); return; } postDispatch({ type: "PrivateZonePostUnlockRequested", postId }); }; const handleCloseGallery = () => { if (openedGalleryInPageRef.current) { openedGalleryInPageRef.current = false; router.back(); return; } router.replace( buildPrivateZoneWithoutGalleryUrl( searchParams, characterRoutes.privateZone, ), { scroll: false }, ); }; const handleGalleryIndexChange = (imageIndex: number) => { if (!galleryAlbum) return; router.replace( buildPrivateAlbumGalleryUrl( galleryAlbum.albumId, imageIndex, characterRoutes.privateZone, ), { scroll: false }, ); }; return (

{displayName}

{visibleTab === "moments" ? "Moments" : "Albums"}

{visibleTab === "moments" ? "Private moments" : "Private albums"}

{visibleTab === "moments" ? postState.items.length : state.items.length}
{visibleTab === "moments" ? ( <> {postState.errorMessage ? ( postDispatch({ type: "PrivateZonePostRefresh" })} /> ) : null} {postState.isLoading && postState.items.length === 0 ? ( ) : null} {!postState.isLoading && !postState.errorMessage && postState.items.length === 0 ? ( ) : null}
{postState.items.map((post, index) => ( handlePostUnlock(post.postId)} onPlaybackError={() => postDispatch({ type: "PrivateZonePostRefresh" }) } /> ))}
{postState.hasMore ? ( ) : null} ) : ( <> {state.errorMessage ? ( dispatch({ type: "PrivateZoneRefresh" })} /> ) : null} {state.isLoading && state.items.length === 0 ? ( ) : null}
{state.items.map((album, index) => ( handleOpenGallery(album.albumId, imageIndex) } onUnlock={() => dispatch({ type: "PrivateZoneUnlockRequested", albumId: album.albumId, }) } /> ))}
)}
navigator.push(characterRoutes.splash, { scroll: false }) } onPrivateZoneClick={() => undefined} onMenuClick={() => navigator.push( buildGlobalPageUrl(ROUTES.profile, characterRoutes.chat), { scroll: false }, ) } /> {pendingAlbum ? ( dispatch({ type: "PrivateZoneUnlockCancelled" })} onConfirm={() => dispatch({ type: "PrivateZoneUnlockConfirmed" })} /> ) : state.unlockErrorMessage ? (
{state.unlockErrorMessage}
) : null} {pendingPost ? ( postDispatch({ type: "PrivateZonePostUnlockCancelled" }) } onConfirm={() => postDispatch({ type: "PrivateZonePostUnlockConfirmed" }) } /> ) : postState.unlockErrorMessage ? (
{postState.unlockErrorMessage}
) : null} {galleryAlbum && galleryState && !isPrivateAlbumLocked(galleryAlbum) && galleryImage ? ( ) : null}
); }