mastodon-ios/Mastodon/Scene/Settings/General Settings/GeneralSettingsViewControll...

231 lines
9.7 KiB
Swift

// Copyright © 2023 Mastodon gGmbH. All rights reserved.
import UIKit
import MastodonSDK
import MastodonCore
import CoreDataStack
import MastodonLocalization
import MastodonUI
struct GeneralSettingsViewModel {
var selectedAppearence: GeneralSetting.Appearance
var playAnimations: Bool
var selectedOpenLinks: GeneralSetting.OpenLinksIn
var askBeforePostingWithoutAltText: Bool
var askBeforeUnfollowingSomeone: Bool
var askBeforeBoostingAPost: Bool
var askBeforeDeletingAPost: Bool
var defaultPostLanguage: String
}
protocol GeneralSettingsViewControllerDelegate: AnyObject {
func save(_ viewController: UIViewController, setting: Setting, viewModel: GeneralSettingsViewModel)
func showLanguagePicker(_ viewModel: GeneralSettingsViewModel, onLanguageSelected: @escaping OnLanguageSelected)
}
class GeneralSettingsViewController: UIViewController {
weak var delegate: GeneralSettingsViewControllerDelegate?
let tableView: UITableView
var tableViewDataSource: GeneralSettingsDiffableTableViewDataSource?
private(set) var viewModel: GeneralSettingsViewModel
let setting: Setting
let sections: [GeneralSettingsSection]
init(appContext: AppContext, setting: Setting) {
tableView = UITableView(frame: .zero, style: .insetGrouped)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(GeneralSettingSelectionCell.self, forCellReuseIdentifier: GeneralSettingSelectionCell.reuseIdentifier)
tableView.register(GeneralSettingToggleTableViewCell.self, forCellReuseIdentifier: GeneralSettingToggleTableViewCell.reuseIdentifier)
sections = [
GeneralSettingsSection(type: .appearance, entries: [
.appearance(.light),
.appearance(.dark),
.appearance(.system)
]),
GeneralSettingsSection(type: .askBefore, entries: [
.askBefore(.postingWithoutAltText),
.askBefore(.unfollowingSomeone),
.askBefore(.boostingAPost),
.askBefore(.deletingAPost)
]),
GeneralSettingsSection(type: .design, entries: [
.design(.showAnimations)
]),
GeneralSettingsSection(type: .language, entries: [
.language(.defaultPostLanguage)
]),
GeneralSettingsSection(type: .links, entries: [
.openLinksIn(.mastodon),
.openLinksIn(.browser),
])
]
let openLinksIn: GeneralSetting.OpenLinksIn
if UserDefaults.shared.preferredUsingDefaultBrowser {
openLinksIn = .browser
} else {
openLinksIn = .mastodon
}
let playAnimations = (UserDefaults.shared.preferredStaticAvatar == false && UserDefaults.shared.preferredStaticEmoji == false)
viewModel = GeneralSettingsViewModel(
selectedAppearence: GeneralSetting.Appearance(rawValue: UserDefaults.shared.customUserInterfaceStyle.rawValue) ?? .system,
playAnimations: playAnimations,
selectedOpenLinks: openLinksIn,
askBeforePostingWithoutAltText: UserDefaults.shared.askBeforePostingWithoutAltText,
askBeforeUnfollowingSomeone: UserDefaults.shared.askBeforeUnfollowingSomeone,
askBeforeBoostingAPost: UserDefaults.shared.askBeforeBoostingAPost,
askBeforeDeletingAPost: UserDefaults.shared.askBeforeDeletingAPost,
defaultPostLanguage: UserDefaults.shared.defaultPostLanguage
)
self.setting = setting
super.init(nibName: nil, bundle: nil)
tableView.delegate = self
let tableViewDataSource = GeneralSettingsDiffableTableViewDataSource(tableView: tableView, cellProvider: { tableView, indexPath, itemIdentifier in
let cell: UITableViewCell
switch itemIdentifier {
case .appearance(let setting):
guard let selectionCell = tableView.dequeueReusableCell(withIdentifier: GeneralSettingSelectionCell.reuseIdentifier, for: indexPath) as? GeneralSettingSelectionCell else { fatalError("WTF? Wrong Cell!") }
selectionCell.configure(with: .appearance(setting), viewModel: self.viewModel)
cell = selectionCell
case .askBefore(let setting):
guard let toggleCell = tableView.dequeueReusableCell(withIdentifier: GeneralSettingToggleTableViewCell.reuseIdentifier, for: indexPath) as? GeneralSettingToggleTableViewCell else { fatalError("WTF? Wrong Cell!") }
toggleCell.configure(with: .askBefore(setting), viewModel: self.viewModel)
toggleCell.delegate = self
cell = toggleCell
case .design(let setting):
guard let toggleCell = tableView.dequeueReusableCell(withIdentifier: GeneralSettingToggleTableViewCell.reuseIdentifier, for: indexPath) as? GeneralSettingToggleTableViewCell else { fatalError("WTF? Wrong Cell!") }
toggleCell.configure(with: .design(setting), viewModel: self.viewModel)
toggleCell.delegate = self
cell = toggleCell
case let .language(setting):
guard let selectionCell = tableView.dequeueReusableCell(withIdentifier: GeneralSettingSelectionCell.reuseIdentifier, for: indexPath) as? GeneralSettingSelectionCell else { fatalError("WTF? Wrong Cell!") }
selectionCell.configure(with: .language(setting), viewModel: self.viewModel)
cell = selectionCell
case .openLinksIn(let setting):
guard let selectionCell = tableView.dequeueReusableCell(withIdentifier: GeneralSettingSelectionCell.reuseIdentifier, for: indexPath) as? GeneralSettingSelectionCell else { fatalError("WTF? Wrong Cell!") }
selectionCell.configure(with: .openLinksIn(setting), viewModel: self.viewModel)
cell = selectionCell
}
return cell
})
self.tableViewDataSource = tableViewDataSource
view.backgroundColor = .systemGroupedBackground
view.addSubview(tableView)
tableView.pinTo(to: view)
title = L10n.Scene.Settings.General.title
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func viewDidLoad() {
super.viewDidLoad()
var snapshot = NSDiffableDataSourceSnapshot<GeneralSettingsSection, GeneralSetting>()
for section in sections {
snapshot.appendSections([section])
snapshot.appendItems(section.entries)
}
tableViewDataSource?.apply(snapshot, animatingDifferences: false)
}
}
extension GeneralSettingsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// switch section
let section = sections[indexPath.section].entries[indexPath.row]
switch section {
case .appearance(let appearanceOption):
viewModel.selectedAppearence = appearanceOption
if let snapshot = tableViewDataSource?.snapshot() {
tableViewDataSource?.applySnapshotUsingReloadData(snapshot)
}
case .askBefore(let askBefore):
guard let cell = tableView.cellForRow(at: indexPath) as? GeneralSettingToggleTableViewCell else { return}
let newValue = (cell.toggle.isOn == false)
cell.toggle.setOn(newValue, animated: true)
toggle(cell, setting: .askBefore(askBefore), isOn: newValue)
case .design(let design):
guard let cell = tableView.cellForRow(at: indexPath) as? GeneralSettingToggleTableViewCell else { return}
let newValue = (cell.toggle.isOn == false)
cell.toggle.setOn(newValue, animated: true)
toggle(cell, setting: .design(design), isOn: newValue)
case .language:
delegate?.showLanguagePicker(viewModel) { [weak self] language in
guard let self else { return }
viewModel.defaultPostLanguage = language
UserDefaults.shared.defaultPostLanguage = language
if let snapshot = tableViewDataSource?.snapshot() {
tableViewDataSource?.applySnapshotUsingReloadData(snapshot)
}
}
case .openLinksIn(let openLinksInOption):
viewModel.selectedOpenLinks = openLinksInOption
if let snapshot = tableViewDataSource?.snapshot() {
tableViewDataSource?.applySnapshotUsingReloadData(snapshot)
}
}
tableView.deselectRow(at: indexPath, animated: true)
delegate?.save(self, setting: setting, viewModel: viewModel)
}
}
extension GeneralSettingsViewController: GeneralSettingToggleTableViewCellDelegate {
func toggle(_ cell: GeneralSettingToggleTableViewCell, setting: GeneralSetting, isOn: Bool) {
switch setting {
case .appearance, .openLinksIn, .language:
assertionFailure("No toggle")
case let .askBefore(askBefore):
switch askBefore {
case .postingWithoutAltText:
UserDefaults.shared.askBeforePostingWithoutAltText = isOn
case .unfollowingSomeone:
UserDefaults.shared.askBeforeUnfollowingSomeone = isOn
case .boostingAPost:
UserDefaults.shared.askBeforeBoostingAPost = isOn
case .deletingAPost:
UserDefaults.shared.askBeforeDeletingAPost = isOn
}
case let .design(designSetting):
switch designSetting {
case .showAnimations:
viewModel.playAnimations = isOn
}
}
delegate?.save(self, setting: self.setting, viewModel: viewModel)
}
}