Skip to content
Snippets Groups Projects
SettingsController.swift 11 KiB
Newer Older
Bruno Muniz's avatar
Bruno Muniz committed
import HUD
import Popup
import UIKit
import Theme
import Shared
import Combine
import DependencyInjection
import ScrollViewController

public final class SettingsController: UIViewController {
    @Dependency private var hud: HUDType
    @Dependency private var coordinator: SettingsCoordinating
    @Dependency private var statusBarController: StatusBarStyleControlling

    lazy private var scrollViewController = ScrollViewController()
    lazy private var screenView = SettingsView {
        switch $0 {
        case .icognitoKeyboard:
            self.presentInfo(
                title: Localized.Settings.InfoPopUp.Icognito.title,
                subtitle: Localized.Settings.InfoPopUp.Icognito.subtitle
            )
        case .biometrics:
            self.presentInfo(
                title: Localized.Settings.InfoPopUp.Biometrics.title,
                subtitle: Localized.Settings.InfoPopUp.Biometrics.subtitle
            )
        case .notifications:
            self.presentInfo(
                title: Localized.Settings.InfoPopUp.Notifications.title,
                subtitle: Localized.Settings.InfoPopUp.Notifications.subtitle,
                urlString: "https://links.xx.network/denseids"
            )

        case .dummyTraffic:
            self.presentInfo(
                title: Localized.Settings.InfoPopUp.Traffic.title,
                subtitle: Localized.Settings.InfoPopUp.Traffic.subtitle,
                urlString: "https://links.xx.network/covertraffic"
            )
        }
    }

    private let viewModel = SettingsViewModel()
    private var cancellables = Set<AnyCancellable>()
    private var popupCancellables = Set<AnyCancellable>()

    public override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        statusBarController.style.send(.darkContent)
        navigationController?.navigationBar
            .customize(backgroundColor: Asset.neutralWhite.color)
    }

    public override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        viewModel.didAppear()
    }

    public override func viewDidLoad() {
        super.viewDidLoad()

        setupNavigationBar()
        setupScrollView()
        setupBindings()

        viewModel.loadCachedSettings()
    }

    private func setupNavigationBar() {
        navigationItem.backButtonTitle = ""

        let title = UILabel()
        title.text = Localized.Settings.title
        title.textColor = Asset.neutralActive.color
        title.font = Fonts.Mulish.semiBold.font(size: 18.0)

        let back = UIButton.back()
        back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside)

        navigationItem.leftBarButtonItem = UIBarButtonItem(
            customView: UIStackView(arrangedSubviews: [back, title])
        )
    }

    private func setupScrollView() {
        scrollViewController.view.backgroundColor = Asset.neutralWhite.color

        addChild(scrollViewController)
        view.addSubview(scrollViewController.view)

        scrollViewController.view.snp.makeConstraints { $0.edges.equalToSuperview() }
        scrollViewController.didMove(toParent: self)
        scrollViewController.contentView = screenView
    }

    private func setupBindings() {
        viewModel.hud
            .receive(on: DispatchQueue.main)
            .sink { [hud] in hud.update(with: $0) }
            .store(in: &cancellables)

        viewModel.infoPopupPublisher
            .receive(on: DispatchQueue.main)
            .sink { [unowned self] in
                presentInfo(
                    title: Localized.Settings.InfoPopUp.Privacy.title,
                    subtitle: Localized.Settings.InfoPopUp.Privacy.subtitle
                )
            }.store(in: &cancellables)

        screenView.inAppNotifications.switcherView
            .publisher(for: .valueChanged)
            .sink { [weak viewModel] in viewModel?.didToggleInAppNotifications() }
            .store(in: &cancellables)

        screenView.dummyTraffic.switcherView
            .publisher(for: .valueChanged)
            .sink { [weak viewModel] in viewModel?.didToggleDummyTraffic() }
            .store(in: &cancellables)

        screenView.remoteNotifications.switcherView
            .publisher(for: .valueChanged)
            .sink { [weak viewModel] in viewModel?.didTogglePushNotifications() }
            .store(in: &cancellables)

        screenView.hideActiveApp.switcherView
            .publisher(for: .valueChanged)
            .sink { [weak viewModel] in viewModel?.didToggleHideActiveApps() }
            .store(in: &cancellables)

        screenView.icognitoKeyboard.switcherView
            .publisher(for: .valueChanged)
            .sink { [weak viewModel] in viewModel?.didToggleIcognitoKeyboard() }
            .store(in: &cancellables)

        screenView.biometrics.switcherView
            .publisher(for: .valueChanged)
            .sink { [weak viewModel] in viewModel?.didToggleBiometrics() }
            .store(in: &cancellables)

        screenView.privacyPolicy
            .publisher(for: .touchUpInside)
            .receive(on: DispatchQueue.main)
            .sink { [unowned self] in
                presentPopup(
                    title: Localized.Settings.Popup.title(Localized.Settings.privacyPolicy),
                    subtitle: Localized.Settings.Popup.subtitle(Localized.Settings.privacyPolicy),
                    actionTitle: Localized.ChatList.Dashboard.open) {
                        guard let url = URL(string: "https://xx.network/privategrity-corporation-privacy-policy") else { return }
                        UIApplication.shared.open(url, options: [:])
                    }
            }.store(in: &cancellables)

        screenView.disclosures
            .publisher(for: .touchUpInside)
            .receive(on: DispatchQueue.main)
            .sink { [unowned self] in
                presentPopup(
                    title: Localized.Settings.Popup.title(Localized.Settings.disclosures),
                    subtitle: Localized.Settings.Popup.subtitle(Localized.Settings.disclosures),
                    actionTitle: Localized.ChatList.Dashboard.open) {
                        guard let url = URL(string: "https://xx.network/privategrity-corporation-terms-of-use") else { return }
                        UIApplication.shared.open(url, options: [:])
                    }
            }.store(in: &cancellables)

        screenView.delete
            .publisher(for: .touchUpInside)
            .receive(on: DispatchQueue.main)
            .sink { [unowned self] in
                coordinator.toAccountDelete(from: self)
            }.store(in: &cancellables)

        screenView.advanced
            .publisher(for: .touchUpInside)
            .receive(on: DispatchQueue.main)
            .sink { [unowned self] in coordinator.toAdvanced(from: self) }
            .store(in: &cancellables)

        viewModel.state
            .map(\.isBiometricsPossible)
            .removeDuplicates()
            .receive(on: DispatchQueue.main)
            .sink { [weak screenView] in screenView?.biometrics.switcherView.isEnabled = $0 }
            .store(in: &cancellables)

        viewModel.state
            .removeDuplicates()
            .receive(on: DispatchQueue.main)
            .sink { [unowned self] state in
                screenView.biometrics.switcherView.setOn(state.isBiometricsEnabled, animated: true)
                screenView.hideActiveApp.switcherView.setOn(state.isHideActiveApps, animated: true)
                screenView.icognitoKeyboard.switcherView.setOn(state.isIcognitoKeyboard, animated: true)
                screenView.inAppNotifications.switcherView.setOn(state.isInAppNotification, animated: true)
                screenView.remoteNotifications.switcherView.setOn(state.isPushNotification, animated: true)
                screenView.dummyTraffic.switcherView.setOn(state.isDummyTrafficOn, animated: true)
            }.store(in: &cancellables)
    }

    private func presentPopup(
        title: String,
        subtitle: String,
        actionTitle: String,
        action: @escaping () -> Void
    ) {
        let actionButton = CapsuleButton()
        actionButton.setStyle(.red)
        actionButton.setTitle(actionTitle, for: .normal)

        let cancelButton = CapsuleButton()
        cancelButton.setStyle(.seeThrough)
        cancelButton.setTitle(Localized.ChatList.Dashboard.cancel, for: .normal)

        let popup = BottomPopup(with: [
            PopupImage(image: Asset.popupNegative.image),
            PopupLabel(
                font: Fonts.Mulish.semiBold.font(size: 18.0),
                text: title,
                color: Asset.neutralActive.color
            ),
            PopupLabel(
                font: Fonts.Mulish.semiBold.font(size: 14.0),
                text: subtitle,
                color: Asset.neutralWeak.color,
                lineHeightMultiple: 1.35,
                spacingAfter: 25
            ),
            PopupStackView(
                spacing: 20.0,
                views: [
                    actionButton,
                    cancelButton
                ]
            )
        ])

        actionButton.publisher(for: .touchUpInside)
            .receive(on: DispatchQueue.main)
            .sink {
                popup.dismiss(animated: true) { [weak self] in
                    guard let self = self else { return }
                    self.popupCancellables.removeAll()

                    action()
                }
            }.store(in: &popupCancellables)

        cancelButton.publisher(for: .touchUpInside)
            .receive(on: DispatchQueue.main)
            .sink {
                popup.dismiss(animated: true) { [weak self] in
                    self?.popupCancellables.removeAll()
                }
            }.store(in: &popupCancellables)

        coordinator.toPopup(popup, from: self)
    }

    @objc private func didTapBack() {
        navigationController?.popViewController(animated: true)
    }
}

extension SettingsController {
    private func presentInfo(
        title: String,
        subtitle: String,
        urlString: String = ""
    ) {
        let actionButton = CapsuleButton()
        actionButton.set(
            style: .seeThrough,
            title: Localized.Settings.InfoPopUp.action
        )

        let popup = BottomPopup(with: [
            PopupLabel(
                font: Fonts.Mulish.bold.font(size: 26.0),
                text: title,
                color: Asset.neutralActive.color,
                alignment: .left,
                spacingAfter: 19
            ),
            PopupLinkText(
                text: subtitle,
                urlString: urlString,
                spacingAfter: 37
            ),
            PopupStackView(views: [actionButton, FlexibleSpace()])
        ])

        actionButton.publisher(for: .touchUpInside)
            .receive(on: DispatchQueue.main)
            .sink {
                popup.dismiss(animated: true) { [weak self] in
                    guard let self = self else { return }
                    self.popupCancellables.removeAll()
                }
            }.store(in: &popupCancellables)

        coordinator.toPopup(popup, from: self)
    }
}