iOS drawer: flat reorderable calendars, long-press menu, left-swipe close, header actions

Testing feedback:
- calendars are now one flat, drag-reorderable list (no per-source grouping);
  order is device-local (CalendarStore.calendarOrder, mirrors the web cal_order).
  Reordering is done via a 'Sort' toggle (native edit mode with drag handles).
- banish + reminder-mute moved from row swipe actions to a long-press context
  menu, which frees the horizontal swipe: swiping the drawer left closes it.
- the hamburger moved to the LEFT (drawer opens left); new device-local setting
  'Hide menu button' (open via edge-swipe only), not synced.
- the settings entry moved from the footer into the header as an icon, next to a
  restored manual-sync icon and the close button; the footer button is gone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-15 16:19:29 +02:00
parent daf0345120
commit 6881c68935
6 changed files with 174 additions and 141 deletions

View File

@@ -165,6 +165,49 @@ class CalendarStore {
return "\(source):\(id)"
}
// MARK: Calendar order (device-local, mirrors the web `cal_order`)
private static let orderDefaultsKey = "calendarOrder"
/// Persisted display order of calendar keys ("source:id"). Device-local; the
/// drawer's flat calendar list is sorted by this.
private(set) var calendarOrder: [String] = CalendarStore.loadOrder()
private static func loadOrder() -> [String] {
guard let raw = UserDefaults.standard.string(forKey: orderDefaultsKey),
let data = raw.data(using: .utf8),
let arr = try? JSONDecoder().decode([String].self, from: data)
else { return [] }
return arr
}
private func saveOrder() {
if let data = try? JSONEncoder().encode(calendarOrder),
let s = String(data: data, encoding: .utf8) {
UserDefaults.standard.set(s, forKey: Self.orderDefaultsKey)
}
}
/// Replace the stored order (after a drag-reorder).
func setCalendarOrder(_ keys: [String]) {
calendarOrder = keys
saveOrder()
}
/// Sort the given keys by the stored order (unknown keys end), then persist
/// the normalized order so newly-added calendars stick (mirrors the web).
func ordered(_ keys: [String]) -> [String] {
let current = calendarOrder
func idx(_ key: String) -> Int { current.firstIndex(of: key) ?? Int.max }
let sorted = keys.sorted { a, b in
let ia = idx(a), ib = idx(b)
return ia != ib ? ia < ib : a < b
}
calendarOrder = sorted
saveOrder()
return sorted
}
// MARK: Banished-calendar persistence
private static let banishedKeysDefaultsKey = "banishedCalendarKeys"