feat(characters): use local character catalog
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { PaymentRouteProvider } from "@/providers/payment-route-provider";
|
||||
|
||||
export default function TipLayout({ children }: { children: ReactNode }) {
|
||||
return <PaymentRouteProvider>{children}</PaymentRouteProvider>;
|
||||
export default function LegacyTipLayout({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return children;
|
||||
}
|
||||
|
||||
+13
-26
@@ -1,34 +1,21 @@
|
||||
import {
|
||||
getFirstPaymentSearchParam,
|
||||
parsePaymentReturnSearchParams,
|
||||
type PaymentSearchParams,
|
||||
} from "@/lib/payment/payment_search_params";
|
||||
import {
|
||||
DEFAULT_TIP_COFFEE_TYPE,
|
||||
resolveTipCoffeeType,
|
||||
TIP_COFFEE_TYPE_PARAM,
|
||||
} from "@/lib/tip/tip_coffee";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { TipScreen } from "./tip-screen";
|
||||
import { DEFAULT_CHARACTER_SLUG } from "@/data/constants/character";
|
||||
import {
|
||||
appendRouteSearchParams,
|
||||
getCharacterRoutes,
|
||||
type RouteSearchParams,
|
||||
} from "@/router/routes";
|
||||
|
||||
export default async function TipPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<PaymentSearchParams>;
|
||||
searchParams: Promise<RouteSearchParams>;
|
||||
}) {
|
||||
const query = await searchParams;
|
||||
const paymentReturn = parsePaymentReturnSearchParams(query);
|
||||
const coffeeType =
|
||||
resolveTipCoffeeType(
|
||||
getFirstPaymentSearchParam(query[TIP_COFFEE_TYPE_PARAM]),
|
||||
) ??
|
||||
DEFAULT_TIP_COFFEE_TYPE;
|
||||
|
||||
return (
|
||||
<TipScreen
|
||||
coffeeType={coffeeType}
|
||||
shouldResumePendingOrder={paymentReturn.shouldResumePendingOrder}
|
||||
initialPayChannel={paymentReturn.initialPayChannel}
|
||||
/>
|
||||
redirect(
|
||||
appendRouteSearchParams(
|
||||
getCharacterRoutes(DEFAULT_CHARACTER_SLUG).tip,
|
||||
await searchParams,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { usePaymentLaunchFlow } from "@/app/_hooks/use-payment-launch-flow";
|
||||
import { PaymentLaunchDialogs } from "@/app/_components/payment/payment-launch-dialogs";
|
||||
import type { TipCoffeeType } from "@/lib/tip/tip_coffee";
|
||||
import { useActiveCharacter } from "@/providers/character-provider";
|
||||
import {
|
||||
usePaymentDispatch,
|
||||
usePaymentState,
|
||||
@@ -28,6 +29,7 @@ export function TipCheckoutButton({
|
||||
onOrder,
|
||||
returnPath,
|
||||
}: TipCheckoutButtonProps) {
|
||||
const character = useActiveCharacter();
|
||||
const payment = usePaymentState();
|
||||
const paymentDispatch = usePaymentDispatch();
|
||||
const paymentLaunch = usePaymentLaunchFlow({
|
||||
@@ -37,6 +39,7 @@ export function TipCheckoutButton({
|
||||
paymentDispatch,
|
||||
subscriptionType: "tip",
|
||||
tipCoffeeType: coffeeType,
|
||||
characterSlug: character.slug,
|
||||
});
|
||||
|
||||
const isLoading =
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
height: 330px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 242, 232, 0.3), #fff2e8 96%),
|
||||
url("/images/cover/elio.png") center 18% / cover no-repeat;
|
||||
var(--tip-cover-image) center 18% / cover no-repeat;
|
||||
opacity: 0.36;
|
||||
filter: saturate(0.95) blur(0.2px);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState, type CSSProperties } from "react";
|
||||
import Image from "next/image";
|
||||
import { ArrowLeft, Heart, Sparkles } from "lucide-react";
|
||||
|
||||
@@ -21,6 +21,10 @@ import {
|
||||
type TipCoffeeType,
|
||||
} from "@/lib/tip/tip_coffee";
|
||||
import { useAppNavigator } from "@/router/use-app-navigator";
|
||||
import {
|
||||
useActiveCharacter,
|
||||
useActiveCharacterRoutes,
|
||||
} from "@/providers/character-provider";
|
||||
import { useAuthState } from "@/stores/auth/auth-context";
|
||||
|
||||
import { TipCheckoutButton } from "./tip-checkout-button";
|
||||
@@ -52,11 +56,16 @@ export function TipScreen({
|
||||
initialPayChannel = null,
|
||||
}: TipScreenProps) {
|
||||
const navigator = useAppNavigator();
|
||||
const character = useActiveCharacter();
|
||||
const characterRoutes = useActiveCharacterRoutes();
|
||||
const authState = useAuthState();
|
||||
const [selectedCoffeeType, setSelectedCoffeeType] =
|
||||
useState<TipCoffeeType>(coffeeType);
|
||||
const coffeeOption = getTipCoffeeOption(selectedCoffeeType);
|
||||
const returnPath = buildTipCoffeePath(selectedCoffeeType);
|
||||
const returnPath = buildTipCoffeePath(
|
||||
selectedCoffeeType,
|
||||
characterRoutes.tip,
|
||||
);
|
||||
const resolvedInitialPayChannel =
|
||||
initialPayChannel ?? navigator.getDefaultPayChannel();
|
||||
const { payment, paymentDispatch } = usePaymentRouteFlow({
|
||||
@@ -155,7 +164,10 @@ export function TipScreen({
|
||||
}
|
||||
if (!canCreateOrder) return;
|
||||
|
||||
paymentDispatch({ type: "PaymentCreateOrderSubmitted" });
|
||||
paymentDispatch({
|
||||
type: "PaymentCreateOrderSubmitted",
|
||||
recipientCharacterId: character.id,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCoffeeTypeChange = (type: TipCoffeeType) => {
|
||||
@@ -182,7 +194,14 @@ export function TipScreen({
|
||||
|
||||
return (
|
||||
<MobileShell background="#fff5ed">
|
||||
<main className={styles.shell}>
|
||||
<main
|
||||
className={styles.shell}
|
||||
style={
|
||||
{
|
||||
"--tip-cover-image": `url("${character.assets.cover}")`,
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<div className={styles.bgImage} aria-hidden="true" />
|
||||
<div className={styles.bgGlowOne} aria-hidden="true" />
|
||||
<div className={styles.bgGlowTwo} aria-hidden="true" />
|
||||
@@ -198,13 +217,14 @@ export function TipScreen({
|
||||
>
|
||||
<ArrowLeft size={19} aria-hidden="true" />
|
||||
</button>
|
||||
<span className={styles.headerPill}>Tip Elio</span>
|
||||
<span className={styles.headerPill}>{character.copy.tipHeader}</span>
|
||||
</header>
|
||||
|
||||
<section className={styles.hero} aria-labelledby="tip-title">
|
||||
<div className={styles.avatarRing}>
|
||||
<CharacterAvatar
|
||||
alt="Elio Silvestri"
|
||||
src={character.assets.avatar}
|
||||
alt={character.displayName}
|
||||
size="100%"
|
||||
imageSize={88}
|
||||
priority
|
||||
@@ -212,7 +232,7 @@ export function TipScreen({
|
||||
</div>
|
||||
<p className={styles.eyebrow}>A little sweetness for today</p>
|
||||
<h1 id="tip-title" className={styles.title}>
|
||||
Buy Elio a coffee
|
||||
{character.copy.tipTitle}
|
||||
</h1>
|
||||
<p className={styles.subtitle}>
|
||||
Send a warm coffee tip and keep the private moments glowing.
|
||||
|
||||
Reference in New Issue
Block a user