Add first-class Actions management (#63)

This commit is contained in:
Georg Bauer
2026-08-15 20:06:52 +02:00
parent f84ab774e6
commit 59bcddfadf
32 changed files with 4039 additions and 275 deletions

View File

@@ -0,0 +1,128 @@
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 on Home"
}
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
section == 0
? "Choose up to four destinations. Tap Edit to reorder them. 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
} else if indexPath.section == 1 {
let add = UIImageView(image: UIImage(systemName: "plus.circle.fill"))
add.tintColor = .tintColor
cell.accessoryView = add
cell.accessibilityHint = "Adds this destination to the tab bar"
}
cell.contentConfiguration = content
return cell
}
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
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 {
indexPath.section == 0 ? .delete : .none
}
override func tableView(
_ tableView: UITableView,
commit editingStyle: UITableViewCell.EditingStyle,
forRowAt indexPath: IndexPath
) {
guard editingStyle == .delete, indexPath.section == 0 else { return }
selected.remove(at: indexPath.row)
save()
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
guard indexPath.section == 1, selected.count < 4 else { return }
selected.append(available[indexPath.row])
save()
tableView.reloadData()
}
private func save() {
do {
try context.core.setPrimaryDestinations(destinations: selected)
context.applyPrimaryDestinations()
} catch {
selected = context.primaryDestinations
tableView.reloadData()
show(error: error)
}
}
}