Skip to content

Fix vault sync loading state and stale closure bug#65

Merged
FSM1 merged 3 commits into
mainfrom
fix/vault-sync-loading-state
Feb 9, 2026
Merged

Fix vault sync loading state and stale closure bug#65
FSM1 merged 3 commits into
mainfrom
fix/vault-sync-loading-state

Conversation

@FSM1

@FSM1 FSM1 commented Feb 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix stale closure bug that caused 30+ second delay loading files on session restore. handleSync captured folders from the React render cycle; root folder wasn't in the closure on initial load. Now reads useFolderStore.getState() directly.
  • Fire initial sync immediately on mount instead of waiting 30s for setInterval to tick.
  • Add vault syncing loading state with initialSyncComplete tracking in sync store and isNewVault flag in vault store. Shows terminal-style animated progress bar while IPNS resolves; new vaults skip straight to empty state.
  • Reset sync store on logout in all three logout paths (normal, error, token refresh failure).

Test plan

  • Build passes (pnpm --filter web build)
  • All 17 web unit tests pass
  • All 376 API unit tests pass
  • E2E: Upload files in session 1, close browser, reopen — files load immediately (was 30+ seconds)
  • E2E: Full logout + fresh email/OTP login — files load immediately after auth
  • E2E: New vault (empty) shows empty state, not stuck syncing spinner

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added visual progress indicator with animated status display during initial vault synchronization on login.
  • Bug Fixes

    • Improved sync state initialization to prevent misleading completion signals during synchronization.
    • Enhanced error handling for IPNS resolution failures to maintain polling visibility when sync is incomplete.

FSM1 and others added 2 commits February 9, 2026 06:30
Moved to done/, beginning implementation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, returning users saw a misleading "EMPTY DIRECTORY" for 30+
seconds while IPNS resolved. Three issues contributed:

1. Stale closure in handleSync captured folders from render cycle — root
   folder wasn't in the closure yet on initial load. Fixed by reading
   useFolderStore.getState() directly inside the async callback.

2. useInterval doesn't fire immediately — added useEffect to trigger
   sync on first mount instead of waiting 30s for setInterval.

3. No distinction between "syncing" and "empty vault" — added
   initialSyncComplete tracking to sync store and isNewVault flag to
   vault store. Shows terminal-style syncing animation until IPNS
   resolves; new vaults skip straight to empty state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 9, 2026 05:55
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@FSM1 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 30 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

This pull request addresses vault synchronization loading state issues on login by introducing stale closure fixes, initial sync tracking, and an "isNewVault" flag to differentiate new vaults from returning ones. It adds UI feedback with styling, state management enhancements, and immediate sync triggering on mount.

Changes

Cohort / File(s) Summary
Documentation
.learnings/2026-02-09-vault-sync-loading-state.md, .planning/STATE.md
Adds debugging analysis of vault sync loading state identifying stale closure, polling timing, and IPNS resolution issues; removes completed task from pending todos.
State Management
apps/web/src/stores/sync.store.ts, apps/web/src/stores/vault.store.ts
Introduces initialSyncComplete flag and completeInitialSync() method to sync store; adds isNewVault boolean flag to vault store with propagation through setVaultKeys(); ensures reset() clears both new fields.
Hooks & Authentication
apps/web/src/hooks/useAuth.ts, apps/web/src/hooks/useSyncPolling.ts
Extends useAuth to set isNewVault: true for new vaults and invoke reset() on logout; refactors useSyncPolling to add immediate first sync on mount, conditional initial sync for new vaults, and concurrent sync guard via initialSyncFired tracking.
API Client
apps/web/src/lib/api/client.ts
Adds sync store reset invocation on token refresh failure alongside existing cleanup steps.
UI & Styling
apps/web/src/components/file-browser/FileBrowser.tsx, apps/web/src/styles/file-browser.css
Replaces stale folder state access with direct store reads; adds conditional UI block for vault syncing during initial sync with progress bar; adjusts EmptyState rendering to prevent flicker; introduces vault-syncing CSS classes and @keyframes vault-syncing-slide animation.

Sequence Diagram

sequenceDiagram
    actor User
    participant Auth as useAuth Hook
    participant VaultStore as Vault Store
    participant SyncStore as Sync Store
    participant Polling as useSyncPolling
    participant FileBrowser as FileBrowser UI

    User->>Auth: Login / Create Vault
    
    alt New Vault
        Auth->>VaultStore: setVaultKeys({isNewVault: true})
        VaultStore->>VaultStore: isNewVault = true
    else Returning Vault
        Auth->>VaultStore: setVaultKeys({isNewVault: false})
        VaultStore->>VaultStore: isNewVault = false
    end

    Auth->>SyncStore: reset()
    SyncStore->>SyncStore: initialSyncComplete = false

    Auth->>Polling: Hook mounted with new vault state

    Polling->>Polling: Trigger immediate sync on mount
    
    alt New Vault Path
        Polling->>SyncStore: completeInitialSync()
        SyncStore->>SyncStore: initialSyncComplete = true
    else Returning Vault Path
        Polling->>Polling: Poll until sync succeeds
        Polling->>Polling: Then completeInitialSync()
        Polling->>SyncStore: completeInitialSync()
        SyncStore->>SyncStore: initialSyncComplete = true
    end

    SyncStore-->>FileBrowser: initialSyncComplete = true
    FileBrowser->>FileBrowser: Hide vault syncing UI
    FileBrowser->>FileBrowser: Show folder contents or empty state
    FileBrowser-->>User: Vault ready
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and concisely summarizes the two main fixes: resolving a stale closure bug and fixing the vault sync loading state display.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/vault-sync-loading-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/web/src/components/file-browser/FileBrowser.tsx`:
- Around line 353-368: The animated loader `.vault-syncing-bar-fill` uses
`animation: vault-syncing-slide ... infinite` but lacks a prefers-reduced-motion
rule; add a `@media (prefers-reduced-motion: reduce)` block in the component's
stylesheet (the CSS module / styles where `.vault-syncing-bar-fill` is defined)
that sets `.vault-syncing-bar-fill { animation: none; }` so users who prefer
reduced motion won't see the continuous `vault-syncing-slide` animation.
🧹 Nitpick comments (2)
apps/web/src/hooks/useSyncPolling.ts (1)

76-81: Consider potential double-sync on mount.

The immediate-on-mount effect fires doSync() once via the initialSyncFired ref guard. However, doSync is in the dependency array and is recreated on every render where its own deps change. Since the ref guard prevents re-firing, this is safe in practice. But note that if useInterval fires its first tick very close to mount (unlikely with 30s), there's no issue since isSyncingRef guards concurrency.

One minor observation: because doSync is a dependency, React's exhaustive-deps lint is satisfied, but the intent is clearly "run once on mount." A comment noting that the ref makes this effectively a one-shot despite doSync being in deps would help future readers — though this is optional.

apps/web/src/components/file-browser/FileBrowser.tsx (1)

354-358: Minor a11y: consider adding an accessible status announcement for screen readers.

The vault syncing block uses aria-hidden="true" on the <pre> ASCII art (good), but there's no role="status" or aria-live="polite" region to announce the syncing state to screen reader users. The <p> elements with the status text could benefit from being in a live region.

♿ Proposed a11y improvement
-        <div className="vault-syncing" data-testid="vault-syncing">
+        <div className="vault-syncing" data-testid="vault-syncing" role="status" aria-live="polite">

Comment thread apps/web/src/components/file-browser/FileBrowser.tsx

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Improves the web vault’s initial synchronization experience and fixes a React/Zustand stale-closure issue that delayed session-restore file loading, while ensuring sync state is correctly cleared on logout/token-refresh failure.

Changes:

  • Fixes stale closure in handleSync by reading the latest folder state via useFolderStore.getState(), and triggers an immediate sync on mount (not after the first 30s interval).
  • Adds an initial vault-sync loading UI driven by new initialSyncComplete (sync store) and isNewVault (vault store) flags.
  • Resets sync store state on logout and on refresh-token failure, alongside existing vault/folder store clears.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
apps/web/src/styles/file-browser.css Adds styles for the new “vault syncing” loading state UI.
apps/web/src/stores/vault.store.ts Introduces isNewVault flag to distinguish first-time vaults from returning users.
apps/web/src/stores/sync.store.ts Adds initialSyncComplete tracking and action to control initial-sync UX.
apps/web/src/lib/api/client.ts Resets sync store on refresh-token failure before logging out.
apps/web/src/hooks/useSyncPolling.ts Fires initial sync immediately; completes initial sync for new vaults.
apps/web/src/hooks/useAuth.ts Sets isNewVault on vault creation; resets sync store on logout paths.
apps/web/src/components/file-browser/FileBrowser.tsx Uses fresh store state in sync callback; renders initial-sync loading UI.
.planning/todos/done/2026-02-08-vault-sync-loading-state-ux.md Adds planning record for the UX change.
.planning/STATE.md Updates pending/completed todo counts.
.learnings/2026-02-09-vault-sync-loading-state.md Captures the root-cause analysis and lessons learned.

Comment thread apps/web/src/components/file-browser/FileBrowser.tsx Outdated
Comment thread apps/web/src/hooks/useSyncPolling.ts Outdated
Comment thread apps/web/src/stores/sync.store.ts
- Add prefers-reduced-motion media query to disable vault syncing animation
- Propagate errors during initial sync so loading UI stays visible on failure
- Skip unnecessary IPNS resolve for new vaults on mount
- Add role="status" aria-live="polite" for screen reader a11y
- Add sync store unit tests for initialSyncComplete lifecycle

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FSM1 FSM1 merged commit 441f13d into main Feb 9, 2026
6 checks passed
@FSM1 FSM1 deleted the fix/vault-sync-loading-state branch February 11, 2026 00:23
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.

2 participants