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.
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
## 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)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
MailboxViewModel.selectFolder()kicked off its backgroundsyncFolder()fetchfire-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.
isSyncingFolder, aStateFlow<Boolean>set for the duration of theselectFolder-triggered sync (mirroring the existingisRefreshingpattern),cleared via
try/finallyregardless of outcome (success, failure, or astale call superseded by rapid folder switching).
MailboxScreen's non-paged empty branch now holdsNoMessagesStatebackwhile
isSyncingFolderis true, showing aCircularProgressIndicatorinstead — mirroring how the unified-inbox paged branch already gates on
pagedMessages.loadState.refresh.Closes #149
Test plan
assembleDebug,testDebugUnitTest,lintDebug,ktlintCheck,detekt,compileDebugAndroidTestKotlinMailboxViewModelTestcases: flag toggles true/false around agated 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
MailboxViewModelTestcoverage (incl.selectFoldersyncing)still passes unmodified
🤖 Generated with Claude Code