{
- // 如果图片加载失败,显示原始文本
- e.currentTarget.style.display = "none";
- const textNode = document.createTextNode(`[${part.name}]`);
- e.currentTarget.parentNode?.insertBefore(textNode, e.currentTarget);
- }}
- />
- );
- })}
- >
- );
-}
-
-type VideoData = {
- type: "video";
- aweme_id: string;
- desc: string;
- created_at: string | Date;
- duration_ms?: number | null;
- video_url: string;
- width?: number | null;
- height?: number | null;
- author: User;
- comments: Comment[];
-};
-
-type ImageData = {
- type: "image";
- aweme_id: string;
- desc: string;
- created_at: string | Date;
- images: { id: string; url: string; width?: number; height?: number }[];
- music_url?: string | null;
- author: User;
- comments: Comment[];
-};
-
-const SEGMENT_MS = 5000; // 图文每段 5s
-
-type Neighbors = { prev: { aweme_id: string } | null; next: { aweme_id: string } | null };
-
-export default function AwemeDetailClient(props: { data: VideoData | ImageData; neighbors?: Neighbors }) {
- const { data, neighbors } = props;
- const isVideo = data.type === "video";
+export default function AwemeDetailClient({ data, neighbors }: AwemeDetailClientProps) {
const router = useRouter();
+ const isVideo = data.type === "video";
- // ====== 布局 & 评论 ======
- const [open, setOpen] = useState(false); // 评论是否展开(竖屏为 bottom sheet;横屏为并排分栏)
- const [mounted, setMounted] = useState(false); // 用于跳过首次加载的动画
- const comments = useMemo(() => data.comments ?? [], [data]);
+ // 状态管理
+ const playerState = usePlayerState();
+ const commentState = useCommentState();
- // ====== 从 localStorage 恢复评论区状态(仅客户端) ======
- useEffect(() => {
- if (typeof window === "undefined") return;
- const saved = localStorage.getItem("aweme_player_comments_open");
- if (saved === "true") {
- setOpen(true);
- }
- // 短暂延迟后标记为已挂载,启用动画
- requestAnimationFrame(() => {
- requestAnimationFrame(() => {
- setMounted(true);
- });
- });
- }, []);
-
- // ====== 媒体引用 ======
+ // 引用
const mediaContainerRef = useRef
-
- {data.desc} -
-
+
{
+ // 如果图片加载失败,显示原始文本
+ e.currentTarget.style.display = "none";
+ const textNode = document.createTextNode(`[${part.name}]`);
+ e.currentTarget.parentNode?.insertBefore(textNode, e.currentTarget);
+ }}
+ />
+ );
+ })}
+ >
+ );
+}
diff --git a/app/aweme/[awemeId]/components/ImageCarousel.tsx b/app/aweme/[awemeId]/components/ImageCarousel.tsx
new file mode 100644
index 0000000..20fc976
--- /dev/null
+++ b/app/aweme/[awemeId]/components/ImageCarousel.tsx
@@ -0,0 +1,39 @@
+import { forwardRef } from "react";
+import type { ImageData } from "../types";
+
+interface ImageCarouselProps {
+ images: ImageData["images"];
+ currentIndex: number;
+ onTogglePlay: () => void;
+}
+
+export const ImageCarousel = forwardRef+ {desc} +
+