diff --git a/Frameworks/Account/AccountError.swift b/Frameworks/Account/AccountError.swift index 41b19fac9..734b957a9 100644 --- a/Frameworks/Account/AccountError.swift +++ b/Frameworks/Account/AccountError.swift @@ -16,6 +16,23 @@ public enum AccountError: LocalizedError { case opmlImportInProgress case wrappedError(error: Error, account: Account) + public var acount: Account? { + if case .wrappedError(_, let account) = self { + return account + } else { + return nil + } + } + + public var isCredentialsError: Bool { + if case .wrappedError(let error, _) = self { + if case TransportError.httpError(let status) = error { + return isCredentialsError(status: status) + } + } + return false + } + public var errorDescription: String? { switch self { case .createErrorNotFound: @@ -27,7 +44,7 @@ public enum AccountError: LocalizedError { case .wrappedError(let error, let account): switch error { case TransportError.httpError(let status): - if status == 401 || status == 403 { + if isCredentialsError(status: status) { let localizedText = NSLocalizedString("Your “%@” credentials are invalid or expired.", comment: "Invalid or expired") return NSString.localizedStringWithFormat(localizedText as NSString, account.nameForDisplay) as String } else { @@ -48,7 +65,7 @@ public enum AccountError: LocalizedError { case .wrappedError(let error, _): switch error { case TransportError.httpError(let status): - if status == 401 || status == 403 { + if isCredentialsError(status: status) { return NSLocalizedString("Please update your credentials for this account, or ensure that your account with this service is still valid.", comment: "Expired credentials") } else { return NSLocalizedString("Please try again later.", comment: "Try later") @@ -61,8 +78,19 @@ public enum AccountError: LocalizedError { } } - private func unknownError(_ error: Error, _ account: Account) -> String { +} + +// MARK: Private + +private extension AccountError { + + func unknownError(_ error: Error, _ account: Account) -> String { let localizedText = NSLocalizedString("An error occurred while processing the “%@” account: %@", comment: "Unknown error") return NSString.localizedStringWithFormat(localizedText as NSString, account.nameForDisplay, error.localizedDescription) as String } + + func isCredentialsError(status: Int) -> Bool { + return status == 401 || status == 403 + } + } diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 2bf1563dc..5f42a18bd 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -123,6 +123,7 @@ 518B2EE82351B45600400001 /* NetNewsWire_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D61952029031D009BC708 /* NetNewsWire_iOSTests.swift */; }; 518C3193237B00D9004D740F /* ArticleIconSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5141E7552374A2890013FF27 /* ArticleIconSchemeHandler.swift */; }; 518C3194237B00DA004D740F /* ArticleIconSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5141E7552374A2890013FF27 /* ArticleIconSchemeHandler.swift */; }; + 518ED21D23D0F26000E0A862 /* UIViewController-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518ED21C23D0F26000E0A862 /* UIViewController-Extensions.swift */; }; 51934CCB230F599B006127BE /* InteractiveNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CC1230F5963006127BE /* InteractiveNavigationController.swift */; }; 51934CCE2310792F006127BE /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; }; 51938DF2231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; }; @@ -1307,6 +1308,7 @@ 5186A634235EF3A800C97195 /* VibrantLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantLabel.swift; sourceTree = ""; }; 518B2ED22351B3DD00400001 /* NetNewsWire-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NetNewsWire-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 518B2EE92351B4C200400001 /* NetNewsWire_iOSTests_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSTests_target.xcconfig; sourceTree = ""; }; + 518ED21C23D0F26000E0A862 /* UIViewController-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController-Extensions.swift"; sourceTree = ""; }; 51934CC1230F5963006127BE /* InteractiveNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractiveNavigationController.swift; sourceTree = ""; }; 51934CCD2310792F006127BE /* ActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityManager.swift; sourceTree = ""; }; 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTimelineFeedDelegate.swift; sourceTree = ""; }; @@ -1889,13 +1891,14 @@ 512363372369155100951F16 /* RoundedProgressView.swift */, 51C45250226506F400C03939 /* String-Extensions.swift */, 5108F6D723763094001ABC45 /* TickMarkSlider.swift */, + C5A6ED6C23C9B0C800AB6BE2 /* UIActivityViewController-Extensions.swift */, 51F85BF82274AA7B00C787DC /* UIBarButtonItem-Extensions.swift */, 51F85BF622749FA100C787DC /* UIFont-Extensions.swift */, 51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */, + 518ED21C23D0F26000E0A862 /* UIViewController-Extensions.swift */, 51FFF0C3235EE8E5002762AA /* VibrantButton.swift */, 5186A634235EF3A800C97195 /* VibrantLabel.swift */, 5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */, - C5A6ED6C23C9B0C800AB6BE2 /* UIActivityViewController-Extensions.swift */, ); path = "UIKit Extensions"; sourceTree = ""; @@ -3835,6 +3838,7 @@ 51EF0F79227716380050506E /* ColorHash.swift in Sources */, 5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */, B2B80778239C4C7000F191E0 /* RSImage-AppIcons.swift in Sources */, + 518ED21D23D0F26000E0A862 /* UIViewController-Extensions.swift in Sources */, 51A9A5F52380F6A60033AADF /* ModalNavigationController.swift in Sources */, 51EAED96231363EF00A9EEE3 /* NonIntrinsicButton.swift in Sources */, 51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */, diff --git a/iOS/ShareExtension/ShareViewController.swift b/iOS/ShareExtension/ShareViewController.swift index 3a2f7c6fc..6a529124d 100644 --- a/iOS/ShareExtension/ShareViewController.swift +++ b/iOS/ShareExtension/ShareViewController.swift @@ -106,7 +106,9 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont } if let urlString = url?.absoluteString, account!.hasWebFeed(withURL: urlString) { - presentError(AccountError.createErrorAlreadySubscribed) + let errorTitle = NSLocalizedString("Error", comment: "Error") + presentError(title: errorTitle, message: AccountError.createErrorAlreadySubscribed.localizedDescription) + self.extensionContext!.cancelRequest(withError: AccountError.createErrorAlreadySubscribed) return } diff --git a/iOS/UIKit Extensions/UIViewController-Extensions.swift b/iOS/UIKit Extensions/UIViewController-Extensions.swift new file mode 100644 index 000000000..7a582bb94 --- /dev/null +++ b/iOS/UIKit Extensions/UIViewController-Extensions.swift @@ -0,0 +1,58 @@ +// +// UIViewController-Extensions.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 1/16/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import UIKit +import RSCore +import Account + +extension UIViewController { + + func presentError(_ error: Error, dismiss: (() -> Void)? = nil) { + if let accountError = error as? AccountError, accountError.isCredentialsError { + presentAccountError(accountError, dismiss: dismiss) + } else { + let errorTitle = NSLocalizedString("Error", comment: "Error") + presentError(title: errorTitle, message: error.localizedDescription, dismiss: dismiss) + } + } + +} + +private extension UIViewController { + + func presentAccountError(_ error: AccountError, dismiss: (() -> Void)? = nil) { + let title = NSLocalizedString("Account Error", comment: "Account Error") + let alertController = UIAlertController(title: title, message: error.localizedDescription, preferredStyle: .alert) + + if error.acount?.type == .feedbin { + + let credentialsTitle = NSLocalizedString("Update Credentials", comment: "Update Credentials") + let credentialsAction = UIAlertAction(title: credentialsTitle, style: .default) { [weak self] _ in + dismiss?() + + let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController + navController.modalPresentationStyle = .formSheet + let addViewController = navController.topViewController as! FeedbinAccountViewController + addViewController.account = error.acount + self?.present(navController, animated: true) + } + + alertController.addAction(credentialsAction) + + } + + let dismissTitle = NSLocalizedString("OK", comment: "OK") + let dismissAction = UIAlertAction(title: dismissTitle, style: .default) { _ in + dismiss?() + } + alertController.addAction(dismissAction) + + self.present(alertController, animated: true, completion: nil) + } + +} diff --git a/submodules/RSCore b/submodules/RSCore index 3a2d030e8..6359d586b 160000 --- a/submodules/RSCore +++ b/submodules/RSCore @@ -1 +1 @@ -Subproject commit 3a2d030e8237bdb6ebbd6c389358aaae34d2d3b0 +Subproject commit 6359d586b370b12539cdf2b3766f138448e1dc97