From 4004fcf5f268baba59ef7621bd2c98882e375179 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Wed, 27 May 2026 20:18:58 +0800 Subject: [PATCH] Expose work delete action in UI --- ...玩法创作】平å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md | 2 +- ...ustomWorldCreationHub.interaction.test.tsx | 145 ++++++++++++++++-- .../custom-world-home/CustomWorldWorkCard.tsx | 106 ++++++++----- .../PlatformEntryFlowShellImpl.tsx | 3 +- src/index.css | 28 +++- 5 files changed, 226 insertions(+), 58 deletions(-) diff --git a/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md b/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md index 6092eeb5..4ca4e204 100644 --- a/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md +++ b/docs/ã€çŽ©æ³•åˆ›ä½œã€‘å¹³å°å…¥å£ä¸ŽçŽ©æ³•é“¾è·¯-2026-05-15.md @@ -42,7 +42,7 @@ 1. è‰ç¨¿é¡µä½œå“å¡å¯¹é½å‘现页列表å¡é£Žæ ¼ï¼šå·¦ä¾§ä¿¡æ¯ï¼Œå³ä¾§å°é¢å›¾ï¼Œç§»åŠ¨ç«¯å•列,桌é¢ä¸¤åˆ°ä¸‰åˆ—。 2. è‰ç¨¿é¡µé¡¶éƒ¨ `全部 / è‰ç¨¿ / å·²å‘布` 筛选与å‘现页 `推è / 今日 / 分类 / 排行` 频铿 ‡ç­¾å¤ç”¨åŒä¸€é€‰ä¸­ / æœªé€‰ä¸­è§†è§‰ï¼Œå³ `platform-mobile-home-channel` 与 `platform-mobile-home-channel--active`,ä¸å†ä½¿ç”¨æ—§ `platform-tab` 胶囊样å¼ã€‚ -3. è‰ç¨¿é¡µä¸Žåº•部导航的未读æç¤ºç‚¹ç»Ÿä¸€ä½¿ç”¨å¹³å°æš–棕色点和暖棕光晕,ä¸å†ä½¿ç”¨çº¢ç‚¹æˆ–红色 glowï¼›è‰ç¨¿ Tab ä½œå“æž¶å¡ç‰‡æ— è®ºè‰ç¨¿ / å·²å‘布都ä¸å¤–露作者信æ¯ï¼›å·²å‘布作å“å¡å³ä¸Šè§’直接显示无边框分享 iconã€‚åˆ é™¤ç­‰ç ´åæ€§åŠ¨ä½œç»§ç»­æ”¶å£åˆ°å·¦æ»‘或长按æ“作层。 +3. è‰ç¨¿é¡µä¸Žåº•部导航的未读æç¤ºç‚¹ç»Ÿä¸€ä½¿ç”¨å¹³å°æš–棕色点和暖棕光晕,ä¸å†ä½¿ç”¨çº¢ç‚¹æˆ–红色 glowï¼›è‰ç¨¿ Tab ä½œå“æž¶å¡ç‰‡æ— è®ºè‰ç¨¿ / å·²å‘布都ä¸å¤–露作者信æ¯ï¼›å·²å‘布作å“å¡å³ä¸Šè§’直接显示无边框分享 iconã€‚åˆ é™¤ç­‰ç ´åæ€§åŠ¨ä½œåœ¨ä½œå“å¡ä¸Šä¹Ÿè¦ç›´æŽ¥å¼€æ”¾ç‹¬ç«‹åˆ é™¤å…¥å£ï¼Œå·¦æ»‘或长按仅作为辅助æ“作层。 4. 生æˆä¸­ä½œå“在整å¡ä¸ŠåŠ ç­‰å¾…é®ç½©ï¼Œä½†ä¸ç§»é™¤ä½œå“基础信æ¯ã€‚ 5. 生æˆä¸­çжæ€ä¸èƒ½åªå­˜åœ¨å‰ç«¯å†…å­˜ notice。åŽç«¯ä½œå“摘è¦å¿…须下å‘坿¢å¤çš„ `generationStatus`ï¼›å‰ç«¯åˆ·æ–°æˆ–退出产å“åŽï¼Œä½œå“架优先用摘è¦çŠ¶æ€æ¢å¤ç­‰å¾…é®ç½©ï¼Œæœ¬è½®å†…å­˜ notice åªä½œä¸ºå³æ—¶å馈。 6. 点击 `generationStatus=generating` çš„è‰ç¨¿å¡å¿…é¡»æ¢å¤å¯¹åº”玩法的生æˆè¿›åº¦é¡µï¼Œä¸èƒ½è¿›å…¥ç©ºç™½ç»“果页或普通工作区;æ¢å¤ç”Ÿæˆé¡µçš„ `startedAtMs` 使用进入生æˆé¡µçš„当剿—¶é—´ï¼Œä½œå“æ‘˜è¦ `updatedAt` åªç”¨äºŽæŽ’åºå’Œæ‘˜è¦å±•示,ä¸å‚与å‡è¿›åº¦èµ·ç®—。 diff --git a/src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx b/src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx index ff231fa7..1d0d9680 100644 --- a/src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx +++ b/src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx @@ -560,7 +560,7 @@ test('creation hub shows RPG public work code from published library entry', () expect(screen.queryByText('CW-00000001')).toBeNull(); }); -test('creation hub hides persisted draft delete action behind swipe underlay', () => { +test('creation hub exposes persisted draft delete action directly on the card', () => { const { container } = render( { @@ -607,7 +607,9 @@ test('creation hub reveals persisted draft delete action from left swipe', () => }); fireEvent.touchEnd(card); - expect(screen.getByRole('button', { name: '删除' })).toBeTruthy(); + expect( + container.querySelector('.creation-work-card__swipe-button--danger'), + ).toBeTruthy(); expect( container.querySelector('.creation-work-card-shell--actions-visible'), ).toBeTruthy(); @@ -615,7 +617,7 @@ test('creation hub reveals persisted draft delete action from left swipe', () => test('creation hub reveals persisted draft delete action from keyboard', async () => { const user = userEvent.setup(); - render( + const { container } = render( { +test('creation hub published work delete action is directly visible', async () => { const user = userEvent.setup(); const onDeletePuzzle = vi.fn(); const onOpenPuzzleDetail = vi.fn(); @@ -751,12 +759,6 @@ test('creation hub published work delete action is revealed without opening card />, ); - expect(screen.queryByRole('button', { name: '删除' })).toBeNull(); - expect(screen.getByRole('button', { name: '分享' })).toBeTruthy(); - - screen.getByRole('button', { name: /查看详情《待删拼图》/u }).focus(); - await user.keyboard('{ArrowLeft}'); - expect(screen.getByRole('button', { name: '删除' })).toBeTruthy(); expect(screen.getByRole('button', { name: '分享' })).toBeTruthy(); @@ -768,6 +770,115 @@ test('creation hub published work delete action is revealed without opening card expect(onOpenPuzzleDetail).not.toHaveBeenCalled(); }); +test('creation hub exposes work delete action directly on card', async () => { + const user = userEvent.setup(); + const onDeletePuzzle = vi.fn(); + const onOpenPuzzleDetail = vi.fn(); + + render( + {}} + onCreateType={noopCreateType} + onOpenDraft={() => {}} + onEnterPublished={() => {}} + onOpenPuzzleDetail={onOpenPuzzleDetail} + onDeletePuzzle={onDeletePuzzle} + entryConfig={testEntryConfig} + creationTypes={testCreationTypes} + />, + ); + + await user.click(screen.getByRole('button', { name: '删除' })); + + expect(onDeletePuzzle).toHaveBeenCalledWith( + expect.objectContaining({ profileId: 'puzzle-profile-direct-delete' }), + ); + expect(onOpenPuzzleDetail).not.toHaveBeenCalled(); +}); + +test('creation hub keeps swipe delete action available', async () => { + const user = userEvent.setup(); + const onDeletePuzzle = vi.fn(); + const onOpenPuzzleDetail = vi.fn(); + + const { container } = render( + {}} + onCreateType={noopCreateType} + onOpenDraft={() => {}} + onEnterPublished={() => {}} + onOpenPuzzleDetail={onOpenPuzzleDetail} + onDeletePuzzle={onDeletePuzzle} + entryConfig={testEntryConfig} + creationTypes={testCreationTypes} + />, + ); + + const card = screen.getByRole('button', { name: /查看详情《左滑删除拼图》/u }); + fireEvent.touchStart(card, { + touches: [{ clientX: 180, clientY: 20 }], + }); + fireEvent.touchMove(card, { + touches: [{ clientX: 80, clientY: 22 }], + }); + fireEvent.touchEnd(card); + + const swipeDeleteButton = container.querySelector( + '.creation-work-card__swipe-button--danger', + ) as HTMLButtonElement | null; + expect(swipeDeleteButton).toBeTruthy(); + await user.click(swipeDeleteButton!); + + expect(onDeletePuzzle).toHaveBeenCalledWith( + expect.objectContaining({ profileId: 'puzzle-profile-swipe-delete' }), + ); + expect(onOpenPuzzleDetail).not.toHaveBeenCalled(); +}); + test('creation hub opens persisted rpg drafts by card click', async () => { const user = userEvent.setup(); const openedItems: CustomWorldWorkSummary[] = []; @@ -942,7 +1053,7 @@ test('creation hub left swipe draft reveals delete without opening card', () => const onDeletePublished = vi.fn(); const onOpenDraft = vi.fn(); - render( + const { container } = render( }); fireEvent.touchEnd(card); - expect(screen.getByRole('button', { name: '删除' })).toBeTruthy(); + expect( + container.querySelector('.creation-work-card__swipe-button--danger'), + ).toBeTruthy(); expect(onOpenDraft).not.toHaveBeenCalled(); }); diff --git a/src/components/custom-world-home/CustomWorldWorkCard.tsx b/src/components/custom-world-home/CustomWorldWorkCard.tsx index 392282c4..00f38323 100644 --- a/src/components/custom-world-home/CustomWorldWorkCard.tsx +++ b/src/components/custom-world-home/CustomWorldWorkCard.tsx @@ -676,43 +676,75 @@ export function CustomWorldWorkCard({ {displayTitle} - {canUseShareAction ? ( - - ) : null} +
+ {canUseShareAction ? ( + + ) : null} + {onDelete ? ( + + ) : null} +
diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 48a1278c..301342ea 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -2081,7 +2081,7 @@ function pickDraftCompletionDialogSourceId( function buildDraftCompletionDialogSource( kind: CreationWorkShelfKind, ids: Array, -) { +): string { const sourceId = pickDraftCompletionDialogSourceId(ids); switch (kind) { case 'rpg': @@ -2103,6 +2103,7 @@ function buildDraftCompletionDialogSource( case 'baby-object-match': return formatPlatformTaskCompletionSource('å®è´è¯†ç‰©è‰ç¨¿', sourceId); } + return formatPlatformTaskCompletionSource('创作è‰ç¨¿', sourceId); } function createMiniGameDraftGenerationStateForRestoredDraft( diff --git a/src/index.css b/src/index.css index d1807475..5d82171f 100644 --- a/src/index.css +++ b/src/index.css @@ -2044,7 +2044,14 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock { white-space: normal; } -.creation-work-card__share-button { +.creation-work-card__quick-actions { + display: inline-flex; + flex: 0 0 auto; + align-items: center; + gap: 0.12rem; +} + +.creation-work-card__quick-action-button { display: inline-flex; width: 2rem; height: 2rem; @@ -2061,17 +2068,32 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock { transform 160ms ease; } -.creation-work-card__share-button:hover { +.creation-work-card__quick-action-button:hover { transform: translateY(-1px); background: color-mix(in srgb, var(--platform-cool-bg) 24%, transparent); color: var(--platform-cool-text); } -.creation-work-card__share-button:focus-visible { +.creation-work-card__quick-action-button:focus-visible { outline: 2px solid var(--platform-cool-border); outline-offset: 2px; } +.creation-work-card__quick-action-button--danger { + color: color-mix(in srgb, #c7653d 78%, var(--platform-text-soft)); +} + +.creation-work-card__quick-action-button--danger:hover { + background: color-mix(in srgb, #c7653d 18%, transparent); + color: #a9472c; +} + +.creation-work-card__quick-action-button:disabled { + cursor: not-allowed; + opacity: 0.62; + transform: none; +} + .creation-work-card__meta { display: flex; min-width: 0;