feat(profile)!: replace sidebar and add avatar navigation
Rename the global Sidebar route, UI, assets, analytics, and payment return context to Profile. Add accessible message-avatar navigation and preserve the source character across auth and logout flows. BREAKING CHANGE: /sidebar has been removed; use /profile instead.
This commit is contained in:
@@ -30,10 +30,10 @@ describe("payment analytics context", () => {
|
||||
|
||||
expect(
|
||||
parsePaymentAnalyticsContext({
|
||||
entryPoint: "sidebar",
|
||||
entryPoint: "profile",
|
||||
triggerReason: null,
|
||||
subscriptionType: "vip",
|
||||
}),
|
||||
).toEqual({ entryPoint: "sidebar", triggerReason: "vip_cta" });
|
||||
).toEqual({ entryPoint: "profile", triggerReason: "vip_cta" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ export type PaymentAnalyticsTriggerReason =
|
||||
| "daily_chat_limit"
|
||||
| "private_topic_limit"
|
||||
| "insufficient_credits"
|
||||
| "sidebar_recharge"
|
||||
| "profile_recharge"
|
||||
| "vip_cta"
|
||||
| "ad_landing"
|
||||
| "manual_recharge"
|
||||
@@ -16,7 +16,7 @@ export type PaymentAnalyticsEntryPoint =
|
||||
| "chat_unlock"
|
||||
| "private_album_unlock"
|
||||
| "private_room"
|
||||
| "sidebar"
|
||||
| "profile"
|
||||
| "chat_offer_banner"
|
||||
| "subscription_direct"
|
||||
| "tip_page"
|
||||
@@ -33,7 +33,7 @@ const ENTRY_POINTS = new Set<PaymentAnalyticsEntryPoint>([
|
||||
"chat_unlock",
|
||||
"private_album_unlock",
|
||||
"private_room",
|
||||
"sidebar",
|
||||
"profile",
|
||||
"chat_offer_banner",
|
||||
"subscription_direct",
|
||||
"tip_page",
|
||||
@@ -45,7 +45,7 @@ const TRIGGER_REASONS = new Set<PaymentAnalyticsTriggerReason>([
|
||||
"daily_chat_limit",
|
||||
"private_topic_limit",
|
||||
"insufficient_credits",
|
||||
"sidebar_recharge",
|
||||
"profile_recharge",
|
||||
"vip_cta",
|
||||
"ad_landing",
|
||||
"manual_recharge",
|
||||
|
||||
@@ -29,7 +29,7 @@ describe("external entry navigation", () => {
|
||||
ROUTES.chat,
|
||||
);
|
||||
expect(resolveExternalEntryTarget({ target: "/tip" })).toBe(ROUTES.chat);
|
||||
expect(resolveExternalEntryTarget({ target: "sidebar" })).toBe(ROUTES.chat);
|
||||
expect(resolveExternalEntryTarget({ target: "profile" })).toBe(ROUTES.chat);
|
||||
});
|
||||
|
||||
it("routes every tip entry to the tier selection page", () => {
|
||||
|
||||
@@ -60,30 +60,30 @@ describe("subscription exit helpers", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to sidebar by default", async () => {
|
||||
it("falls back to profile by default", async () => {
|
||||
consumePendingChatImageReturnMock.mockResolvedValue(null);
|
||||
peekPendingChatUnlockMock.mockResolvedValue(null);
|
||||
|
||||
expect(getSubscriptionFallbackExitUrl(null)).toBe(ROUTES.sidebar);
|
||||
expect(getSubscriptionFallbackExitUrl(null)).toBe(ROUTES.profile);
|
||||
await expect(consumeSubscriptionExitUrl(null)).resolves.toBe(
|
||||
ROUTES.sidebar,
|
||||
ROUTES.profile,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns to global sidebar with the source character", async () => {
|
||||
it("returns to global profile with the source character", async () => {
|
||||
const expectedUrl = buildGlobalPageUrl(
|
||||
ROUTES.sidebar,
|
||||
ROUTES.profile,
|
||||
getCharacterRoutes("maya").chat,
|
||||
);
|
||||
|
||||
expect(getSubscriptionFallbackExitUrl("sidebar", "maya")).toBe(
|
||||
expect(getSubscriptionFallbackExitUrl("profile", "maya")).toBe(
|
||||
expectedUrl,
|
||||
);
|
||||
await expect(
|
||||
consumeSubscriptionExitUrl("sidebar", "maya"),
|
||||
consumeSubscriptionExitUrl("profile", "maya"),
|
||||
).resolves.toBe(expectedUrl);
|
||||
await expect(
|
||||
resolveSubscriptionSuccessExitUrl("sidebar", "maya"),
|
||||
resolveSubscriptionSuccessExitUrl("profile", "maya"),
|
||||
).resolves.toBe(expectedUrl);
|
||||
expect(consumePendingChatImageReturnMock).not.toHaveBeenCalled();
|
||||
expect(peekPendingChatUnlockMock).not.toHaveBeenCalled();
|
||||
|
||||
@@ -48,10 +48,10 @@ export function getSubscriptionFallbackExitUrl(
|
||||
const routes = getCharacterRoutes(characterSlug);
|
||||
if (returnTo === "chat") return routes.chat;
|
||||
if (returnTo === "private-room") return routes.privateRoom;
|
||||
if (returnTo === "sidebar") {
|
||||
return buildGlobalPageUrl(ROUTES.sidebar, routes.chat);
|
||||
if (returnTo === "profile") {
|
||||
return buildGlobalPageUrl(ROUTES.profile, routes.chat);
|
||||
}
|
||||
return ROUTES.sidebar;
|
||||
return ROUTES.profile;
|
||||
}
|
||||
|
||||
export async function consumeSubscriptionExitUrl(
|
||||
@@ -61,7 +61,7 @@ export async function consumeSubscriptionExitUrl(
|
||||
if (returnTo === "private-room") {
|
||||
return getCharacterRoutes(characterSlug).privateRoom;
|
||||
}
|
||||
if (returnTo === "sidebar") {
|
||||
if (returnTo === "profile") {
|
||||
return getSubscriptionFallbackExitUrl(returnTo, characterSlug);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export async function resolveSubscriptionSuccessExitUrl(
|
||||
if (returnTo === "private-room") {
|
||||
return getCharacterRoutes(characterSlug).privateRoom;
|
||||
}
|
||||
if (returnTo === "sidebar") {
|
||||
if (returnTo === "profile") {
|
||||
return getSubscriptionFallbackExitUrl(returnTo, characterSlug);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ 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("sidebar")).toBe("sidebar");
|
||||
expect(parseSubscriptionReturnTo("profile")).toBe("profile");
|
||||
expect(parseSubscriptionReturnTo(["private-room", "chat"])).toBe(
|
||||
"private-room",
|
||||
);
|
||||
|
||||
@@ -54,12 +54,12 @@ describe("pending payment order helpers", () => {
|
||||
await clearPendingPaymentOrder();
|
||||
});
|
||||
|
||||
it("stores and rebuilds global sidebar returns", async () => {
|
||||
it("stores and rebuilds global profile returns", async () => {
|
||||
await clearPendingPaymentOrder();
|
||||
|
||||
const saveResult = await savePendingEzpayOrder({
|
||||
orderId: "order-sidebar",
|
||||
returnTo: "sidebar",
|
||||
orderId: "order-profile",
|
||||
returnTo: "profile",
|
||||
subscriptionType: "topup",
|
||||
characterSlug: "maya",
|
||||
createdAt: 1,
|
||||
@@ -70,20 +70,20 @@ describe("pending payment order helpers", () => {
|
||||
expect(storedResult).toMatchObject({
|
||||
success: true,
|
||||
data: {
|
||||
orderId: "order-sidebar",
|
||||
returnTo: "sidebar",
|
||||
orderId: "order-profile",
|
||||
returnTo: "profile",
|
||||
characterSlug: "maya",
|
||||
},
|
||||
});
|
||||
expect(
|
||||
buildPendingPaymentSubscriptionUrl({
|
||||
payChannel: "ezpay",
|
||||
returnTo: "sidebar",
|
||||
returnTo: "profile",
|
||||
subscriptionType: "topup",
|
||||
characterSlug: "maya",
|
||||
}),
|
||||
).toBe(
|
||||
"/subscription?type=topup&payChannel=ezpay&paymentReturn=1&returnTo=sidebar&character=maya",
|
||||
"/subscription?type=topup&payChannel=ezpay&paymentReturn=1&returnTo=profile&character=maya",
|
||||
);
|
||||
await clearPendingPaymentOrder();
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ export function parseSubscriptionReturnTo(
|
||||
if (
|
||||
returnTo === "chat" ||
|
||||
returnTo === "private-room" ||
|
||||
returnTo === "sidebar"
|
||||
returnTo === "profile"
|
||||
) {
|
||||
return returnTo;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user