123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- import UIKit
- import DcCore
- class WelcomeViewController: UIViewController, ProgressAlertHandler {
- private var dcContext: DcContext
- private let dcAccounts: DcAccounts
- private let accountCode: String?
- private var backupProgressObserver: NSObjectProtocol?
- var progressObserver: NSObjectProtocol?
- var onProgressSuccess: VoidFunction?
- private var securityScopedResource: NSURL?
- private lazy var scrollView: UIScrollView = {
- let scrollView = UIScrollView()
- scrollView.showsVerticalScrollIndicator = false
- return scrollView
- }()
- private lazy var welcomeView: WelcomeContentView = {
- let view = WelcomeContentView()
- view.onLogin = { [weak self] in
- guard let self = self else { return }
- self.showAccountSetupController()
- }
- view.onScanQRCode = { [weak self] in
- guard let self = self else { return }
- let qrReader = QrCodeReaderController()
- qrReader.delegate = self
- self.qrCodeReader = qrReader
- self.navigationController?.pushViewController(qrReader, animated: true)
- }
- view.onImportBackup = { [weak self] in
- guard let self = self else { return }
- self.restoreBackup()
- }
- view.translatesAutoresizingMaskIntoConstraints = false
- return view
- }()
- private lazy var canCancel: Bool = {
- // "cancel" removes selected unconfigured account, so there needs to be at least one other account
- return dcAccounts.getAll().count >= 2
- }()
- private lazy var cancelButton: UIBarButtonItem = {
- return UIBarButtonItem(title: String.localized("cancel"), style: .plain, target: self, action: #selector(cancelAccountCreation))
- }()
- private lazy var moreButton: UIBarButtonItem = {
- let image: UIImage?
- if #available(iOS 13.0, *) {
- image = UIImage(systemName: "ellipsis.circle")
- } else {
- image = UIImage(named: "ic_more")
- }
- return UIBarButtonItem(image: image,
- style: .plain,
- target: self,
- action: #selector(moreButtonPressed))
- }()
- private lazy var mediaPicker: MediaPicker? = {
- let mediaPicker = MediaPicker(navigationController: navigationController)
- mediaPicker.delegate = self
- return mediaPicker
- }()
- private var qrCodeReader: QrCodeReaderController?
- weak var progressAlert: UIAlertController?
- init(dcAccounts: DcAccounts, accountCode: String? = nil) {
- self.dcAccounts = dcAccounts
- self.dcContext = dcAccounts.getSelected()
- self.accountCode = accountCode
- super.init(nibName: nil, bundle: nil)
- self.navigationItem.title = String.localized(canCancel ? "add_account" : "welcome_desktop")
- onProgressSuccess = { [weak self] in
- guard let self = self else { return }
- let profileInfoController = ProfileInfoViewController(context: self.dcContext)
- profileInfoController.onClose = {
- if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
- appDelegate.reloadDcContext()
- }
- }
- self.navigationController?.setViewControllers([profileInfoController], animated: true)
- }
- }
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- // MARK: - lifecycle
- override func viewDidLoad() {
- super.viewDidLoad()
- setupSubviews()
- if canCancel {
- navigationItem.leftBarButtonItem = cancelButton
- }
- navigationItem.rightBarButtonItem = moreButton
- if let accountCode = accountCode {
- handleQrCode(accountCode)
- }
- }
- override func viewDidLayoutSubviews() {
- super.viewDidLayoutSubviews()
- welcomeView.minContainerHeight = view.frame.height - view.safeAreaInsets.top
- }
- override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
- super.viewWillTransition(to: size, with: coordinator)
- welcomeView.minContainerHeight = size.height - view.safeAreaInsets.top
- }
- override func viewDidDisappear(_ animated: Bool) {
- let nc = NotificationCenter.default
- if let observer = self.progressObserver {
- nc.removeObserver(observer)
- self.progressObserver = nil
- }
- removeBackupProgressObserver()
- }
- private func removeBackupProgressObserver() {
- if let backupProgressObserver = self.backupProgressObserver {
- NotificationCenter.default.removeObserver(backupProgressObserver)
- self.backupProgressObserver = nil
- }
- }
- // MARK: - setup
- private func setupSubviews() {
- view.addSubview(scrollView)
- scrollView.translatesAutoresizingMaskIntoConstraints = false
- scrollView.addSubview(welcomeView)
- let frameGuide = scrollView.frameLayoutGuide
- let contentGuide = scrollView.contentLayoutGuide
- frameGuide.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
- frameGuide.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
- frameGuide.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
- frameGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
- contentGuide.leadingAnchor.constraint(equalTo: welcomeView.leadingAnchor).isActive = true
- contentGuide.topAnchor.constraint(equalTo: welcomeView.topAnchor).isActive = true
- contentGuide.trailingAnchor.constraint(equalTo: welcomeView.trailingAnchor).isActive = true
- contentGuide.bottomAnchor.constraint(equalTo: welcomeView.bottomAnchor).isActive = true
- // this enables vertical scrolling
- frameGuide.widthAnchor.constraint(equalTo: contentGuide.widthAnchor).isActive = true
- }
- // MARK: - actions
- private func createAccountFromQRCode(qrCode: String) {
- if dcAccounts.getSelected().isConfigured() {
- UserDefaults.standard.setValue(dcAccounts.getSelected().id, forKey: Constants.Keys.lastSelectedAccountKey)
- // FIXME: what do we want to do with QR-Code created accounts? For now: adding an unencrypted account
- // ensure we're configuring on an empty new account
- _ = dcAccounts.add()
- }
- let accountId = dcAccounts.getSelected().id
- if accountId != 0 {
- dcContext = dcAccounts.get(id: accountId)
- addProgressAlertListener(dcAccounts: self.dcAccounts,
- progressName: dcNotificationConfigureProgress,
- onSuccess: self.handleLoginSuccess)
- showProgressAlert(title: String.localized("login_header"), dcContext: self.dcContext)
- DispatchQueue.global().async { [weak self] in
- guard let self = self else { return }
- let success = self.dcContext.setConfigFromQR(qrCode: qrCode)
- DispatchQueue.main.async {
- if success {
- self.dcAccounts.stopIo()
- self.dcContext.configure()
- } else {
- self.updateProgressAlert(error: self.dcContext.lastErrorString,
- completion: self.accountCode != nil ? self.cancelAccountCreation : nil)
- }
- }
- }
- }
- }
- private func handleLoginSuccess() {
- guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
- if !UserDefaults.standard.bool(forKey: "notifications_disabled") {
- appDelegate.registerForNotifications()
- }
- onProgressSuccess?()
- }
- private func handleBackupRestoreSuccess() {
- guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
- if !UserDefaults.standard.bool(forKey: "notifications_disabled") {
- appDelegate.registerForNotifications()
- }
- if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
- appDelegate.reloadDcContext()
- }
- }
- @objc private func moreButtonPressed() {
- let alert = UIAlertController(title: "Encrypted Account (experimental)",
- message: "Do you want to encrypt your account database? This cannot be undone.",
- preferredStyle: .safeActionSheet)
- let encryptedAccountAction = UIAlertAction(title: "Create encrypted account", style: .default, handler: switchToEncrypted(_:))
- let cancelAction = UIAlertAction(title: String.localized("cancel"), style: .destructive, handler: nil)
- alert.addAction(encryptedAccountAction)
- alert.addAction(cancelAction)
- self.present(alert, animated: true, completion: nil)
- }
- private func switchToEncrypted(_ action: UIAlertAction) {
- let lastContextId = dcAccounts.getSelected().id
- let newContextId = dcAccounts.addClosedAccount()
- _ = dcAccounts.remove(id: lastContextId)
- KeychainManager.deleteAccountSecret(id: lastContextId)
- _ = dcAccounts.select(id: newContextId)
- dcContext = dcAccounts.getSelected()
- do {
- let secret = try KeychainManager.getAccountSecret(accountID: dcContext.id)
- guard dcContext.open(passphrase: secret) else {
- logger.error("Failed to open account database for account \(dcContext.id)")
- return
- }
- self.navigationItem.title = String.localized("add_encrypted_account")
- } catch KeychainError.unhandledError(let message, let status) {
- logger.error("Keychain error. Failed to create encrypted account. \(message). Error status: \(status)")
- } catch {
- logger.error("Keychain error. Failed to create encrypted account.")
- }
- }
- private func showAccountSetupController() {
- let accountSetupController = AccountSetupController(dcAccounts: self.dcAccounts, editView: false)
- accountSetupController.onLoginSuccess = {
- if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
- appDelegate.reloadDcContext()
- }
- }
- self.navigationController?.pushViewController(accountSetupController, animated: true)
- }
- @objc private func cancelAccountCreation() {
- guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
- // take a bit care on account removal:
- // remove only openend and unconfigured and make sure, there is another account
- // (normally, both checks are not needed, however, some resilience wrt future program-flow-changes seems to be reasonable here)
- let selectedAccount = dcAccounts.getSelected()
- if selectedAccount.isOpen() && !selectedAccount.isConfigured() {
- _ = dcAccounts.remove(id: selectedAccount.id)
- KeychainManager.deleteAccountSecret(id: selectedAccount.id)
- if self.dcAccounts.getAll().isEmpty {
- _ = self.dcAccounts.add()
- }
- }
- let lastSelectedAccountId = UserDefaults.standard.integer(forKey: Constants.Keys.lastSelectedAccountKey)
- if lastSelectedAccountId != 0 {
- _ = dcAccounts.select(id: lastSelectedAccountId)
- dcAccounts.startIo()
- }
- appDelegate.reloadDcContext()
- }
- private func restoreBackup() {
- logger.info("restoring backup")
- if dcContext.isConfigured() {
- return
- }
- mediaPicker?.showDocumentLibrary(selectFolder: true)
- }
- private func importBackup(at filepath: String) {
- logger.info("restoring backup: \(filepath)")
- showProgressAlert(title: String.localized("import_backup_title"), dcContext: dcContext)
- dcAccounts.stopIo()
- dcContext.imex(what: DC_IMEX_IMPORT_BACKUP, directory: filepath)
- }
- private func addProgressHudBackupListener() {
- let nc = NotificationCenter.default
- UIApplication.shared.isIdleTimerDisabled = true
- backupProgressObserver = nc.addObserver(
- forName: dcNotificationImexProgress,
- object: nil,
- queue: nil
- ) { [weak self] notification in
- guard let self = self else { return }
- if let ui = notification.userInfo {
- if let error = ui["error"] as? Bool, error {
- UIApplication.shared.isIdleTimerDisabled = false
- if self.dcContext.isConfigured() {
- let accountId = self.dcContext.id
- _ = self.dcAccounts.remove(id: accountId)
- KeychainManager.deleteAccountSecret(id: accountId)
- _ = self.dcAccounts.add()
- self.dcContext = self.dcAccounts.getSelected()
- self.navigationItem.title = String.localized(self.canCancel ? "add_account" : "welcome_desktop")
- }
- var error = ui["errorMessage"] as? String ?? ""
- if error.isEmpty {
- error = self.dcContext.lastErrorString
- }
- self.updateProgressAlert(error: error)
- self.stopAccessingSecurityScopedResource()
- self.removeBackupProgressObserver()
- } else if let done = ui["done"] as? Bool, done {
- UIApplication.shared.isIdleTimerDisabled = false
- self.dcAccounts.startIo()
- self.updateProgressAlertSuccess(completion: self.handleBackupRestoreSuccess)
- self.stopAccessingSecurityScopedResource()
- } else {
- self.updateProgressAlertValue(value: ui["progress"] as? Int)
- }
- }
- }
- }
- }
- extension WelcomeViewController: QrCodeReaderDelegate {
- func handleQrCode(_ code: String) {
- let lot = dcContext.checkQR(qrCode: code)
- if let domain = lot.text1, lot.state == DC_QR_ACCOUNT {
- let title = String.localizedStringWithFormat(
- String.localized(dcAccounts.getAll().count > 1 ? "qraccount_ask_create_and_login_another" : "qraccount_ask_create_and_login"),
- domain)
- confirmQrAccountAlert(title: title, qrCode: code)
- } else if let email = lot.text1, lot.state == DC_QR_LOGIN {
- let title = String.localizedStringWithFormat(
- String.localized(dcAccounts.getAll().count > 1 ? "qrlogin_ask_login_another" : "qrlogin_ask_login"),
- email)
- confirmQrAccountAlert(title: title, qrCode: code)
- } else if lot.state == DC_QR_BACKUP {
- confirmSetupNewDevice(qrCode: code)
- } else {
- qrErrorAlert()
- }
- }
- private func confirmQrAccountAlert(title: String, qrCode: String) {
- let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
- let okAction = UIAlertAction(
- title: String.localized("ok"),
- style: .default,
- handler: { [weak self] _ in
- guard let self = self else { return }
- self.dismissQRReader()
- self.createAccountFromQRCode(qrCode: qrCode)
- }
- )
- let qrCancelAction = UIAlertAction(
- title: String.localized("cancel"),
- style: .cancel,
- handler: { [weak self] _ in
- guard let self = self else { return }
- self.dismissQRReader()
- // if an injected accountCode exists, the WelcomeViewController was only opened to handle that
- // cancelling the action should also dismiss the whole controller
- if self.accountCode != nil {
- self.cancelAccountCreation()
- }
- }
- )
- alert.addAction(okAction)
- alert.addAction(qrCancelAction)
- if qrCodeReader != nil {
- qrCodeReader?.present(alert, animated: true)
- } else {
- self.present(alert, animated: true)
- }
- }
- private func confirmSetupNewDevice(qrCode: String) {
- // triggerLocalNetworkPrivacyAlert() // TODO: is that needed with new iroh?
- let alert = UIAlertController(title: String.localized("add_another_device"),
- message: String.localized("scan_other_device_explain"),
- preferredStyle: .alert)
- alert.addAction(UIAlertAction(
- title: String.localized("ok"),
- style: .default,
- handler: { [weak self] _ in
- guard let self = self else { return }
- self.dismissQRReader()
- self.addProgressHudBackupListener()
- self.showProgressAlert(title: String.localized("add_another_device"), dcContext: self.dcContext)
- self.dcAccounts.stopIo() // TODO: is this needed?
- DispatchQueue.global(qos: .userInitiated).async { [weak self] in
- guard let self = self else { return }
- self.dcContext.logger?.info("##### receiveBackup() with qr: \(qrCode)")
- let res = self.dcContext.receiveBackup(qrCode: qrCode)
- self.dcContext.logger?.info("##### receiveBackup() done with result: \(res)")
- }
- }
- ))
- alert.addAction(UIAlertAction(
- title: String.localized("cancel"),
- style: .cancel,
- handler: { [weak self] _ in
- self?.dcContext.stopOngoingProcess()
- self?.dismissQRReader()
- }
- ))
- qrCodeReader?.present(alert, animated: true)
- }
- private func qrErrorAlert() {
- let title = String.localized("qraccount_qr_code_cannot_be_used")
- let alert = UIAlertController(title: title, message: dcContext.lastErrorString, preferredStyle: .alert)
- let okAction = UIAlertAction(
- title: String.localized("ok"),
- style: .default,
- handler: { [weak self] _ in
- guard let self = self else { return }
- if self.accountCode != nil {
- // if an injected accountCode exists, the WelcomeViewController was only opened to handle that
- // if the action failed the whole controller should be dismissed
- self.cancelAccountCreation()
- } else {
- self.qrCodeReader?.startSession()
- }
- }
- )
- alert.addAction(okAction)
- qrCodeReader?.present(alert, animated: true, completion: nil)
- }
- private func dismissQRReader() {
- self.navigationController?.popViewController(animated: true)
- self.qrCodeReader = nil
- }
- private func stopAccessingSecurityScopedResource() {
- self.securityScopedResource?.stopAccessingSecurityScopedResource()
- self.securityScopedResource = nil
- }
- }
- // MARK: - WelcomeContentView
- class WelcomeContentView: UIView {
- var onLogin: VoidFunction?
- var onScanQRCode: VoidFunction?
- var onImportBackup: VoidFunction?
- var minContainerHeight: CGFloat = 0 {
- didSet {
- containerMinHeightConstraint.constant = minContainerHeight
- logoHeightConstraint.constant = calculateLogoHeight()
- }
- }
- private lazy var containerMinHeightConstraint: NSLayoutConstraint = {
- return container.heightAnchor.constraint(greaterThanOrEqualToConstant: 0)
- }()
- private lazy var logoHeightConstraint: NSLayoutConstraint = {
- return logoView.heightAnchor.constraint(equalToConstant: 0)
- }()
- private var container = UIView()
- private var logoView: UIImageView = {
- let image = #imageLiteral(resourceName: "background_intro")
- let view = UIImageView(image: image)
- return view
- }()
- private lazy var titleLabel: UILabel = {
- let label = UILabel()
- label.text = String.localized("welcome_chat_over_email")
- label.textColor = DcColors.grayTextColor
- label.textAlignment = .center
- label.numberOfLines = 0
- label.font = UIFont.systemFont(ofSize: 24, weight: .bold)
- return label
- }()
- private lazy var buttonStack: UIStackView = {
- let stack = UIStackView(arrangedSubviews: [loginButton, qrCodeButton, importBackupButton])
- stack.axis = .vertical
- stack.spacing = 15
- return stack
- }()
- private lazy var loginButton: UIButton = {
- let button = UIButton(type: .roundedRect)
- let title = String.localized("login_header").uppercased()
- button.setTitle(title, for: .normal)
- button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .regular)
- button.setTitleColor(.white, for: .normal)
- button.backgroundColor = DcColors.primary
- let insets = button.contentEdgeInsets
- button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 15, bottom: 8, right: 15)
- button.layer.cornerRadius = 5
- button.clipsToBounds = true
- button.addTarget(self, action: #selector(loginButtonPressed(_:)), for: .touchUpInside)
- return button
- }()
- private lazy var qrCodeButton: UIButton = {
- let button = UIButton()
- let title = String.localized("qrscan_title")
- button.setTitleColor(UIColor.systemBlue, for: .normal)
- button.setTitle(title, for: .normal)
- button.addTarget(self, action: #selector(qrCodeButtonPressed(_:)), for: .touchUpInside)
- return button
- }()
- private lazy var importBackupButton: UIButton = {
- let button = UIButton()
- let title = String.localized("import_backup_title")
- button.setTitleColor(UIColor.systemBlue, for: .normal)
- button.setTitle(title, for: .normal)
- button.addTarget(self, action: #selector(importBackupButtonPressed(_:)), for: .touchUpInside)
- return button
- }()
- private let defaultSpacing: CGFloat = 20
- init() {
- super.init(frame: .zero)
- setupSubviews()
- backgroundColor = DcColors.defaultBackgroundColor
- }
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- // MARK: - setup
- private func setupSubviews() {
- addSubview(container)
- container.translatesAutoresizingMaskIntoConstraints = false
- container.topAnchor.constraint(equalTo: topAnchor).isActive = true
- container.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
- container.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.75).isActive = true
- container.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0).isActive = true
- containerMinHeightConstraint.isActive = true
- _ = [logoView, titleLabel].map {
- addSubview($0)
- $0.translatesAutoresizingMaskIntoConstraints = false
- }
- let bottomLayoutGuide = UILayoutGuide()
- container.addLayoutGuide(bottomLayoutGuide)
- bottomLayoutGuide.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
- bottomLayoutGuide.heightAnchor.constraint(equalTo: container.heightAnchor, multiplier: 0.45).isActive = true
- titleLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
- titleLabel.trailingAnchor.constraint(equalTo: container.trailingAnchor).isActive = true
- titleLabel.topAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
- titleLabel.setContentHuggingPriority(.defaultHigh, for: .vertical)
- logoView.bottomAnchor.constraint(equalTo: titleLabel.topAnchor, constant: -defaultSpacing).isActive = true
- logoView.centerXAnchor.constraint(equalTo: container.centerXAnchor).isActive = true
- logoHeightConstraint.constant = calculateLogoHeight()
- logoHeightConstraint.isActive = true
- logoView.widthAnchor.constraint(equalTo: logoView.heightAnchor).isActive = true
- let logoTopAnchor = logoView.topAnchor.constraint(equalTo: container.topAnchor, constant: 20) // this will allow the container to grow in height
- logoTopAnchor.priority = .defaultLow
- logoTopAnchor.isActive = true
- let buttonContainerGuide = UILayoutGuide()
- container.addLayoutGuide(buttonContainerGuide)
- buttonContainerGuide.topAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true
- buttonContainerGuide.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
- loginButton.setContentHuggingPriority(.defaultHigh, for: .vertical)
- container.addSubview(buttonStack)
- buttonStack.translatesAutoresizingMaskIntoConstraints = false
- buttonStack.centerXAnchor.constraint(equalTo: container.centerXAnchor).isActive = true
- buttonStack.centerYAnchor.constraint(equalTo: buttonContainerGuide.centerYAnchor).isActive = true
- let buttonStackTopAnchor = buttonStack.topAnchor.constraint(equalTo: buttonContainerGuide.topAnchor, constant: defaultSpacing)
- // this will allow the container to grow in height
- let buttonStackBottomAnchor = buttonStack.bottomAnchor.constraint(equalTo: buttonContainerGuide.bottomAnchor, constant: -50)
- _ = [buttonStackTopAnchor, buttonStackBottomAnchor].map {
- $0.priority = .defaultLow
- $0.isActive = true
- }
- }
- private func calculateLogoHeight() -> CGFloat {
- if UIDevice.current.userInterfaceIdiom == .phone {
- return UIApplication.shared.statusBarOrientation.isLandscape ? UIScreen.main.bounds.height * 0.5 : UIScreen.main.bounds.width * 0.75
- } else {
- return 275
- }
- }
- // MARK: - actions
- @objc private func loginButtonPressed(_ sender: UIButton) {
- onLogin?()
- }
- @objc private func qrCodeButtonPressed(_ sender: UIButton) {
- onScanQRCode?()
- }
- @objc private func importBackupButtonPressed(_ sender: UIButton) {
- onImportBackup?()
- }
- }
- extension WelcomeViewController: MediaPickerDelegate {
- func onDocumentSelected(url: NSURL) {
- // ensure we can access folders outside of the app's sandbox
- let isSecurityScopedResource = url.startAccessingSecurityScopedResource()
- if isSecurityScopedResource {
- securityScopedResource = url
- }
- if let selectedBackupFilePath = url.relativePath {
- addProgressHudBackupListener()
- importBackup(at: selectedBackupFilePath)
- } else {
- stopAccessingSecurityScopedResource()
- }
- }
- }
|