eleven / ocho Public

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>
James Adam b6459d73916eb69721334ee9da7bf956512d0ee5 parent f5b3ec3
3 files changed, +19 −6
  • changelog/2026-07-06-ios-reorder-servers.md +1 −0
  • ios/LChat/LChatApp.swift +7 −0
  • ios/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()