fix(mailbox): show spinner during initial folder fetch instead of empty state #167

Merged
JMR-dev merged 3 commits from fix-149-folder-loading-spinner into main 2026-07-02 22:25:22 +00:00
JMR-dev commented 2026-07-02 21:48:55 +00:00 (Migrated from github.com)

Summary

MailboxViewModel.selectFolder() kicked off its background syncFolder() fetch
fire-and-forget with no loading flag, so opening a per-account folder with no
cached messages yet flashed "No messages to display" for the entire IMAP fetch
instead of showing a spinner.

  • Adds isSyncingFolder, a StateFlow<Boolean> set for the duration of the
    selectFolder-triggered sync (mirroring the existing isRefreshing pattern),
    cleared via try/finally regardless of outcome (success, failure, or a
    stale call superseded by rapid folder switching).
  • MailboxScreen's non-paged empty branch now holds NoMessagesState back
    while isSyncingFolder is true, showing a CircularProgressIndicator
    instead — mirroring how the unified-inbox paged branch already gates on
    pagedMessages.loadState.refresh.

Closes #149

Test plan

  • Local fast gate green: assembleDebug, testDebugUnitTest, lintDebug,
    ktlintCheck, detekt, compileDebugAndroidTestKotlin
  • Added MailboxViewModelTest cases: flag toggles true/false around a
    gated sync, clears on failure, and — the rapid-switch race the issue
    calls out — a stale sync for a folder no longer selected can't clear
    the spinner for the folder actually selected now
  • Existing MailboxViewModelTest coverage (incl. selectFolder syncing)
    still passes unmodified

🤖 Generated with Claude Code

## Summary `MailboxViewModel.selectFolder()` kicked off its background `syncFolder()` fetch fire-and-forget with no loading flag, so opening a per-account folder with no cached messages yet flashed "No messages to display" for the entire IMAP fetch instead of showing a spinner. - Adds `isSyncingFolder`, a `StateFlow<Boolean>` set for the duration of the `selectFolder`-triggered sync (mirroring the existing `isRefreshing` pattern), cleared via `try`/`finally` regardless of outcome (success, failure, or a stale call superseded by rapid folder switching). - `MailboxScreen`'s non-paged empty branch now holds `NoMessagesState` back while `isSyncingFolder` is true, showing a `CircularProgressIndicator` instead — mirroring how the unified-inbox paged branch already gates on `pagedMessages.loadState.refresh`. Closes #149 ## Test plan - [x] Local fast gate green: `assembleDebug`, `testDebugUnitTest`, `lintDebug`, `ktlintCheck`, `detekt`, `compileDebugAndroidTestKotlin` - [x] Added `MailboxViewModelTest` cases: flag toggles true/false around a gated sync, clears on failure, and — the rapid-switch race the issue calls out — a stale sync for a folder no longer selected can't clear the spinner for the folder actually selected now - [x] Existing `MailboxViewModelTest` coverage (incl. `selectFolder` syncing) still passes unmodified 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.