177 lines
4.6 KiB
TypeScript
177 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
|
|
import {
|
|
consumePendingChatUnlock,
|
|
peekPendingChatUnlock,
|
|
type PendingChatUnlock,
|
|
type PendingChatUnlockKind,
|
|
} from "@/lib/navigation/chat_unlock_session";
|
|
import { useAppNavigator } from "@/router/use-app-navigator";
|
|
import { useChatDispatch, useChatState } from "@/stores/chat/chat-context";
|
|
import type { ChatUnlockPaywallRequest } from "@/stores/chat/chat-state";
|
|
import { useUserState } from "@/stores/user/user-context";
|
|
|
|
import { getInsufficientCreditsSubscriptionType } from "../chat-screen.helpers";
|
|
|
|
export interface UseChatUnlockNavigationFlowInput {
|
|
returnUrl: string;
|
|
expectedKind?: PendingChatUnlockKind;
|
|
expectedMessageId?: string;
|
|
ignoredKind?: PendingChatUnlockKind;
|
|
enabled?: boolean;
|
|
}
|
|
|
|
export interface UseChatUnlockNavigationFlowOutput {
|
|
unlockPaywallRequest: ChatUnlockPaywallRequest | null;
|
|
requestMessageUnlock: (
|
|
messageId: string,
|
|
kind: PendingChatUnlockKind,
|
|
) => void;
|
|
closeInsufficientCreditsDialog: () => void;
|
|
confirmInsufficientCreditsDialog: () => void;
|
|
}
|
|
|
|
export function useChatUnlockNavigationFlow({
|
|
returnUrl,
|
|
expectedKind,
|
|
expectedMessageId,
|
|
ignoredKind,
|
|
enabled = true,
|
|
}: UseChatUnlockNavigationFlowInput): UseChatUnlockNavigationFlowOutput {
|
|
const navigator = useAppNavigator();
|
|
const userState = useUserState();
|
|
const chatState = useChatState();
|
|
const chatDispatch = useChatDispatch();
|
|
const unlockPaywallRequest = getScopedUnlockPaywallRequest({
|
|
request: chatState.unlockPaywallRequest,
|
|
expectedKind,
|
|
expectedMessageId,
|
|
ignoredKind,
|
|
enabled,
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (!enabled) return;
|
|
if (!chatState.historyLoaded || !navigator.isAuthenticatedUser) return;
|
|
|
|
let cancelled = false;
|
|
|
|
const resumePendingUnlock = async () => {
|
|
const pending = await peekPendingChatUnlock();
|
|
if (
|
|
cancelled ||
|
|
!pending ||
|
|
pending.returnUrl !== returnUrl ||
|
|
!matchesPendingUnlockScope({
|
|
pending,
|
|
expectedKind,
|
|
expectedMessageId,
|
|
})
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const consumed = await consumePendingChatUnlock();
|
|
if (cancelled || !consumed) return;
|
|
|
|
chatDispatch({
|
|
type: "ChatUnlockMessageRequested",
|
|
messageId: consumed.messageId,
|
|
kind: consumed.kind,
|
|
});
|
|
};
|
|
|
|
void resumePendingUnlock();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [
|
|
chatDispatch,
|
|
chatState.historyLoaded,
|
|
enabled,
|
|
expectedKind,
|
|
expectedMessageId,
|
|
ignoredKind,
|
|
navigator.isAuthenticatedUser,
|
|
returnUrl,
|
|
]);
|
|
|
|
function requestMessageUnlock(
|
|
messageId: string,
|
|
kind: PendingChatUnlockKind,
|
|
): void {
|
|
navigator.startMessageUnlock({
|
|
messageId,
|
|
kind,
|
|
returnUrl,
|
|
onAuthenticated: () => {
|
|
chatDispatch({
|
|
type: "ChatUnlockMessageRequested",
|
|
messageId,
|
|
kind,
|
|
});
|
|
},
|
|
});
|
|
}
|
|
|
|
function closeInsufficientCreditsDialog(): void {
|
|
chatDispatch({ type: "ChatUnlockPaywallNavigationConsumed" });
|
|
}
|
|
|
|
function confirmInsufficientCreditsDialog(): void {
|
|
if (!unlockPaywallRequest) return;
|
|
|
|
chatDispatch({ type: "ChatUnlockPaywallNavigationConsumed" });
|
|
navigator.openSubscriptionForPendingUnlock({
|
|
messageId: unlockPaywallRequest.messageId,
|
|
kind: unlockPaywallRequest.kind,
|
|
returnUrl,
|
|
type: getInsufficientCreditsSubscriptionType(userState.isVip),
|
|
});
|
|
}
|
|
|
|
return {
|
|
unlockPaywallRequest,
|
|
requestMessageUnlock,
|
|
closeInsufficientCreditsDialog,
|
|
confirmInsufficientCreditsDialog,
|
|
};
|
|
}
|
|
|
|
function getScopedUnlockPaywallRequest(input: {
|
|
request: ChatUnlockPaywallRequest | null;
|
|
expectedKind?: PendingChatUnlockKind;
|
|
expectedMessageId?: string;
|
|
ignoredKind?: PendingChatUnlockKind;
|
|
enabled?: boolean;
|
|
}): ChatUnlockPaywallRequest | null {
|
|
const {
|
|
request,
|
|
expectedKind,
|
|
expectedMessageId,
|
|
ignoredKind,
|
|
enabled = true,
|
|
} = input;
|
|
if (!enabled) return null;
|
|
if (!request) return null;
|
|
if (ignoredKind && request.kind === ignoredKind) return null;
|
|
if (expectedKind && request.kind !== expectedKind) return null;
|
|
if (expectedMessageId && request.messageId !== expectedMessageId) return null;
|
|
return request;
|
|
}
|
|
|
|
function matchesPendingUnlockScope(input: {
|
|
pending: PendingChatUnlock;
|
|
expectedKind?: PendingChatUnlockKind;
|
|
expectedMessageId?: string;
|
|
}): boolean {
|
|
const { pending, expectedKind, expectedMessageId } = input;
|
|
if (expectedKind && pending.kind !== expectedKind) return false;
|
|
if (expectedMessageId && pending.messageId !== expectedMessageId) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|