Skip to content

Commit

Permalink
Update QR-code-scanning
Browse files Browse the repository at this point in the history
  • Loading branch information
zeitschlag committed May 21, 2024
1 parent 6a95c4d commit 1f67a7c
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 245 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,6 @@ class InstantOnboardingView: UIView {

privacyButton = UIButton(type: .system)
privacyButton.translatesAutoresizingMaskIntoConstraints = false
if let customProvider {
privacyButton.setTitle(String.localized(stringID: "instant_onboarding_agree_instance", parameter: customProvider), for: .normal)
} else {
privacyButton.setTitle(String.localized("instant_onboarding_agree_default"), for: .normal)
}
privacyButtonWrapper = UIView()
privacyButtonWrapper.translatesAutoresizingMaskIntoConstraints = false
privacyButtonWrapper.addSubview(privacyButton)
Expand Down Expand Up @@ -113,6 +108,7 @@ class InstantOnboardingView: UIView {

setupConstraints()
validateTextfield(text: nameTextField.text)
updateContent(with: customProvider)
}

required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
Expand Down Expand Up @@ -171,4 +167,12 @@ class InstantOnboardingView: UIView {
agreeButton.backgroundColor = UIColor.systemGray.withAlphaComponent(0.3)
}
}

func updateContent(with customProvider: String?) {
if let customProvider {
privacyButton.setTitle(String.localized(stringID: "instant_onboarding_agree_instance", parameter: customProvider), for: .normal)
} else {
privacyButton.setTitle(String.localized("instant_onboarding_agree_default"), for: .normal)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,120 +233,29 @@ extension InstantOnboardingViewController: MediaPickerDelegate {
// MARK: - QrCodeReaderDelegate
extension InstantOnboardingViewController: 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)
// update with new code
let parsedQrCode = dcContext.checkQR(qrCode: code)
if parsedQrCode.state == DC_QR_LOGIN || parsedQrCode.state == DC_QR_ACCOUNT,
let host = parsedQrCode.text1,
let url = URL(string: "https://\(host)") {
self.providerHostURL = url
self.qrCodeData = code

contentView?.updateContent(with: host)
dismissQRReader()
} 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 else { return }
self.dismissQRReader()
self.createAccountFromQRCode(qrCode: qrCode)
}
)

let qrCancelAction = UIAlertAction(
title: String.localized("cancel"),
style: .cancel,
handler: { [weak self] _ in
guard let self else { return }
self.dismissQRReader()
// if an injected qrCodeData exists, the InstantOnboardingViewController was only opened to handle that
// cancelling the action should also dismiss the whole controller
if self.qrCodeData != 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()
let alert = UIAlertController(title: String.localized("multidevice_receiver_title"),
message: String.localized("multidevice_receiver_scanning_ask"),
preferredStyle: .alert)
alert.addAction(UIAlertAction(
title: String.localized("ok"),
style: .default,
handler: { [weak self] _ in
guard let self else { return }
if self.dcAccounts.getSelected().isConfigured() {
UserDefaults.standard.setValue(self.dcAccounts.getSelected().id, forKey: Constants.Keys.lastSelectedAccountKey)
_ = self.dcAccounts.add()
}
let accountId = self.dcAccounts.getSelected().id
if accountId != 0 {
self.dcContext = self.dcAccounts.get(id: accountId)
self.dismissQRReader()
self.addProgressHudBackupListener(importByFile: false)
self.showProgressAlert(title: String.localized("multidevice_receiver_title"), dcContext: self.dcContext)
self.dcAccounts.stopIo()
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let self else { return }
logger.info("##### receiveBackup() with qr: \(qrCode)")
let res = self.dcContext.receiveBackup(qrCode: qrCode)
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()
}
))
if let qrCodeReader {
qrCodeReader.present(alert, animated: true)
} else {
self.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 else { return }
if self.qrCodeData != nil {
// if an injected qrCodeData 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()
}
self?.qrCodeReader?.startSession()
}
)
alert.addAction(okAction)
Expand All @@ -357,142 +266,4 @@ extension InstantOnboardingViewController: QrCodeReaderDelegate {
self.navigationController?.popViewController(animated: true)
self.qrCodeReader = nil
}

private func stopAccessingSecurityScopedResource() {
self.securityScopedResource?.stopAccessingSecurityScopedResource()
self.securityScopedResource = nil
}

private func createAccountFromQRCode(qrCode: String) {
if dcAccounts.getSelected().isConfigured() {
UserDefaults.standard.setValue(dcAccounts.getSelected().id, forKey: Constants.Keys.lastSelectedAccountKey)
_ = dcAccounts.add()
}
let accountId = dcAccounts.getSelected().id

if accountId != 0 {
dcContext = dcAccounts.get(id: accountId)
addProgressAlertListener(dcAccounts: self.dcAccounts,
progressName: eventConfigureProgress,
onSuccess: self.handleLoginSuccess)
showProgressAlert(title: String.localized("login_header"), dcContext: self.dcContext)
DispatchQueue.global().async { [weak self] in
guard let 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.qrCodeData != nil ? self.cancelAccountCreation : nil)
}
}
}
}
}

@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 handleLoginSuccess() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
if !UserDefaults.standard.bool(forKey: "notifications_disabled") {
appDelegate.registerForNotifications()
}

let profileInfoController = ProfileInfoViewController(context: self.dcContext)
profileInfoController.onClose = {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.reloadDcContext()
}
}

navigationController?.setViewControllers([profileInfoController], animated: true)
}

private func addProgressHudBackupListener(importByFile: Bool) {
UIApplication.shared.isIdleTimerDisabled = true
backupProgressObserver = NotificationCenter.default.addObserver(
forName: eventImexProgress,
object: nil,
queue: nil
) { [weak self] notification in
guard let 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")
}
self.updateProgressAlert(error: ui["errorMessage"] as? String)
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 if importByFile {
self.updateProgressAlertValue(value: ui["progress"] as? Int)
} else {
guard let permille = ui["progress"] as? Int else { return }
var statusLineText = ""
if permille <= 100 {
statusLineText = String.localized("preparing_account")
} else if permille <= 950 {
let percent = ((permille-100)*100)/850
statusLineText = String.localized("transferring") + " \(percent)%"
} else {
statusLineText = "Finishing..." // range not used, should not happen
}
self.updateProgressAlert(message: statusLineText)
}
}
}
}

private func removeBackupProgressObserver() {
if let backupProgressObserver {
NotificationCenter.default.removeObserver(backupProgressObserver)
self.backupProgressObserver = nil
}
}

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()
}
}
}

0 comments on commit 1f67a7c

Please sign in to comment.