134 lines
5.7 KiB
Swift
134 lines
5.7 KiB
Swift
import UIKit
|
|
|
|
@MainActor
|
|
final class SettingsViewController: UITableViewController {
|
|
private let context: AppContext
|
|
private let appearanceControl = UISegmentedControl(items: ["Auto", "Light", "Dark"])
|
|
private let notificationSwitch = UISwitch()
|
|
private var notificationStatus = "Managed by iOS"
|
|
|
|
init(context: AppContext) {
|
|
self.context = context
|
|
super.init(style: .insetGrouped)
|
|
title = "Settings"
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
let settings = context.core.settings()
|
|
appearanceControl.selectedSegmentIndex = Int(settings.appearance)
|
|
appearanceControl.addTarget(self, action: #selector(appearanceChanged), for: .valueChanged)
|
|
notificationSwitch.isOn = settings.notificationsEnabled
|
|
notificationSwitch.accessibilityLabel = "Background notifications"
|
|
notificationSwitch.addTarget(self, action: #selector(notificationsChanged), for: .valueChanged)
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
notificationSwitch.isOn = context.core.settings().notificationsEnabled
|
|
Task {
|
|
notificationStatus = await context.notifications.authorizationDescription()
|
|
tableView.reloadSections(IndexSet(integer: 1), with: .none)
|
|
}
|
|
}
|
|
|
|
override func numberOfSections(in tableView: UITableView) -> Int { 2 }
|
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
section == 0 ? 1 : 2
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
section == 0 ? "Appearance" : "Notifications"
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
|
if section == 0 {
|
|
return "Follow iOS automatically or choose a fixed appearance."
|
|
}
|
|
return "Gotcha checks periodically for server notifications. iOS decides when background refresh runs; delivery style, sounds, Focus, and summaries remain under your control in iOS Settings."
|
|
}
|
|
|
|
override func tableView(
|
|
_ tableView: UITableView,
|
|
cellForRowAt indexPath: IndexPath
|
|
) -> UITableViewCell {
|
|
guard indexPath.section == 0 else {
|
|
if indexPath.row == 0 {
|
|
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
|
|
var content = cell.defaultContentConfiguration()
|
|
content.text = "Background notifications"
|
|
cell.contentConfiguration = content
|
|
cell.accessoryView = notificationSwitch
|
|
cell.selectionStyle = .none
|
|
return cell
|
|
}
|
|
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
|
|
var content = cell.defaultContentConfiguration()
|
|
content.text = "Notification Settings"
|
|
content.secondaryText = notificationStatus
|
|
cell.contentConfiguration = content
|
|
cell.accessoryType = .disclosureIndicator
|
|
return cell
|
|
}
|
|
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
|
|
appearanceControl.translatesAutoresizingMaskIntoConstraints = false
|
|
cell.contentView.addSubview(appearanceControl)
|
|
NSLayoutConstraint.activate([
|
|
appearanceControl.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 16),
|
|
appearanceControl.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -16),
|
|
appearanceControl.topAnchor.constraint(equalTo: cell.contentView.topAnchor, constant: 8),
|
|
appearanceControl.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -8),
|
|
])
|
|
return cell
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
tableView.deselectRow(at: indexPath, animated: true)
|
|
guard indexPath.section == 1, indexPath.row == 1 else { return }
|
|
context.notifications.openSystemSettings()
|
|
}
|
|
|
|
@objc private func appearanceChanged() {
|
|
do {
|
|
try context.core.setAppearance(index: UInt32(appearanceControl.selectedSegmentIndex))
|
|
context.applyAppearance()
|
|
} catch {
|
|
show(error: error)
|
|
}
|
|
}
|
|
|
|
@objc private func notificationsChanged() {
|
|
let requested = notificationSwitch.isOn
|
|
notificationSwitch.isEnabled = false
|
|
Task {
|
|
do {
|
|
let enabled = try await context.notifications.setEnabled(requested)
|
|
notificationSwitch.isOn = enabled
|
|
if requested && !enabled { showNotificationsDisabledAlert() }
|
|
} catch {
|
|
notificationSwitch.isOn = context.core.settings().notificationsEnabled
|
|
show(error: error)
|
|
}
|
|
notificationSwitch.isEnabled = true
|
|
notificationStatus = await context.notifications.authorizationDescription()
|
|
tableView.reloadSections(IndexSet(integer: 1), with: .none)
|
|
}
|
|
}
|
|
|
|
private func showNotificationsDisabledAlert() {
|
|
let alert = UIAlertController(
|
|
title: "Notifications Are Disabled",
|
|
message: "Allow notifications in iOS Settings, then turn Background notifications on again.",
|
|
preferredStyle: .alert
|
|
)
|
|
alert.addAction(UIAlertAction(title: "Not Now", style: .cancel))
|
|
alert.addAction(UIAlertAction(title: "Open Settings", style: .default) { [weak self] _ in
|
|
self?.context.notifications.openSystemSettings()
|
|
})
|
|
present(alert, animated: true)
|
|
}
|
|
}
|