diff --git a/Yattee/Models/Instance.swift b/Yattee/Models/Instance.swift index 1b64ccc6..1087b4df 100644 --- a/Yattee/Models/Instance.swift +++ b/Yattee/Models/Instance.swift @@ -142,6 +142,13 @@ struct Instance: Identifiable, Codable, Hashable, Sendable { name ?? url.host ?? url.absoluteString } + /// The shortest thing that names this source to the user: its title when + /// one is set, else its host. What account labels append after the `@`. + var shortLabel: String? { + if let name, !name.isEmpty { return name } + return url.host + } + /// Returns the URL string with embedded credentials stripped for safe display in the UI. var displayURL: String { guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false), diff --git a/Yattee/Services/Subscriptions/DataContextValidator.swift b/Yattee/Services/Subscriptions/DataContextValidator.swift index c6707374..3085fbc0 100644 --- a/Yattee/Services/Subscriptions/DataContextValidator.swift +++ b/Yattee/Services/Subscriptions/DataContextValidator.swift @@ -260,6 +260,10 @@ final class DataContextValidator { /// What the account is called with no user-set label: the stored login /// identifier, else the source's name, else its host. /// + /// After the `@` comes the source's *title* when it has one — the host is + /// the fallback, not the name: `arek@nerdvpn.de` reads, while + /// `arek@invidious.nerdvpn.de` overflows every menu row it appears in. + /// /// Device-local by nature. The username lives in the Keychain beside the /// credential rather than in synced metadata, so it is only available on a /// device that is signed in — two devices holding the very same account can @@ -269,15 +273,14 @@ final class DataContextValidator { .accountCredentialsManager(for: account)? .username(for: account), !username.isEmpty { - if let host = accountsManager.instance(for: account)?.url.host { - return "\(username)@\(host)" + if let label = accountsManager.instance(for: account)?.shortLabel { + return "\(username)@\(label)" } return username } if let instance = accountsManager.instance(for: account) { - if let name = instance.name, !name.isEmpty { return name } - return instance.url.host ?? instance.type.displayName + return instance.shortLabel ?? instance.type.displayName } return String(localized: "accounts.status.instanceMissing") diff --git a/Yattee/Views/Components/DataContextSwitcherMenu.swift b/Yattee/Views/Components/DataContextSwitcherMenu.swift index fc768106..06d9833c 100644 --- a/Yattee/Views/Components/DataContextSwitcherMenu.swift +++ b/Yattee/Views/Components/DataContextSwitcherMenu.swift @@ -153,11 +153,12 @@ enum DataContextSwitcherLabels { /// /// The identifier comes first: two logins on the *same* instance share a /// host, so a host suffix would repeat the server and still leave the user - /// choosing between two identical rows. The host alone is what is left when - /// the identifier is not on this device — tvOS never receives one. Neither - /// is repeated when the name already carries it: the default name for an - /// account *is* `identifier@host`, so appending it unguarded produced two - /// rows reading `X · X`. + /// choosing between two identical rows. The host — the source's title when + /// it has one — alone is what is left when the identifier is not on this + /// device — tvOS never receives one. Neither is repeated when the name + /// already carries it: the default name for an account *is* + /// `identifier@host`, so appending it unguarded produced two rows reading + /// `X · X`. /// /// The source is the last resort and the only one that can name what two /// logins of the same server user on two copies of the same server differ @@ -197,11 +198,15 @@ enum DataContextSwitcherLabels { accountsManager: AccountsManager, username: (Account) -> String? ) -> String { + // The same short label the default account name is built from — the + // source's title, host only as the fallback — or a name that already + // reads `user@Title` would fail the "already carries it" guard and + // grow a second, longer `user@full.host` suffix. func withoutSource(_ peer: Account) -> String { disambiguated( name, username: username(peer), - host: accountsManager.instance(for: peer)?.url.host + host: accountsManager.instance(for: peer)?.shortLabel ) } @@ -212,7 +217,7 @@ enum DataContextSwitcherLabels { return disambiguated( name, username: username(account), - host: accountsManager.instance(for: account)?.url.host, + host: accountsManager.instance(for: account)?.shortLabel, source: accountsManager.instance(for: account)?.displayName ) }