Skip to content

fix(ui): restrict pause button to Downloading state (fixes #58) - #63

Merged
mpiton merged 2 commits into
mainfrom
fix/issue-58-pause-button-invalid-states
Apr 14, 2026
Merged

fix(ui): restrict pause button to Downloading state (fixes #58)#63
mpiton merged 2 commits into
mainfrom
fix/issue-58-pause-button-invalid-states

Conversation

@mpiton

@mpiton mpiton commented Apr 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • Pause button in ActionCell now only renders for Downloading state (was Downloading || Queued), aligning UI with the domain state machine that only allows Downloading → Paused
  • Bulk toggle handler (handleToggleSelected) similarly restricted to only pause Downloading downloads, preventing silent IPC errors for Queued selections
  • Added 7 regression tests covering action button visibility per download state and bulk toggle behavior with Queued downloads

Test plan

  • npx vitest run — 336/336 pass, 0 fail
  • npm run lint — 0 warnings, 0 errors
  • Manual: start a download, verify Pause button appears while Downloading
  • Manual: queue a download, verify NO Pause button appears while Queued
  • Manual: trigger Retry state, verify NO Pause button appears
  • Manual: select mixed-state downloads, press Space — only Downloading are paused, Queued are skipped

Closes #58


Summary by cubic

Restricts the Pause action to only Downloading items and updates the Space bulk toggle to ignore Queued, preventing invalid IPC calls. Adds i18n-backed aria-labels for action buttons and fixes #58.

  • Bug Fixes
    • Show Pause button only when state is 'Downloading'.
    • Space toggle pauses only 'Downloading' and resumes 'Paused'; 'Queued' are ignored.
    • Add pause, resume, and retry labels to en/fr and set aria-labels on action buttons.

Written for commit b168ae5. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Pause control now shows only for actively downloading items; queued downloads are excluded to prevent invalid pause attempts.
    • Bulk pause (Space) no longer tries to pause queued items.
  • Accessibility

    • Action buttons gained descriptive aria-labels for better screen-reader support.
  • Tests

    • Added tests verifying per-item action visibility and bulk-selection behavior.
  • Localization

    • Added Pause/Resume/Retry labels for English and French.

@github-actions github-actions Bot added documentation Improvements or additions to documentation frontend labels Apr 14, 2026
@coderabbitai

coderabbitai Bot commented Apr 14, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a1c75fb-05dd-4941-8395-3044820078a2

📥 Commits

Reviewing files that changed from the base of the PR and between 6c8b5a0 and b168ae5.

📒 Files selected for processing (4)
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
  • src/views/DownloadsView/DownloadsTable.tsx
  • src/views/DownloadsView/__tests__/DownloadsView.test.tsx
✅ Files skipped from review due to trivial changes (2)
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/views/DownloadsView/tests/DownloadsView.test.tsx

📝 Walkthrough

Walkthrough

Pause controls and bulk-pause logic were tightened so pause is shown and invoked only for downloads in the Downloading state; tests and i18n labels were added/updated and the changelog documents the fix.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Recorded fix: pause control now shown only for Downloading state downloads to avoid invalid backend transitions.
UI Pause Controls
src/views/DownloadsView/DownloadsTable.tsx, src/views/DownloadsView/DownloadsView.tsx
Inline pause button rendering restricted to download.state === 'Downloading'; bulk pause (handleToggleSelected) now targets only Downloading items. Action buttons gained aria-labels using downloads.table.actions.* keys.
Tests
src/views/DownloadsView/__tests__/DownloadsTable.test.tsx, src/views/DownloadsView/__tests__/DownloadsView.test.tsx
Added tests verifying per-state action button visibility; expanded mock downloads to include a Queued item and updated shortcut/select-all tests to assert queued items are not paused.
i18n
src/i18n/locales/en.json, src/i18n/locales/fr.json
Added downloads.table.actions.pause, resume, and retry translation keys (English and French).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Soft paw taps on a tiny UI tweak,
Pause now listens only when downloads streak,
Queued ones sit patient, no accidental stop,
Tests hopped in to guard each state on the hop,
🥕🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: restricting the pause button to the Downloading state to fix issue #58.
Linked Issues check ✅ Passed Changes comprehensively address issue #58 objectives: pause button restricted to Downloading state, bulk toggle filters non-pausable states, and regression tests added to prevent regressions.
Out of Scope Changes check ✅ Passed All changes align with issue #58: pause state filtering in UI and bulk operations, added tests, and i18n labels for action buttons. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-58-pause-button-invalid-states

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Apr 14, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes issue #58 by restricting the Pause button in ActionCell to Downloading state only (removing the previous || Queued condition) and aligning the bulk Space-shortcut handler (handleToggleSelected) to skip Queued downloads, preventing silent IPC errors from calling download_pause on downloads that can't transition to Paused. Seven targeted regression tests cover action-button visibility per state and the corrected bulk-toggle behaviour.

Confidence Score: 5/5

  • Safe to merge — the fix is minimal, well-scoped, and fully tested with no remaining blocking issues.
  • All findings are P2 or lower. The logic change is a one-line condition tightening in ActionCell and a one-line filter addition in handleToggleSelected, both aligned with the domain state machine. New regression tests cover all affected states. ActionsBar's Pause All / Resume All delegate to backend batch commands that already handle state filtering server-side, so no related fix is needed there.
  • No files require special attention.

Important Files Changed

Filename Overview
src/views/DownloadsView/DownloadsTable.tsx Pause button in ActionCell correctly restricted to state === 'Downloading'; all other per-row action conditions are unchanged and remain correct.
src/views/DownloadsView/DownloadsView.tsx handleToggleSelected now correctly filters only Downloading downloads for pause; Queued selections produce an empty task list and return early without emitting any IPC calls.
src/views/DownloadsView/tests/DownloadsTable.test.tsx Adds 6 new regression tests for action button visibility per download state; covers Downloading, Queued, Retry, Paused, and Completed states with targeted assertions.
src/views/DownloadsView/tests/DownloadsView.test.tsx Adds regression test for Queued bulk-toggle; negative assertion on line 485 runs outside the waitFor block — a minor pattern concern that could miss a race in future refactors.
CHANGELOG.md Changelog correctly documents both the UI fix and the bulk-toggle fix under [Unreleased] → Fixed.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User presses Space / clicks Pause] --> B{Action source}
    B -->|Per-row ActionCell| C{download.state}
    B -->|Bulk handleToggleSelected| D[Filter visibleSelectedDownloads]

    C -->|Downloading| E[Show Pause button → pause IPC]
    C -->|Queued| F[No button shown ✅ fixed]
    C -->|Paused| G[Show Resume button → resume IPC]
    C -->|Error / Retry| H[Show Retry button → retry IPC]
    C -->|Completed / other| I[No action button]

    D --> J[state === Downloading → pauseMut]
    D --> K[state === Paused → resumeMut]
    D --> L[state === Queued → skip ✅ fixed]
    D --> M[state === other → skip]

    J & K --> N{tasks.length === 0?}
    L & M --> N
    N -->|Yes| O[Return early — no IPC calls]
    N -->|No| P[Promise.allSettled tasks]
Loading

Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix(ui): restrict pause to Downloading s..." | Re-trigger Greptile

Comment on lines +481 to +485
await waitFor(() => {
expect(mockInvoke).toHaveBeenCalledWith('download_pause', { id: 1 });
expect(mockInvoke).toHaveBeenCalledWith('download_resume', { id: 2 });
});
expect(mockInvoke).not.toHaveBeenCalledWith('download_pause', { id: 3 });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Negative assertion outside waitFor

The not.toHaveBeenCalledWith check on line 485 runs synchronously after the waitFor resolves. In the current implementation this is safe — all three pause/resume calls are dispatched in the same Promise.allSettled batch, so id 3 would have been called before ids 1 and 2. However, if the implementation is ever changed to schedule calls asynchronously (e.g., debounced or queued), a late call for id 3 could slip past this assertion. Moving the negative check inside the same waitFor makes the intent explicit and future-proof.

Suggested change
await waitFor(() => {
expect(mockInvoke).toHaveBeenCalledWith('download_pause', { id: 1 });
expect(mockInvoke).toHaveBeenCalledWith('download_resume', { id: 2 });
});
expect(mockInvoke).not.toHaveBeenCalledWith('download_pause', { id: 3 });
await waitFor(() => {
expect(mockInvoke).toHaveBeenCalledWith('download_pause', { id: 1 });
expect(mockInvoke).toHaveBeenCalledWith('download_resume', { id: 2 });
expect(mockInvoke).not.toHaveBeenCalledWith('download_pause', { id: 3 });
});

Fix in Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/views/DownloadsView/DownloadsTable.tsx (1)

145-181: ⚠️ Potential issue | 🟠 Major

Add accessible labels to icon-only action buttons.

Lines 145–181 render icon-only controls without aria-label, so these actions are not reliably discoverable for assistive tech.

🔧 Suggested fix
       {download.state === 'Downloading' && (
         <Button
           variant="ghost"
           size="icon"
           className="h-7 w-7"
+          aria-label={t('downloads.table.actions.pause', { defaultValue: 'Pause' })}
           onClick={(e) => {
             e.stopPropagation();
             actions.pause(download.id);
           }}
         >
           <Pause className="size-3.5" />
         </Button>
       )}
       {download.state === 'Paused' && (
         <Button
           variant="ghost"
           size="icon"
           className="h-7 w-7"
+          aria-label={t('downloads.table.actions.resume', { defaultValue: 'Resume' })}
           onClick={(e) => {
             e.stopPropagation();
             actions.resume(download.id);
           }}
         >
           <Play className="size-3.5" />
         </Button>
       )}
       {(download.state === 'Error' || download.state === 'Retry') && (
         <Button
           variant="ghost"
           size="icon"
           className="h-7 w-7"
+          aria-label={t('downloads.table.actions.retry', { defaultValue: 'Retry' })}
           onClick={(e) => {
             e.stopPropagation();
             actions.start(download.id);
           }}
         >
           <RotateCcw className="size-3.5" />
         </Button>
       )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/DownloadsView/DownloadsTable.tsx` around lines 145 - 181, The
icon-only action Buttons for Pause, Play and RotateCcw lack accessible labels;
update each Button (the ones rendering when download.state === 'Downloading',
'Paused', and when download.state === 'Error' || 'Retry') to include a
descriptive aria-label (e.g., "Pause download", "Resume download", "Retry
download") and include contextual text like the download name or id (e.g.,
`aria-label={`Pause download ${download.name || download.id}`}`) so assistive
tech can announce the action; keep the existing onClick handlers (actions.pause,
actions.resume, actions.start) and leave the icons (Pause, Play, RotateCcw) as
visual-only content.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/views/DownloadsView/DownloadsTable.tsx`:
- Around line 145-181: The icon-only action Buttons for Pause, Play and
RotateCcw lack accessible labels; update each Button (the ones rendering when
download.state === 'Downloading', 'Paused', and when download.state === 'Error'
|| 'Retry') to include a descriptive aria-label (e.g., "Pause download", "Resume
download", "Retry download") and include contextual text like the download name
or id (e.g., `aria-label={`Pause download ${download.name || download.id}`}`) so
assistive tech can announce the action; keep the existing onClick handlers
(actions.pause, actions.resume, actions.start) and leave the icons (Pause, Play,
RotateCcw) as visual-only content.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bcffe3dc-abeb-4331-9e8b-549dc4d8c6fc

📥 Commits

Reviewing files that changed from the base of the PR and between a58fad1 and 6c8b5a0.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/views/DownloadsView/DownloadsTable.tsx
  • src/views/DownloadsView/DownloadsView.tsx
  • src/views/DownloadsView/__tests__/DownloadsTable.test.tsx
  • src/views/DownloadsView/__tests__/DownloadsView.test.tsx

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

@mpiton
mpiton merged commit 9c9dcce into main Apr 14, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[QA] MEDIUM: Pause button shown for downloads in Retry state — invalid transition, no user feedback

1 participant