112 lines
3.2 KiB
TypeScript
112 lines
3.2 KiB
TypeScript
"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,
|
|
};
|
|
}
|