[client/ui] Show active profile name and account email in tray menu

The Profiles submenu label now reflects the active profile name instead
of the static "Profiles" text. A disabled email item appears directly
below it in the main menu, matching the legacy Fyne/systray behaviour.

Email is read from the per-profile state file via profilemanager in the
UI process — not through the daemon RPC — because the daemon runs as
root and its getConfigDir() resolves to the root home directory, making
the user-owned state file inaccessible from the daemon side.
This commit is contained in:
Zoltan Papp
2026-05-13 13:07:36 +02:00
parent 74ea03da9b
commit 5efdac11b0
3 changed files with 60 additions and 2 deletions

View File

@@ -128,7 +128,9 @@ type Tray struct {
downItem *application.MenuItem
exitNodeItem *application.MenuItem
networksItem *application.MenuItem
profileSubmenu *application.Menu
profileSubmenu *application.Menu
profileSubmenuItem *application.MenuItem
profileEmailItem *application.MenuItem
settingsItem *application.MenuItem
debugItem *application.MenuItem
updateItem *application.MenuItem
@@ -220,6 +222,16 @@ func (t *Tray) buildMenu() *application.Menu {
// has started — Menu.Update() is a no-op before app.running is true,
// so the initial fill is gated on the ApplicationStarted hook.
t.profileSubmenu = menu.AddSubmenu(menuProfiles)
// profileSubmenuItem is the parent MenuItem whose label is the active
// profile name. AddSubmenu returns the child *Menu, so we retrieve the
// parent *MenuItem via FindByLabel immediately after insertion.
t.profileSubmenuItem = menu.FindByLabel(menuProfiles)
// profileEmailItem shows the account email of the active profile directly
// in the main menu, below the Profiles submenu — matching the behaviour of
// the legacy Fyne/systray UI. It is hidden until loadProfiles resolves a
// non-empty email for the active profile.
t.profileEmailItem = menu.Add("").SetEnabled(false)
t.profileEmailItem.SetHidden(true)
menu.AddSeparator()
// Only the action that applies to the current state is visible: Connect
// when disconnected, Disconnect when connected. applyStatus swaps them on
@@ -736,6 +748,7 @@ func (t *Tray) loadProfiles() {
log.Infof("tray loadProfiles: received %d profile(s) for user %q", len(profiles), username)
t.profileSubmenu.Clear()
var activeName, activeEmail string
for _, p := range profiles {
name := p.Name
active := p.IsActive
@@ -748,6 +761,21 @@ func (t *Tray) loadProfiles() {
}
t.switchProfile(name)
})
if active {
activeName = name
activeEmail = p.Email
}
}
if t.profileSubmenuItem != nil && activeName != "" {
t.profileSubmenuItem.SetLabel(activeName)
}
if t.profileEmailItem != nil {
if activeEmail != "" {
t.profileEmailItem.SetLabel(fmt.Sprintf("(%s)", activeEmail))
t.profileEmailItem.SetHidden(false)
} else {
t.profileEmailItem.SetHidden(true)
}
}
// Wails v3 alpha's submenu.Update() builds a fresh, detached NSMenu on
// darwin that never replaces the empty NSMenu attached to the parent