Native lists: unnamed servers show their host, not "lchat" (#484)
* core: unnamed servers show their host, never the 'lchat' placeholder The server's default group name for a never-named instance is 'lchat', and it leaked into every native list (rail menu, drawer, palette). LChatAPI.groupName/serverInfo now return "" for it so callers fall back to the host, and SavedInstance.displayName treats a cached 'lchat' groupName the same way (older vault entries carry it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * core: filter the never-named default on the server's name_set flag The literal-'lchat' check erased a legitimately chosen name; /api/group already sends name_set (admin-chosen vs compiled-in default), so that's the authority — the literal check remains only for pre-name_set servers. displayName drops its own literal guard: the API layer is the one filter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> ---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2 files changed,
+24
−9
ios/LChatCore/Sources/LChatCore/LChatAPI.swift+19 −7ios/LChatCore/Sources/LChatCore/Models.swift+5 −2
diff --git a/ios/LChatCore/Sources/LChatCore/LChatAPI.swift b/ios/LChatCore/Sources/LChatCore/LChatAPI.swift| index c77819b..a36a66f 100644 |
| --- a/ios/LChatCore/Sources/LChatCore/LChatAPI.swift |
| +++ b/ios/LChatCore/Sources/LChatCore/LChatAPI.swift |
| @@ -184,18 +184,30 @@ public struct LChatAPI { |
| description: r.description ?? "", imageURL: URL(string: r.image ?? "")) |
| } |
| - /// The instance's display name (public; substituted into pages too). |
| + /// The instance's display name, or "" when it was never named, so callers |
| + /// fall back to the host instead of parroting the compiled-in default. |
| + /// The server's `name_set` flag is the authority (an admin-chosen name — |
| + /// even literally "lchat" — always shows); the literal check is only the |
| + /// degraded path for older servers that don't send the flag. |
| public func groupName() async -> String { |
| - struct Resp: Codable { let name: String? } |
| - return (try? await getJSON("/api/group", Resp.self, authed: false))?.name ?? "lchat" |
| + struct Resp: Codable { let name: String?; let name_set: Bool? } |
| + guard let r = try? await getJSON("/api/group", Resp.self, authed: false) else { return "" } |
| + return Self.chosenName(r.name, nameSet: r.name_set) |
| } |
| - /// Public per-instance branding: the display name plus whether a server photo |
| - /// is set. Both are public (shown during add-a-chat, before any key exists). |
| + /// Public per-instance branding: the display name ("" when never named — |
| + /// see groupName) plus whether a server photo is set. Both are public |
| + /// (shown during add-a-chat, before any key exists). |
| public func serverInfo() async -> (name: String, hasAvatar: Bool) { |
| - struct Resp: Codable { let name: String?; let avatar: Bool? } |
| + struct Resp: Codable { let name: String?; let name_set: Bool?; let avatar: Bool? } |
| let r = try? await getJSON("/api/group", Resp.self, authed: false) |
| - return (r?.name ?? "lchat", r?.avatar ?? false) |
| + return (Self.chosenName(r?.name, nameSet: r?.name_set), r?.avatar ?? false) |
| + } |
| + |
| + static func chosenName(_ name: String?, nameSet: Bool?) -> String { |
| + let n = name ?? "" |
| + if let nameSet { return nameSet ? n : "" } |
| + return n == "lchat" ? "" : n // pre-name_set servers: filter the default |
| } |
| /// The instance's public branding photo bytes, or nil if none is set. Served |
diff --git a/ios/LChatCore/Sources/LChatCore/Models.swift b/ios/LChatCore/Sources/LChatCore/Models.swift| index dcc16da..2d821f6 100644 |
| --- a/ios/LChatCore/Sources/LChatCore/Models.swift |
| +++ b/ios/LChatCore/Sources/LChatCore/Models.swift |
| @@ -45,8 +45,11 @@ public struct SavedInstance: Codable, Identifiable, Equatable, Hashable { |
| public var baseURL: URL? { URL(string: origin) } |
| - /// A human label for the switcher until the real group name loads. |
| - public var displayName: String { groupName.isEmpty ? (baseURL?.host ?? "lchat") : groupName } |
| + /// A human label for the switcher until the real group name loads. An |
| + /// unnamed server shows as its host — the API layer already maps the |
| + /// never-named default to "" (LChatAPI.chosenName), so a non-empty |
| + /// groupName here is always admin-chosen. |
| + public var displayName: String { groupName.isEmpty ? hostLabel : groupName } |
| /// The server's host (dropping a leading www.), for the switcher subtitle. |
| public var hostLabel: String { |