feat(tip): support tiered coffee gifts

This commit is contained in:
2026-07-14 10:44:46 +08:00
parent 37ae152abb
commit 0fe74b5371
17 changed files with 307 additions and 41 deletions
@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
import { ROUTES } from "@/router/routes";
import {
resolveExternalEntryDestination,
resolveExternalEntryPromotionType,
resolveExternalEntryTarget,
shouldBindExternalFacebookIdentity,
@@ -30,6 +31,30 @@ describe("external entry navigation", () => {
expect(resolveExternalEntryTarget({ target: "/tip" })).toBe(ROUTES.chat);
expect(resolveExternalEntryTarget({ target: "sidebar" })).toBe(ROUTES.chat);
});
it("routes tip entries to the requested coffee type", () => {
expect(
resolveExternalEntryDestination({
target: "tip",
coffeeType: "medium",
}),
).toBe("/tip?coffee_type=medium");
expect(
resolveExternalEntryDestination({
target: "tip",
coffeeType: "large",
}),
).toBe("/tip?coffee_type=large");
});
it("defaults invalid tip coffee types to small", () => {
expect(
resolveExternalEntryDestination({
target: "tip",
coffeeType: "unknown",
}),
).toBe("/tip?coffee_type=small");
});
});
describe("external entry facebook identity binding", () => {
+21
View File
@@ -5,6 +5,11 @@ import { UserStorage } from "@/data/storage/user/user_storage";
import type { PendingChatPromotionType } from "@/data/storage/navigation";
import type { LoginStatus } from "@/data/dto/auth";
import { ROUTES } from "@/router/routes";
import {
buildTipCoffeePath,
DEFAULT_TIP_COFFEE_TYPE,
resolveTipCoffeeType,
} from "@/lib/tip/tip_coffee";
export type ExternalEntryTarget =
| typeof ROUTES.chat
@@ -22,6 +27,10 @@ export interface ExternalEntryTargetInput {
target?: string | null;
}
export interface ExternalEntryDestinationInput extends ExternalEntryTargetInput {
coffeeType?: string | null;
}
export interface ExternalEntryPromotionInput {
mode?: string | null;
promotionType?: string | null;
@@ -77,6 +86,18 @@ export function resolveExternalEntryTarget({
return resolveTarget(target) ?? ROUTES.chat;
}
export function resolveExternalEntryDestination({
target,
coffeeType,
}: ExternalEntryDestinationInput): string {
const resolvedTarget = resolveExternalEntryTarget({ target });
if (resolvedTarget !== ROUTES.tip) return resolvedTarget;
return buildTipCoffeePath(
resolveTipCoffeeType(coffeeType) ?? DEFAULT_TIP_COFFEE_TYPE,
);
}
export async function persistExternalEntryPayload({
deviceId,
asid,