Files
cozsweet-frontend-nextjs/src/app/private-zone/use-private-zone-flow.ts
T

150 lines
4.3 KiB
TypeScript

"use client";
import { type Dispatch, useEffect, useRef } from "react";
import type { LoginStatus } from "@/data/schemas/auth";
import { useGuestLoginBootstrap } from "@/hooks/use-guest-login-bootstrap";
import { behaviorAnalytics } from "@/lib/analytics";
import {
useActiveCharacter,
useActiveCharacterRoutes,
} from "@/providers/character-provider";
import { useAppNavigator } from "@/router/use-app-navigator";
import type { AuthEvent } from "@/stores/auth/auth-events";
import type { PrivateZoneEvent } from "@/stores/private-zone";
import type { PrivateZoneUnlockPaywallRequest } from "@/stores/private-zone/private-zone-state";
import { useUserDispatch } from "@/stores/user/user-context";
import { recordChatActionEventById } from "@/lib/chat/chat_action_events";
export interface UsePrivateZoneBootstrapFlowInput {
authDispatch: Dispatch<AuthEvent>;
hasInitialized: boolean;
isAuthLoading: boolean;
loginStatus: LoginStatus;
roomDispatch: Dispatch<PrivateZoneEvent>;
roomStatus: string;
}
export interface UsePrivateZoneUnlockPaywallNavigationInput {
loginStatus: LoginStatus;
roomDispatch: Dispatch<PrivateZoneEvent>;
unlockPaywallRequest: PrivateZoneUnlockPaywallRequest | null;
isVip: boolean;
}
export function isPrivateZoneAuthRequired(loginStatus: LoginStatus): boolean {
return loginStatus === "guest" || loginStatus === "notLoggedIn";
}
export function usePrivateZoneBootstrapFlow({
authDispatch,
hasInitialized,
isAuthLoading,
loginStatus,
roomDispatch,
roomStatus,
}: UsePrivateZoneBootstrapFlowInput): void {
const previousLoginStatusRef = useRef(loginStatus);
useGuestLoginBootstrap({
dispatch: authDispatch,
hasInitialized,
isLoading: isAuthLoading,
loginStatus,
});
useEffect(() => {
if (!hasInitialized || isAuthLoading) return;
if (loginStatus === "notLoggedIn") return;
const previousLoginStatus = previousLoginStatusRef.current;
previousLoginStatusRef.current = loginStatus;
if (roomStatus === "idle") {
roomDispatch({ type: "PrivateZoneInit" });
return;
}
if (previousLoginStatus !== loginStatus && roomStatus !== "loading") {
roomDispatch({ type: "PrivateZoneRefresh" });
}
}, [
hasInitialized,
isAuthLoading,
loginStatus,
roomDispatch,
roomStatus,
]);
}
export function usePrivateZoneUnlockPaywallNavigation({
loginStatus,
roomDispatch,
unlockPaywallRequest,
isVip,
}: UsePrivateZoneUnlockPaywallNavigationInput): void {
const navigator = useAppNavigator();
const character = useActiveCharacter();
const characterRoutes = useActiveCharacterRoutes();
useEffect(() => {
if (!unlockPaywallRequest) return;
if (isPrivateZoneAuthRequired(loginStatus)) {
navigator.openAuth(characterRoutes.privateZone);
} else {
behaviorAnalytics.paywallShown({
entryPoint: "private_album_unlock",
triggerReason: "insufficient_credits",
});
const guidance = unlockPaywallRequest.paymentGuidance;
const activeGuidance =
guidance?.characterId === character.id ? guidance : null;
if (activeGuidance) {
void recordChatActionEventById(
activeGuidance.guidanceId,
activeGuidance.characterId,
"opened",
).catch(() => undefined);
}
navigator.openSubscription({
type:
isVip ||
(activeGuidance && !activeGuidance.purchaseOptions.includes("vip"))
? "topup"
: "vip",
returnTo: "private-zone",
...(activeGuidance
? { chatActionId: activeGuidance.guidanceId }
: {}),
analytics: {
entryPoint: "private_album_unlock",
triggerReason: "insufficient_credits",
},
});
}
roomDispatch({ type: "PrivateZoneUnlockPaywallConsumed" });
}, [
characterRoutes.privateZone,
character.id,
loginStatus,
isVip,
navigator,
roomDispatch,
unlockPaywallRequest,
]);
}
export function usePrivateZoneUnlockSuccessRefresh(
unlockSuccessNonce: number,
): void {
const userDispatch = useUserDispatch();
const lastUnlockSuccessNonceRef = useRef(0);
useEffect(() => {
if (unlockSuccessNonce <= lastUnlockSuccessNonceRef.current) return;
lastUnlockSuccessNonceRef.current = unlockSuccessNonce;
userDispatch({ type: "UserFetch" });
}, [unlockSuccessNonce, userDispatch]);
}