Files
Genarrative/packages/shared/src/components/CreatorControls/index.tsx
T
lhk229 51cb05f418
Project CI / AI game creator shell Rust crates (push) Successful in 5m56s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 6m36s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 7m37s
Project CI / Backend tests (push) Successful in 9m22s
Project CI / Frontend tests (push) Successful in 4m40s
Project CI / AI game creator shell web tests (push) Successful in 3m50s
Project CI / Repository checks (push) Successful in 6m30s
Project CI / Native shell tests (push) Successful in 8m32s
补充创作者主页与关注粉丝工程文档 (#638)
本 PR 补齐创作者主页与关注粉丝功能的产品合同和工程设计,供后续按里程碑实施;本次只修改文档。

桌面导航第四项进入自己的创作者主页,“我的”移到第五项。游戏详情可进入作者主页并关注/取消关注;自己或他人的关注、粉丝列表均公开可查看,点击用户头像或昵称可进入其主页。本人支持取消/重新关注、回关和移除粉丝,并明确两个关注方向独立。

工程设计包含关系表与 DTO、认证和事务边界、跨页状态同步、共享组件、深链及验收要求,拆分关系查询和页面交互两个里程碑。按用户要求,游戏列表沿用最多 48 项的现有限制,不增加游戏目录分页改造。

验证:
- 已合入最新 origin/master(21c79f54a);共享决策记录冲突保留双方新增内容。
- npm run check:encoding 通过。
- npm run check:doc-index 通过。
- git diff --check 与暂存差异检查通过。
- 相对 master 仅六份文档发生变化;没有业务代码或 schema 修改,未执行业务测试或部署。

Reviewed-on: #638
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-10-05 22:05:11 +08:00

81 lines
1.8 KiB
TypeScript

import './style.css';
import type { MouseEventHandler, ReactNode } from 'react';
export function CreatorFollowButton({
followed,
pending = false,
disabled = false,
followLabel = '关注',
followedLabel = '取消关注',
onClick,
}: {
followed: boolean;
pending?: boolean;
disabled?: boolean;
followLabel?: string;
followedLabel?: string;
onClick: () => void;
}) {
return (
<button
type="button"
className={`creator-follow ${followed ? 'creator-follow--active' : ''}`}
disabled={disabled || pending}
aria-busy={pending}
aria-label={followed && !pending ? '取消关注' : undefined}
onClick={onClick}
>
{pending ? '处理中…' : followed ? followedLabel : followLabel}
</button>
);
}
export function CreatorUserRow({
name,
avatarUrl,
subtitle,
href,
onClick,
actions,
}: {
name: string;
avatarUrl?: string | null;
subtitle?: string;
href?: string;
onClick?: MouseEventHandler<HTMLAnchorElement>;
actions?: ReactNode;
}) {
const content = (
<>
<span className="creator-avatar" aria-hidden="true">
{avatarUrl ? (
<img src={avatarUrl} alt="" />
) : (
Array.from(name)[0] || '创'
)}
</span>
<span className="creator-user-copy">
<strong>{name}</strong>
{subtitle && <small>{subtitle}</small>}
</span>
</>
);
return (
<div className="creator-user-row">
{href ? (
<a
className="creator-user-link"
href={href}
onClick={onClick}
aria-label={name ? undefined : '查看创作者主页'}
>
{content}
</a>
) : (
<div className="creator-user-link">{content}</div>
)}
{actions && <div className="creator-user-actions">{actions}</div>}
</div>
);
}