"use client"; import { useEffect, useRef } from "react"; import { behaviorAnalytics } from "@/lib/analytics"; import { useAppNavigator } from "@/router/use-app-navigator"; import type { ChatUpgradeReason } from "@/stores/chat/chat-state"; import type { PaymentGuidance } from "@/data/schemas/chat"; import { recordChatActionEventById } from "@/lib/chat/chat_action_events"; import { useActiveCharacter } from "@/providers/character-provider"; import { useUserSelector } from "@/stores/user/user-context"; import { getInsufficientCreditsMessageLimitView, getInsufficientCreditsSubscriptionType, shouldShowMessageLimitBanner, type InsufficientCreditsMessageLimitView, } from "../chat-screen.helpers"; export interface UseChatMessageLimitBannerInput { upgradePromptVisible: boolean; upgradeReason: ChatUpgradeReason | null; paymentGuidance: PaymentGuidance | null; } export interface ChatMessageLimitBannerView { title: string; description: string; ctaLabel: string | null; guidance: PaymentGuidance | null; visible: boolean; unlock: () => void; } export function useChatMessageLimitBanner({ upgradePromptVisible, upgradeReason, paymentGuidance, }: UseChatMessageLimitBannerInput): ChatMessageLimitBannerView { const navigator = useAppNavigator(); const character = useActiveCharacter(); const isVip = useUserSelector((state) => state.context.isVip); const fallbackView = getInsufficientCreditsMessageLimitView(); const guidance = paymentGuidance?.characterId === character.id ? paymentGuidance : null; const view: InsufficientCreditsMessageLimitView = { title: guidance?.title ?? fallbackView.title, description: guidance?.copy ?? fallbackView.description, ctaLabel: guidance?.ctaLabel ?? fallbackView.ctaLabel, }; const visible = shouldShowMessageLimitBanner({ upgradePromptVisible, upgradeReason, }); const trackedVisibleRef = useRef(false); useEffect(() => { if (!visible) { trackedVisibleRef.current = false; return; } if (trackedVisibleRef.current) return; trackedVisibleRef.current = true; behaviorAnalytics.paywallShown( { entryPoint: "chat_input", triggerReason: "insufficient_credits", }, { isVip }, ); if (guidance) { void recordChatActionEventById( guidance.guidanceId, character.id, "viewed", ).catch(() => undefined); } }, [character.id, guidance, isVip, visible]); function unlock(): void { if (guidance?.mode !== "guide" && guidance !== null) return; if (guidance) { void recordChatActionEventById( guidance.guidanceId, character.id, "opened", ).catch(() => undefined); } navigator.openSubscription({ type: isVip || (guidance && !guidance.purchaseOptions.includes("vip")) ? "topup" : getInsufficientCreditsSubscriptionType(isVip), returnTo: "chat", ...(guidance ? { chatActionId: guidance.guidanceId } : {}), analytics: { entryPoint: "chat_input", triggerReason: "insufficient_credits", }, }); } return { ...view, ctaLabel: guidance && guidance.mode !== "guide" ? null : view.ctaLabel, guidance, visible, unlock, }; }