perf(payment): parse route params on the server
This commit is contained in:
@@ -1,19 +1,53 @@
|
|||||||
import { Suspense } from "react";
|
import type { PayChannel } from "@/data/dto/payment";
|
||||||
|
import {
|
||||||
|
PAYMENT_ANALYTICS_ENTRY_PARAM,
|
||||||
|
PAYMENT_ANALYTICS_REASON_PARAM,
|
||||||
|
parsePaymentAnalyticsContext,
|
||||||
|
} from "@/lib/analytics/payment_analytics_context";
|
||||||
|
|
||||||
import { PageLoadingFallback } from "@/app/_components/core";
|
import {
|
||||||
|
SubscriptionScreen,
|
||||||
|
type SubscriptionType,
|
||||||
|
} from "./subscription-screen";
|
||||||
|
|
||||||
import { SubscriptionPageClient } from "./subscription-page-client";
|
type SubscriptionSearchParams = Record<string, string | string[] | undefined>;
|
||||||
|
|
||||||
|
export default async function SubscriptionPage({
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
searchParams: Promise<SubscriptionSearchParams>;
|
||||||
|
}) {
|
||||||
|
const query = await searchParams;
|
||||||
|
const subscriptionType = toSubscriptionType(firstSearchParam(query.type));
|
||||||
|
const analyticsContext = parsePaymentAnalyticsContext({
|
||||||
|
entryPoint: firstSearchParam(query[PAYMENT_ANALYTICS_ENTRY_PARAM]),
|
||||||
|
triggerReason: firstSearchParam(query[PAYMENT_ANALYTICS_REASON_PARAM]),
|
||||||
|
subscriptionType,
|
||||||
|
});
|
||||||
|
|
||||||
export default function SubscriptionPage() {
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<SubscriptionFallback />}>
|
<SubscriptionScreen
|
||||||
<SubscriptionPageClient />
|
subscriptionType={subscriptionType}
|
||||||
</Suspense>
|
shouldResumePendingOrder={firstSearchParam(query.paymentReturn) === "1"}
|
||||||
|
returnTo={toReturnTo(firstSearchParam(query.returnTo))}
|
||||||
|
initialPayChannel={toPayChannel(firstSearchParam(query.payChannel))}
|
||||||
|
analyticsContext={analyticsContext}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubscriptionFallback() {
|
function firstSearchParam(value: string | string[] | undefined): string | null {
|
||||||
return (
|
return Array.isArray(value) ? (value[0] ?? null) : (value ?? null);
|
||||||
<PageLoadingFallback>Loading subscription...</PageLoadingFallback>
|
}
|
||||||
);
|
|
||||||
|
function toSubscriptionType(value: string | null): SubscriptionType {
|
||||||
|
return value === "topup" ? "topup" : "vip";
|
||||||
|
}
|
||||||
|
|
||||||
|
function toReturnTo(value: string | null): "chat" | null {
|
||||||
|
return value === "chat" ? "chat" : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toPayChannel(value: string | null): PayChannel | null {
|
||||||
|
return value === "ezpay" || value === "stripe" ? value : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useSearchParams } from "next/navigation";
|
|
||||||
|
|
||||||
import type { PayChannel } from "@/data/dto/payment";
|
|
||||||
import {
|
|
||||||
PAYMENT_ANALYTICS_ENTRY_PARAM,
|
|
||||||
PAYMENT_ANALYTICS_REASON_PARAM,
|
|
||||||
parsePaymentAnalyticsContext,
|
|
||||||
} from "@/lib/analytics";
|
|
||||||
|
|
||||||
import {
|
|
||||||
SubscriptionScreen,
|
|
||||||
type SubscriptionType,
|
|
||||||
} from "./subscription-screen";
|
|
||||||
|
|
||||||
function toSubscriptionType(value: string | null): SubscriptionType {
|
|
||||||
return value === "topup" ? "topup" : "vip";
|
|
||||||
}
|
|
||||||
|
|
||||||
function toReturnTo(value: string | null): "chat" | null {
|
|
||||||
return value === "chat" ? "chat" : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toPayChannel(value: string | null): PayChannel | null {
|
|
||||||
if (value === "ezpay" || value === "stripe") return value;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SubscriptionPageClient() {
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const subscriptionType = toSubscriptionType(searchParams.get("type"));
|
|
||||||
const shouldResumePendingOrder = searchParams.get("paymentReturn") === "1";
|
|
||||||
const returnTo = toReturnTo(searchParams.get("returnTo"));
|
|
||||||
const initialPayChannel = toPayChannel(searchParams.get("payChannel"));
|
|
||||||
const analyticsContext = parsePaymentAnalyticsContext({
|
|
||||||
entryPoint: searchParams.get(PAYMENT_ANALYTICS_ENTRY_PARAM),
|
|
||||||
triggerReason: searchParams.get(PAYMENT_ANALYTICS_REASON_PARAM),
|
|
||||||
subscriptionType,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SubscriptionScreen
|
|
||||||
subscriptionType={subscriptionType}
|
|
||||||
shouldResumePendingOrder={shouldResumePendingOrder}
|
|
||||||
returnTo={returnTo}
|
|
||||||
initialPayChannel={initialPayChannel}
|
|
||||||
analyticsContext={analyticsContext}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
+29
-13
@@ -1,21 +1,37 @@
|
|||||||
import { Suspense } from "react";
|
import type { PayChannel } from "@/data/dto/payment";
|
||||||
|
import {
|
||||||
|
DEFAULT_TIP_COFFEE_TYPE,
|
||||||
|
resolveTipCoffeeType,
|
||||||
|
TIP_COFFEE_TYPE_PARAM,
|
||||||
|
} from "@/lib/tip/tip_coffee";
|
||||||
|
|
||||||
import { PageLoadingFallback } from "@/app/_components/core";
|
import { TipScreen } from "./tip-screen";
|
||||||
|
|
||||||
import { TipPageClient } from "./tip-page-client";
|
type TipSearchParams = Record<string, string | string[] | undefined>;
|
||||||
|
|
||||||
|
export default async function TipPage({
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
searchParams: Promise<TipSearchParams>;
|
||||||
|
}) {
|
||||||
|
const query = await searchParams;
|
||||||
|
const coffeeType =
|
||||||
|
resolveTipCoffeeType(firstSearchParam(query[TIP_COFFEE_TYPE_PARAM])) ??
|
||||||
|
DEFAULT_TIP_COFFEE_TYPE;
|
||||||
|
|
||||||
export default function TipPage() {
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<TipFallback />}>
|
<TipScreen
|
||||||
<TipPageClient />
|
coffeeType={coffeeType}
|
||||||
</Suspense>
|
shouldResumePendingOrder={firstSearchParam(query.paymentReturn) === "1"}
|
||||||
|
initialPayChannel={toPayChannel(firstSearchParam(query.payChannel))}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function TipFallback() {
|
function firstSearchParam(value: string | string[] | undefined): string | null {
|
||||||
return (
|
return Array.isArray(value) ? (value[0] ?? null) : (value ?? null);
|
||||||
<PageLoadingFallback background="#fff6ed" tone="warm">
|
}
|
||||||
Loading tip...
|
|
||||||
</PageLoadingFallback>
|
function toPayChannel(value: string | null): PayChannel | null {
|
||||||
);
|
return value === "ezpay" || value === "stripe" ? value : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useSearchParams } from "next/navigation";
|
|
||||||
|
|
||||||
import type { PayChannel } from "@/data/dto/payment";
|
|
||||||
import {
|
|
||||||
DEFAULT_TIP_COFFEE_TYPE,
|
|
||||||
resolveTipCoffeeType,
|
|
||||||
TIP_COFFEE_TYPE_PARAM,
|
|
||||||
} from "@/lib/tip/tip_coffee";
|
|
||||||
|
|
||||||
import { TipScreen } from "./tip-screen";
|
|
||||||
|
|
||||||
function toPayChannel(value: string | null): PayChannel | null {
|
|
||||||
if (value === "ezpay" || value === "stripe") return value;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TipPageClient() {
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const coffeeType =
|
|
||||||
resolveTipCoffeeType(searchParams.get(TIP_COFFEE_TYPE_PARAM)) ??
|
|
||||||
DEFAULT_TIP_COFFEE_TYPE;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TipScreen
|
|
||||||
coffeeType={coffeeType}
|
|
||||||
shouldResumePendingOrder={searchParams.get("paymentReturn") === "1"}
|
|
||||||
initialPayChannel={toPayChannel(searchParams.get("payChannel"))}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user