ios: drag to reorder servers in the drawer (#241)
The server drawer's rows now live in a List with .onMove, so a long-press drag reorders them; AppState persists the new order to the Keychain the same way add/remove already do. The array order was already the display order, so saving the shuffle is the whole change. Fixes #223
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
3 files changed,
+19
−6
changelog/2026-07-06-ios-reorder-servers.md+1 −0ios/LChat/LChatApp.swift+7 −0ios/LChat/Views/InstanceSidebar.swift+11 −6
diff --git a/changelog/2026-07-06-ios-reorder-servers.md b/changelog/2026-07-06-ios-reorder-servers.md| new file mode 100644 |
| index 0000000..172c641 |
| --- /dev/null |
| +++ b/changelog/2026-07-06-ios-reorder-servers.md |
| @@ -0,0 +1 @@ |
| +🤖 On iOS you can now reorder your servers: open the server drawer, press and hold a server, and drag it to where you want it. The order sticks. |
diff --git a/ios/LChat/LChatApp.swift b/ios/LChat/LChatApp.swift| index 8fa74c0..244b7cd 100644 |
| --- a/ios/LChat/LChatApp.swift |
| +++ b/ios/LChat/LChatApp.swift |
| @@ -61,6 +61,13 @@ final class AppState: ObservableObject { |
| InstancesStore.save(instances) |
| } |
| + /// Reorder the server list (drag in the drawer). The array order IS the |
| + /// display order, so persisting it is all there is to it. |
| + func move(from source: IndexSet, to destination: Int) { |
| + instances.move(fromOffsets: source, toOffset: destination) |
| + InstancesStore.save(instances) |
| + } |
| + |
| /// Persist resolved fields discovered after first load (our user id, group name). |
| func note(id: String, myUserId: String? = nil, groupName: String? = nil) { |
| guard let i = instances.firstIndex(where: { $0.id == id }) else { return } |
diff --git a/ios/LChat/Views/InstanceSidebar.swift b/ios/LChat/Views/InstanceSidebar.swift| index 30f49e2..8587eaa 100644 |
| --- a/ios/LChat/Views/InstanceSidebar.swift |
| +++ b/ios/LChat/Views/InstanceSidebar.swift |
| @@ -18,14 +18,19 @@ struct ServerDrawer: View { |
| .padding(.top, 18) |
| .padding(.bottom, 10) |
| - ScrollView { |
| - VStack(spacing: 2) { |
| - ForEach(app.instances) { inst in |
| - row(inst) |
| - } |
| + // A List (not a ScrollView) so rows can be reordered: .onMove gives the |
| + // standard long-press-and-drag, and the new order persists via AppState. |
| + List { |
| + ForEach(app.instances) { inst in |
| + row(inst) |
| + .listRowSeparator(.hidden) |
| + .listRowBackground(Color.clear) |
| + .listRowInsets(EdgeInsets(top: 1, leading: 10, bottom: 1, trailing: 10)) |
| } |
| - .padding(.horizontal, 10) |
| + .onMove { app.move(from: $0, to: $1) } |
| } |
| + .listStyle(.plain) |
| + .scrollContentBackground(.hidden) |
| Divider() |