Skip to content

feat(media): support paused indexing with resume action#108

Merged
wizzomafizzo merged 2 commits into
mainfrom
feat/media-generate-resume
Apr 30, 2026
Merged

feat(media): support paused indexing with resume action#108
wizzomafizzo merged 2 commits into
mainfrom
feat/media-generate-resume

Conversation

@wizzomafizzo

@wizzomafizzo wizzomafizzo commented Apr 30, 2026

Copy link
Copy Markdown
Member
  • Add media.generate.resume API method and surface a paused flag on IndexResponse.
  • MediaDatabaseCard now shows paused/reconnecting status text, swaps the Cancel button for Resume while paused, and pauses the progress animation when not actively indexing.
  • ConnectionProvider invalidates the ["media"] query and refetches media state on (re)connect, with one delayed retry to avoid racing the transport coming back up.
  • Suppress media/tokens fetch error toasts unless the WS is actually CONNECTED — the connection-state UI already conveys reconnect/disconnect, so flapping shouldn't spam toasts.
  • Drop the special-case "writing database to disk" string — Core now sends currentStepDisplay for every phase, including the final write.

Summary by CodeRabbit

  • New Features

    • Pause/resume for media indexing with a visible "Resume" / "Resuming..." flow and reconnecting indicator.
  • Bug Fixes

    • Suppresses error toasts unless fully connected and clears pending retries when connections change to avoid stale updates.
    • Progress/spinner behavior refined at phase boundaries to avoid misleading UI.
  • Tests

    • Expanded tests for connection transitions, toast behavior, resume flow, and retry/cancellation scenarios.

Adds a media.generate.resume API call and surfaces paused/reconnecting
states in MediaDatabaseCard so users can resume an interrupted index
generation. ConnectionProvider now refetches media state on connect (with
one delayed retry) so the card reflects an in-progress run after a
reconnect, and suppresses fetch error toasts while the WS isn't live.
@coderabbitai

coderabbitai Bot commented Apr 30, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 61c2c20d-4f06-4ca1-b0eb-3485a8567e48

📥 Commits

Reviewing files that changed from the base of the PR and between 4e4c5d9 and 8bc5214.

📒 Files selected for processing (3)
  • src/__tests__/unit/components/ConnectionProvider.test.tsx
  • src/__tests__/unit/components/MediaDatabaseCard.test.tsx
  • src/__tests__/unit/coreApi.api-contract.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/tests/unit/components/MediaDatabaseCard.test.tsx

📝 Walkthrough

Walkthrough

Refactors media fetch to handle cancellations and delayed retry, adds connection-state-aware toast suppression, implements pause/resume for media indexing with a new mediaGenerateResume() API method and optional paused flag, and updates UI/translation strings and tests to cover these behaviors. (≤50 words)

Changes

Cohort / File(s) Summary
API & Models
src/lib/coreApi.ts, src/lib/models.ts
Added CoreApi.mediaGenerateResume() and Method.MediaGenerateResume. Extended IndexResponse with optional paused?: boolean.
Connection & Media Fetching
src/components/ConnectionProvider.tsx
Introduced fetchMediaState helper that invalidates ["media"] query, calls CoreAPI.media(), handles cancelled responses with a single delayed retry timer, suppresses media/token error toasts unless connectionState === CONNECTED, and clears retry timer on cleanup.
Media Indexing UI
src/components/MediaDatabaseCard.tsx, src/components/MediaIndexingToast.tsx
Added local resume flow (resumeRequested, handleResumeUpdate) calling mediaGenerateResume() and invalidating ["media"]. Status text now prefers gamesIndex.currentStepDisplay; shows paused/reconnecting indicators; refines spinner/animation and button states for paused/resuming/cancel flows. Removed special-case writingDb toast in favor of currentStepDisplay or preparingDb.
Translations
src/translations/en-US.json
Added resuming, settings.updateDb.resume, settings.updateDb.status.paused, settings.updateDb.status.reconnecting; removed toast.writingDb.
Tests
src/__tests__/unit/components/ConnectionProvider.test.tsx, src/__tests__/unit/components/MediaDatabaseCard.test.tsx, src/__tests__/unit/components/MediaIndexingToast.test.tsx, src/__tests__/unit/coreApi.api-contract.test.ts
Updated tests to inject ConnectionState, reset toast rate limiter, cover cancelled-media retry and unmount-cancellation, add media-resume API contract test, assert currentStepDisplay rendering and paused/reconnecting UI behaviors, and ensure toasts are gated by connection state.

Sequence Diagram

sequenceDiagram
    participant UI as MediaDatabaseCard
    participant CP as ConnectionProvider
    participant API as CoreAPI
    participant RQ as React Query
    participant Store as gamesIndex

    UI->>Store: Subscribe/read indexing state (currentStep, currentStepDisplay, paused)
    CP->>RQ: Invalidate ["media"] cache
    CP->>API: Call media()
    alt media returns results
        API-->>CP: Media results
        CP->>Store: Update gamesIndex / playing
    else media request cancelled
        CP->>CP: Schedule single delayed retry (mediaRetryTimer)
    end

    UI->>API: (user) Click Resume -> mediaGenerateResume()
    API-->>UI: Promise resolves/rejects
    UI->>RQ: Invalidate ["media"]
    UI->>Store: Update resumeRequested / paused state

    Note over CP,UI: Connection state changes
    CP->>UI: Update connectionState
    alt connectionState == CONNECTED
        UI->>UI: Show error toasts for media/tokens
    else
        UI->>UI: Suppress error toasts
    end

    CP->>CP: On unmount/connection change -> clear mediaRetryTimer
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰
I hopped through retries, soft and slow,
Paused the search where mushrooms grow.
A little resume, a gentle chime—
Connections steady, indexing in time. 🥕

🚥 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 accurately summarizes the main feature being added: support for paused indexing and a resume action for media database updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 feat/media-generate-resume

Review rate limit: 4/5 reviews remaining, refill in 12 minutes.

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

@codecov

codecov Bot commented Apr 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.26923% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/components/ConnectionProvider.tsx 91.17% 3 Missing ⚠️
src/lib/coreApi.ts 62.50% 3 Missing ⚠️
src/components/MediaDatabaseCard.tsx 98.30% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@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.

🧹 Nitpick comments (2)
src/__tests__/unit/components/MediaDatabaseCard.test.tsx (2)

37-50: ⚡ Quick win

Keep connectionState typed as the ConnectionState union, not string.

connectionState: ConnectionState.CONNECTED as string removes compile-time validation and can allow invalid states in test setup.

Suggested typing adjustment
 const { ConnectionState, mockStore } = vi.hoisted(() => {
+  type ConnectionStateValue =
+    (typeof ConnectionState)[keyof typeof ConnectionState];
+
   const mockStore = {
     connected: true,
-    connectionState: ConnectionState.CONNECTED as string,
+    connectionState: ConnectionState.CONNECTED as ConnectionStateValue,

As per coding guidelines: **/*.{ts,tsx}: Use TypeScript strict mode and avoid any without justification.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/unit/components/MediaDatabaseCard.test.tsx` around lines 37 -
50, The test is casting connectionState to string which removes compile-time
validation; change the mockStore typing so connectionState uses the
ConnectionState union instead of string (e.g., declare mockStore with
connectionState: typeof ConnectionState[keyof typeof ConnectionState] or type
alias using ConnectionState's value union and assign ConnectionState.CONNECTED
without "as string") so the value must be one of the defined ConnectionState
members; update the mockStore declaration and any related type annotations to
use that union rather than a plain string.

299-346: ⚡ Quick win

Collapse spinner visibility variants into it.each to reduce duplication.

These three tests are the same assertion shape with different inputs; a table-driven test keeps behavior coverage while reducing maintenance drift.

Refactor sketch with it.each
- it("should hide the spinner during phase steps (currentStep===0)", () => { ... });
- it("should hide the spinner during the writing phase (currentStep===totalSteps)", () => { ... });
- it("should show the spinner during per-system steps", () => { ... });
+ it.each([
+   ["phase steps", { currentStep: 0, totalSteps: 0, currentStepDisplay: "Initializing database" }, false],
+   ["writing phase", { currentStep: 11, totalSteps: 11, currentStepDisplay: "Writing database" }, false],
+   ["per-system steps", { currentStep: 3, totalSteps: 11, currentStepDisplay: "Nintendo Entertainment System" }, true],
+ ])("should handle spinner visibility for %s", (_, stepState, shouldShow) => {
+   mockStore.gamesIndex = {
+     indexing: true,
+     exists: true,
+     totalFiles: 100,
+     ...stepState,
+   };
+   render(<MediaDatabaseCard />);
+   const spinner = screen.queryByRole("status", { name: "Loading" });
+   if (shouldShow) expect(spinner).toBeInTheDocument();
+   else expect(spinner).not.toBeInTheDocument();
+ });

As per coding guidelines: In tests, use it.each for repetitive test patterns.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/unit/components/MediaDatabaseCard.test.tsx` around lines 299 -
346, Collapse the three duplicate tests into a table-driven it.each that
iterates over cases for mockStore.gamesIndex (rows: phase step with
currentStep:0 and totalSteps:0 expecting spinner hidden; writing phase with
currentStep===totalSteps expecting hidden; per-system step with currentStep
between 1 and totalSteps-1 expecting spinner visible), render MediaDatabaseCard
inside each iteration, set mockStore.gamesIndex accordingly, and assert presence
using screen.queryByRole("status", { name: "Loading" }) and
expect(...).not.toBeInTheDocument() for hidden cases or
expect(screen.getByRole("status", { name: "Loading" })).toBeInTheDocument() for
visible case; name the test string to include the case description for clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/__tests__/unit/components/MediaDatabaseCard.test.tsx`:
- Around line 37-50: The test is casting connectionState to string which removes
compile-time validation; change the mockStore typing so connectionState uses the
ConnectionState union instead of string (e.g., declare mockStore with
connectionState: typeof ConnectionState[keyof typeof ConnectionState] or type
alias using ConnectionState's value union and assign ConnectionState.CONNECTED
without "as string") so the value must be one of the defined ConnectionState
members; update the mockStore declaration and any related type annotations to
use that union rather than a plain string.
- Around line 299-346: Collapse the three duplicate tests into a table-driven
it.each that iterates over cases for mockStore.gamesIndex (rows: phase step with
currentStep:0 and totalSteps:0 expecting spinner hidden; writing phase with
currentStep===totalSteps expecting hidden; per-system step with currentStep
between 1 and totalSteps-1 expecting spinner visible), render MediaDatabaseCard
inside each iteration, set mockStore.gamesIndex accordingly, and assert presence
using screen.queryByRole("status", { name: "Loading" }) and
expect(...).not.toBeInTheDocument() for hidden cases or
expect(screen.getByRole("status", { name: "Loading" })).toBeInTheDocument() for
visible case; name the test string to include the case description for clarity.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a822a98-e335-4049-92a2-a1007b2ca2b4

📥 Commits

Reviewing files that changed from the base of the PR and between 1a4cad1 and 4e4c5d9.

📒 Files selected for processing (9)
  • src/__tests__/unit/components/ConnectionProvider.test.tsx
  • src/__tests__/unit/components/MediaDatabaseCard.test.tsx
  • src/__tests__/unit/components/MediaIndexingToast.test.tsx
  • src/components/ConnectionProvider.tsx
  • src/components/MediaDatabaseCard.tsx
  • src/components/MediaIndexingToast.tsx
  • src/lib/coreApi.ts
  • src/lib/models.ts
  • src/translations/en-US.json

Adds an api-contract test for `mediaGenerateResume`, a regression test
for the MediaDatabaseCard re-enabling the Resume button after a failed
resume call, and two ConnectionProvider tests covering the new
retry-on-cancel behaviour and its cleanup-on-unmount path.
@wizzomafizzo
wizzomafizzo merged commit 4b8b7ba into main Apr 30, 2026
6 checks passed
@wizzomafizzo
wizzomafizzo deleted the feat/media-generate-resume branch April 30, 2026 01:27
wizzomafizzo added a commit that referenced this pull request May 20, 2026
* feat(media): support paused indexing with resume action

Adds a media.generate.resume API call and surfaces paused/reconnecting
states in MediaDatabaseCard so users can resume an interrupted index
generation. ConnectionProvider now refetches media state on connect (with
one delayed retry) so the card reflects an in-progress run after a
reconnect, and suppresses fetch error toasts while the WS isn't live.

* test(media): cover resume API, retry-on-cancel, and resume-failure paths

Adds an api-contract test for `mediaGenerateResume`, a regression test
for the MediaDatabaseCard re-enabling the Resume button after a failed
resume call, and two ConnectionProvider tests covering the new
retry-on-cancel behaviour and its cleanup-on-unmount path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant