Skip to content

feat(nav): nest the account rows under a Profile group in the sidebar (G31) #45

Closed

Description

@Adron

Summary

On the web, Settings and Integrations live inside the profile/account menu. The native app inverts that: Settings is an unrelated ⌘, scene, "Integrations" does not exist as a concept, and the sidebar is ten flat peers with no account grouping at all.

The web's account menu (walked live 2026-09-07)

My User Page · Dashboard · Messages · My Organizations · Documents · Lists ·
Settings ▸ { Preferences, Integrations, Subscription } · Switch to ▸ · Log out

Proposal

Group the account-shaped sidebar rows under a Profile DisclosureGroup:

  • My Profile (the new own-account page)
  • Organizations (moved)
  • Connections (moved)
  • Settings
  • Integrations (the new surface)

⚠️ HIG constraint — read before writing code

The App menu's Settings… (⌘,) item and SettingsRootView must keep working exactly as they do today. macOS convention is that Settings is its own window, and re-homing the panes into the detail column would break that expectation and the keyboard shortcut users already have.

So the sidebar rows are an additional entrance that opens the Settings scene via SettingsLink — never a relocation of the panes. Same for Integrations: the row opens the Settings scene on the Integrations tab.

Division of labor

App — navigation only. No Kit or Domain changes.

  • App/Navigation: introduce the Profile disclosure group and move Organizations and Connections under it.
  • My Profile row → the own-account page.
  • Settings and Integrations rows → SettingsLink, landing on the right tab.
  • Every existing deep link that sets selection must still land, expanding the parent group automatically — a deep link into Organizations cannot leave the user staring at a collapsed group.
  • Persist the group's expanded/collapsed state across launches.
  • Decision 0003 still applies: no import InterlinedKit under App/Navigation/**.

Tests

  • happy — the group renders with all five rows, ⌘, still opens Settings
  • invalid — a deep link to a row inside a collapsed group expands it and selects the row
  • upstream-failure — n/a (no I/O); instead assert the group renders when the current user is not yet loaded
  • boundary — collapsed/expanded state survives a relaunch; selection survives a group toggle

Acceptance criteria

  • ⌘, and File ▸ Settings behave identically to today.
  • No Settings pane is reachable only from the sidebar, and none only from ⌘,.
  • Full E2E gate green.

Notes

work-consolidation.md tracks this as G31. Size S–M. Do this after the My Profile page and the Integrations surface exist, otherwise the group has two dead rows.


Implementation plan (added 2026-09-15)

Sequenced as this issue asked — after My Profile (#44) and the Integrations surface (#47), so the group has no dead rows.

The group

Profile DisclosureGroup holding My Profile · Organizations · Connections · Settings · Integrations. The three reading surfaces that moved in were previously top-level peers; the seven that stayed (Search, Timeline, Scheduled, Notifications, Messages, Lists, Documents) are reading surfaces, not account management, and grouping them would be the web's IA applied where it does not fit.

The HIG constraint, honoured

⌘, and File ▸ Settings are untouched. The sidebar rows use SettingsLink, so they are 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

SettingsLink opens the Settings window but cannot address a tab. So SettingsRootView's TabView gains a selection bound to @AppStorage("settings.selectedTab"), and the sidebar row stores its target before the link opens the scene.

Two details that follow from that:

  • 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 renaming a case would silently send an existing user to a different pane. There is a test pinning them.

Deep links expanding the group

This issue's sharpest requirement: "a deep link into Organizations cannot leave the user staring at a collapsed group."

Every selection = … in MainWindowView — thirteen of them, across the menu commands, notification routing, search routing and share-link resolution — now goes through one select(_:) 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 something a menu command should undo.

MainWindowView.profileGroupSections is the single list that both the group's contents and select(_:) read, so a row cannot be moved into the group without deep links learning about it.

Persistence

@AppStorage("sidebar.profileGroup.expanded"), defaulting to expanded: on first run the three rows that moved in were previously top-level, 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 no section is orphaned by the partition, and that the persisted SettingsTab identities round-trip.

Acceptance

  • ✅ ⌘, and File ▸ Settings behave identically to today.
  • ✅ No Settings pane is reachable only from the sidebar, and none only from ⌘,.
  • ✅ Every deep link still lands, expanding the group when needed.
  • ✅ Expanded state survives a relaunch.

Activity

  1. added
    enhancementNew feature or request
    parityWeb-parity gap with the InterlinedList web app
    on Sep 7, 2026
  2. Adron commented on Sep 16, 2026

    @Adron
    MemberAuthor

    Implemented in PR #94 (G31). Work continues on the PR.

    Sequenced after #44 and #47 as this issue asked, so the group has no dead rows.

    The HIG constraint is honoured: ⌘, and File ▸ Settings are untouched, and the sidebar rows use SettingsLink — three doors into one window, never a relocation.

    The requirement with teeth — "a deep link into Organizations cannot leave the user staring at a collapsed group" — is met by routing all thirteen selection = … assignments through one select(_:). Expanding is one-way: a deep link to a top-level row does not collapse the group.

    SettingsLink cannot address a tab, so SettingsRootView's TabView gained a persisted selection the sidebar row sets first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestparityWeb-parity gap with the InterlinedList web app

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions