Files
Gotcha/ios/Sources/NavigationSettingsViewController.swift
2026-08-15 20:18:51 +02:00

131 lines
4.6 KiB
Swift

import UIKit
@MainActor
final class NavigationSettingsViewController: UITableViewController {
private let context: AppContext
private var selected: [PrimaryDestination]
init(context: AppContext) {
self.context = context
selected = context.primaryDestinations
super.init(style: .insetGrouped)
title = "Primary Navigation"
navigationItem.rightBarButtonItem = editButtonItem
}
@available(*, unavailable)
required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }
private var available: [PrimaryDestination] {
PrimaryDestination.allCases.filter { !selected.contains($0) }
}
override func numberOfSections(in tableView: UITableView) -> Int { 2 }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
section == 0 ? selected.count : available.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
section == 0 ? "Shown After Home" : "Available Destinations"
}
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
section == 0
? "Tap Edit to add, remove, or reorder up to four destinations. Home is always first."
: "Destinations not in the tab bar remain available as buttons on Home."
}
override func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
let destination = indexPath.section == 0 ? selected[indexPath.row] : available[indexPath.row]
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
var content = cell.defaultContentConfiguration()
content.text = destination.title
content.image = UIImage(systemName: destination.symbolName)
content.imageProperties.tintColor = .tintColor
if indexPath.section == 1 && selected.count == 4 {
content.textProperties.color = .secondaryLabel
content.imageProperties.tintColor = .tertiaryLabel
}
cell.selectionStyle = .none
cell.contentConfiguration = content
return cell
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
tableView.isEditing && (indexPath.section == 0 || selected.count < 4)
}
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
tableView.isEditing && indexPath.section == 0
}
override func tableView(
_ tableView: UITableView,
moveRowAt sourceIndexPath: IndexPath,
to destinationIndexPath: IndexPath
) {
guard sourceIndexPath.section == 0, destinationIndexPath.section == 0 else {
tableView.reloadData()
return
}
let destination = selected.remove(at: sourceIndexPath.row)
selected.insert(destination, at: destinationIndexPath.row)
save()
}
override func tableView(
_ tableView: UITableView,
targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath,
toProposedIndexPath proposedDestinationIndexPath: IndexPath
) -> IndexPath {
guard proposedDestinationIndexPath.section == 0 else {
return IndexPath(row: max(selected.count - 1, 0), section: 0)
}
return proposedDestinationIndexPath
}
override func tableView(
_ tableView: UITableView,
editingStyleForRowAt indexPath: IndexPath
) -> UITableViewCell.EditingStyle {
if indexPath.section == 0 { return .delete }
return selected.count < 4 ? .insert : .none
}
override func tableView(
_ tableView: UITableView,
commit editingStyle: UITableViewCell.EditingStyle,
forRowAt indexPath: IndexPath
) {
switch (editingStyle, indexPath.section) {
case (.delete, 0):
selected.remove(at: indexPath.row)
case (.insert, 1) where selected.count < 4:
selected.append(available[indexPath.row])
default:
return
}
save()
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
private func save() {
do {
try context.core.setPrimaryDestinations(destinations: selected)
context.applyPrimaryDestinations()
} catch {
selected = context.primaryDestinations
tableView.reloadData()
show(error: error)
}
}
}