feat: 播放页按来源列表切换,新增作者/搜索/hashtag 侧栏
- 播放上下文:作品链接带上来源(搜索、标签、作者、hashtag),上一条/下一条 在该列表内移动;当前作品不在列表中时退回全部作品的发布时间顺序 - 顶部提示标签显示当前列表与位置,点击打开侧栏列表,✕ 退出 - 侧栏改为「评论 | TA的作品 | 搜索结果/#hashtag」标签页,作品封面网格可就地切换 - 作者模式:打开「TA的作品」进入,关闭侧栏不退出,点提示 ✕ 后回到原来的列表 - 从搜索结果进入或点 hashtag 时自动打开对应列表 - 描述中的 hashtag 可点击,按完整 hashtag 精确匹配(#丰川祥子 不含 #丰川祥子cos) - 新增 /api/playlist;搜索 SQL 抽到 lib/search-query.ts 与播放页共用,保证顺序一致 - 移动端:底部抽屉、点作品后收起,媒体区支持上下滑切换 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,8 @@ import {
|
||||
VolumeX,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { memo, type ReactNode, type RefObject } from "react";
|
||||
import { memo, type ReactNode, type RefObject, useMemo } from "react";
|
||||
import { parseDescHashtags } from "@/lib/hashtag";
|
||||
import type { AbLoop, LoopMode, ObjectFit, User } from "../types.ts";
|
||||
import { formatAbsoluteUTC, formatRelativeTime, formatTime } from "../utils";
|
||||
import { MoreMenu, MoreMenuItem } from "./MoreMenu";
|
||||
@@ -59,6 +60,10 @@ interface MediaControlsProps {
|
||||
onLoopModeChange: (mode: LoopMode) => void;
|
||||
onDownload: () => void;
|
||||
onToggleFullscreen: () => void;
|
||||
/** 点作者头像/名字:在侧栏打开「TA的作品」 */
|
||||
onAuthorClick?: () => void;
|
||||
/** 点描述里的 hashtag:切到该 hashtag 的作品列表 */
|
||||
onHashtagClick?: (name: string) => void;
|
||||
}
|
||||
|
||||
const CONTROL_BTN_CLASS =
|
||||
@@ -96,7 +101,10 @@ export const MediaControls = memo(function MediaControls({
|
||||
onLoopModeChange,
|
||||
onDownload,
|
||||
onToggleFullscreen,
|
||||
onAuthorClick,
|
||||
onHashtagClick,
|
||||
}: MediaControlsProps) {
|
||||
const descParts = useMemo(() => parseDescHashtags(desc || ""), [desc]);
|
||||
const normalizedLoopMode =
|
||||
isVideo && loopMode === "single" ? "loop" : loopMode;
|
||||
const loopLabel = isVideo
|
||||
@@ -129,10 +137,29 @@ export const MediaControls = memo(function MediaControls({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute left-0 right-0 bottom-0 z-40 px-3 pb-4 pt-2 bg-gradient-to-b from-black/0 via-black/45 to-black/65 flex flex-col gap-2.5">
|
||||
<div
|
||||
data-no-swipe
|
||||
className="absolute left-0 right-0 bottom-0 z-40 px-3 pb-4 pt-2 bg-gradient-to-b from-black/0 via-black/45 to-black/65 flex flex-col gap-2.5"
|
||||
>
|
||||
{/* 描述行 */}
|
||||
<div className="flex items-center gap-2.5 mb-1 pointer-events-none">
|
||||
{author.sec_uid ? (
|
||||
{author.sec_uid && onAuthorClick ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onAuthorClick}
|
||||
className="flex items-center gap-2.5 pointer-events-auto hover:opacity-80 transition-opacity cursor-pointer"
|
||||
title="查看TA的作品"
|
||||
>
|
||||
<img
|
||||
src={author.avatar_url ?? ""}
|
||||
alt=""
|
||||
className="w-8 h-8 rounded-full"
|
||||
/>
|
||||
<span className="text-[15px] leading-tight text-white/95 drop-shadow font-medium">
|
||||
{author.nickname}
|
||||
</span>
|
||||
</button>
|
||||
) : author.sec_uid ? (
|
||||
<Link
|
||||
href={`/author/${author.sec_uid}`}
|
||||
className="flex items-center gap-2.5 pointer-events-auto hover:opacity-80 transition-opacity"
|
||||
@@ -172,7 +199,23 @@ export const MediaControls = memo(function MediaControls({
|
||||
{desc ? (
|
||||
<div className="pointer-events-none">
|
||||
<p className="text-[13px] leading-tight text-white/95 max-h-[3.9em] overflow-hidden [display:-webkit-box] [-webkit-line-clamp:3] [-webkit-box-orient:vertical] drop-shadow">
|
||||
{desc}
|
||||
{descParts.map((part, i) =>
|
||||
part.type === "hashtag" && onHashtagClick ? (
|
||||
<button
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: 描述片段顺序固定
|
||||
key={i}
|
||||
type="button"
|
||||
onClick={() => onHashtagClick(part.name)}
|
||||
className="pointer-events-auto font-semibold text-white hover:underline cursor-pointer"
|
||||
title={`查看所有带 #${part.name} 的作品`}
|
||||
>
|
||||
{part.text}
|
||||
</button>
|
||||
) : (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: 描述片段顺序固定
|
||||
<span key={i}>{part.text}</span>
|
||||
),
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user