Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
folium-app
GitHub Repository: folium-app/Folium
Path: blob/a-new-beginning/Folium-iOS/Controllers/SettingsControllers/FoliumSettingsController.swift
2 views
//
//  FoliumSettingsController.swift
//  Folium-iOS
//
//  Created by Jarrod Norwell on 29/10/2024.
//  Copyright © 2024 Jarrod Norwell. All rights reserved.
//

import Foundation
import SettingsKit
import UIKit

enum FoliumSettingsHeaders : String, CaseIterable {
    case emulation = "Emulation"
    
    var header: SettingHeader {
        switch self {
        case .emulation:
            SettingHeader(text: rawValue,
                          secondaryText: "Emulation Controller Settings")
        }
    }
    
    static var allHeaders: [SettingHeader] { allCases.map { $0.header } }
}

enum FoliumSettingsItems : String, CaseIterable {
    // Emulation
    case buttonTransparency = "folium.v1.38.buttonTransparency"
    case expandButtonsBy = "folium.v1.38.expandButtonsBy"
    
    var title: String {
        switch self {
        case .buttonTransparency:
            "Button Transparency"
        case .expandButtonsBy:
            "Expand Buttons By"
        }
    }
    
    var details: String? {
        switch self {
        case .buttonTransparency:
            "Changes the opacity of the on-screen emulation controller buttons improving gameplay visibility on smaller screens"
        case .expandButtonsBy:
            "Expands the on-screen emulation controller buttons improving user interaction"
        }
    }
    
    func setting(_ delegate: SettingDelegate? = nil) -> BaseSetting {
        switch self {
        case .buttonTransparency:
            StepperSetting(key: rawValue,
                           title: title,
                           details: details,
                           min: 0,
                           max: 1,
                           value: UserDefaults.standard.double(forKey: rawValue),
                           delegate: delegate)
        case .expandButtonsBy:
            StepperSetting(key: rawValue,
                           title: title,
                           details: details,
                           min: 0,
                           max: 10,
                           value: UserDefaults.standard.double(forKey: rawValue),
                           delegate: delegate)
        }
    }
    
    static func settings(_ header: FoliumSettingsHeaders) -> [FoliumSettingsItems] {
        switch header {
        case .emulation:
            [.buttonTransparency, .expandButtonsBy]
        }
    }
}

class FoliumSettingsController : UIViewController, UICollectionViewDelegate {
    var dataSource: UICollectionViewDiffableDataSource<FoliumSettingsHeaders, AnyHashableSendable>! = nil
    var snapshot: NSDiffableDataSourceSnapshot<FoliumSettingsHeaders, AnyHashableSendable>! = nil
    
    let settingsKit: SettingsKit = SettingsKit()
 
    override func viewDidLoad() {
        super.viewDidLoad()
        if let navigationController {
            navigationController.navigationBar.prefersLargeTitles = true
        }
        navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "xmark"), primaryAction: UIAction { _ in
            self.dismiss(animated: true)
        })
        if #available(iOS 26, *) {
            navigationItem.title = "Folium"
            navigationItem.largeTitle = navigationItem.title
            navigationItem.subtitle = "Settings"
            navigationItem.largeSubtitle = navigationItem.subtitle
        } else {
            title = "Folium"
        }
        navigationItem.style = .browser
        
        var configuration: UICollectionLayoutListConfiguration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
        configuration.backgroundColor = .clear
        configuration.headerMode = .supplementary
        configuration.trailingSwipeActionsConfigurationProvider = { indexPath in
            guard let dataSource = self.dataSource, let item: BaseSetting = dataSource.itemIdentifier(for: indexPath) as? BaseSetting else {
                return UISwipeActionsConfiguration()
            }
            
            let informationContextualAction: UIContextualAction = UIContextualAction(style: .normal, title: nil, handler: { action, view, performed in
                let alertController: UIAlertController = UIAlertController(title: item.title, message: item.details, preferredStyle: .alert)
                alertController.addAction(UIAlertAction(title: "Dismiss", style: .default) { _ in
                    performed(true)
                })
                // alertController.preferredAction = alertController.actions.first
                self.present(alertController, animated: true)
            })
            informationContextualAction.image = UIImage(systemName: "info")
            
            return UISwipeActionsConfiguration(actions: [
                informationContextualAction
            ])
        }
        
        let collectionView: UICollectionView = UICollectionView(frame: .zero,
                                                                collectionViewLayout: UICollectionViewCompositionalLayout.list(using: configuration))
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.delegate = self
        view.addSubview(collectionView)
        
        collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        
        if #available(iOS 26, *) {
            let backgroundView: UIVisualEffectView = UIVisualEffectView(effect: UIGlassEffect(style: .regular))
            backgroundView.cornerConfiguration = .corners(radius: .containerConcentric())
            
            collectionView.backgroundColor = .clear
            collectionView.backgroundView = backgroundView
            view.backgroundColor = .clear
        } else {
            view.backgroundColor = .systemBackground
        }
        
        let headerCellRegistration = UICollectionView.SupplementaryRegistration<UICollectionViewListCell>(elementKind: UICollectionView.elementKindSectionHeader) { supplementaryView, elementKind, indexPath in
            var contentConfiguration = UIListContentConfiguration.extraProminentInsetGroupedHeader()
            contentConfiguration.text = self.snapshot.sectionIdentifiers[indexPath.section].header.text
            contentConfiguration.secondaryText = self.snapshot.sectionIdentifiers[indexPath.section].header.secondaryText
            contentConfiguration.secondaryTextProperties.color = .secondaryLabel
            supplementaryView.contentConfiguration = contentConfiguration
        }
        
        let stepperCell: UICollectionView.CellRegistration<UICollectionViewListCell, StepperSetting> = CellManager.Settings.stepperCell
        
        dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
            switch itemIdentifier {
            case let stepperSetting as StepperSetting:
                collectionView.dequeueConfiguredReusableCell(using: stepperCell, for: indexPath, item: stepperSetting)
            default:
                nil
            }
        }
        
        dataSource.supplementaryViewProvider = { collectionView, elementKind, indexPath in
            collectionView.dequeueConfiguredReusableSupplementary(using: headerCellRegistration, for: indexPath)
        }
        
        snapshot = .init()
        snapshot.appendSections(FoliumSettingsHeaders.allCases)
        snapshot.sectionIdentifiers.forEach { header in
            snapshot.appendItems(FoliumSettingsItems.settings(header).map { $0.setting(self) }, toSection: header)
        }
        
        Task {
            await dataSource.apply(snapshot)
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        collectionView.deselectItem(at: indexPath, animated: true)
    }
}

extension FoliumSettingsController : SettingDelegate {
    func didChangeSetting(at indexPath: IndexPath) {
        guard let sectionIdentifier = dataSource.sectionIdentifier(for: indexPath.section) else {
            return
        }
        
        var snapshot = dataSource.snapshot()
        let item = snapshot.itemIdentifiers(inSection: sectionIdentifier)[indexPath.item]
        
        switch item {
        case let stepperSetting as StepperSetting:
            stepperSetting.value = UserDefaults.standard.double(forKey: stepperSetting.key)
        default:
            break
        }
        
        snapshot.reloadItems([item])
        Task {
            await dataSource.apply(snapshot, animatingDifferences: false)
        }
    }
}