diff --git a/Brand/NCBrand.swift b/Brand/NCBrand.swift index 3fac622ea9..55060a521e 100755 --- a/Brand/NCBrand.swift +++ b/Brand/NCBrand.swift @@ -73,6 +73,7 @@ let userAgent: String = { var doNotAskPasscodeAtStartup: Bool = false var disable_source_code_in_settings: Bool = false var enforce_passcode_lock = false + var use_in_app_browser_for_login = true // (name: "Name 1", url: "https://cloud.nextcloud.com"),(name: "Name 2", url: "https://cloud.nextcloud.com") var enforce_servers: [(name: String, url: String)] = [] diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index b0c9548050..11db9f8231 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -79,6 +79,7 @@ F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; }; F314F1142A30E2DE00BC7FAB /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E8A390295DC5E0006CB2D0 /* View+Extension.swift */; }; F321DA8A2B71205A00DDA0E6 /* NCTrashSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */; }; + F32FADA92D1176E3007035E2 /* UIButton+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32FADA82D1176DE007035E2 /* UIButton+Extension.swift */; }; F33918C42C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33918C32C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift */; }; F33918C52C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33918C32C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift */; }; F33918C62C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33918C32C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift */; }; @@ -180,6 +181,7 @@ F3BB464D2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */; }; F3BB46522A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */; }; F3BB46542A3A1E9D00461F6E /* CCCellMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */; }; + F3CA33842D10726E00672333 /* NCLoginPollModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3CA33832D10726E00672333 /* NCLoginPollModel.swift */; }; F3E173B02C9AF637006D177A /* ScreenAwakeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E173AF2C9AF637006D177A /* ScreenAwakeManager.swift */; }; F3E173C02C9B1067006D177A /* AwakeMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E173BF2C9B1067006D177A /* AwakeMode.swift */; }; F3E173C12C9B1067006D177A /* AwakeMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E173BF2C9B1067006D177A /* AwakeMode.swift */; }; @@ -1181,7 +1183,6 @@ AAF806B32CE25EFE009C2D43 /* NCShareToggleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareToggleCell.swift; sourceTree = ""; }; AAF806B52CE34C72009C2D43 /* NCShareDownloadLimitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareDownloadLimitViewController.swift; sourceTree = ""; }; AAF806B72CE37C15009C2D43 /* NCShareDownloadLimitTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareDownloadLimitTableViewController.swift; sourceTree = ""; }; - AAF806B92CE38BB2009C2D43 /* NextcloudKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NextcloudKit; path = ../NextcloudKit; sourceTree = SOURCE_ROOT; }; AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Extension.swift"; sourceTree = ""; }; AF22B20B277C6F4D00DAB0CC /* NCShareCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCell.swift; sourceTree = ""; }; AF22B215277D196700DAB0CC /* NCShareExtension+DataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+DataSource.swift"; sourceTree = ""; }; @@ -1220,6 +1221,7 @@ D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; }; F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCViewerMedia+VisionKit.swift"; sourceTree = ""; }; F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTrashSelectTabBar.swift; sourceTree = ""; }; + F32FADA82D1176DE007035E2 /* UIButton+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Extension.swift"; sourceTree = ""; }; F33918C32C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileNameValidator+Extensions.swift"; sourceTree = ""; }; F33EE6F12BF4C9B200CA1A51 /* PKCS12.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PKCS12.swift; sourceTree = ""; }; F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PHAsset+Extension.swift"; sourceTree = ""; }; @@ -1242,6 +1244,7 @@ F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCMoreAppSuggestionsCell.xib; sourceTree = ""; }; F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMoreAppSuggestionsCell.swift; sourceTree = ""; }; F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CCCellMore.swift; sourceTree = ""; }; + F3CA33832D10726E00672333 /* NCLoginPollModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCLoginPollModel.swift; sourceTree = ""; }; F3E173AF2C9AF637006D177A /* ScreenAwakeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenAwakeManager.swift; sourceTree = ""; }; F3E173BF2C9B1067006D177A /* AwakeMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AwakeMode.swift; sourceTree = ""; }; F3EF2E0B2BFCF3810025EF46 /* NCLoginPoll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCLoginPoll.swift; sourceTree = ""; }; @@ -2069,6 +2072,15 @@ path = Cells; sourceTree = ""; }; + F3CA33802D106FF900672333 /* Poll */ = { + isa = PBXGroup; + children = ( + F3EF2E0B2BFCF3810025EF46 /* NCLoginPoll.swift */, + F3CA33832D10726E00672333 /* NCLoginPollModel.swift */, + ); + path = Poll; + sourceTree = ""; + }; F3E173BE2C9B1057006D177A /* ScreenAwakeManager */ = { isa = PBXGroup; children = ( @@ -2654,6 +2666,7 @@ F7A0D14E259229FA008F8A13 /* Extensions */ = { isa = PBXGroup; children = ( + F32FADA82D1176DE007035E2 /* UIButton+Extension.swift */, F7AC1CAF28AB94490032D99F /* Array+Extension.swift */, F7817CF729801A3500FFBC65 /* Data+Extension.swift */, AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */, @@ -2766,6 +2779,7 @@ F7BFFA621A24D7300044ED85 /* Login */ = { isa = PBXGroup; children = ( + F3CA33802D106FF900672333 /* Poll */, F702F2F025EE5CDA008F8E80 /* NCLogin.storyboard */, F702F2F625EE5CEC008F8E80 /* NCLogin.swift */, F738D48F2756740100CD1D38 /* NCLoginNavigationController.swift */, @@ -2773,7 +2787,6 @@ F7AE00F4230D5F9E007ACF8A /* NCLoginProvider.swift */, F7BC287D26663F6C004D46C5 /* NCViewCertificateDetails.storyboard */, F7BC287F26663F85004D46C5 /* NCViewCertificateDetails.swift */, - F3EF2E0B2BFCF3810025EF46 /* NCLoginPoll.swift */, ); path = Login; sourceTree = ""; @@ -3069,7 +3082,6 @@ F7F67B9F1A24D27800EE80DA = { isa = PBXGroup; children = ( - AAF806B92CE38BB2009C2D43 /* NextcloudKit */, F7B8B82F25681C3400967775 /* GoogleService-Info.plist */, F7C1CDD91E6DFC6F005D92BE /* Brand */, F7F67BAA1A24D27800EE80DA /* iOSClient */, @@ -4326,8 +4338,10 @@ F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */, F77DD6A82C5CC093009448FB /* NCSession.swift in Sources */, F702F30825EE5D47008F8E80 /* NCPopupViewController.swift in Sources */, + F3CA33842D10726E00672333 /* NCLoginPollModel.swift in Sources */, F733598125C1C188002ABA72 /* NCAskAuthorization.swift in Sources */, 370D26AF248A3D7A00121797 /* NCCellProtocol.swift in Sources */, + F32FADA92D1176E3007035E2 /* UIButton+Extension.swift in Sources */, F768822C2C0DD1E7001CF441 /* NCKeychain.swift in Sources */, F7BFFD282C8846020029A201 /* NCHud.swift in Sources */, F71CD6CA2930D7B1006C95C1 /* NCApplicationHandle.swift in Sources */, diff --git a/iOSClient/Extensions/UIButton+Extension.swift b/iOSClient/Extensions/UIButton+Extension.swift new file mode 100644 index 0000000000..70a85857a5 --- /dev/null +++ b/iOSClient/Extensions/UIButton+Extension.swift @@ -0,0 +1,40 @@ +// +// UIButton+Extension.swift +// Nextcloud +// +// Created by Milen Pivchev on 17.12.24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// + +extension UIButton { + func hideButtonAndShowSpinner(tint: UIColor = .white) { + self.isHidden = true + + let spinnerTag = Int(bitPattern: Unmanaged.passUnretained(self).toOpaque()) + if self.superview?.subviews.first(where: { view -> Bool in + return view.isKind(of: UIActivityIndicatorView.self) && view.tag == spinnerTag + }) != nil { + return + } + + let spinner = UIActivityIndicatorView(style: .medium) + spinner.tag = spinnerTag + spinner.color = tint + spinner.startAnimating() + spinner.center = self.center + self.superview?.addSubview(spinner) + spinner.translatesAutoresizingMaskIntoConstraints = false + spinner.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true + spinner.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true + } + + func hideSpinnerAndShowButton() { + let spinnerTag = Int(bitPattern: Unmanaged.passUnretained(self).toOpaque()) + let spinner = self.superview?.subviews.first(where: { view -> Bool in + return view.isKind(of: UIActivityIndicatorView.self) && view.tag == spinnerTag + }) + + spinner?.removeFromSuperview() + self.isHidden = false + } +} diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift index 8ee3813b07..95d4f38b63 100644 --- a/iOSClient/Login/NCLogin.swift +++ b/iOSClient/Login/NCLogin.swift @@ -26,6 +26,7 @@ import UIKit import NextcloudKit import SwiftEntryKit import SwiftUI +import SafariServices class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { @IBOutlet weak var imageBrand: UIImageView! @@ -46,10 +47,6 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { private var shareAccounts: [NKShareAccounts.DataAccounts]? - var loginFlowV2Token = "" - var loginFlowV2Endpoint = "" - var loginFlowV2Login = "" - /// The URL that will show up on the URL field when this screen appears var urlBase = "" @@ -62,6 +59,12 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { private var p12Data: Data? private var p12Password: String? + var pollTimer: DispatchSourceTimer? + + var ncLoginPollModel = NCLoginPollModel() + + var loginFlowInProgress = false + // MARK: - View Life Cycle override func viewDidLoad() { @@ -182,11 +185,11 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { enforceServersButton.menu = .init(title: NSLocalizedString("_servers_", comment: ""), children: actions) enforceServersButton.showsMenuAsPrimaryAction = true enforceServersButton.configuration?.titleTextAttributesTransformer = - UIConfigurationTextAttributesTransformer { incoming in - var outgoing = incoming - outgoing.font = UIFont.systemFont(ofSize: 13) - return outgoing - } + UIConfigurationTextAttributesTransformer { incoming in + var outgoing = incoming + outgoing.font = UIFont.systemFont(ofSize: 13) + return outgoing + } } } @@ -283,7 +286,6 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { } @IBAction func actionQRCode(_ sender: Any) { - let qrCode = NCLoginQRCode(delegate: self) qrCode.scan() } @@ -316,6 +318,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { guard var url = baseUrlTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) else { return } if url.hasSuffix("/") { url = String(url.dropLast()) } if url.isEmpty { return } + // Check whether baseUrl contain protocol. If not add https:// by default. if url.hasPrefix("https") == false && url.hasPrefix("http") == false { url = "https://" + url @@ -326,26 +329,41 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { func isUrlValid(url: String, user: String? = nil) { loginButton.isEnabled = false - NextcloudKit.shared.getServerStatus(serverUrl: url) { _, serverInfoResult in + loginButton.hideButtonAndShowSpinner() + + NextcloudKit.shared.getServerStatus(serverUrl: url) { [self] _, serverInfoResult in switch serverInfoResult { case .success(let serverInfo): if let host = URL(string: url)?.host { NCNetworking.shared.writeCertificate(host: host) } - NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { token, endpoint, login, _, error in - self.loginButton.isEnabled = true + NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { [self] token, endpoint, login, _, error in // Login Flow V2 if error == .success, let token, let endpoint, let login { - let vc = UIHostingController(rootView: NCLoginPoll(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login)) - self.present(vc, animated: true) + guard let url = URL(string: login) else { return } + let vc: UIViewController + + poll(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login) + + if NCBrandOptions.shared.use_in_app_browser_for_login { + let safariVC = SFSafariViewController(url: url) + safariVC.delegate = self + vc = safariVC + } else { + vc = UIHostingController(rootView: NCLoginPoll(loginFlowV2Login: login, model: ncLoginPollModel)) + } + + present(vc, animated: true) } else if serverInfo.versionMajor < NCGlobal.shared.nextcloudVersion12 { // No login flow available let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_webflow_not_available_", comment: ""), preferredStyle: .alert) alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in })) - self.present(alertController, animated: true, completion: { }) + present(alertController, animated: true, completion: { }) } } case .failure(let error): - self.loginButton.isEnabled = true + loginButton.hideSpinnerAndShowButton() + loginButton.isEnabled = true + if error.errorCode == NSURLErrorServerCertificateUntrusted { let alertController = UIAlertController(title: NSLocalizedString("_ssl_certificate_untrusted_", comment: ""), message: NSLocalizedString("_connect_server_anyway_", comment: ""), preferredStyle: .alert) alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in @@ -473,7 +491,6 @@ extension NCLogin: ClientCertificateDelegate, UIDocumentPickerDelegate { let alertEnterPassword = UIAlertController(title: NSLocalizedString("_client_cert_enter_password_", comment: ""), message: "", preferredStyle: .alert) alertEnterPassword.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: nil)) alertEnterPassword.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in - // let documentProviderMenu = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.pkcs12]) NCNetworking.shared.p12Data = try? Data(contentsOf: urls[0]) NCNetworking.shared.p12Password = alertEnterPassword.textFields?[0].text self.login() @@ -491,4 +508,63 @@ extension NCLogin: ClientCertificateDelegate, UIDocumentPickerDelegate { alertWrongPassword.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default)) present(alertWrongPassword, animated: true) } + + func poll(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginFlowV2Login: String) { + let queue = DispatchQueue.global(qos: .background) + pollTimer = DispatchSource.makeTimerSource(queue: queue) + + guard let timer = pollTimer else { return } + + timer.schedule(deadline: .now(), repeating: .seconds(1), leeway: .seconds(1)) + timer.setEventHandler(handler: { + DispatchQueue.main.async { + let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController + NextcloudKit.shared.getLoginFlowV2Poll(token: loginFlowV2Token, endpoint: loginFlowV2Endpoint) { [self] server, loginName, appPassword, _, error in + if error == .success, let urlBase = server, let user = loginName, let appPassword { + loginFlowInProgress = true + ncLoginPollModel.isLoading = true + + NCAccount().createAccount(urlBase: urlBase, user: user, password: appPassword, controller: controller) { account, error in + + if error == .success { + let window = UIApplication.shared.firstWindow + if let controller = window?.rootViewController as? NCMainTabBarController { + controller.account = account + controller.dismiss(animated: true, completion: nil) + } else { + if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController { + controller.account = account + controller.modalPresentationStyle = .fullScreen + controller.view.alpha = 0 + + window?.rootViewController = controller + window?.makeKeyAndVisible() + + if let scene = window?.windowScene { + SceneManager.shared.register(scene: scene, withRootViewController: controller) + } + + UIView.animate(withDuration: 0.5) { + controller.view.alpha = 1 + } + } + } + } + } + } + } + } + }) + + timer.resume() + } +} + +extension NCLogin: SFSafariViewControllerDelegate { + func safariViewControllerDidFinish(_ controller: SFSafariViewController) { + if !loginFlowInProgress { + loginButton.isEnabled = true + loginButton.hideSpinnerAndShowButton() + } + } } diff --git a/iOSClient/Login/NCLoginPoll.swift b/iOSClient/Login/NCLoginPoll.swift deleted file mode 100644 index 99e513a56e..0000000000 --- a/iOSClient/Login/NCLoginPoll.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// SwiftUIView.swift -// Nextcloud -// -// Created by Milen on 21.05.24. -// Copyright © 2024 Marino Faggiana. All rights reserved. -// -// Author Marino Faggiana -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -import NextcloudKit -import SwiftUI - -struct NCLoginPoll: View { - let loginFlowV2Token: String - let loginFlowV2Endpoint: String - let loginFlowV2Login: String - - var cancelButtonDisabled = false - - @ObservedObject private var loginManager = LoginManager() - @Environment(\.dismiss) private var dismiss - - var body: some View { - VStack { - Text(NSLocalizedString("_poll_desc_", comment: "")) - .multilineTextAlignment(.center) - .foregroundStyle(.white) - .padding() - - ProgressView() - .scaleEffect(1.5) - .tint(.white) - .padding() - - HStack { - Button(NSLocalizedString("_cancel_", comment: "")) { - dismiss() - } - .disabled(loginManager.isLoading || cancelButtonDisabled) - .buttonStyle(.bordered) - .tint(.white) - - Button(NSLocalizedString("_retry_", comment: "")) { - loginManager.openLoginInBrowser() - } - .buttonStyle(.borderedProminent) - .foregroundStyle(Color(NCBrandColor.shared.customer)) - .tint(.white) - } - .padding() - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .onChange(of: loginManager.pollFinished) { value in - if value { - let window = UIApplication.shared.firstWindow - if let controller = window?.rootViewController as? NCMainTabBarController { - controller.account = loginManager.account - controller.dismiss(animated: true, completion: nil) - } else { - if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController { - controller.account = loginManager.account - controller.modalPresentationStyle = .fullScreen - controller.view.alpha = 0 - - window?.rootViewController = controller - window?.makeKeyAndVisible() - - if let scene = window?.windowScene { - SceneManager.shared.register(scene: scene, withRootViewController: controller) - } - - UIView.animate(withDuration: 0.5) { - controller.view.alpha = 1 - } - } - } - } - } - .background(Color(NCBrandColor.shared.customer)) - .onAppear { - loginManager.configure(loginFlowV2Token: loginFlowV2Token, loginFlowV2Endpoint: loginFlowV2Endpoint, loginFlowV2Login: loginFlowV2Login) - - if !isRunningForPreviews { - loginManager.openLoginInBrowser() - } - } - .onDisappear { - loginManager.onDisappear() - } - .interactiveDismissDisabled() - } -} - -#Preview { - NCLoginPoll(loginFlowV2Token: "", loginFlowV2Endpoint: "", loginFlowV2Login: "") -} - -private class LoginManager: ObservableObject { - var loginFlowV2Token = "" - var loginFlowV2Endpoint = "" - var loginFlowV2Login = "" - - @Published var pollFinished = false - @Published var isLoading = false - @Published var account = "" - - var timer: DispatchSourceTimer? - - func configure(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginFlowV2Login: String) { - self.loginFlowV2Token = loginFlowV2Token - self.loginFlowV2Endpoint = loginFlowV2Endpoint - self.loginFlowV2Login = loginFlowV2Login - - poll() - } - - func poll() { - let queue = DispatchQueue.global(qos: .background) - timer = DispatchSource.makeTimerSource(queue: queue) - - guard let timer = timer else { return } - - timer.schedule(deadline: .now(), repeating: .seconds(1), leeway: .seconds(1)) - timer.setEventHandler(handler: { - DispatchQueue.main.async { - let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController - NextcloudKit.shared.getLoginFlowV2Poll(token: self.loginFlowV2Token, endpoint: self.loginFlowV2Endpoint) { server, loginName, appPassword, _, error in - if error == .success, let urlBase = server, let user = loginName, let appPassword { - self.isLoading = true - NCAccount().createAccount(urlBase: urlBase, user: user, password: appPassword, controller: controller) { account, error in - if error == .success { - self.account = account - self.pollFinished = true - } - } - } - } - } - }) - - timer.resume() - } - - func onDisappear() { - timer?.cancel() - } - - func openLoginInBrowser() { - UIApplication.shared.open(URL(string: loginFlowV2Login)!) - } -} diff --git a/iOSClient/Login/Poll/NCLoginPoll.swift b/iOSClient/Login/Poll/NCLoginPoll.swift new file mode 100644 index 0000000000..0a2638c784 --- /dev/null +++ b/iOSClient/Login/Poll/NCLoginPoll.swift @@ -0,0 +1,75 @@ +// +// SwiftUIView.swift +// Nextcloud +// +// Created by Milen on 21.05.24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import NextcloudKit +import SwiftUI + +struct NCLoginPoll: View { + let loginFlowV2Login: String + + @ObservedObject var model: NCLoginPollModel + @Environment(\.dismiss) private var dismiss + + var body: some View { + VStack { + Text(NSLocalizedString("_poll_desc_", comment: "")) + .multilineTextAlignment(.center) + .foregroundStyle(.white) + .padding() + + ProgressView() + .scaleEffect(1.5) + .tint(.white) + .padding() + + HStack { + Button(NSLocalizedString("_cancel_", comment: "")) { + dismiss() + } + .disabled(model.isLoading) + .buttonStyle(.bordered) + .tint(.white) + + Button(NSLocalizedString("_retry_", comment: "")) { + model.openLoginInBrowser(loginFlowV2Login: loginFlowV2Login) + } + .buttonStyle(.borderedProminent) + .foregroundStyle(Color(NCBrandColor.shared.customer)) + .tint(.white) + } + .padding() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(NCBrandColor.shared.customer)) + .onAppear { + if !isRunningForPreviews { + model.openLoginInBrowser(loginFlowV2Login: loginFlowV2Login) + } + } + .interactiveDismissDisabled() + } +} + +#Preview { + NCLoginPoll(loginFlowV2Login: "", model: NCLoginPollModel()) +} diff --git a/iOSClient/Login/Poll/NCLoginPollModel.swift b/iOSClient/Login/Poll/NCLoginPollModel.swift new file mode 100644 index 0000000000..26480fd2de --- /dev/null +++ b/iOSClient/Login/Poll/NCLoginPollModel.swift @@ -0,0 +1,17 @@ +// +// NCLoginPollModel.swift +// Nextcloud +// +// Created by Milen Pivchev on 16.12.24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// + +import Foundation + +class NCLoginPollModel: ObservableObject { + @Published var isLoading = false + + func openLoginInBrowser(loginFlowV2Login: String = "") { + UIApplication.shared.open(URL(string: loginFlowV2Login)!) + } +}