fix(subscription): sync vip state before chat return

This commit is contained in:
2026-06-25 17:03:09 +08:00
parent 2c668399ad
commit b8ee286b76
+73 -1
View File
@@ -68,8 +68,11 @@ export function SubscriptionScreen({
const refreshedPaidOrderRef = useRef<string | null>(null); const refreshedPaidOrderRef = useRef<string | null>(null);
const resumedPendingOrderRef = useRef<string | null>(null); const resumedPendingOrderRef = useRef<string | null>(null);
const successDialogShownOrderRef = useRef<string | null>(null); const successDialogShownOrderRef = useRef<string | null>(null);
const syncedVipOrderRef = useRef<string | null>(null);
const [showPaymentSuccessDialog, setShowPaymentSuccessDialog] = const [showPaymentSuccessDialog, setShowPaymentSuccessDialog] =
useState(false); useState(false);
const [pendingChatReturnAfterVipSync, setPendingChatReturnAfterVipSync] =
useState(false);
useEffect(() => { useEffect(() => {
if (payment.status === "idle") { if (payment.status === "idle") {
@@ -84,6 +87,35 @@ export function SubscriptionScreen({
userDispatch({ type: "UserFetch" }); userDispatch({ type: "UserFetch" });
}, [payment.currentOrderId, payment.isPaid, userDispatch]); }, [payment.currentOrderId, payment.isPaid, userDispatch]);
useEffect(() => {
const currentUser = user.currentUser;
const canMarkVipSynced =
subscriptionType === "vip" &&
payment.isPaid &&
payment.currentOrderId &&
payment.vipStatus?.isVip === true &&
currentUser &&
currentUser.isVip !== true;
if (!canMarkVipSynced) return;
if (syncedVipOrderRef.current === payment.currentOrderId) return;
syncedVipOrderRef.current = payment.currentOrderId;
userDispatch({
type: "UserUpdate",
user: {
...currentUser,
isVip: true,
},
});
}, [
payment.currentOrderId,
payment.isPaid,
payment.vipStatus?.isVip,
subscriptionType,
user.currentUser,
userDispatch,
]);
useEffect(() => { useEffect(() => {
if (!payment.isPaid || !payment.currentOrderId) return; if (!payment.isPaid || !payment.currentOrderId) return;
if (successDialogShownOrderRef.current === payment.currentOrderId) return; if (successDialogShownOrderRef.current === payment.currentOrderId) return;
@@ -181,7 +213,8 @@ export function SubscriptionScreen({
? "Choose a voice package" ? "Choose a voice package"
: "Choose a subscription plan"; : "Choose a subscription plan";
const handlePaymentSuccessClose = () => { const finishPaymentSuccessClose = () => {
setPendingChatReturnAfterVipSync(false);
setShowPaymentSuccessDialog(false); setShowPaymentSuccessDialog(false);
paymentDispatch({ type: "PaymentReset" }); paymentDispatch({ type: "PaymentReset" });
if (returnTo === "chat") { if (returnTo === "chat") {
@@ -189,6 +222,45 @@ export function SubscriptionScreen({
} }
}; };
const handlePaymentSuccessClose = () => {
const shouldWaitForVipSync =
returnTo === "chat" &&
subscriptionType === "vip" &&
user.currentUser?.isVip !== true;
if (shouldWaitForVipSync) {
setPendingChatReturnAfterVipSync(true);
if (payment.vipStatus?.isVip === true && user.currentUser) {
userDispatch({
type: "UserUpdate",
user: {
...user.currentUser,
isVip: true,
},
});
} else {
userDispatch({ type: "UserFetch" });
}
return;
}
finishPaymentSuccessClose();
};
useEffect(() => {
if (!pendingChatReturnAfterVipSync) return;
if (subscriptionType === "vip" && user.currentUser?.isVip !== true) return;
const timer = window.setTimeout(() => {
finishPaymentSuccessClose();
}, 0);
return () => window.clearTimeout(timer);
// `finishPaymentSuccessClose` intentionally stays local to this component;
// this effect is gated by pendingChatReturnAfterVipSync to avoid repeated closes.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pendingChatReturnAfterVipSync, subscriptionType, user.currentUser?.isVip]);
useEffect(() => { useEffect(() => {
if (payment.isLoadingPlans || payment.plans.length === 0) return; if (payment.isLoadingPlans || payment.plans.length === 0) return;
if (selectedPlan !== null) return; if (selectedPlan !== null) return;