feat(chat): render role-bound payment guidance
Docker Image / Build and Push Docker Image (push) Successful in 5m2s

This commit is contained in:
Codex
2026-07-27 18:14:04 +08:00
parent 019caae598
commit 8c0d1ad3ce
49 changed files with 882 additions and 81 deletions
+23 -2
View File
@@ -19,6 +19,8 @@ import {
} from "@/providers/character-provider";
import { isPrivateAlbumLocked } from "@/lib/private-zone/private_album";
import { behaviorAnalytics } from "@/lib/analytics";
import { recordChatActionEventById } from "@/lib/chat/chat_action_events";
import { useUserSelector } from "@/stores/user/user-context";
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
import {
usePrivateZoneDispatch,
@@ -63,6 +65,7 @@ export function PrivateZoneScreen() {
const dispatch = usePrivateZoneDispatch();
const postState = usePrivateZonePostState();
const postDispatch = usePrivateZonePostDispatch();
const isVip = useUserSelector((user) => user.context.isVip);
const [activeTab, setActiveTab] = useState<"moments" | "albums">("moments");
const openedGalleryInPageRef = useRef(false);
const previousPostLoginStatusRef = useRef(authState.loginStatus);
@@ -84,6 +87,7 @@ export function PrivateZoneScreen() {
loginStatus: authState.loginStatus,
roomDispatch: dispatch,
unlockPaywallRequest: state.unlockPaywallRequest,
isVip,
});
usePrivateZoneUnlockSuccessRefresh(state.unlockSuccessNonce);
usePrivateZoneUnlockSuccessRefresh(postState.unlockSuccessNonce);
@@ -119,9 +123,24 @@ export function PrivateZoneScreen() {
entryPoint: "private_zone",
triggerReason: "insufficient_credits",
});
const guidance =
request.paymentGuidance?.characterId === character.id
? request.paymentGuidance
: null;
if (guidance) {
void recordChatActionEventById(
guidance.guidanceId,
character.id,
"opened",
).catch(() => undefined);
}
navigator.openSubscription({
type: "topup",
type:
isVip || (guidance && !guidance.purchaseOptions.includes("vip"))
? "topup"
: "vip",
returnTo: "private-zone",
...(guidance ? { chatActionId: guidance.guidanceId } : {}),
analytics: {
entryPoint: "private_zone",
triggerReason: "insufficient_credits",
@@ -132,6 +151,8 @@ export function PrivateZoneScreen() {
}, [
authState.loginStatus,
characterRoutes.privateZone,
character.id,
isVip,
navigator,
postDispatch,
postState.unlockPaywallRequest,
@@ -205,7 +226,7 @@ export function PrivateZoneScreen() {
return;
}
navigator.openSubscription({
type: "topup",
type: isVip ? "topup" : "vip",
returnTo: "private-zone",
analytics: {
entryPoint: "private_zone",