client/systray: don't repeat account name for single-user tailnets (#19930)
Single-user tailnets often have the same tailnet display name as login name. This change omits the duplication when matching, and skips the user-switching submenu when only one account is configured, to clean up the account display a little bit. Fixes #16889 Signed-off-by: Evan Lowry <evan@tailscale.com>
This commit is contained in:
+35
-22
@@ -292,21 +292,23 @@ func (menu *Menu) rebuild() {
|
||||
accounts := systray.AddMenuItem(account, "")
|
||||
setRemoteIcon(accounts, menu.curProfile.UserProfile.ProfilePicURL)
|
||||
time.Sleep(newMenuDelay)
|
||||
for _, profile := range menu.allProfiles {
|
||||
title := profileTitle(profile)
|
||||
var item *systray.MenuItem
|
||||
if profile.ID == menu.curProfile.ID {
|
||||
item = accounts.AddSubMenuItemCheckbox(title, "", true)
|
||||
} else {
|
||||
item = accounts.AddSubMenuItem(title, "")
|
||||
}
|
||||
setRemoteIcon(item, profile.UserProfile.ProfilePicURL)
|
||||
onClick(ctx, item, func(ctx context.Context) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case menu.accountsCh <- profile.ID:
|
||||
if len(menu.allProfiles) > 1 {
|
||||
for _, profile := range menu.allProfiles {
|
||||
title := profileTitle(profile)
|
||||
var item *systray.MenuItem
|
||||
if profile.ID == menu.curProfile.ID {
|
||||
item = accounts.AddSubMenuItemCheckbox(title, "", true)
|
||||
} else {
|
||||
item = accounts.AddSubMenuItem(title, "")
|
||||
}
|
||||
})
|
||||
setRemoteIcon(item, profile.UserProfile.ProfilePicURL)
|
||||
onClick(ctx, item, func(ctx context.Context) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case menu.accountsCh <- profile.ID:
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,16 +354,27 @@ func (menu *Menu) rebuild() {
|
||||
|
||||
// profileTitle returns the title string for a profile menu item.
|
||||
func profileTitle(profile ipn.LoginProfile) string {
|
||||
title := profile.Name
|
||||
tailnet := ""
|
||||
if profile.NetworkProfile.DomainName != "" {
|
||||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||||
// windows and mac don't support multi-line menu
|
||||
title += " (" + profile.NetworkProfile.DisplayNameOrDefault() + ")"
|
||||
} else {
|
||||
title += "\n" + profile.NetworkProfile.DisplayNameOrDefault()
|
||||
}
|
||||
tailnet = profile.NetworkProfile.DisplayNameOrDefault()
|
||||
}
|
||||
return title
|
||||
// windows and mac don't support multi-line menu items.
|
||||
multiline := runtime.GOOS != "windows" && runtime.GOOS != "darwin"
|
||||
|
||||
return formatProfileTitle(profile.Name, tailnet, multiline)
|
||||
}
|
||||
|
||||
// formatProfileTitle builds a profile menu label from a login name and an
|
||||
// optional tailnet name. The tailnet portion is omitted when it matches the
|
||||
// login name, so single-user tailnets don't show the same string twice.
|
||||
func formatProfileTitle(name, tailnet string, multiline bool) string {
|
||||
if tailnet == "" || strings.EqualFold(name, tailnet) {
|
||||
return name
|
||||
}
|
||||
if multiline {
|
||||
return name + "\n" + tailnet
|
||||
}
|
||||
return name + " (" + tailnet + ")"
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -13,6 +13,33 @@ import (
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
func TestProfileTitleMultiline(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
login string
|
||||
tailnet string
|
||||
multiline bool
|
||||
want string
|
||||
}{
|
||||
{"no_tailnet", "alice@example.com", "", true, "alice@example.com"},
|
||||
{"dup_exact", "example.com", "example.com", true, "example.com"},
|
||||
{"dup_casefold", "Example.com", "example.com", false, "Example.com"},
|
||||
{"distinct_multiline", "alice@example.com", "example.com", true, "alice@example.com\nexample.com"},
|
||||
{"distinct_singleline", "alice@example.com", "example.com", false, "alice@example.com (example.com)"},
|
||||
{"empty", "", "", true, ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if got := formatProfileTitle(tt.login, tt.tailnet, tt.multiline); got != tt.want {
|
||||
t.Errorf("profileTitleMultiline; got %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecommendedIsActive(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user