From 11f187fce4152faeab03e6ce76afeba56a84980d Mon Sep 17 00:00:00 2001 From: chenhang Date: Thu, 9 Jul 2026 12:54:00 +0800 Subject: [PATCH] refactor(private-room): split screen components --- src/app/private-room/components/index.ts | 3 + .../components/private-room-post-card.tsx | 109 +++++++++++ .../private-room/components/status-card.tsx | 24 +++ .../components/unlock-confirm-dialog.tsx | 51 +++++ src/app/private-room/private-room-screen.tsx | 181 +----------------- 5 files changed, 193 insertions(+), 175 deletions(-) create mode 100644 src/app/private-room/components/index.ts create mode 100644 src/app/private-room/components/private-room-post-card.tsx create mode 100644 src/app/private-room/components/status-card.tsx create mode 100644 src/app/private-room/components/unlock-confirm-dialog.tsx diff --git a/src/app/private-room/components/index.ts b/src/app/private-room/components/index.ts new file mode 100644 index 00000000..59a18593 --- /dev/null +++ b/src/app/private-room/components/index.ts @@ -0,0 +1,3 @@ +export * from "./private-room-post-card"; +export * from "./status-card"; +export * from "./unlock-confirm-dialog"; diff --git a/src/app/private-room/components/private-room-post-card.tsx b/src/app/private-room/components/private-room-post-card.tsx new file mode 100644 index 00000000..ade300c4 --- /dev/null +++ b/src/app/private-room/components/private-room-post-card.tsx @@ -0,0 +1,109 @@ +import type { CSSProperties } from "react"; +import Image from "next/image"; +import { ImageIcon, LockKeyhole } from "lucide-react"; + +import type { PrivateRoomMoment } from "@/data/dto/private-room"; + +import styles from "../private-room-screen.module.css"; + +export interface PrivateRoomPostCardProps { + moment: PrivateRoomMoment; + displayName: string; + avatarUrl: string; + index: number; + isUnlocking: boolean; + onUnlock: () => void; +} + +export function PrivateRoomPostCard({ + moment, + displayName, + avatarUrl, + index, + isUnlocking, + onUnlock, +}: PrivateRoomPostCardProps) { + const isLocked = moment.locked || moment.lockDetail.locked; + const imageUrls = moment.images + .filter((image) => !image.locked && image.url) + .map((image) => image.url as string); + const photoCount = moment.mediaCount || moment.images.length || 1; + + return ( +
+
+
+ +
+

{moment.author.name || displayName}

+

+ {moment.title || "Private room drop"} +

+
+
+ +
+ + {moment.text || moment.content ? ( +

{moment.text || moment.content}

+ ) : null} + + {isLocked ? ( + + ) : ( +
+ {imageUrls.map((url, imageIndex) => ( + + ))} +
+ )} +
+ ); +} + +function formatMomentTime(value: string | null): string { + if (!value) return ""; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return ""; + return date.toLocaleDateString(undefined, { + month: "short", + day: "numeric", + }); +} diff --git a/src/app/private-room/components/status-card.tsx b/src/app/private-room/components/status-card.tsx new file mode 100644 index 00000000..9d281220 --- /dev/null +++ b/src/app/private-room/components/status-card.tsx @@ -0,0 +1,24 @@ +import styles from "../private-room-screen.module.css"; + +export interface StatusCardProps { + message: string; + actionLabel?: string; + onAction?: () => void; +} + +export function StatusCard({ + message, + actionLabel, + onAction, +}: StatusCardProps) { + return ( +
+

{message}

+ {actionLabel && onAction ? ( + + ) : null} +
+ ); +} diff --git a/src/app/private-room/components/unlock-confirm-dialog.tsx b/src/app/private-room/components/unlock-confirm-dialog.tsx new file mode 100644 index 00000000..cc2533db --- /dev/null +++ b/src/app/private-room/components/unlock-confirm-dialog.tsx @@ -0,0 +1,51 @@ +import type { PrivateRoomMoment } from "@/data/dto/private-room"; + +import styles from "../private-room-screen.module.css"; + +export interface UnlockConfirmDialogProps { + moment: PrivateRoomMoment; + isUnlocking: boolean; + errorMessage: string | null; + onCancel: () => void; + onConfirm: () => void; +} + +export function UnlockConfirmDialog({ + moment, + isUnlocking, + errorMessage, + onCancel, + onConfirm, +}: UnlockConfirmDialogProps) { + return ( +
+
+

Unlock private moment?

+

+ This will use {moment.unlockCost} credits. +

+ {errorMessage ? ( +

{errorMessage}

+ ) : null} +
+ + +
+
+
+ ); +} diff --git a/src/app/private-room/private-room-screen.tsx b/src/app/private-room/private-room-screen.tsx index 1acc7ac0..7b98c4b9 100644 --- a/src/app/private-room/private-room-screen.tsx +++ b/src/app/private-room/private-room-screen.tsx @@ -1,16 +1,9 @@ "use client"; -import type { CSSProperties } from "react"; import { useMemo } from "react"; import Image from "next/image"; -import { - ImageIcon, - LockKeyhole, - RefreshCw, - Sparkles, -} from "lucide-react"; +import { RefreshCw, Sparkles } from "lucide-react"; -import type { PrivateRoomMoment } from "@/data/dto/private-room"; import { AppBottomNav, MobileShell } from "@/app/_components/core"; import { ROUTES } from "@/router/routes"; import { useAppNavigator } from "@/router/use-app-navigator"; @@ -20,6 +13,11 @@ import { usePrivateRoomState, } from "@/stores/private-room"; +import { + PrivateRoomPostCard, + StatusCard, + UnlockConfirmDialog, +} from "./components"; import styles from "./private-room-screen.module.css"; import { isPrivateRoomAuthRequired, @@ -210,170 +208,3 @@ export function PrivateRoomScreen() { ); } - -function PrivateRoomPostCard({ - moment, - displayName, - avatarUrl, - index, - isUnlocking, - onUnlock, -}: { - moment: PrivateRoomMoment; - displayName: string; - avatarUrl: string; - index: number; - isUnlocking: boolean; - onUnlock: () => void; -}) { - const isLocked = moment.locked || moment.lockDetail.locked; - const imageUrls = moment.images - .filter((image) => !image.locked && image.url) - .map((image) => image.url as string); - const photoCount = moment.mediaCount || moment.images.length || 1; - - return ( -
-
-
- -
-

{moment.author.name || displayName}

-

- {moment.title || "Private room drop"} -

-
-
- -
- - {moment.text || moment.content ? ( -

{moment.text || moment.content}

- ) : null} - - {isLocked ? ( - - ) : ( -
- {imageUrls.map((url, imageIndex) => ( - - ))} -
- )} -
- ); -} - -function UnlockConfirmDialog({ - moment, - isUnlocking, - errorMessage, - onCancel, - onConfirm, -}: { - moment: PrivateRoomMoment; - isUnlocking: boolean; - errorMessage: string | null; - onCancel: () => void; - onConfirm: () => void; -}) { - return ( -
-
-

Unlock private moment?

-

- This will use {moment.unlockCost} credits. -

- {errorMessage ? ( -

{errorMessage}

- ) : null} -
- - -
-
-
- ); -} - -function StatusCard({ - message, - actionLabel, - onAction, -}: { - message: string; - actionLabel?: string; - onAction?: () => void; -}) { - return ( -
-

{message}

- {actionLabel && onAction ? ( - - ) : null} -
- ); -} - -function formatMomentTime(value: string | null): string { - if (!value) return ""; - const date = new Date(value); - if (Number.isNaN(date.getTime())) return ""; - return date.toLocaleDateString(undefined, { - month: "short", - day: "numeric", - }); -}