fix: hide public card account identifiers
This commit is contained in:
@@ -4,7 +4,7 @@ name = "Genarrative"
|
|||||||
|
|
||||||
[setup]
|
[setup]
|
||||||
script = '''
|
script = '''
|
||||||
cp "$CODEX_SOURCE_TREE_PATH\.env.secrets.local" "$CODEX_WORKTREE_PATH\.env.secrets.local"
|
cp "$CODEX_SOURCE_TREE_PATH/.env.secrets.local" "$CODEX_WORKTREE_PATH/.env.secrets.local"
|
||||||
npm install
|
npm install
|
||||||
npm run codegraph:init
|
npm run codegraph:init
|
||||||
npm run codegraph:index
|
npm run codegraph:index
|
||||||
|
|||||||
@@ -1572,6 +1572,14 @@
|
|||||||
- 验证:`npm run test -- src/components/rpg-entry/rpgEntryWorldPresentation.test.ts src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`。
|
- 验证:`npm run test -- src/components/rpg-entry/rpgEntryWorldPresentation.test.ts src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`、`npm run typecheck`、`npm run check:encoding`。
|
||||||
- 关联:`src/index.css`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、`src/components/rpg-entry/rpgEntryWorldPresentation.ts`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
- 关联:`src/index.css`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、`src/components/rpg-entry/rpgEntryWorldPresentation.ts`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
||||||
|
|
||||||
|
## 公开作品卡作者行不要拼手机号或陶泥号
|
||||||
|
|
||||||
|
- 现象:发现页 / 推荐页公开作品卡作者行显示 `158****3533 · SY-00000003` 这类手机号掩码和陶泥号组合,列表卡片看起来像暴露账号标识。
|
||||||
|
- 原因:`resolvePlatformWorkAuthorDisplayName(...)` 曾把公开昵称和 `publicUserCode` 拼接为 `昵称 · SY-*`,并在无法解析公开昵称时直接回退后端卡片里的 `authorDisplayName`;当后端或旧投影把手机号掩码写进展示名时,卡片会原样外露。
|
||||||
|
- 处理:公开卡片作者名只取可读公开昵称;识别手机号掩码、单独 `SY-*` 或 `手机号掩码 · SY-*` 时回退为 `玩家`。作品号复制、陶泥号搜索和完整身份展示只放在详情页、搜索或明确复制入口,不塞进卡片作者行。
|
||||||
|
- 验证:`npm run test -- src/components/rpg-entry/rpgEntryWorldPresentation.test.ts src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx src/components/platform-entry/PlatformWorkDetailView.test.tsx`。
|
||||||
|
- 关联:`src/components/rpg-entry/rpgEntryWorldPresentation.ts`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、`src/components/platform-entry/PlatformWorkDetailView.tsx`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
||||||
|
|
||||||
## 生成中草稿恢复要按后端时间戳计时
|
## 生成中草稿恢复要按后端时间戳计时
|
||||||
|
|
||||||
- 现象:拼图或抓大鹅草稿生成中刷新网页后,进入生成页的“已耗时”从 `0 秒` 重新开始;另一类旧问题是后端 `progressPercent=88` 时总进度首帧直接跳到 `88%`。
|
- 现象:拼图或抓大鹅草稿生成中刷新网页后,进入生成页的“已耗时”从 `0 秒` 重新开始;另一类旧问题是后端 `progressPercent=88` 时总进度首帧直接跳到 `88%`。
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
10. 敲木鱼作品架读取当前用户作品列表时走 `GET /api/creation/wooden-fish/works`;发布成功后平台壳必须同时刷新作品架与公开广场,避免作品刚发布时仍停留在旧列表。
|
10. 敲木鱼作品架读取当前用户作品列表时走 `GET /api/creation/wooden-fish/works`;发布成功后平台壳必须同时刷新作品架与公开广场,避免作品刚发布时仍停留在旧列表。
|
||||||
11. 移动端草稿页整体禁止长按选择文字,避免误触系统选区;输入框、文本域和可编辑区域仍必须保留文本选择能力。
|
11. 移动端草稿页整体禁止长按选择文字,避免误触系统选区;输入框、文本域和可编辑区域仍必须保留文本选择能力。
|
||||||
|
|
||||||
|
发现页 / 推荐页公开作品卡的作者行只显示可读公开昵称;不得把手机号掩码、`SY-*` 陶泥号或作品号拼接进卡片作者名。陶泥号搜索、作品号复制和完整作品身份只在搜索、详情页或明确的复制入口展示,避免卡片列表暴露账号标识。
|
||||||
|
|
||||||
发现 Tab、创作 Tab 与草稿 Tab 的页面根内容区不再套 `platform-page-stage` 外层全局卡片壳,让列表、筛选和玩法卡获得更宽的横向空间;推荐页和我的页仍按各自页面设计保留原有全局卡片口径。移动端“我的”页仍按顶部头像 / 昵称 / 陶泥号、会员横幅、三张统计卡、每日任务、五项常用功能宫格、设置入口和法律信息组织,不保留旧的底部“填邀请码”次级入口;常用功能当前只展示四项常驻入口时必须按四列铺满整行,不保留五列网格导致左对齐空位;每日任务卡必须读取 `/api/profile/tasks` 的当前任务摘要并在领取后同步刷新卡片进度。字号必须维持平台普通 UI 档位,不能因为窄屏把卡片标题、功能 label 或法律信息撑成展示级字号;最后一屏内容必须能在底部 dock 上方完整滚动露出,不得被固定底部导航遮挡。
|
发现 Tab、创作 Tab 与草稿 Tab 的页面根内容区不再套 `platform-page-stage` 外层全局卡片壳,让列表、筛选和玩法卡获得更宽的横向空间;推荐页和我的页仍按各自页面设计保留原有全局卡片口径。移动端“我的”页仍按顶部头像 / 昵称 / 陶泥号、会员横幅、三张统计卡、每日任务、五项常用功能宫格、设置入口和法律信息组织,不保留旧的底部“填邀请码”次级入口;常用功能当前只展示四项常驻入口时必须按四列铺满整行,不保留五列网格导致左对齐空位;每日任务卡必须读取 `/api/profile/tasks` 的当前任务摘要并在领取后同步刷新卡片进度。字号必须维持平台普通 UI 档位,不能因为窄屏把卡片标题、功能 label 或法律信息撑成展示级字号;最后一屏内容必须能在底部 dock 上方完整滚动露出,不得被固定底部导航遮挡。
|
||||||
|
|
||||||
## RPG / 自定义世界
|
## RPG / 自定义世界
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ test('PlatformWorkDetailView prefers resolved public user display name', () => {
|
|||||||
expect(screen.queryByText('137****6613')).toBeNull();
|
expect(screen.queryByText('137****6613')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PlatformWorkDetailView prefers display name then public user code for wooden fish works', () => {
|
test('PlatformWorkDetailView prefers display name without appending public user code', () => {
|
||||||
render(
|
render(
|
||||||
<PlatformWorkDetailView
|
<PlatformWorkDetailView
|
||||||
entry={createWoodenFishEntry()}
|
entry={createWoodenFishEntry()}
|
||||||
@@ -183,10 +183,11 @@ test('PlatformWorkDetailView prefers display name then public user code for wood
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText('公开昵称 · SY-00000004')).toBeTruthy();
|
expect(screen.getByText('公开昵称')).toBeTruthy();
|
||||||
|
expect(screen.queryByText('公开昵称 · SY-00000004')).toBeNull();
|
||||||
|
expect(screen.queryByText('SY-00000004')).toBeNull();
|
||||||
expect(screen.queryByText('phone_00000004')).toBeNull();
|
expect(screen.queryByText('phone_00000004')).toBeNull();
|
||||||
expect(screen.queryByText('敲木鱼玩家')).toBeNull();
|
expect(screen.queryByText('敲木鱼玩家')).toBeNull();
|
||||||
expect(screen.queryByText('公开昵称')).toBeNull();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PlatformWorkDetailView calls like handler', () => {
|
test('PlatformWorkDetailView calls like handler', () => {
|
||||||
|
|||||||
@@ -3434,6 +3434,34 @@ test('public gallery cards hide work code until detail is opened', async () => {
|
|||||||
expect(onOpenGalleryDetail).toHaveBeenCalledWith(puzzlePublicEntry);
|
expect(onOpenGalleryDetail).toHaveBeenCalledWith(puzzlePublicEntry);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('public gallery cards hide phone masked author and public user code', async () => {
|
||||||
|
mockDesktopLayout();
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const maskedAuthorEntry = {
|
||||||
|
...puzzlePublicEntry,
|
||||||
|
workId: 'puzzle-work-masked-author',
|
||||||
|
profileId: 'puzzle-profile-masked-author',
|
||||||
|
publicWorkCode: 'PZ-MASKED1',
|
||||||
|
authorDisplayName: '158****3533 · SY-00000003',
|
||||||
|
worldName: '喜气洋洋',
|
||||||
|
} satisfies PlatformPublicGalleryCard;
|
||||||
|
|
||||||
|
renderStatefulLoggedOutHomeView(
|
||||||
|
{
|
||||||
|
latestEntries: [maskedAuthorEntry],
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
await user.click(screen.getByRole('button', { name: '发现' }));
|
||||||
|
|
||||||
|
const card = screen.getByRole('button', { name: /喜气洋洋/u });
|
||||||
|
expect(card).toBeTruthy();
|
||||||
|
expect(within(card).getByText('公开作者')).toBeTruthy();
|
||||||
|
expect(within(card).queryByText('158****3533 · SY-00000003')).toBeNull();
|
||||||
|
expect(within(card).queryByText('158****3533')).toBeNull();
|
||||||
|
expect(within(card).queryByText('SY-00000003')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
test('logged out mobile shell defaults to discover tab', () => {
|
test('logged out mobile shell defaults to discover tab', () => {
|
||||||
const { container } = renderStatefulLoggedOutHomeView({
|
const { container } = renderStatefulLoggedOutHomeView({
|
||||||
latestEntries: [puzzlePublicEntry],
|
latestEntries: [puzzlePublicEntry],
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ test('resolves public work author from display name and public user code before
|
|||||||
displayName: '公开昵称',
|
displayName: '公开昵称',
|
||||||
avatarUrl: null,
|
avatarUrl: null,
|
||||||
}),
|
}),
|
||||||
).toBe('公开昵称 · SY-00000004');
|
).toBe('公开昵称');
|
||||||
expect(
|
expect(
|
||||||
resolvePlatformWorkAuthorDisplayName(card, {
|
resolvePlatformWorkAuthorDisplayName(card, {
|
||||||
id: 'user_00000004',
|
id: 'user_00000004',
|
||||||
@@ -237,6 +237,36 @@ test('resolves public work author from display name and public user code before
|
|||||||
expect(resolvePlatformWorkAuthorDisplayName(card, null)).toBe('敲木鱼玩家');
|
expect(resolvePlatformWorkAuthorDisplayName(card, null)).toBe('敲木鱼玩家');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('public work author display hides phone masks and public user codes on cards', () => {
|
||||||
|
const card = mapWoodenFishWorkToPlatformGalleryCard({
|
||||||
|
publicWorkCode: 'WF-AUTHOR2',
|
||||||
|
workId: 'wooden-fish-work-author-mask',
|
||||||
|
profileId: 'wooden-fish-profile-author-mask',
|
||||||
|
ownerUserId: 'user-author-mask',
|
||||||
|
authorDisplayName: '158****3533 · SY-00000003',
|
||||||
|
workTitle: '喜气洋洋',
|
||||||
|
workDescription: '喜庆主题敲木鱼。',
|
||||||
|
coverImageSrc: null,
|
||||||
|
themeTags: ['敲木鱼'],
|
||||||
|
publicationStatus: 'published',
|
||||||
|
playCount: 0,
|
||||||
|
updatedAt: '2026-05-20T00:00:00.000Z',
|
||||||
|
publishedAt: '2026-05-20T00:00:00.000Z',
|
||||||
|
generationStatus: 'ready',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolvePlatformWorkAuthorDisplayName(card, {
|
||||||
|
id: 'user_00000003',
|
||||||
|
publicUserCode: 'SY-00000003',
|
||||||
|
username: '158****3533',
|
||||||
|
displayName: '158****3533',
|
||||||
|
avatarUrl: null,
|
||||||
|
}),
|
||||||
|
).toBe('玩家');
|
||||||
|
expect(resolvePlatformWorkAuthorDisplayName(card, null)).toBe('玩家');
|
||||||
|
});
|
||||||
|
|
||||||
test('keeps baby object match public card code and template label intact', () => {
|
test('keeps baby object match public card code and template label intact', () => {
|
||||||
const card: PlatformEdutainmentGalleryCard = {
|
const card: PlatformEdutainmentGalleryCard = {
|
||||||
sourceType: 'edutainment',
|
sourceType: 'edutainment',
|
||||||
|
|||||||
@@ -867,14 +867,31 @@ export function resolvePlatformWorkAuthorDisplayName(
|
|||||||
entry: PlatformPublicGalleryCard,
|
entry: PlatformPublicGalleryCard,
|
||||||
authorSummary?: PublicUserSummary | null,
|
authorSummary?: PublicUserSummary | null,
|
||||||
) {
|
) {
|
||||||
const displayName = authorSummary?.displayName?.trim();
|
const displayName = normalizePlatformPublicAuthorName(
|
||||||
const publicUserCode = authorSummary?.publicUserCode?.trim();
|
authorSummary?.displayName,
|
||||||
|
);
|
||||||
|
const entryAuthorName = normalizePlatformPublicAuthorName(
|
||||||
|
entry.authorDisplayName,
|
||||||
|
);
|
||||||
|
|
||||||
if (displayName && publicUserCode) {
|
return displayName || entryAuthorName || '玩家';
|
||||||
return `${displayName} · ${publicUserCode}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return displayName || publicUserCode || entry.authorDisplayName.trim() || '玩家';
|
function normalizePlatformPublicAuthorName(value: string | null | undefined) {
|
||||||
|
const normalized = value?.trim() ?? '';
|
||||||
|
if (!normalized || normalized === 'null' || normalized === 'undefined') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const compact = normalized.replace(/\s+/gu, '');
|
||||||
|
if (/^\d+\*+\d+(?:[·.-]?SY-\d+)?$/iu.test(compact)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (/^SY-\d+$/iu.test(compact)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildPlatformWorldDisplayTags(
|
export function buildPlatformWorldDisplayTags(
|
||||||
|
|||||||
Reference in New Issue
Block a user