authorclover caruso <git@paperclover.net> 2026-10-11 17:47:37-07:00
committerclover caruso <git@paperclover.net> 2026-10-11 17:59:58-07:00
log970fdd7440162d30295c9e624ff2d295da7869f5
tree34909c706e51b18273527a40c4b9d811c06de87f
parent7ca09704a666b9adcf7406210f5707fccce92614
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover)

fix(ios): offer only valid directional page moves

Keep expanded page moves scoped to one page, matching the OneNote movement corpus. Omit Move Down when the first page would leave a subpage leading the section. Resolve directional actions against the current section and stable page ID, and return the actual arrangement acceptance result. Fixes: #134 Assisted-by: gpt-6.1-sol

1 files changed, 25 insertions(+), 9 deletions(-)

apps/ios/Snowbound/Arranging.swift+25-9
......@@ -91,14 +91,16 @@ enum Arranging {
9191 }
9292
9393 /// Puts the pages of `section` in `order`, page `id` the one moved, as one edit.
94 static func arrange(_ section: Section, _ order: [Placed], moving id: String) {
95 guard available() else { return }
94 @discardableResult
95 static func arrange(_ section: Section, _ order: [Placed], moving id: String) -> Bool {
96 guard available() else { return false }
9697 let before = section.rows.map { Placed(id: $0.id, level: $0.level) }
97 guard order != before, section.arrange(order, moving: id) else { return }
98 guard order != before, section.arrange(order, moving: id) else { return false }
9899 let (notebook, path) = (section.notebook, section.tab.path)
99100 done("Move Page") {
100101 opening(path, of: notebook) { arrange($0, before, moving: id) }
101102 }
103 return true
102104 }
103105
104106 /// Gives page `id` of `section` `level` where it stands, as Make Subpage and Promote
......@@ -393,10 +395,13 @@ extension PagesViewController: UITableViewDragDelegate, UITableViewDropDelegate
393395 }
394396
395397 /// Page `id` moved to `index` of the section's pages.
396 private func move(_ id: String, to index: Int) {
397 guard let section else { return }
398 Arranging.arrange(section, Arranging.order(section.rows, moving: id, to: index), moving: id)
398 @discardableResult
399 private func move(_ id: String, to index: Int) -> Bool {
400 guard let section else { return false }
401 let order = Arranging.order(section.rows, moving: id, to: index)
402 guard order.first?.level == 1, Arranging.arrange(section, order, moving: id) else { return false }
399403 becomeFirstResponder()
404 return true
400405 }
401406
402407 /// Page `id` a level deeper or shallower, where it stays.
......@@ -449,15 +454,26 @@ extension PagesViewController: UITableViewDragDelegate, UITableViewDropDelegate
449454
450455 /// VoiceOver's ways to move a page without dragging it.
451456 func arrangeActions(_ item: Item) -> [UIAccessibilityCustomAction]? {
452 guard !Arranging.busy, item.version == nil, let rows = section?.rows, let at = rows.firstIndex(where: { $0.id == item.row.id })
457 guard !Arranging.busy, item.version == nil, let section,
458 let at = section.rows.firstIndex(where: { $0.id == item.row.id })
453459 else { return nil }
460 let rows = section.rows
454461 let id = item.row.id
455462 var actions: [UIAccessibilityCustomAction] = []
463 for (name, direction) in [("Move Up", -1), ("Move Down", 1)] {
464 guard rows.indices.contains(at + direction),
465 at > 0 || direction < 0 || rows[1].level == 1
466 else { continue }
467 actions.append(UIAccessibilityCustomAction(name: name) { [weak self] _ in
468 guard let self, self.section === section,
469 let at = section.rows.firstIndex(where: { $0.id == id }), section.rows.indices.contains(at + direction)
470 else { return false }
471 return move(id, to: at + direction)
472 })
473 }
456474 let add = { (name: String, act: @escaping () -> Void) in
457475 actions.append(UIAccessibilityCustomAction(name: name) { _ in act(); return true })
458476 }
459 if at > 0 { add("Move Up") { [weak self] in self?.move(id, to: at - 1) } }
460 if at < rows.count - 1 { add("Move Down") { [weak self] in self?.move(id, to: at + 1) } }
461477 if indentable(rows, at) { add("Make Subpage") { [weak self] in self?.indent(id, by: 1) } }
462478 if rows[at].level > 1 { add("Promote Subpage") { [weak self] in self?.indent(id, by: -1) } }
463479 if !moveActions(item).isEmpty {