Fixes the folder-label display-bug trio from the PR #54 code review. All three live in the same label-resolution/presentation path, so they land as one PR.
#59 — move-to picker and app-bar title still showed ambiguous duplicate names
Root cause:resolveDrawerLabels was wired only into FolderDrawer; the move-to picker and the app-bar title still rendered the bare folderDisplayLabel, so a Gmail account with both [Gmail]/Drafts and a user folder "Drafts" showed two pixel-identical "Drafts" rows in the picker — and picking the wrong one silently filed mail into the wrong folder.
Fix: a shared resolvedFolderLabels(folders, accounts) helper now feeds all three surfaces (drawer, picker, title) from the same resolution. Picker rows resolve against the unfiltered target list, so a row keeps its disambiguation even when its colliding twin (e.g. the current folder) is filtered out of the picker itself.
#60 — self-referential "Sent [Sent]" when two top-level folders share a role
Root cause: on servers without SPECIAL-USE (GreenMail, many shared hosts), top-level Sent and Sent Items both classify as SENT via the name fallback and share the friendly "Sent" base label. Both are non-special and top-level, so both kept the base, tied, and fell through to the full-path safety net as "Sent [Sent]" and "Sent [Sent Items]". The resolver KDoc's uniqueness sketch assumed base labels are leaf names — false for role-derived friendly names.
Fix: colliding top-level user folders now tie-break on the display name before the safety net — the folder actually named like the base label keeps it ("Sent"); the others show their real server name ("Sent Items"). The KDoc no longer overclaims the main pass's distinctness or the final pass's uniqueness guarantee.
#61 — transient wrong provider suffix while switching accounts in the open drawer
Root cause: the de-dup suffix combined drawerAccount (updates immediately on switch) with the folders StateFlow (lags until the new account's Room query emits), and the drawer never checked the folders' own accountId. In the gap, account A's [Gmail]/Drafts rendered as "Drafts - Outlook".
Fix: the suffix now derives from the rendered folder list's own accountId via providerLabelFor(folders, accounts) — the ticket's suggested fix — so a stale list keeps its own account's brand for those frames (and shows no suffix if the owner is unknown, which beats a wrong one).
Tests
FolderLabelsTest: role tie-break ("Sent"/"Sent Items", no-friendly-match variant, special+canonical+synonym trio), providerLabelFor owner derivation and empty/orphaned fallback.
MailboxViewModelTest (Turbine): pins the switch gap — drawerAccount flips to B while folders still holds A's list until B's query emits.
MailboxScreenTest (UI): the move picker tells the two "Drafts" apart and moving via "Drafts - Gmail" lands in [Gmail]/Drafts; the app-bar title keeps the disambiguated label after selecting the folder.
FolderDrawerTest (UI): renders the exact transient frame (drawerAccount = Outlook, folders = Gmail's) and asserts "Drafts - Gmail" shows and "Drafts - Outlook" never does.
Deliberately not done here (queued as their own tickets): #65 role-derivation table, #66 hierarchy-delimiter persistence, #67 memoization, #68 strings.xml patterns, #69 provider-brand host matching.
Fixes the folder-label display-bug trio from the PR #54 code review. All three live in the same label-resolution/presentation path, so they land as one PR.
## #59 — move-to picker and app-bar title still showed ambiguous duplicate names
**Root cause:** `resolveDrawerLabels` was wired only into `FolderDrawer`; the move-to picker and the app-bar title still rendered the bare `folderDisplayLabel`, so a Gmail account with both `[Gmail]/Drafts` and a user folder "Drafts" showed two pixel-identical "Drafts" rows in the picker — and picking the wrong one silently filed mail into the wrong folder.
**Fix:** a shared `resolvedFolderLabels(folders, accounts)` helper now feeds all three surfaces (drawer, picker, title) from the same resolution. Picker rows resolve against the *unfiltered* target list, so a row keeps its disambiguation even when its colliding twin (e.g. the current folder) is filtered out of the picker itself.
## #60 — self-referential "Sent [Sent]" when two top-level folders share a role
**Root cause:** on servers without SPECIAL-USE (GreenMail, many shared hosts), top-level `Sent` and `Sent Items` both classify as SENT via the name fallback and share the friendly "Sent" base label. Both are non-special and top-level, so both kept the base, tied, and fell through to the full-path safety net as **"Sent [Sent]"** and "Sent [Sent Items]". The resolver KDoc's uniqueness sketch assumed base labels are leaf names — false for role-derived friendly names.
**Fix:** colliding top-level user folders now tie-break on the display name *before* the safety net — the folder actually named like the base label keeps it ("Sent"); the others show their real server name ("Sent Items"). The KDoc no longer overclaims the main pass's distinctness or the final pass's uniqueness guarantee.
## #61 — transient wrong provider suffix while switching accounts in the open drawer
**Root cause:** the de-dup suffix combined `drawerAccount` (updates immediately on switch) with the `folders` StateFlow (lags until the new account's Room query emits), and the drawer never checked the folders' own `accountId`. In the gap, account A's `[Gmail]/Drafts` rendered as "Drafts - Outlook".
**Fix:** the suffix now derives from the rendered folder list's own `accountId` via `providerLabelFor(folders, accounts)` — the ticket's suggested fix — so a stale list keeps its own account's brand for those frames (and shows no suffix if the owner is unknown, which beats a wrong one).
## Tests
- `FolderLabelsTest`: role tie-break ("Sent"/"Sent Items", no-friendly-match variant, special+canonical+synonym trio), `providerLabelFor` owner derivation and empty/orphaned fallback.
- `MailboxViewModelTest` (Turbine): pins the switch gap — `drawerAccount` flips to B while `folders` still holds A's list until B's query emits.
- `MailboxScreenTest` (UI): the move picker tells the two "Drafts" apart and moving via "Drafts - Gmail" lands in `[Gmail]/Drafts`; the app-bar title keeps the disambiguated label after selecting the folder.
- `FolderDrawerTest` (UI): renders the exact transient frame (drawerAccount = Outlook, folders = Gmail's) and asserts "Drafts - Gmail" shows and "Drafts - Outlook" never does.
Deliberately **not** done here (queued as their own tickets): #65 role-derivation table, #66 hierarchy-delimiter persistence, #67 memoization, #68 strings.xml patterns, #69 provider-brand host matching.
Closes #59
Closes #60
Closes #61
🤖 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.
Fixes the folder-label display-bug trio from the PR #54 code review. All three live in the same label-resolution/presentation path, so they land as one PR.
#59 — move-to picker and app-bar title still showed ambiguous duplicate names
Root cause:
resolveDrawerLabelswas wired only intoFolderDrawer; the move-to picker and the app-bar title still rendered the barefolderDisplayLabel, so a Gmail account with both[Gmail]/Draftsand a user folder "Drafts" showed two pixel-identical "Drafts" rows in the picker — and picking the wrong one silently filed mail into the wrong folder.Fix: a shared
resolvedFolderLabels(folders, accounts)helper now feeds all three surfaces (drawer, picker, title) from the same resolution. Picker rows resolve against the unfiltered target list, so a row keeps its disambiguation even when its colliding twin (e.g. the current folder) is filtered out of the picker itself.#60 — self-referential "Sent [Sent]" when two top-level folders share a role
Root cause: on servers without SPECIAL-USE (GreenMail, many shared hosts), top-level
SentandSent Itemsboth classify as SENT via the name fallback and share the friendly "Sent" base label. Both are non-special and top-level, so both kept the base, tied, and fell through to the full-path safety net as "Sent [Sent]" and "Sent [Sent Items]". The resolver KDoc's uniqueness sketch assumed base labels are leaf names — false for role-derived friendly names.Fix: colliding top-level user folders now tie-break on the display name before the safety net — the folder actually named like the base label keeps it ("Sent"); the others show their real server name ("Sent Items"). The KDoc no longer overclaims the main pass's distinctness or the final pass's uniqueness guarantee.
#61 — transient wrong provider suffix while switching accounts in the open drawer
Root cause: the de-dup suffix combined
drawerAccount(updates immediately on switch) with thefoldersStateFlow (lags until the new account's Room query emits), and the drawer never checked the folders' ownaccountId. In the gap, account A's[Gmail]/Draftsrendered as "Drafts - Outlook".Fix: the suffix now derives from the rendered folder list's own
accountIdviaproviderLabelFor(folders, accounts)— the ticket's suggested fix — so a stale list keeps its own account's brand for those frames (and shows no suffix if the owner is unknown, which beats a wrong one).Tests
FolderLabelsTest: role tie-break ("Sent"/"Sent Items", no-friendly-match variant, special+canonical+synonym trio),providerLabelForowner derivation and empty/orphaned fallback.MailboxViewModelTest(Turbine): pins the switch gap —drawerAccountflips to B whilefoldersstill holds A's list until B's query emits.MailboxScreenTest(UI): the move picker tells the two "Drafts" apart and moving via "Drafts - Gmail" lands in[Gmail]/Drafts; the app-bar title keeps the disambiguated label after selecting the folder.FolderDrawerTest(UI): renders the exact transient frame (drawerAccount = Outlook, folders = Gmail's) and asserts "Drafts - Gmail" shows and "Drafts - Outlook" never does.Deliberately not done here (queued as their own tickets): #65 role-derivation table, #66 hierarchy-delimiter persistence, #67 memoization, #68 strings.xml patterns, #69 provider-brand host matching.
Closes #59
Closes #60
Closes #61
🤖 Generated with Claude Code