Repository navigation
feat(nav): nest the account rows under a Profile group in the sidebar (G31) - #94
Merged
Merged
Conversation
The sidebar was ten flat peers with no account grouping, where the web keeps Settings and Integrations inside the profile menu. My Profile, Organizations, Connections, Settings and Integrations now live under a Profile disclosure group; the seven reading surfaces stay top level, because grouping Messages or Lists under an account menu would be the web's information architecture applied where it does not fit. Settings stays its own window. The sidebar rows use SettingsLink, so they are an additional entrance to the same scene rather than a relocation of the panes into the detail column — macOS convention is that Settings is a window, and re-homing it would break both that expectation and the keyboard shortcut people already have. SettingsLink opens the window but cannot address a tab, so SettingsRootView's TabView gains a selection bound to @AppStorage and the sidebar row stores its target before opening. The Settings row targets Preferences explicitly rather than whatever was open last: a row labelled Settings that opens Crash Reporting reads as a bug. SettingsTab's raw values are persisted, so a rename would silently send an existing user to a different pane — there is a test pinning them. The requirement with teeth was that a deep link into a collapsed group must not leave the user staring at nothing. All thirteen `selection = …` assignments — menu commands, notification routing, search routing, share-link resolution — now go through one `select(_:)` that opens the group first when the target is inside it. Expanding is one-way: a deep link to a top-level row does not collapse the group, because the user's choice to keep it open is not a menu command's to undo. One `profileGroupSections` list feeds both the group's contents and that function, so a row cannot move into the group without deep links learning about it. The expanded state persists and defaults to open: the three rows that moved in were top-level yesterday, and collapsing them by default would look like they had been removed. Refs #45 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016gSWb3scYobtxLJioV1qF9
Adron
added a commit
that referenced
this pull request
Sep 17, 2026
Four files conflicted. Three were textual overlaps where both sides add distinct members; the fourth needed more than a mechanical resolution. AppTests/Support/StubUserService.swift was the only file git could not merge itself. Both sides add members to the same type — this branch's profile-settings stubs and #93's identity stubs — so the resolution is to keep both. Rebuilt from dev's version with this branch's four additions reapplied, rather than by concatenating the conflict hunks: the hunk boundaries crossed a closing brace, so a naive keep-both dropped the `}` of `enqueueRequestPasswordReset(failure:)` and nested the rest of the type inside it. It compiled far enough to look plausible and failed with "Attribute 'private' can only be used in a non-local scope" 300 lines later. UserDTO.swift, UserService.swift and SettingsRootView.swift auto-merged, and the first two are genuinely fine — both sides only added. SettingsRootView's auto-merge was textually clean and semantically wrong, which is the interesting part of this merge. PR #94 introduced `SettingsTab` and converted the pane into a `TabView(selection:)`, tagging every tab it knew about. It was written against a dev that predated both the Integrations tab (#93) and the Profile tab (this branch), so neither got a tag. Git had no reason to flag that — no line conflicts — but under `TabView(selection:)` an untagged tab cannot be selected programmatically at all. Both are tagged here, and the enum gains the two cases. The same gap had a second consequence: the sidebar's Integrations row set `settingsTab = .linkedAccounts`, which was the honest best available when no Integrations tab existed and is simply wrong now that one does. A row labelled Integrations opening Linked accounts is the kind of thing that survives review because it compiles. Retargeted. The tab-count assertion becomes a full set. A count was already there and did not catch this — the count was right, the tabs were unreachable — so it now pins the identities, which at least fails loudly when a pane arrives without one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #45 (G31). Sequenced after #44 (My Profile) and #47 (Integrations), as the issue asked, so the group has no dead rows.
The sidebar was ten flat peers with no account grouping, where the web keeps Settings and Integrations inside the profile menu.
The group
Profile ▸ My Profile · Organizations · Connections · Settings · Integrations.
The seven reading surfaces stay top level. Grouping Messages or Lists under an account menu would be the web's information architecture applied where it does not fit.
The HIG constraint, honoured
⌘,and File ▸ Settings are untouched. The sidebar rows useSettingsLink— an additional entrance to the same scene, three doors into one window, never a relocation of the panes into the detail column.Landing on the right tab
SettingsLinkopens the Settings window but cannot address a tab. SoSettingsRootView'sTabViewgains a selection bound to@AppStorage, and the sidebar row stores its target before the link opens the scene.Two things follow:
SettingsTab's raw values are persisted, so renaming a case would silently send an existing user to a different pane on next launch. There is a test pinning them.The requirement with teeth
All thirteen
selection = …assignments inMainWindowView— menu commands, notification routing, search routing, share-link resolution — now go through oneselect(_:)that opens the group first when the target lives inside it.Expanding is deliberately one-way: a deep link to a top-level row does not collapse the group. The user's choice to keep it open is not a menu command's to undo.
MainWindowView.profileGroupSectionsis the single list that feeds both the group's contents andselect(_:), so a row cannot move into the group without deep links learning about it.Persistence
@AppStorage("sidebar.profileGroup.expanded"), defaulting to expanded — the three rows that moved in were top-level yesterday, and collapsing them by default would look like they had been removed.Tests
Per the project's view-layer rule the SwiftUI tree is not rendered. What is tested is the part with real logic and a real failure mode: the group's membership, that the partition orphans no section, and that the persisted
SettingsTabidentities round-trip.Verification
xcodebuild build→** BUILD SUCCEEDED **xcodebuild test(App) →Executed 973 tests, with 0 failures·** TEST SUCCEEDED **(was 968)swift test InterlinedDomain→Executed 1012 tests, with 0 failuresswift test InterlinedPersistence→Executed 140 tests, with 0 failuresAcceptance
⌘,and File ▸ Settings behave identically to today.⌘,.Note on stacking
Branches from
dev. The Integrations tab it links to arrives with #93 — until that merges, the Integrations row opens Settings on Linked accounts, which is the honest fallback rather than a dead tab.🤖 Generated with Claude Code
https://claude.ai/code/session_016gSWb3scYobtxLJioV1qF9