From 728cf677a75c30cf5fd06ea14d007b9a41f56a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20H=C3=BChne?= Date: Fri, 12 Nov 2021 13:28:56 +0100 Subject: [PATCH] [fix/branding-login-retry] Retry Section for Branded Login Error (#1047) * add a error section with a retry button in branded login view * #4786 added a retry interval of 10 seconds. In this time interval the app retries to establish the connection before a error message will be shown * only retry for network issues * only show retry section on network issues * - changed retry time interval to 30 seconds - retries minimum of 3 times * Update ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift Co-authored-by: Felix Schwarz * fixed connection retry and show retry section UI * HTTP pipeline is ready to schedule HTTP requests before the Static Login View Controller is brought up. Moved it before setting up the UIWindow * removed connection retries output and put it into the log Co-authored-by: Felix Schwarz --- ownCloud/AppDelegate.swift | 5 +- .../Resources/en.lproj/Localizable.strings | 1 + ownCloud/SceneDelegate.swift | 3 + .../StaticLoginSetupViewController.swift | 93 +++++++++++++------ 4 files changed, 73 insertions(+), 29 deletions(-) diff --git a/ownCloud/AppDelegate.swift b/ownCloud/AppDelegate.swift index ae61b10ac..a1fe7fd4f 100644 --- a/ownCloud/AppDelegate.swift +++ b/ownCloud/AppDelegate.swift @@ -44,6 +44,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Set up license management OCLicenseManager.shared.setupLicenseManagement() + // Set up HTTP pipelines + OCHTTPPipelineManager.setupPersistentPipelines() + // Set up app window = ThemeWindow(frame: UIScreen.main.bounds) @@ -83,8 +86,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { AppLockManager.shared.showLockscreenIfNeeded() } - OCHTTPPipelineManager.setupPersistentPipelines() // Set up HTTP pipelines - FileProviderInterfaceManager.shared.updateDomainsFromBookmarks() ScheduledTaskManager.shared.setup() diff --git a/ownCloud/Resources/en.lproj/Localizable.strings b/ownCloud/Resources/en.lproj/Localizable.strings index a37118716..da32cf161 100644 --- a/ownCloud/Resources/en.lproj/Localizable.strings +++ b/ownCloud/Resources/en.lproj/Localizable.strings @@ -70,6 +70,7 @@ "Manage Storage" = "Manage Storage"; "Access Files" = "Access Files"; "Log out" = "Log out"; +"Retry" = "Retry"; /* Bookmark Info */ "Bookmark Metadata" = "Bookmark Metadata"; diff --git a/ownCloud/SceneDelegate.swift b/ownCloud/SceneDelegate.swift index b420f5bc1..2204d8afc 100644 --- a/ownCloud/SceneDelegate.swift +++ b/ownCloud/SceneDelegate.swift @@ -26,6 +26,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // UIWindowScene delegate func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Set up HTTP pipelines + OCHTTPPipelineManager.setupPersistentPipelines() + if let windowScene = scene as? UIWindowScene { window = ThemeWindow(windowScene: windowScene) var navigationController: UINavigationController? diff --git a/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift b/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift index f91860306..e9d4786c9 100644 --- a/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift +++ b/ownCloud/Static Login/Interface/StaticLoginSetupViewController.swift @@ -24,12 +24,18 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { var profile : StaticLoginProfile var bookmark : OCBookmark? var busySection : StaticTableViewSection? + var retrySection : StaticTableViewSection? private var urlString : String? private var username : String? private var password : String? private var passwordRow : StaticTableViewRow? private var isAuthenticating = false + private var retryTimeout : Date? + private let retryInterval : TimeInterval = 30 + private var retries : Int = 0 + private let numberOfRetries : Int = 3 + private let busySectionMessageLabel : UILabel = UILabel() init(loginViewController theLoginViewController: StaticLoginViewController, profile theProfile: StaticLoginProfile) { profile = theProfile @@ -160,39 +166,53 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { tokenMaskSection.addStaticHeader(title: profile.welcome!, message: profile.promptForTokenAuth) if VendorServices.shared.canAddAccount, OCBookmarkManager.shared.bookmarks.count > 0 { - let (proceedButton, cancelButton) = tokenMaskSection.addButtonFooter(proceedLabel: "Continue", cancelLabel: "Cancel") + let (proceedButton, cancelButton) = tokenMaskSection.addButtonFooter(proceedLabel: "Continue".localized, cancelLabel: "Cancel".localized) proceedButton?.addTarget(self, action: #selector(self.startAuthentication), for: .touchUpInside) cancelButton?.addTarget(self, action: #selector(self.cancel(_:)), for: .touchUpInside) } else { - let (proceedButton, _) = tokenMaskSection.addButtonFooter(proceedLabel: "Continue", proceedItemStyle: .welcome, cancelLabel: nil) + let (proceedButton, _) = tokenMaskSection.addButtonFooter(proceedLabel: "Continue".localized, proceedItemStyle: .welcome, cancelLabel: nil) proceedButton?.addTarget(self, action: #selector(self.startAuthentication), for: .touchUpInside) } return tokenMaskSection } + func retrySection(issues: DisplayIssues) -> StaticTableViewSection { + let retrySection : StaticTableViewSection = StaticTableViewSection(headerTitle: nil, identifier: "retrySection") + + for issue in issues.displayIssues { + if let title = issue.localizedTitle, let localizedDescription = issue.localizedDescription { + retrySection.add(row: StaticTableViewRow(message: localizedDescription, title: title)) + } + } + + let (proceedButton, _) = retrySection.addButtonFooter(proceedLabel: "Retry".localized, proceedItemStyle: .welcome, cancelLabel: nil) + proceedButton?.addTarget(self, action: #selector(proceedWithLogin), for: .touchUpInside) + + return retrySection + } + func busySection(message: String) -> StaticTableViewSection { let busySection : StaticTableViewSection = StaticTableViewSection(headerTitle: nil, identifier: "busySection") let activityIndicator : UIActivityIndicatorView = UIActivityIndicatorView(style: Theme.shared.activeCollection.activityIndicatorViewStyle) let containerView : FullWidthHeaderView = FullWidthHeaderView() let centerView : UIView = UIView() - let messageLabel : UILabel = UILabel() containerView.translatesAutoresizingMaskIntoConstraints = false centerView.translatesAutoresizingMaskIntoConstraints = false - messageLabel.translatesAutoresizingMaskIntoConstraints = false + busySectionMessageLabel.translatesAutoresizingMaskIntoConstraints = false activityIndicator.translatesAutoresizingMaskIntoConstraints = false centerView.addSubview(activityIndicator) - centerView.addSubview(messageLabel) + centerView.addSubview(busySectionMessageLabel) containerView.addSubview(centerView) containerView.addThemeApplier({ (_, collection, _) in - messageLabel.applyThemeCollection(collection, itemStyle: .welcomeMessage) + self.busySectionMessageLabel.applyThemeCollection(collection, itemStyle: .welcomeMessage) }) - messageLabel.text = message + busySectionMessageLabel.text = message NSLayoutConstraint.activate([ activityIndicator.widthAnchor.constraint(equalToConstant: 30), @@ -201,9 +221,9 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { activityIndicator.topAnchor.constraint(equalTo: centerView.topAnchor), activityIndicator.bottomAnchor.constraint(equalTo: centerView.bottomAnchor), - messageLabel.centerYAnchor.constraint(equalTo: centerView.centerYAnchor), - messageLabel.leftAnchor.constraint(equalTo: activityIndicator.rightAnchor, constant: 20), - messageLabel.rightAnchor.constraint(equalTo: centerView.rightAnchor), + busySectionMessageLabel.centerYAnchor.constraint(equalTo: centerView.centerYAnchor), + busySectionMessageLabel.leftAnchor.constraint(equalTo: activityIndicator.rightAnchor, constant: 20), + busySectionMessageLabel.rightAnchor.constraint(equalTo: centerView.rightAnchor), centerView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 40), centerView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor), @@ -288,7 +308,7 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { } } - func proceedWithLogin() { + @objc func proceedWithLogin() { guard self.bookmark != nil else { let alertController = ThemedAlertController(title: "Missing Profile URL".localized, message: String(format: "The Profile '%@' does not have a URL configured.\nPlease provide a URL via configuration or MDM.".localized, profile.name ?? ""), preferredStyle: .alert) @@ -306,6 +326,9 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { if let onboardingSection = self.sectionForIdentifier("onboardingSection") { self.removeSection(onboardingSection) } + if let retrySection = self.sectionForIdentifier("retrySection") { + self.removeSection(retrySection) + } if busySection == nil { busySection = self.busySection(message: "Contacting server…".localized) @@ -448,27 +471,39 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { var proceed : Bool = true if let issue = connectionIssue { - proceed = self.show(issue: issue, proceed: { () in + if self.retryTimeout == nil { + self.retryTimeout = Date().addingTimeInterval(self.retryInterval) + } + if let retryDate = self.retryTimeout, ((Date() < retryDate) || self.retries <= self.numberOfRetries), issue.issues?.first?.error?.isNetworkConnectionError == true { + self.retries += 1 + proceed = false OnMainThread { - if isInitialRequest { - self.determineSupportedAuthMethod(false) - } + Log.warning(String(format: "%@ (%ld)", "Contacting server…".localized, self.retries)) + self.determineSupportedAuthMethod(false) } - }, cancel: { () in - OnMainThread { - if self.profile.canConfigureURL { - if let busySection = self.busySection, busySection.attached { - self.removeSection(busySection) + } else { + proceed = self.show(issue: issue, proceed: { () in + OnMainThread { + if isInitialRequest { + self.determineSupportedAuthMethod(false) } - self.addSection(self.urlSection()) - if OCBookmarkManager.shared.bookmarks.count == 0, self.profile.isOnboardingEnabled, self.sectionForIdentifier("onboardingSection") == nil { - self.addSection(self.onboardingSection()) + } + }, cancel: { () in + OnMainThread { + if self.profile.canConfigureURL { + if let busySection = self.busySection, busySection.attached { + self.removeSection(busySection) + } + self.addSection(self.urlSection()) + if OCBookmarkManager.shared.bookmarks.count == 0, self.profile.isOnboardingEnabled, self.sectionForIdentifier("onboardingSection") == nil { + self.addSection(self.onboardingSection()) + } + } else { + self.cancel(nil) } - } else { - self.cancel(nil) } - } - }) + }) + } } if proceed { @@ -548,6 +583,10 @@ class StaticLoginSetupViewController : StaticLoginStepViewController { if let busySection = self.busySection, busySection.attached { self.removeSection(busySection) } + self.retrySection = self.retrySection(issues: displayIssues) + if let retrySection = self.retrySection, issue.issues?.first?.error?.isNetworkConnectionError == true { + self.addSection(retrySection) + } IssuesCardViewController.present(on: loginViewController, issue: issue, displayIssues: displayIssues, completion: { [weak issue] (response) in switch response {