Files
cozsweet-frontend-nextjs/src/stores/sync/chat-payment-success-sync.tsx
T

53 lines
1.5 KiB
TypeScript

"use client";
/** Payment → Chat bridge, mounted only while the chat route is active. */
import { useEffect, useRef } from "react";
import { shallowEqual } from "@xstate/react";
import { hasPendingChatUnlock } from "@/lib/navigation/chat_unlock_session";
import {
useChatDispatch,
useChatSelector,
} from "@/stores/chat/chat-context";
import { usePaymentSelector } from "@/stores/payment/payment-context";
export function ChatPaymentSuccessSync() {
const paymentState = usePaymentSelector(
(state) => ({
currentOrderId: state.context.currentOrderId,
isPaid: state.matches("paid"),
orderStatus: state.context.orderStatus,
}),
shallowEqual,
);
const characterId = useChatSelector((state) => state.context.characterId);
const chatDispatch = useChatDispatch();
const lastPaidKeyRef = useRef<string | null>(null);
useEffect(() => {
if (!paymentState.isPaid || !paymentState.currentOrderId) return;
const paidKey = `${paymentState.currentOrderId}:${paymentState.orderStatus}`;
if (lastPaidKeyRef.current === paidKey) return;
lastPaidKeyRef.current = paidKey;
let cancelled = false;
void (async () => {
if (await hasPendingChatUnlock(characterId)) return;
if (!cancelled) chatDispatch({ type: "ChatPaymentSucceeded" });
})();
return () => {
cancelled = true;
};
}, [
chatDispatch,
characterId,
paymentState.currentOrderId,
paymentState.isPaid,
paymentState.orderStatus,
]);
return null;
}