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
本 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>
81 lines
1.8 KiB
TypeScript
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>
|
|
);
|
|
}
|