2021-06-29 18:09:42 -07:00
|
|
|
//
|
|
|
|
|
// RedirectRulesSettingsViewController.swift
|
|
|
|
|
// RedirectRulesSettingsViewController
|
|
|
|
|
//
|
|
|
|
|
// Created by James Magahern on 6/25/21.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
|
|
2021-12-14 18:50:33 -08:00
|
|
|
class RedirectRulesSettingsViewController: UICollectionViewController
|
2021-06-29 18:09:42 -07:00
|
|
|
{
|
2021-12-14 18:50:33 -08:00
|
|
|
private enum Section {
|
|
|
|
|
case rules
|
|
|
|
|
case addButton
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private enum Items: String {
|
|
|
|
|
case addButton
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private lazy var dataSource: UICollectionViewDiffableDataSource<Section, String> = {
|
|
|
|
|
let registry = UICollectionView.CellRegistration<UICollectionViewListCell, String> { [unowned self] cell, indexPath, item in
|
|
|
|
|
var config = cell.defaultContentConfiguration()
|
|
|
|
|
if item == Items.addButton.rawValue {
|
|
|
|
|
config.text = "Add Redirect Rule"
|
|
|
|
|
config.textProperties.font = .preferredFont(forTextStyle: .subheadline)
|
|
|
|
|
config.textProperties.alignment = .center
|
|
|
|
|
config.image = .init(systemName: "plus.circle")
|
|
|
|
|
|
|
|
|
|
cell.accessories = []
|
|
|
|
|
} else {
|
|
|
|
|
if let rule = Settings.shared.redirectRules[item] {
|
|
|
|
|
config.text = "\(item) → \(rule)"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let deleteOptions = UICellAccessory.DeleteOptions(isHidden: false, reservedLayoutWidth: nil, tintColor: .red, backgroundColor: nil)
|
|
|
|
|
cell.accessories = [
|
|
|
|
|
.delete(displayed: .always, options: deleteOptions, actionHandler: { [unowned self] in
|
|
|
|
|
deleteRedirectRule(fromHost: item)
|
|
|
|
|
})
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cell.contentConfiguration = config
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return UICollectionViewDiffableDataSource<Section, String>(collectionView: collectionView) { (collectionView, indexPath, itemIdentifier) in
|
|
|
|
|
collectionView.dequeueConfiguredReusableCell(using: registry, for: indexPath, item: itemIdentifier)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
2021-06-29 18:09:42 -07:00
|
|
|
init() {
|
2021-12-14 18:50:33 -08:00
|
|
|
let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
|
2021-12-16 19:05:10 -08:00
|
|
|
|
|
|
|
|
#if targetEnvironment(macCatalyst)
|
2021-12-16 16:32:47 -08:00
|
|
|
let layout = UICollectionViewCompositionalLayout { section, layoutEnvironment in
|
|
|
|
|
let listSection = NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment)
|
|
|
|
|
listSection.contentInsets = NSDirectionalEdgeInsets(top: 24.0, leading: 164.0, bottom: 24.0, trailing: 164.0)
|
|
|
|
|
|
|
|
|
|
return listSection
|
|
|
|
|
}
|
2021-12-16 19:05:10 -08:00
|
|
|
#else
|
|
|
|
|
let layout = UICollectionViewCompositionalLayout.list(using: config)
|
|
|
|
|
#endif
|
2021-12-16 16:32:47 -08:00
|
|
|
|
2021-12-14 18:50:33 -08:00
|
|
|
super.init(collectionViewLayout: layout)
|
2021-06-29 18:09:42 -07:00
|
|
|
|
|
|
|
|
tabBarItem.title = "Redirect Rules"
|
|
|
|
|
tabBarItem.image = UIImage(systemName: "arrowshape.zigzag.forward")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override func viewDidLoad() {
|
2021-12-14 18:50:33 -08:00
|
|
|
var snapshot = dataSource.snapshot()
|
|
|
|
|
snapshot.appendSections([ .rules, .addButton ])
|
|
|
|
|
snapshot.appendItems(Settings.shared.redirectRules.keys.sorted(), toSection: .rules)
|
|
|
|
|
snapshot.appendItems([ Items.addButton.rawValue ], toSection: .addButton)
|
|
|
|
|
dataSource.applySnapshotUsingReloadData(snapshot)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
2021-12-16 16:32:47 -08:00
|
|
|
collectionView.deselectItem(at: indexPath, animated: true)
|
|
|
|
|
|
2021-12-14 18:50:33 -08:00
|
|
|
let item = dataSource.itemIdentifier(for: indexPath)
|
|
|
|
|
if item == Items.addButton.rawValue {
|
|
|
|
|
let createRuleViewController = CreateRedirectRuleViewController()
|
|
|
|
|
createRuleViewController.finishedCreatingRule = { [unowned self] (fromHost, toHost) in
|
|
|
|
|
if fromHost.count > 0 && toHost.count > 0 {
|
|
|
|
|
addRedirectRule(fromHost: fromHost, toHost: toHost)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let navController = UINavigationController(rootViewController: createRuleViewController)
|
|
|
|
|
present(navController, animated: true, completion: nil)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func addRedirectRule(fromHost: String, toHost: String) {
|
|
|
|
|
Settings.shared.redirectRules[fromHost] = toHost
|
|
|
|
|
|
|
|
|
|
var snapshot = dataSource.snapshot()
|
|
|
|
|
snapshot.appendItems([ fromHost ], toSection: .rules)
|
|
|
|
|
dataSource.apply(snapshot, animatingDifferences: true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func deleteRedirectRule(fromHost: String) {
|
|
|
|
|
Settings.shared.redirectRules.removeValue(forKey: fromHost)
|
|
|
|
|
|
|
|
|
|
var snapshot = dataSource.snapshot()
|
|
|
|
|
snapshot.deleteItems([ fromHost ])
|
|
|
|
|
dataSource.apply(snapshot, animatingDifferences: true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class CreateRedirectRuleViewController: UICollectionViewController
|
|
|
|
|
{
|
2022-02-22 22:01:39 -08:00
|
|
|
enum Section: Int {
|
|
|
|
|
case header
|
2021-12-14 18:50:33 -08:00
|
|
|
case fromHost
|
|
|
|
|
case toHost
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-22 22:01:39 -08:00
|
|
|
enum Item: Hashable {
|
|
|
|
|
case header(section: Section)
|
|
|
|
|
case textField(section: Section)
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 18:50:33 -08:00
|
|
|
struct TextFieldCellConfiguration: UIContentConfiguration {
|
|
|
|
|
var textField: UITextField
|
|
|
|
|
|
|
|
|
|
func makeContentView() -> UIView & UIContentView {
|
|
|
|
|
return GenericContentView<UITextField, TextFieldCellConfiguration>(configuration: self, view: textField) { _,_ in }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func updated(for state: UIConfigurationState) -> TextFieldCellConfiguration {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-22 22:01:39 -08:00
|
|
|
struct ImageViewCellConfiguration: UIContentConfiguration {
|
|
|
|
|
var imageView: UIImageView
|
|
|
|
|
|
|
|
|
|
func makeContentView() -> UIView & UIContentView {
|
|
|
|
|
return GenericContentView<UIImageView, Self>(configuration: self, view: imageView) { _,_ in }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func updated(for state: UIConfigurationState) -> Self {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 18:50:33 -08:00
|
|
|
public var finishedCreatingRule: ((_ fromHost: String, _ toHost: String) -> Void)?
|
|
|
|
|
|
2022-02-22 22:01:39 -08:00
|
|
|
private let sectionHeaderImageView = UIImageView(image: .init(systemName: "arrowshape.zigzag.forward"))
|
2021-12-14 18:50:33 -08:00
|
|
|
private let fromHostField = UITextField(frame: .zero)
|
|
|
|
|
private let toHostField = UITextField(frame: .zero)
|
|
|
|
|
|
|
|
|
|
private let doneButton = UIBarButtonItem(systemItem: .done, primaryAction: nil, menu: nil)
|
|
|
|
|
|
|
|
|
|
private lazy var dataSource: UICollectionViewDiffableDataSource<Section, Item> = {
|
2022-02-22 22:01:39 -08:00
|
|
|
let listCellRegistry = UICollectionView.CellRegistration<UICollectionViewListCell, Item> { [unowned self] cell, indexPath, item in
|
|
|
|
|
let section = dataSource.snapshot().sectionIdentifier(containingItem: item)
|
|
|
|
|
|
|
|
|
|
switch item {
|
|
|
|
|
case .header(_):
|
|
|
|
|
var contentConfig = cell.defaultContentConfiguration()
|
|
|
|
|
contentConfig.text = section == .fromHost ? "From Host" : "To Host"
|
|
|
|
|
cell.contentConfiguration = contentConfig
|
|
|
|
|
case .textField(_):
|
|
|
|
|
cell.contentConfiguration = TextFieldCellConfiguration(textField:
|
|
|
|
|
section == .fromHost ? fromHostField
|
|
|
|
|
: toHostField)
|
2021-12-14 18:50:33 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-22 22:01:39 -08:00
|
|
|
let headerCellRegistry = UICollectionView.CellRegistration<UICollectionViewCell, Item> { [unowned self] cell, indexPath, item in
|
|
|
|
|
let config = ImageViewCellConfiguration(imageView: sectionHeaderImageView)
|
|
|
|
|
cell.contentConfiguration = config
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { [unowned self] (collectionView, indexPath, itemIdentifier) in
|
|
|
|
|
let section = self.dataSource.sectionIdentifier(for: indexPath.section)
|
|
|
|
|
if section == .header {
|
|
|
|
|
return collectionView.dequeueConfiguredReusableCell(using: headerCellRegistry, for: indexPath, item: itemIdentifier)
|
|
|
|
|
} else {
|
|
|
|
|
return collectionView.dequeueConfiguredReusableCell(using: listCellRegistry, for: indexPath, item: itemIdentifier)
|
|
|
|
|
}
|
2021-12-14 18:50:33 -08:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
init() {
|
2022-02-22 22:01:39 -08:00
|
|
|
var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
|
|
|
|
|
config.headerMode = .firstItemInSection
|
|
|
|
|
|
|
|
|
|
let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) in
|
|
|
|
|
let section = Section(rawValue: sectionIndex)
|
|
|
|
|
if section == .header {
|
|
|
|
|
let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(80.0))
|
|
|
|
|
let group = NSCollectionLayoutGroup.vertical(
|
|
|
|
|
layoutSize: size,
|
|
|
|
|
subitems: [
|
|
|
|
|
NSCollectionLayoutItem(layoutSize: size)
|
|
|
|
|
])
|
|
|
|
|
return NSCollectionLayoutSection(group: group)
|
|
|
|
|
} else {
|
|
|
|
|
return NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 18:50:33 -08:00
|
|
|
super.init(collectionViewLayout: layout)
|
|
|
|
|
|
|
|
|
|
preferredContentSize = CGSize(width: -1, height: 320.0)
|
|
|
|
|
|
|
|
|
|
self.title = "Add Redirect Rule"
|
|
|
|
|
|
|
|
|
|
navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .cancel, primaryAction: UIAction { [unowned self] _ in
|
|
|
|
|
dismiss(animated: true, completion: nil)
|
|
|
|
|
}, menu: nil)
|
|
|
|
|
|
|
|
|
|
doneButton.primaryAction = UIAction { [unowned self] _ in
|
|
|
|
|
finishedCreatingRule?(fromHostField.text!, toHostField.text!)
|
|
|
|
|
dismiss(animated: true, completion: nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
navigationItem.rightBarButtonItem = doneButton
|
|
|
|
|
|
2022-02-22 22:01:39 -08:00
|
|
|
sectionHeaderImageView.contentMode = .scaleAspectFit
|
|
|
|
|
|
|
|
|
|
fromHostField.placeholder = "https://fromhostname.com"
|
|
|
|
|
fromHostField.autocorrectionType = .no
|
|
|
|
|
fromHostField.autocapitalizationType = .none
|
|
|
|
|
|
|
|
|
|
toHostField.placeholder = "https://tohostname.com"
|
|
|
|
|
toHostField.autocorrectionType = .no
|
|
|
|
|
toHostField.autocapitalizationType = .none
|
2021-12-14 18:50:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override func viewDidLoad() {
|
|
|
|
|
var snapshot = dataSource.snapshot()
|
2022-02-22 22:01:39 -08:00
|
|
|
snapshot.appendSections([ Section.header, Section.fromHost, Section.toHost ])
|
|
|
|
|
snapshot.appendItems([ .header(section: .header) ], toSection: .header)
|
|
|
|
|
snapshot.appendItems([ .header(section: .fromHost), .textField(section: .fromHost) ], toSection: .fromHost)
|
|
|
|
|
snapshot.appendItems([ .header(section: .toHost), .textField(section: .toHost) ], toSection: .toHost)
|
2021-12-14 18:50:33 -08:00
|
|
|
dataSource.applySnapshotUsingReloadData(snapshot)
|
2021-06-29 18:09:42 -07:00
|
|
|
}
|
2021-12-16 16:32:47 -08:00
|
|
|
|
|
|
|
|
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
|
|
|
|
|
false
|
|
|
|
|
}
|
2021-06-29 18:09:42 -07:00
|
|
|
}
|
|
|
|
|
|