eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1import LChatCore
2import SwiftUI
3import UIKit
4
5// The full space list — everything the home screen elides: ALL active spaces
6// (favourites first, the rest below), standard List drag-to-reorder within
7// each group, and the manage context menu. Archived spaces stay hidden here
8// too (they live in the account — unarchive on the home page).
9struct SpaceListSheet: View {
10 @EnvironmentObject var app: AppState
11 @Environment(\.dismiss) private var dismiss
12 let activeID: String?
13 var onSelect: (SavedInstance) -> Void
14
15 private var active: [SavedInstance] { app.instances.filter { !$0.isArchived } }
16 private var starred: [SavedInstance] { active.filter(\.isStarred) }
17 private var others: [SavedInstance] { active.filter { !$0.isStarred } }
18
19 var body: some View {
20 NavigationStack {
21 List {
22 if starred.isEmpty {
23 Section {
24 ForEach(active) { row($0) }
25 .onMove { app.move(from: $0, to: $1) }
26 }
27 } else {
28 // Each group drags independently (home's independent lists);
29 // without these, starring ANYTHING killed reorder entirely.
30 Section("Favourites") {
31 ForEach(starred) { row($0) }
32 .onMove { app.moveInGroup(starred: true, from: $0, to: $1) }
33 }
34 if !others.isEmpty {
35 Section("More") {
36 ForEach(others) { row($0) }
37 .onMove { app.moveInGroup(starred: false, from: $0, to: $1) }
38 }
39 }
40 }
41 }
42 .navigationTitle("Spaces")
43 .navigationBarTitleDisplayMode(.inline)
44 .toolbar {
45 ToolbarItem(placement: .confirmationAction) {
46 Button("Done") { dismiss() }
47 }
48 }
49 }
50 }
51
52 private func row(_ inst: SavedInstance) -> some View {
53 Button { onSelect(inst) } label: {
54 SpaceRow(instance: inst, active: inst.id == activeID,
55 photo: app.serverAvatars[inst.id])
56 }
57 .foregroundStyle(.primary)
58 .contextMenu { SpaceMenuItems(instance: inst) }
59 }
60}
61
62// One space: avatar, name, host subtitle — shared by the home screen's lists
63// (a trailing disclosure chevron: every row navigates) and the More sheet
64// (a checkmark on the active one instead).
65struct SpaceRow: View {
66 let instance: SavedInstance
67 let active: Bool
68 var photo: UIImage?
69 var chevron = false
70
71 var body: some View {
72 HStack(spacing: 12) {
73 Avatar(name: instance.displayName, size: 36, photo: photo)
74 VStack(alignment: .leading, spacing: 2) {
75 Text(instance.displayName).font(.headline).lineLimit(1)
76 // The host, unless the title already IS the host (no group
77 // name yet) — no point showing it twice.
78 if instance.displayName != instance.hostLabel {
79 Text(instance.hostLabel).font(.caption).foregroundStyle(.secondary).lineLimit(1)
80 }
81 }
82 Spacer()
83 if chevron {
84 Image(systemName: "chevron.right")
85 .font(.footnote.weight(.semibold))
86 .foregroundStyle(.tertiary)
87 } else if active {
88 Image(systemName: "checkmark")
89 .font(.subheadline.weight(.semibold))
90 .foregroundStyle(Color.accentColor)
91 }
92 }
93 .contentShape(Rectangle())
94 }
95}
96
97// The manage menu shared by the home screen rows and the More sheet rows.
98struct SpaceMenuItems: View {
99 @EnvironmentObject var app: AppState
100 let instance: SavedInstance
101
102 var body: some View {
103 Button { app.toggleStar(instance) } label: {
104 Label(instance.isStarred ? "Unstar" : "Star",
105 systemImage: instance.isStarred ? "star.slash" : "star")
106 }
107 Button { app.toggleArchive(instance) } label: {
108 Label(instance.isArchived ? "Unarchive" : "Archive",
109 systemImage: instance.isArchived ? "tray.and.arrow.up" : "archivebox")
110 }
111 Button(role: .destructive) { app.remove(instance) } label: {
112 Label("Remove", systemImage: "trash")
113 }
114 }
115}
116