Skip to content

fix(drawer): show Settings when the account roster is empty (#360) - #361

Merged
garfiec merged 1 commit into
developfrom
fix/settings-unreachable-empty-roster
Aug 13, 2026
Merged

fix(drawer): show Settings when the account roster is empty (#360)#361
garfiec merged 1 commit into
developfrom
fix/settings-unreachable-empty-roster

Conversation

@garfiec

@garfiec garfiec commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Closes #360.

Summary

The drawer footer rendered the Settings row and the account avatar inside a single accounts.firstOrNull { it.isActive }?.let, so an empty account roster removed both. That footer is the only route to Settings on either layout — the phone drawer and the tablet sidebar both render DrawerContent, and the chat overflow menu has no Settings item — so an empty roster leaves the server-connection screen, sign-out, and the data and diagnostic-log exports unreachable.

Why the roster can be empty while signed in

isLoggedIn is a pure token check and does not consult the roster, so the two can disagree. An iOS reinstall guarantees it: the access token and the server URL live in the Keychain and survive app deletion, while the roster lives in DataStore and is wiped. The rebuild path, AuthRepository.restoreAccountIfNeeded, needs a live getUser() — so a user left pointed at an unreachable server can reach the screen that would fix the server only by first reaching the server.

The gate was not deliberate. Settings was an ungated footer item alongside Agents, Skills and Files until #221 moved it into the footer slot to share a row with the account avatar, where it was swept under the avatar's null check. (#215 had introduced that slot and its null check a day earlier, but held only the avatar.)

Changes

  • Render the Settings footer item unconditionally, and gate only AccountChip — the sole part that needs a resolved account.
  • Make the row's trailing inset conditional on the chip being drawn, so Settings lines up with the Files and Agents rows above it when it renders alone.
  • Cosmetic side effect: during the cold-start Warming window Settings now renders immediately rather than popping in once the roster resolves.

Testing

  • :feature:conversations:detektMetadataCommonMain and :feature:conversations:testDebugUnitTest pass.
  • Not device-tested. The natural repro is an iOS reinstall against an unreachable server; it can also be forced on a debug Android build by deleting the DataStore file while leaving the token store intact.
  • No regression test added: feature/conversations has no Compose test source set, and adding one is disproportionate to the remaining branch, which AccountChip's non-null account parameter makes compiler-enforced.

Notes

Reaching Settings is necessary but not sufficient to recover from this state. Sign-out skips its local teardown while identity is Resolved(null), so the tokens survive it: AuthRepositoryImpl.logout skips accountSwitcher.remove() in the account == null branch, and that is the only thing that clears tokens and emits session-expired. The server-side revocation still fires. That, and letting restoreAccountIfNeeded fall back to an identity derived from the persisted server URL, are tracked separately on #360 and deliberately not in this PR — the latter touches tenancy scoping.

The drawer footer rendered Settings and the account avatar inside one
`accounts.firstOrNull { it.isActive }?.let`, so an empty roster removed
both. On phone the footer is the only route to Settings, which makes the
server/account screens, sign-out and the diagnostic-log export
unreachable in exactly the states that need them.

isLoggedIn is a pure token check, independent of the roster, so the two
can disagree. An iOS reinstall guarantees it: the token and server URL
live in the Keychain and survive app deletion, while the roster lives in
DataStore and is wiped. The rebuild path needs a live getUser(), so a
user pointed at an unreachable server can only reach the screen that
would fix the server by first reaching the server.

Render Settings unconditionally and gate only AccountChip, which is the
sole part that needs a resolved account. The trailing inset becomes
conditional too, so Settings lines up with the Files/Agents rows above
it when no chip is drawn.
@garfiec
garfiec merged commit 6ad5571 into develop Aug 13, 2026
5 checks passed
@garfiec
garfiec deleted the fix/settings-unreachable-empty-roster branch August 13, 2026 21:36
@github-actions

Copy link
Copy Markdown
Contributor

Android debug APK

Artifact: switchboard-android-debug-361
Download: switchboard-android-debug-361.zip
Retention: 90 days
Commit: 8d0202110b39f17943e37a1ca83fbd36cd137e93

Download requires a GitHub login. Installs over previous debug builds without uninstalling (stable signing key).

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.

Settings is unreachable when the account roster is empty

1 participant