53 lines
1.5 KiB
TypeScript
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;
|
|
}
|