refactor(private-zoom): rename full feature surface

This commit is contained in:
Codex
2026-07-22 18:21:47 +08:00
parent e813333607
commit ed3b34ce1c
102 changed files with 695 additions and 519 deletions
@@ -15,7 +15,7 @@ export type PaymentAnalyticsEntryPoint =
| "chat_input"
| "chat_unlock"
| "private_album_unlock"
| "private_room"
| "private_zoom"
| "profile"
| "chat_offer_banner"
| "subscription_direct"
@@ -32,7 +32,7 @@ const ENTRY_POINTS = new Set<PaymentAnalyticsEntryPoint>([
"chat_input",
"chat_unlock",
"private_album_unlock",
"private_room",
"private_zoom",
"profile",
"chat_offer_banner",
"subscription_direct",
@@ -17,15 +17,15 @@ describe("external entry navigation", () => {
it("resolves only canonical targets", () => {
expect(resolveExternalEntryTarget({ target: "chat" })).toBe(ROUTES.chat);
expect(resolveExternalEntryTarget({ target: "tip" })).toBe(ROUTES.tip);
expect(resolveExternalEntryTarget({ target: "private-room" })).toBe(
ROUTES.privateRoom,
expect(resolveExternalEntryTarget({ target: "private-zoom" })).toBe(
ROUTES.privateZoom,
);
});
it("rejects removed aliases and unsupported targets", () => {
expect(resolveExternalEntryTarget({ target: "tips" })).toBe(ROUTES.chat);
expect(resolveExternalEntryTarget({ target: "coffee" })).toBe(ROUTES.chat);
expect(resolveExternalEntryTarget({ target: "private_room" })).toBe(
expect(resolveExternalEntryTarget({ target: "private_zoom" })).toBe(
ROUTES.chat,
);
expect(resolveExternalEntryTarget({ target: "/tip" })).toBe(ROUTES.chat);
@@ -44,10 +44,10 @@ describe("external entry navigation", () => {
).toBe(getCharacterRoutes("maya").chat);
expect(
resolveExternalEntryDestination({
target: "private-room",
target: "private-zoom",
character: "unknown",
}),
).toBe(getCharacterRoutes("elio").privateRoom);
).toBe(getCharacterRoutes("elio").privateZoom);
});
});
@@ -89,7 +89,7 @@ describe("subscription exit helpers", () => {
expect(peekPendingChatUnlockMock).not.toHaveBeenCalled();
});
it("returns directly to private room without consuming chat state", async () => {
it("returns directly to private zoom without consuming chat state", async () => {
consumePendingChatImageReturnMock.mockResolvedValue({
reason: "image_paywall",
characterId: "elio",
@@ -108,18 +108,18 @@ describe("subscription exit helpers", () => {
createdAt: 1,
});
expect(getSubscriptionFallbackExitUrl("private-room")).toBe(
getCharacterRoutes("elio").privateRoom,
expect(getSubscriptionFallbackExitUrl("private-zoom")).toBe(
getCharacterRoutes("elio").privateZoom,
);
await expect(consumeSubscriptionExitUrl("private-room")).resolves.toBe(
getCharacterRoutes("elio").privateRoom,
await expect(consumeSubscriptionExitUrl("private-zoom")).resolves.toBe(
getCharacterRoutes("elio").privateZoom,
);
expect(consumePendingChatImageReturnMock).not.toHaveBeenCalled();
expect(peekPendingChatUnlockMock).not.toHaveBeenCalled();
expect(clearPendingChatUnlockMock).not.toHaveBeenCalled();
});
it("keeps private room ahead of stale chat state after payment", async () => {
it("keeps private zoom ahead of stale chat state after payment", async () => {
peekPendingChatUnlockMock.mockResolvedValue({
reason: "single_message_unlock",
characterId: "elio",
@@ -132,8 +132,8 @@ describe("subscription exit helpers", () => {
});
await expect(
resolveSubscriptionSuccessExitUrl("private-room", "maya"),
).resolves.toBe(getCharacterRoutes("maya").privateRoom);
resolveSubscriptionSuccessExitUrl("private-zoom", "maya"),
).resolves.toBe(getCharacterRoutes("maya").privateZoom);
expect(peekPendingChatUnlockMock).not.toHaveBeenCalled();
expect(clearPendingChatUnlockMock).not.toHaveBeenCalled();
});
+3 -3
View File
@@ -13,7 +13,7 @@ import { getCharacterRoutes, ROUTES } from "@/router/routes";
export type ExternalEntryTarget =
| typeof ROUTES.chat
| typeof ROUTES.tip
| typeof ROUTES.privateRoom;
| typeof ROUTES.privateZoom;
export interface ExternalEntryPayload {
deviceId?: string | null;
@@ -91,7 +91,7 @@ export function resolveExternalEntryDestination({
const routes = getCharacterRoutes(characterSlug);
const resolvedTarget = resolveExternalEntryTarget({ target });
if (resolvedTarget === ROUTES.tip) return routes.tip;
if (resolvedTarget === ROUTES.privateRoom) return routes.privateRoom;
if (resolvedTarget === ROUTES.privateZoom) return routes.privateZoom;
return routes.chat;
}
@@ -129,7 +129,7 @@ function resolveTarget(
if (target === "chat") return ROUTES.chat;
if (target === "tip") return ROUTES.tip;
if (target === "private-room") return ROUTES.privateRoom;
if (target === "private-zoom") return ROUTES.privateZoom;
return null;
}
+5 -5
View File
@@ -47,7 +47,7 @@ export function getSubscriptionFallbackExitUrl(
): string {
const routes = getCharacterRoutes(characterSlug);
if (returnTo === "chat") return routes.chat;
if (returnTo === "private-room") return routes.privateRoom;
if (returnTo === "private-zoom") return routes.privateZoom;
if (returnTo === "profile") {
return buildGlobalPageUrl(ROUTES.profile, routes.chat);
}
@@ -58,8 +58,8 @@ export async function consumeSubscriptionExitUrl(
returnTo: SubscriptionReturnTo,
characterSlug: string = DEFAULT_CHARACTER_SLUG,
): Promise<string> {
if (returnTo === "private-room") {
return getCharacterRoutes(characterSlug).privateRoom;
if (returnTo === "private-zoom") {
return getCharacterRoutes(characterSlug).privateZoom;
}
if (returnTo === "profile") {
return getSubscriptionFallbackExitUrl(returnTo, characterSlug);
@@ -75,8 +75,8 @@ export async function resolveSubscriptionSuccessExitUrl(
returnTo: SubscriptionReturnTo,
characterSlug: string = DEFAULT_CHARACTER_SLUG,
): Promise<string> {
if (returnTo === "private-room") {
return getCharacterRoutes(characterSlug).privateRoom;
if (returnTo === "private-zoom") {
return getCharacterRoutes(characterSlug).privateZoom;
}
if (returnTo === "profile") {
return getSubscriptionFallbackExitUrl(returnTo, characterSlug);
@@ -39,10 +39,10 @@ describe("payment search params", () => {
it("accepts only supported subscription return targets", () => {
expect(parseSubscriptionReturnTo("chat")).toBe("chat");
expect(parseSubscriptionReturnTo("private-room")).toBe("private-room");
expect(parseSubscriptionReturnTo("private-zoom")).toBe("private-zoom");
expect(parseSubscriptionReturnTo("profile")).toBe("profile");
expect(parseSubscriptionReturnTo(["private-room", "chat"])).toBe(
"private-room",
expect(parseSubscriptionReturnTo(["private-zoom", "chat"])).toBe(
"private-zoom",
);
expect(parseSubscriptionReturnTo("tip")).toBeNull();
expect(parseSubscriptionReturnTo(undefined)).toBeNull();
@@ -20,24 +20,24 @@ describe("pending payment order helpers", () => {
);
});
it("preserves private room returns when rebuilding Ezpay urls", () => {
it("preserves private zoom returns when rebuilding Ezpay urls", () => {
expect(
buildPendingPaymentSubscriptionUrl({
payChannel: "ezpay",
returnTo: "private-room",
returnTo: "private-zoom",
subscriptionType: "topup",
}),
).toBe(
"/subscription?type=topup&payChannel=ezpay&paymentReturn=1&returnTo=private-room&character=elio",
"/subscription?type=topup&payChannel=ezpay&paymentReturn=1&returnTo=private-zoom&character=elio",
);
});
it("stores private room as an Ezpay return target", async () => {
it("stores private zoom as an Ezpay return target", async () => {
await clearPendingPaymentOrder();
const saveResult = await savePendingEzpayOrder({
orderId: "order-private-room",
returnTo: "private-room",
orderId: "order-private-zoom",
returnTo: "private-zoom",
subscriptionType: "topup",
createdAt: 1,
});
@@ -47,8 +47,8 @@ describe("pending payment order helpers", () => {
expect(storedResult).toMatchObject({
success: true,
data: {
orderId: "order-private-room",
returnTo: "private-room",
orderId: "order-private-zoom",
returnTo: "private-zoom",
},
});
await clearPendingPaymentOrder();
+1 -1
View File
@@ -28,7 +28,7 @@ export function parseSubscriptionReturnTo(
const returnTo = getFirstPaymentSearchParam(value);
if (
returnTo === "chat" ||
returnTo === "private-room" ||
returnTo === "private-zoom" ||
returnTo === "profile"
) {
return returnTo;
@@ -1,4 +1,4 @@
import type { PrivateAlbum } from "@/data/schemas/private-room";
import type { PrivateAlbum } from "@/data/schemas/private-zoom";
export function isPrivateAlbumLocked(album: PrivateAlbum): boolean {
return album.locked || !album.unlocked || album.lockDetail.locked;