mac: members sheet — don't offer un-accepted invitees; Mac-assed add button (#355)
Two fixes to "View members": - The ADD PEOPLE list offered members who hadn't accepted their invite yet. Adding someone wraps the thread key to their public key, and a pending invitee has none — the web filters these out; now the native sheet does too. - The "Add N to group" button was an iOS-style full-width bar in a hard-coded classic-light purple (wrong in dark mode and under other themes). Now a compact, bold push button at the sheet's bottom-right (the dialog idiom, à la Ivory's Post), default-action key equivalent, tinted with the user's actual theme accent resolved for light/dark. Invite + Rename sheets get the same resolved tint.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 file changed,
+48
−20
ios/LChatMac/GroupSheets.swift+48 −20
diff --git a/ios/LChatMac/GroupSheets.swift b/ios/LChatMac/GroupSheets.swift| index a7ba13b..7dfe6c6 100644 |
| --- a/ios/LChatMac/GroupSheets.swift |
| +++ b/ios/LChatMac/GroupSheets.swift |
| @@ -18,7 +18,12 @@ struct MembersSheet: View { |
| thread.members.compactMap { id in store.roster.first { $0.user_id == id } } |
| } |
| private var others: [RosterEntry] { |
| - store.roster.filter { !thread.members.contains($0.user_id) && !$0.label.isEmpty } |
| + // Only people who have ACCEPTED their invite: adding someone wraps the |
| + // thread key to their public key, so an entry without one (invite minted, |
| + // not yet redeemed) can't be added — the web applies the same filter. |
| + store.roster.filter { |
| + !thread.members.contains($0.user_id) && !$0.label.isEmpty && !$0.public_key.isEmpty |
| + } |
| } |
| var body: some View { |
| @@ -49,31 +54,44 @@ struct MembersSheet: View { |
| Text(note).font(.system(size: 12)).foregroundStyle(.secondary) |
| } |
| if !selected.isEmpty { |
| - Button { |
| - busy = true |
| - Task { |
| - let added = await store.addMembers(thread, userIDs: Array(selected)) |
| - busy = false |
| - selected = [] |
| - note = added.isEmpty ? "Couldn't add anyone — try again." |
| - : "Added \(added.count)." |
| - } |
| - } label: { |
| - if busy { |
| - ProgressView().controlSize(.small).frame(maxWidth: .infinity) |
| - } else { |
| - Text("Add \(selected.count) to group").frame(maxWidth: .infinity) |
| + // Mac-assed action placement: a compact, bold push button at the |
| + // sheet's bottom-right (dialog idiom), not an iOS full-width bar. |
| + HStack { |
| + Spacer() |
| + Button { |
| + busy = true |
| + Task { |
| + let added = await store.addMembers(thread, userIDs: Array(selected)) |
| + busy = false |
| + selected = [] |
| + note = added.isEmpty ? "Couldn't add anyone — try again." |
| + : "Added \(added.count)." |
| + } |
| + } label: { |
| + if busy { |
| + ProgressView().controlSize(.small) |
| + } else { |
| + Text(selected.count == 1 ? "Add to Group" : "Add \(selected.count) to Group") |
| + .fontWeight(.semibold) |
| + } |
| } |
| + .buttonStyle(.borderedProminent) |
| + .keyboardShortcut(.defaultAction) |
| + .disabled(busy) |
| } |
| - .buttonStyle(.borderedProminent) |
| - .disabled(busy) |
| } |
| } |
| .padding(14) |
| .frame(width: 300) |
| - .tint(ChatTheme.classic.accent.light) |
| + .tint(accent) |
| } |
| + // The user's chosen theme accent, resolved for light/dark — the hard-coded |
| + // classic-light purple read as a random colour in dark mode / other themes. |
| + @AppStorage("nativeTheme") private var themeName = "classic" |
| + @Environment(\.colorScheme) private var scheme |
| + private var accent: Color { ChatTheme.named(themeName).accent.resolved(scheme) } |
| + |
| private func row(_ person: RosterEntry, selectable: Bool) -> some View { |
| Button { |
| guard selectable else { return } |
| @@ -166,9 +184,14 @@ struct InviteSomeoneSheet: View { |
| } |
| .padding(14) |
| .frame(width: 340) |
| - .tint(ChatTheme.classic.accent.light) |
| + .tint(accent) |
| } |
| + // Theme accent resolved for light/dark (see MembersSheet.accent). |
| + @AppStorage("nativeTheme") private var themeName = "classic" |
| + @Environment(\.colorScheme) private var scheme |
| + private var accent: Color { ChatTheme.named(themeName).accent.resolved(scheme) } |
| + |
| private func create() { |
| let trimmed = name.trimmingCharacters(in: .whitespaces) |
| guard !trimmed.isEmpty, !busy else { return } |
| @@ -209,9 +232,14 @@ struct RenameGroupSheet: View { |
| .padding(14) |
| .frame(width: 300) |
| .onAppear { name = thread.title } |
| - .tint(ChatTheme.classic.accent.light) |
| + .tint(accent) |
| } |
| + // Theme accent resolved for light/dark (see MembersSheet.accent). |
| + @AppStorage("nativeTheme") private var themeName = "classic" |
| + @Environment(\.colorScheme) private var scheme |
| + private var accent: Color { ChatTheme.named(themeName).accent.resolved(scheme) } |
| + |
| private func save() { |
| let trimmed = name.trimmingCharacters(in: .whitespaces) |
| guard !trimmed.isEmpty else { return } |