diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 1db337ea1..209686553 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -287,7 +287,6 @@ 519ED47A24482AEB007F8E94 /* EnableExtensionPointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */; }; 51A052CE244FB9D7006C2024 /* AddFeedWIndowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */; }; 51A052CF244FB9D7006C2024 /* AddFeedWIndowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */; }; - 51A16999235E10D700EB091F /* LocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698F235E10D600EB091F /* LocalAccountViewController.swift */; }; 51A1699A235E10D700EB091F /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51A16990235E10D600EB091F /* Settings.storyboard */; }; 51A169A0235E10D700EB091F /* FeedbinAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */; }; 51A66685238075AE00CB272D /* AddWebFeedDefaultContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */; }; @@ -857,13 +856,20 @@ DF5AD10128D6922200CA3BF7 /* SmartFeedSummaryWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1768144D2564BCE000D98635 /* SmartFeedSummaryWidget.swift */; }; DF766FED29377FD9006FBBE2 /* ExtensionsManagementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF766FEC29377FD9006FBBE2 /* ExtensionsManagementView.swift */; }; DF790D6228E990A900455FC7 /* AboutData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF790D6128E990A900455FC7 /* AboutData.swift */; }; - DFB3497A294A962D00BC81AD /* AddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34979294A962D00BC81AD /* AddAccountView.swift */; }; + DFB3497A294A962D00BC81AD /* AddAccountListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34979294A962D00BC81AD /* AddAccountListView.swift */; }; DFB34980294B085100BC81AD /* AccountInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3497F294B085100BC81AD /* AccountInspectorView.swift */; }; DFB34982294B0B9B00BC81AD /* Inspector.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34981294B0B9B00BC81AD /* Inspector.strings */; }; DFB34984294B3AFF00BC81AD /* Buttons.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34983294B3AFF00BC81AD /* Buttons.strings */; }; DFB34988294B447F00BC81AD /* InjectedNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34987294B447F00BC81AD /* InjectedNavigationView.swift */; }; DFB3498A294B45AC00BC81AD /* ExtensionInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34989294B45AC00BC81AD /* ExtensionInspectorView.swift */; }; DFB3498C294B4CA700BC81AD /* WebFeedInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3498B294B4CA700BC81AD /* WebFeedInspectorView.swift */; }; + DFB34993294C0B7400BC81AD /* Account.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34992294C0B7400BC81AD /* Account.strings */; }; + DFB34994294C0E3900BC81AD /* ReaderAPIAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34990294C0B2200BC81AD /* ReaderAPIAccountView.swift */; }; + DFB34996294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34995294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift */; }; + DFB34997294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34995294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift */; }; + DFB34999294C4F1D00BC81AD /* Errors.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34998294C4F1D00BC81AD /* Errors.strings */; }; + DFB3499A294C4F1D00BC81AD /* Errors.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34998294C4F1D00BC81AD /* Errors.strings */; }; + DFB3499E294C5D5000BC81AD /* iCloudAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3499D294C5D5000BC81AD /* iCloudAccountView.swift */; }; DFC14F0F28EA55BD00F6EE86 /* AboutWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFC14F0E28EA55BD00F6EE86 /* AboutWindowController.swift */; }; DFC14F1228EA5DC500F6EE86 /* AboutData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF790D6128E990A900455FC7 /* AboutData.swift */; }; DFC14F1328EA677C00F6EE86 /* Bundle-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F85BF42273625800C787DC /* Bundle-Extensions.swift */; }; @@ -1304,7 +1310,6 @@ 519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExtensionPointViewController.swift; sourceTree = ""; }; 519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionPointViewController.swift; sourceTree = ""; }; 51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedWIndowController.swift; path = AddFeed/AddFeedWIndowController.swift; sourceTree = ""; }; - 51A1698F235E10D600EB091F /* LocalAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalAccountViewController.swift; sourceTree = ""; }; 51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = ""; }; 51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedDefaultContainer.swift; sourceTree = ""; }; @@ -1614,13 +1619,18 @@ DF59F0732920DB5100ACD33D /* AccountsManagementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsManagementView.swift; sourceTree = ""; }; DF766FEC29377FD9006FBBE2 /* ExtensionsManagementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionsManagementView.swift; sourceTree = ""; }; DF790D6128E990A900455FC7 /* AboutData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutData.swift; sourceTree = ""; }; - DFB34979294A962D00BC81AD /* AddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountView.swift; sourceTree = ""; }; + DFB34979294A962D00BC81AD /* AddAccountListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountListView.swift; sourceTree = ""; }; DFB3497F294B085100BC81AD /* AccountInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountInspectorView.swift; sourceTree = ""; }; DFB34981294B0B9B00BC81AD /* Inspector.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Inspector.strings; sourceTree = ""; }; DFB34983294B3AFF00BC81AD /* Buttons.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Buttons.strings; sourceTree = ""; }; DFB34987294B447F00BC81AD /* InjectedNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedNavigationView.swift; sourceTree = ""; }; DFB34989294B45AC00BC81AD /* ExtensionInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionInspectorView.swift; sourceTree = ""; }; DFB3498B294B4CA700BC81AD /* WebFeedInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebFeedInspectorView.swift; sourceTree = ""; }; + DFB34990294C0B2200BC81AD /* ReaderAPIAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderAPIAccountView.swift; sourceTree = ""; }; + DFB34992294C0B7400BC81AD /* Account.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Account.strings; sourceTree = ""; }; + DFB34995294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedNetNewsWireError.swift; sourceTree = ""; }; + DFB34998294C4F1D00BC81AD /* Errors.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Errors.strings; sourceTree = ""; }; + DFB3499D294C5D5000BC81AD /* iCloudAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iCloudAccountView.swift; sourceTree = ""; }; DFC14F0E28EA55BD00F6EE86 /* AboutWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutWindowController.swift; sourceTree = ""; }; DFC14F1428EB177000F6EE86 /* AboutNetNewsWireView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutNetNewsWireView.swift; sourceTree = ""; }; DFC14F1628EB17A800F6EE86 /* CreditsNetNewsWireView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsNetNewsWireView.swift; sourceTree = ""; }; @@ -1982,8 +1992,9 @@ 516A093E236123A800EAE89B /* Account */ = { isa = PBXGroup; children = ( + DFB34992294C0B7400BC81AD /* Account.strings */, + DFB3498F294C0B0D00BC81AD /* Views */, 516A093F2361240900EAE89B /* Account.storyboard */, - 51A1698F235E10D600EB091F /* LocalAccountViewController.swift */, 512DD4C82430086400C17B1F /* CloudKitAccountViewController.swift */, 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */, 769F2D3643779DB02786278E /* NewsBlurAccountViewController.swift */, @@ -2940,7 +2951,7 @@ isa = PBXGroup; children = ( DF59F0732920DB5100ACD33D /* AccountsManagementView.swift */, - DFB34979294A962D00BC81AD /* AddAccountView.swift */, + DFB34979294A962D00BC81AD /* AddAccountListView.swift */, ); path = Accounts; sourceTree = ""; @@ -2975,6 +2986,8 @@ isa = PBXGroup; children = ( DFB34983294B3AFF00BC81AD /* Buttons.strings */, + DFB34998294C4F1D00BC81AD /* Errors.strings */, + DFB34995294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift */, ); path = Translations; sourceTree = ""; @@ -2987,6 +3000,15 @@ path = "SwiftUI Extensions"; sourceTree = ""; }; + DFB3498F294C0B0D00BC81AD /* Views */ = { + isa = PBXGroup; + children = ( + DFB34990294C0B2200BC81AD /* ReaderAPIAccountView.swift */, + DFB3499D294C5D5000BC81AD /* iCloudAccountView.swift */, + ); + path = Views; + sourceTree = ""; + }; DFC14F0928EA51AB00F6EE86 /* About */ = { isa = PBXGroup; children = ( @@ -3581,6 +3603,7 @@ 51C452862265093600C03939 /* Add.storyboard in Resources */, 511D43EF231FBDE900FB1562 /* LaunchScreenPad.storyboard in Resources */, 511D43D2231FA62C00FB1562 /* GlobalKeyboardShortcuts.plist in Resources */, + DFB34993294C0B7400BC81AD /* Account.strings in Resources */, 84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */, 51BB7C312335ACDE008E8144 /* page.html in Resources */, DFB34982294B0B9B00BC81AD /* Inspector.strings in Resources */, @@ -3591,6 +3614,7 @@ 51DEE81A26FBFF84006DAA56 /* Promenade.nnwtheme in Resources */, 1768140B2564BB8300D98635 /* NetNewsWire_iOSwidgetextension_target.xcconfig in Resources */, 5103A9B424216A4200410853 /* blank.html in Resources */, + DFB3499A294C4F1D00BC81AD /* Errors.strings in Resources */, 51D0214826ED617100FF2E0F /* core.css in Resources */, 84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */, 516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */, @@ -3636,6 +3660,7 @@ DFCE4F9128EF26F100405869 /* About.plist in Resources */, 84C9FC8C22629E8F00D921D6 /* KeyboardShortcuts.html in Resources */, B27EEBF9244D15F3000932E6 /* stylesheet.css in Resources */, + DFB34999294C4F1D00BC81AD /* Errors.strings in Resources */, 5144EA3B227A379E00D19003 /* ImportOPMLSheet.xib in Resources */, 844B5B691FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist in Resources */, DDF9E1D728EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */, @@ -4201,7 +4226,7 @@ 519ED47A24482AEB007F8E94 /* EnableExtensionPointViewController.swift in Sources */, 51C4528F226509BD00C03939 /* UnreadFeed.swift in Sources */, 51FD413B2342BD0500880194 /* MasterTimelineUnreadCountView.swift in Sources */, - DFB3497A294A962D00BC81AD /* AddAccountView.swift in Sources */, + DFB3497A294A962D00BC81AD /* AddAccountListView.swift in Sources */, 513146B2235A81A400387FDC /* AddWebFeedIntentHandler.swift in Sources */, 51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */, 51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */, @@ -4211,7 +4236,6 @@ DF3630ED2936183D00326FB8 /* OPMLDocument.swift in Sources */, 176813D22564BA5900D98635 /* WidgetDataEncoder.swift in Sources */, 51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */, - 51A16999235E10D700EB091F /* LocalAccountViewController.swift in Sources */, 176813D12564BA5900D98635 /* WidgetDataDecoder.swift in Sources */, 176813D02564BA5900D98635 /* WidgetData.swift in Sources */, 510289CD24519A1D00426DDF /* SelectComboTableViewCell.swift in Sources */, @@ -4227,6 +4251,7 @@ 5126EE97226CB48A00C22AFC /* SceneCoordinator.swift in Sources */, 84CAFCB022BC8C35007694F0 /* FetchRequestOperation.swift in Sources */, DFD406FA291FB5E400C02962 /* SettingsRows.swift in Sources */, + DFB3499E294C5D5000BC81AD /* iCloudAccountView.swift in Sources */, DFB3498C294B4CA700BC81AD /* WebFeedInspectorView.swift in Sources */, 5193CD5A245E44A90092735E /* RedditFeedProvider-Extensions.swift in Sources */, 51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */, @@ -4286,6 +4311,7 @@ 515A517B243E90260089E588 /* ExtensionPoint.swift in Sources */, 51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */, 17D643B226F8A436008D4C05 /* ArticleThemeDownloader.swift in Sources */, + DFB34997294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift in Sources */, 51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */, 51F9F3F723DF6DB200A314FD /* ArticleIconSchemeHandler.swift in Sources */, 512392C524E3451400F11704 /* TwitterEnterDetailTableViewController.swift in Sources */, @@ -4305,6 +4331,7 @@ 51EF0F7E2277A57D0050506E /* MasterTimelineAccessibilityCellLayout.swift in Sources */, 512D554423C804DE0023FFFA /* OpenInSafariActivity.swift in Sources */, DF47CDB2294803AB00FCD57E /* AddExtensionListView.swift in Sources */, + DFB34994294C0E3900BC81AD /* ReaderAPIAccountView.swift in Sources */, 512392C224E33A3C00F11704 /* RedditEnterDetailTableViewController.swift in Sources */, 51C452762265091600C03939 /* MasterTimelineViewController.swift in Sources */, 5195C1DC2720BD3000888867 /* MasterFeedRowIdentifier.swift in Sources */, @@ -4465,6 +4492,7 @@ 84C9FC7722629E1200D921D6 /* AdvancedPreferencesViewController.swift in Sources */, 849EE72120391F560082A1EA /* SharingServicePickerDelegate.swift in Sources */, 1710B9132552354E00679C0D /* AddAccountHelpView.swift in Sources */, + DFB34996294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift in Sources */, 51D205EF28E3CF8D007C46EF /* LinkTextField.swift in Sources */, 5108F6B62375E612001ABC45 /* CacheCleaner.swift in Sources */, 849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */, diff --git a/Shared/Translations/Buttons.strings b/Shared/Translations/Buttons.strings index c869b0268..527363307 100644 --- a/Shared/Translations/Buttons.strings +++ b/Shared/Translations/Buttons.strings @@ -7,4 +7,11 @@ */ "CANCEL_BUTTON_TITLE" = "Cancel"; +"DISMISS_BUTTON_TITLE" = "Dismiss"; "DONE_BUTTON_TITLE" = "Done"; +"REMOVE_ACCOUNT_BUTTON_TITLE" = "Remove Account"; +"DEACTIVATE_BUTTON_TITLE" = "Deactivate"; +"CREDENTIALS_BUTTON_TITLE" = "Credentials"; +"ADD_ACCOUNT_BUTTON_TITLE" = "Add Account"; +"UPDATE_CREDENTIALS_BUTTON_TITLE" = "Update Credentials"; +"USE_CLOUDKIT_BUTTON_TITLE" = "Use iCloud"; diff --git a/Shared/Translations/Errors.strings b/Shared/Translations/Errors.strings new file mode 100644 index 000000000..4844e4b15 --- /dev/null +++ b/Shared/Translations/Errors.strings @@ -0,0 +1,12 @@ +/* + Errors.strings + NetNewsWire + + Created by Stuart Breckenridge on 16/12/2022. + Copyright © 2022 Ranchero Software. All rights reserved. +*/ + +"ERROR_TITLE" = "Error"; +"DUPLICATE_ACCOUNT_ERROR" = "There is already an account of that type with that username created."; +"CLOUDKIT_NOT_ENABLED_ERROR" = "Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings."; + diff --git a/Shared/Translations/LocalizedNetNewsWireError.swift b/Shared/Translations/LocalizedNetNewsWireError.swift new file mode 100644 index 000000000..b16ddf099 --- /dev/null +++ b/Shared/Translations/LocalizedNetNewsWireError.swift @@ -0,0 +1,30 @@ +// +// LocalizedNetNewsWireError.swift +// NetNewsWire +// +// Created by Stuart Breckenridge on 16/12/2022. +// Copyright © 2022 Ranchero Software. All rights reserved. +// + +import Foundation + +public enum LocalizedNetNewsWireError: LocalizedError { + + /// Displayed when the user tries to create a duplicate + /// account with the same username. + case duplicateAccount + + /// Displayed when the user attempts to add a + /// iCloud account but iCloud and/or iCloud Drive + /// are not enabled/ + case iCloudDriveMissing + + public var errorDescription: String? { + switch self { + case .duplicateAccount: + return Bundle.main.localizedString(forKey: "DUPLICATE_ACCOUNT_ERROR", value: nil, table: "Errors") + case .iCloudDriveMissing: + return Bundle.main.localizedString(forKey: "CLOUDKIT_NOT_ENABLED_ERROR", value: nil, table: "Errors") + } + } +} diff --git a/iOS/Account/Account.strings b/iOS/Account/Account.strings new file mode 100644 index 000000000..b26672b10 --- /dev/null +++ b/iOS/Account/Account.strings @@ -0,0 +1,20 @@ +/* + Account.strings + NetNewsWire + + Created by Stuart Breckenridge on 16/12/2022. + Copyright © 2022 Ranchero Software. All rights reserved. +*/ + +/* Account Names */ +"CLOUDKIT" = "iCloud"; + + +/* Explainers */ +"BAZQUX_FOOTER_EXPLAINER" = "Sign in to your BazQux account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a BazQux account? [Sign Up Here](https://bazqux.com)"; +"INOREADER_FOOTER_EXPLAINER" = "Sign in to your InoReader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an InoReader account? [Sign Up Here](https://www.inoreader.com)"; +"OLDREADER_FOOTER_EXPLAINER" = "Sign in to your The Old Reader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a The Old Reader account? [Sign Up Here](https://theoldreader.com)"; +"FRESHRSS_FOOTER_EXPLAINER" = "Sign in to your FreshRSS instance and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an FreshRSS instance? [Sign Up Here](https://freshrss.org)"; +"CLOUDKIT_FOOTER_EXPLAINER" = "NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices."; + + diff --git a/iOS/Account/CloudKitAccountViewController.swift b/iOS/Account/CloudKitAccountViewController.swift index ca3ce71e9..08d10d010 100644 --- a/iOS/Account/CloudKitAccountViewController.swift +++ b/iOS/Account/CloudKitAccountViewController.swift @@ -20,7 +20,7 @@ enum CloudKitAccountViewControllerError: LocalizedError { class CloudKitAccountViewController: UITableViewController { - weak var delegate: AddAccountDismissDelegate? + //weak var delegate: AddAccountDismissDelegate? @IBOutlet weak var footerLabel: UILabel! override func viewDidLoad() { @@ -36,7 +36,7 @@ class CloudKitAccountViewController: UITableViewController { @IBAction func cancel(_ sender: Any) { dismiss(animated: true, completion: nil) - delegate?.dismiss() + //delegate?.dismiss() } @IBAction func add(_ sender: Any) { @@ -47,7 +47,7 @@ class CloudKitAccountViewController: UITableViewController { let _ = AccountManager.shared.createAccount(type: .cloudKit) dismiss(animated: true, completion: nil) - delegate?.dismiss() + //delegate?.dismiss() } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { diff --git a/iOS/Account/FeedbinAccountViewController.swift b/iOS/Account/FeedbinAccountViewController.swift index 964ff7e93..33abc33c1 100644 --- a/iOS/Account/FeedbinAccountViewController.swift +++ b/iOS/Account/FeedbinAccountViewController.swift @@ -24,7 +24,7 @@ class FeedbinAccountViewController: UITableViewController, Logging { @IBOutlet weak var footerLabel: UILabel! weak var account: Account? - weak var delegate: AddAccountDismissDelegate? + //weak var delegate: AddAccountDismissDelegate? override func viewDidLoad() { super.viewDidLoad() @@ -132,7 +132,7 @@ class FeedbinAccountViewController: UITableViewController, Logging { } self.dismiss(animated: true, completion: nil) - self.delegate?.dismiss() + //self.delegate?.dismiss() } catch { self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")) self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public).") diff --git a/iOS/Account/LocalAccountViewController.swift b/iOS/Account/LocalAccountViewController.swift deleted file mode 100644 index 31c415d8b..000000000 --- a/iOS/Account/LocalAccountViewController.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// LocalAccountViewController.swift -// NetNewsWire-iOS -// -// Created by Maurice Parker on 5/19/19. -// Copyright © 2019 Ranchero Software. All rights reserved. -// - -import UIKit -import Account - -class LocalAccountViewController: UITableViewController { - - @IBOutlet weak var nameTextField: UITextField! - @IBOutlet weak var footerLabel: UILabel! - - weak var delegate: AddAccountDismissDelegate? - - override func viewDidLoad() { - super.viewDidLoad() - setupFooter() - navigationItem.title = Account.defaultLocalAccountName - nameTextField.delegate = self - tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader") - } - - private func setupFooter() { - footerLabel.text = NSLocalizedString("Local accounts do not sync your feeds across devices.", comment: "Local") - } - - @IBAction func cancel(_ sender: Any) { - dismiss(animated: true, completion: nil) - } - - @IBAction func add(_ sender: Any) { - let account = AccountManager.shared.createAccount(type: .onMyMac) - account.name = nameTextField.text - dismiss(animated: true, completion: nil) - delegate?.dismiss() - } - - override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: section) - } - - override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - if section == 0 { - let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView - headerView.imageView.image = AppAssets.image(for: .onMyMac) - return headerView - } else { - return super.tableView(tableView, viewForHeaderInSection: section) - } - } - -} - -extension LocalAccountViewController: UITextFieldDelegate { - - func textFieldShouldReturn(_ textField: UITextField) -> Bool { - textField.resignFirstResponder() - return true - } - -} diff --git a/iOS/Account/NewsBlurAccountViewController.swift b/iOS/Account/NewsBlurAccountViewController.swift index 9f46361d0..845e6f429 100644 --- a/iOS/Account/NewsBlurAccountViewController.swift +++ b/iOS/Account/NewsBlurAccountViewController.swift @@ -24,7 +24,7 @@ class NewsBlurAccountViewController: UITableViewController, Logging { @IBOutlet weak var footerLabel: UILabel! weak var account: Account? - weak var delegate: AddAccountDismissDelegate? + //weak var delegate: AddAccountDismissDelegate? override func viewDidLoad() { super.viewDidLoad() @@ -135,7 +135,7 @@ class NewsBlurAccountViewController: UITableViewController, Logging { } self.dismiss(animated: true, completion: nil) - self.delegate?.dismiss() + //self.delegate?.dismiss() } catch { self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")) self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public).") diff --git a/iOS/Account/ReaderAPIAccountViewController.swift b/iOS/Account/ReaderAPIAccountViewController.swift index 1f93ad95a..11705b63d 100644 --- a/iOS/Account/ReaderAPIAccountViewController.swift +++ b/iOS/Account/ReaderAPIAccountViewController.swift @@ -27,7 +27,7 @@ class ReaderAPIAccountViewController: UITableViewController, Logging { weak var account: Account? var accountType: AccountType? - weak var delegate: AddAccountDismissDelegate? + //weak var delegate: AddAccountDismissDelegate? override func viewDidLoad() { super.viewDidLoad() @@ -185,7 +185,7 @@ class ReaderAPIAccountViewController: UITableViewController, Logging { } } - self.delegate?.dismiss() + //self.delegate?.dismiss() } catch { self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")) self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public).") diff --git a/iOS/Account/Views/ReaderAPIAccountView.swift b/iOS/Account/Views/ReaderAPIAccountView.swift new file mode 100644 index 000000000..6de4a553c --- /dev/null +++ b/iOS/Account/Views/ReaderAPIAccountView.swift @@ -0,0 +1,254 @@ +// +// ReaderAPIAccountView.swift +// NetNewsWire +// +// Created by Stuart Breckenridge on 16/12/2022. +// Copyright © 2022 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Account +import Secrets +import RSWeb +import SafariServices +import RSCore + +struct ReaderAPIAccountView: View { + + @Environment(\.dismiss) var dismiss + + var accountType: AccountType? + @State var account: Account? + @State private var accountCredentials: Credentials? + @State private var accountUserName: String = "" + @State private var accountSecret: String = "" + @State private var accountAPIUrl: String = "" + @State private var showProgressIndicator: Bool = false + @State private var accountSetupError: (Error?, Bool) = (nil, false) + + var body: some View { + NavigationView { + Form { + Section(header: readerAccountImage) {} + accountDetailsSection + Section(footer: readerAccountExplainer) {} + } + .navigationTitle(Text(accountType?.localizedAccountName() ?? "")) + .navigationBarTitleDisplayMode(.inline) + .task { + try? retrieveAccountCredentials() + } + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") }) + } + ToolbarItem(placement: .navigationBarTrailing) { + if showProgressIndicator { ProgressView() } + } + } + .alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountSetupError.1) { + Button(role: .cancel) { + // + } label: { + Text("DISMISS_BUTTON_TITLE", tableName: "Buttons") + } + } message: { + Text(accountSetupError.0?.localizedDescription ?? "") + } + .onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in + dismiss() + } + .edgesIgnoringSafeArea(.bottom) // Fix to make sure view is not offset from the top when presented + } + } + + var readerAccountExplainer: some View { + if accountType == nil { return Text("").multilineTextAlignment(.center) } + switch accountType! { + case .bazQux: + return Text("BAZQUX_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center) + case .inoreader: + return Text("INOREADER_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center) + case .theOldReader: + return Text("OLDREADER_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center) + case .freshRSS: + return Text("FRESHRSS_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center) + default: + return Text("").multilineTextAlignment(.center) + } + } + + var readerAccountImage: some View { + HStack { + Spacer() + if accountType == nil { Text("") } + switch accountType! { + case .bazQux: + Image(uiImage: AppAssets.accountBazQuxImage) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 48, height: 48) + case .inoreader: + Image(uiImage: AppAssets.accountInoreaderImage) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 48, height: 48) + case .theOldReader: + Image(uiImage: AppAssets.accountTheOldReaderImage) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 48, height: 48) + case .freshRSS: + Image(uiImage: AppAssets.accountFreshRSSImage) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 48, height: 48) + default: + Text("") + } + Spacer() + } + } + + var accountDetailsSection: some View { + Group { + Section { + TextField("Username", text: $accountUserName) + .autocorrectionDisabled() + SecureField("Password", text: $accountSecret) + if accountType == .freshRSS && accountCredentials == nil { + TextField("FreshRSS URL", text: $accountAPIUrl, prompt: Text("fresh.rss.net/api/greader.php")) + .autocorrectionDisabled() + } + } + + Section { + Button { + Task { + do { + try await executeAccountCredentials() + } catch { + accountSetupError = (error, true) + } + } + } label: { + HStack { + Spacer() + if accountCredentials == nil { + Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + } else { + Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons") + } + Spacer() + } + } + .disabled(!validateCredentials()) + } + } + + } + + // MARK: - API + + private func retrieveAccountCredentials() throws { + if let account = account { + do { + if let creds = try account.retrieveCredentials(type: .readerBasic) { + self.accountCredentials = creds + accountUserName = creds.username + accountSecret = creds.secret + } + } catch { + accountSetupError = (error, true) + } + } + } + + private func validateCredentials() -> Bool { + if accountType == nil { return false } + switch accountType! { + case .freshRSS: + if (accountUserName.trimmingWhitespace.count == 0) || (accountSecret.trimmingWhitespace.count == 0) || (accountAPIUrl.trimmingWhitespace.count == 0) { + return false + } + default: + if (accountUserName.trimmingWhitespace.count == 0) || (accountSecret.trimmingWhitespace.count == 0) { + return false + } + } + return true + } + + @MainActor + private func executeAccountCredentials() async throws { + + let trimmedAccountUserName = accountUserName.trimmingWhitespace + + guard (account != nil || !AccountManager.shared.duplicateServiceAccount(type: accountType!, username: trimmedAccountUserName)) else { + throw LocalizedNetNewsWireError.duplicateAccount + } + + showProgressIndicator = true + let credentials = Credentials(type: .readerBasic, username: trimmedAccountUserName, secret: accountSecret) + + return try await withCheckedThrowingContinuation { continuation in + Account.validateCredentials(type: accountType!, credentials: credentials, endpoint: apiURL()) { result in + switch result { + case .success(let validatedCredentials): + if let validatedCredentials = validatedCredentials { + if self.account == nil { + self.account = AccountManager.shared.createAccount(type: accountType!) + } + + do { + self.account?.endpointURL = apiURL() + try? self.account?.removeCredentials(type: .readerBasic) + try? self.account?.removeCredentials(type: .readerAPIKey) + try self.account?.storeCredentials(credentials) + try self.account?.storeCredentials(validatedCredentials) + + self.account?.refreshAll(completion: { result in + switch result { + case .success: + showProgressIndicator = false + continuation.resume() + case .failure(let error): + showProgressIndicator = false + continuation.resume(throwing: error) + } + }) + } catch { + showProgressIndicator = false + continuation.resume(throwing: error) + } + } + case .failure(let failure): + showProgressIndicator = false + continuation.resume(throwing: failure) + } + } + } + } + + private func apiURL() -> URL? { + switch accountType! { + case .freshRSS: + return URL(string: accountAPIUrl)! + case .inoreader: + return URL(string: ReaderAPIVariant.inoreader.host)! + case .bazQux: + return URL(string: ReaderAPIVariant.bazQux.host)! + case .theOldReader: + return URL(string: ReaderAPIVariant.theOldReader.host)! + default: + return nil + } + } + +} + +struct ReaderAPIAccountView_Previews: PreviewProvider { + static var previews: some View { + ReaderAPIAccountView() + } +} diff --git a/iOS/Account/Views/iCloudAccountView.swift b/iOS/Account/Views/iCloudAccountView.swift new file mode 100644 index 000000000..6e42be0a4 --- /dev/null +++ b/iOS/Account/Views/iCloudAccountView.swift @@ -0,0 +1,83 @@ +// +// iCloudAccountView.swift +// NetNewsWire-iOS +// +// Created by Stuart Breckenridge on 16/12/2022. +// Copyright © 2022 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Account + +struct iCloudAccountView: View { + + @Environment(\.dismiss) private var dismiss + @State private var addAccountError: (LocalizedError?, Bool) = (nil, false) + + var body: some View { + NavigationView { + Form { + Section(header: cloudKitHeader) {} + Section { + createCloudKitAccount + } + Section(footer: cloudKitExplainer) {} + } + .navigationTitle(Text("CLOUDKIT", tableName: "Account")) + .navigationBarTitleDisplayMode(.inline) + .alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $addAccountError.1) { + Button(action: {}, label: { Text("DISMISS_BUTTON_TITLE", tableName: "Buttons") }) + } message: { + Text(addAccountError.0?.localizedDescription ?? "Unknown Error") + } + .onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in + dismiss() + } + } + } + + var cloudKitHeader: some View { + HStack { + Spacer() + Image(uiImage: AppAssets.accountCloudKitImage) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 48, height: 48) + Spacer() + } + } + + var createCloudKitAccount: some View { + Button { + guard FileManager.default.ubiquityIdentityToken != nil else { + addAccountError = (LocalizedNetNewsWireError.iCloudDriveMissing, true) + return + } + let _ = AccountManager.shared.createAccount(type: .cloudKit) + } label: { + HStack { + Spacer() + Text("USE_CLOUDKIT_BUTTON_TITLE", tableName: "Buttons") + Spacer() + } + } + } + + var cloudKitExplainer: some View { + VStack(spacing: 4) { + if !AccountManager.shared.accounts.contains(where: { $0.type == .cloudKit }) { + // The explainer is only shown when a CloudKit account doesn't exist. + Text("CLOUDKIT_FOOTER_EXPLAINER", tableName: "Account") + } + Text("CLOUDKIT_LIMITATIONS_TITLE", tableName: "Inspector") + }.multilineTextAlignment(.center) + } + + +} + +struct iCloudAccountView_Previews: PreviewProvider { + static var previews: some View { + iCloudAccountView() + } +} diff --git a/iOS/Inspector/Inspector.strings b/iOS/Inspector/Inspector.strings index fa413a537..6a9e0ad64 100644 --- a/iOS/Inspector/Inspector.strings +++ b/iOS/Inspector/Inspector.strings @@ -11,7 +11,6 @@ "ACTIVE" = "Active"; "CLOUDKIT_LIMITATIONS_TITLE" = "[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)"; "REMOVE_ACCOUNT_TITLE" = "Remove Account"; -"REMOVE_ACCOUNT_BUTTON_TITLE" = "Remove Account"; "REMOVE_FEEDLY_MESSAGE" = "Are you sure you want to remove this account? NetNewsWire will no longer be able to access articles and feeds unless the account is added again."; "REMOVE_ACCOUNT_MESSAGE" = "Are you sure you want to remove this account? This cannot be undone."; "REMOVE_ACCOUNT_TITLE" = "Remove Account"; diff --git a/iOS/Inspector/Views/AccountInspectorView.swift b/iOS/Inspector/Views/AccountInspectorView.swift index 13f75a441..e03d34425 100644 --- a/iOS/Inspector/Views/AccountInspectorView.swift +++ b/iOS/Inspector/Views/AccountInspectorView.swift @@ -14,12 +14,56 @@ struct AccountInspectorView: View { @Environment(\.dismiss) var dismiss @State private var showRemoveAccountAlert: Bool = false + @State private var showAccountCredentialsSheet: Bool = false var account: Account + var body: some View { Form { Section(header: accountHeaderView){} + accountNameAndActiveSection + + if account.type != .onMyMac && + account.type != .cloudKit && + account.type != .feedly { + credentialsSection + } + + if account != AccountManager.shared.defaultAccount { + removeAccountSection + } + + if account.type == .cloudKit { + Section(footer: cloudKitLimitations){} + } + } + .navigationBarTitleDisplayMode(.inline) + .navigationTitle(account.nameForDisplay) + .tint(Color(uiColor: AppAssets.primaryAccentColor)) + .edgesIgnoringSafeArea(.bottom) + .sheet(isPresented: $showAccountCredentialsSheet) { + switch account.type { + case .theOldReader, .bazQux, .inoreader, .freshRSS: + ReaderAPIAccountView(accountType: account.type, account: account) + default: + EmptyView() + } + } + } + var accountHeaderView: some View { + HStack { + Spacer() + Image(uiImage: account.smallIcon!.image) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 48, height: 48) + Spacer() + } + } + + var accountNameAndActiveSection: some View { + Section { TextField(text: Binding( get: { account.name ?? account.defaultName }, set: { account.name = $0 }), @@ -32,60 +76,55 @@ struct AccountInspectorView: View { }, set: { account.isActive = $0 })) { Text("ACTIVE", tableName: "Inspector") } - - if account != AccountManager.shared.defaultAccount { - Section { - Button(role: .destructive) { - showRemoveAccountAlert = true - } label: { - HStack { - Spacer() - Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Inspector") - Spacer() - } - } - .confirmationDialog(Text("REMOVE_ACCOUNT_TITLE", tableName: "Inspector"), isPresented: $showRemoveAccountAlert, titleVisibility: .visible) { - Button(role: .destructive) { - AccountManager.shared.deleteAccount(account) - dismiss() - } label: { - Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Inspector") - } - - Button(role: .cancel) { - // - } label: { - Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") - } - - } message: { - if account.type == .feedly { - Text("REMOVE_FEEDLY_MESSAGE", tableName: "Inspector") - } else { - Text("REMOVE_ACCOUNT_MESSAGE", tableName: "Inspector") - } - } + } + } + + var credentialsSection: some View { + Section { + Button { + showAccountCredentialsSheet = true + } label: { + HStack { + Spacer() + Text("CREDENTIALS_BUTTON_TITLE", tableName: "Buttons") + Spacer() } } - - if account.type == .cloudKit { - Section(footer: cloudKitLimitations){} - } } - .navigationBarTitleDisplayMode(.inline) - .navigationTitle(account.nameForDisplay) - .tint(Color(uiColor: AppAssets.primaryAccentColor)) - .edgesIgnoringSafeArea(.bottom) // Fix to make sure view is not offset from the top when presented - } + } - var accountHeaderView: some View { - HStack { - Spacer() - Image(uiImage: account.smallIcon!.image) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 30, height: 30) - Spacer() + var removeAccountSection: some View { + Section { + Button(role: .destructive) { + showRemoveAccountAlert = true + } label: { + HStack { + Spacer() + Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + Spacer() + } + } + .confirmationDialog(Text("REMOVE_ACCOUNT_TITLE", tableName: "Inspector"), isPresented: $showRemoveAccountAlert, titleVisibility: .visible) { + Button(role: .destructive) { + AccountManager.shared.deleteAccount(account) + dismiss() + } label: { + Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + } + + Button(role: .cancel) { + // + } label: { + Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") + } + + } message: { + if account.type == .feedly { + Text("REMOVE_FEEDLY_MESSAGE", tableName: "Inspector") + } else { + Text("REMOVE_ACCOUNT_MESSAGE", tableName: "Inspector") + } + } } } diff --git a/iOS/Inspector/Views/ExtensionInspectorView.swift b/iOS/Inspector/Views/ExtensionInspectorView.swift index 148aee0e2..a2a42ee14 100644 --- a/iOS/Inspector/Views/ExtensionInspectorView.swift +++ b/iOS/Inspector/Views/ExtensionInspectorView.swift @@ -32,7 +32,7 @@ struct ExtensionInspectorView: View { Button(role: .destructive) { ExtensionPointManager.shared.deactivateExtensionPoint(extensionPoint!.extensionPointID) } label: { - Text("DEACTIVATE", tableName: "Settings") + Text("DEACTIVATE_BUTTON_TITLE", tableName: "Buttons") } Button(role: .cancel) { @@ -59,7 +59,7 @@ struct ExtensionInspectorView: View { Image(uiImage: extensionPoint!.image) .resizable() .aspectRatio(contentMode: .fit) - .frame(width: 30, height: 30) + .frame(width: 48, height: 48) Spacer() } } diff --git a/iOS/Inspector/Views/WebFeedInspectorView.swift b/iOS/Inspector/Views/WebFeedInspectorView.swift index 32a7321f9..679aee445 100644 --- a/iOS/Inspector/Views/WebFeedInspectorView.swift +++ b/iOS/Inspector/Views/WebFeedInspectorView.swift @@ -71,7 +71,7 @@ struct WebFeedInspectorView: View { Image(uiImage: webFeed.smallIcon!.image) .resizable() .aspectRatio(contentMode: .fit) - .frame(width: 30, height: 30) + .frame(width: 48, height: 48) Spacer() } } diff --git a/iOS/Settings/Settings.strings b/iOS/Settings/Settings.strings index a6650a130..5462e5feb 100644 --- a/iOS/Settings/Settings.strings +++ b/iOS/Settings/Settings.strings @@ -92,4 +92,3 @@ "EXPORT_OPML_SUCCESS_TITLE" = "Exported Successfully"; "EXPORT_OPML_SUCCESS_MESSAGE" = "Your OPML file has been successfully exported."; "ERROR_TITLE" = "Error"; -"REMOVE" = "Remove"; diff --git a/iOS/Settings/Views/Account and Extensions/Accounts/AccountsManagementView.swift b/iOS/Settings/Views/Account and Extensions/Accounts/AccountsManagementView.swift index 2bc756eb0..5c2ee0fc3 100644 --- a/iOS/Settings/Views/Account and Extensions/Accounts/AccountsManagementView.swift +++ b/iOS/Settings/Views/Account and Extensions/Accounts/AccountsManagementView.swift @@ -31,7 +31,6 @@ struct AccountsManagementView: View { accountRow(account, showRemoveAccountAlert: $showRemoveAccountAlert, accountToRemove: $accountToRemove) } } - } .navigationTitle(Text("MANAGE_ACCOUNTS", tableName: "Settings")) .tint(Color(uiColor: AppAssets.primaryAccentColor)) @@ -60,14 +59,14 @@ struct AccountsManagementView: View { refreshAccounts() } .sheet(isPresented: $showAddAccountSheet) { - AddAccountView() + AddAccountListView() } .alert(Text("ACCOUNT_REMOVE \(accountToRemove?.nameForDisplay ?? "")", tableName: "Settings"), isPresented: $showRemoveAccountAlert) { Button(role: .destructive) { AccountManager.shared.deleteAccount(accountToRemove!) } label: { - Text("REMOVE", tableName: "Settings") + Text("REMOVE_BUTTON_TITLE", tableName: "Buttons") } Button(role: .cancel) { @@ -113,7 +112,7 @@ struct AccountsManagementView: View { showRemoveAccountAlert.wrappedValue = true } label: { Label { - Text("REMOVE_ACCOUNT_TITLE", tableName: "Settings") + Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") } icon: { Image(systemName: "trash") } diff --git a/iOS/Settings/Views/Account and Extensions/Accounts/AddAccountView.swift b/iOS/Settings/Views/Account and Extensions/Accounts/AddAccountListView.swift similarity index 93% rename from iOS/Settings/Views/Account and Extensions/Accounts/AddAccountView.swift rename to iOS/Settings/Views/Account and Extensions/Accounts/AddAccountListView.swift index 6e19a8e6e..cc65656f8 100644 --- a/iOS/Settings/Views/Account and Extensions/Accounts/AddAccountView.swift +++ b/iOS/Settings/Views/Account and Extensions/Accounts/AddAccountListView.swift @@ -1,5 +1,5 @@ // -// AddAccountView.swift +// AddAccountListView.swift // NetNewsWire-iOS // // Created by Stuart Breckenridge on 15/12/2022. @@ -10,7 +10,7 @@ import SwiftUI import Account import RSCore -public final class AddAcccountViewModel: ObservableObject, OAuthAccountAuthorizationOperationDelegate { +public final class AddAccountListViewModel: ObservableObject, OAuthAccountAuthorizationOperationDelegate { @Published public var showAddAccountSheet: (Bool, accountType: AccountType) = (false, .onMyMac) public var webAccountTypes: [AccountType] { @@ -58,10 +58,10 @@ public final class AddAcccountViewModel: ObservableObject, OAuthAccountAuthoriza } } -struct AddAccountView: View { +struct AddAccountListView: View { @Environment(\.dismiss) var dismiss - @StateObject private var viewModel = AddAcccountViewModel() + @StateObject private var viewModel = AddAccountListViewModel() var body: some View { @@ -89,9 +89,9 @@ struct AddAccountView: View { case .onMyMac: Text("ON MY MAC") case .cloudKit: - Text("CLOUDKIT") - case .freshRSS: - Text("SELF_HOSTED") + iCloudAccountView() + case .freshRSS, .inoreader, .bazQux, .theOldReader: + ReaderAPIAccountView(accountType: viewModel.showAddAccountSheet.accountType, account: nil) default: Text(viewModel.showAddAccountSheet.accountType.localizedAccountName()) } diff --git a/iOS/Settings/Views/General/SettingsView.swift b/iOS/Settings/Views/General/SettingsView.swift index 62807d874..203712e0e 100644 --- a/iOS/Settings/Views/General/SettingsView.swift +++ b/iOS/Settings/Views/General/SettingsView.swift @@ -86,7 +86,7 @@ struct SettingsView: View { .navigationTitle(Text("SETTINGS_TITLE", tableName: "Settings")) .navigationBarTitleDisplayMode(.inline) .sheet(isPresented: $viewModel.showAddAccountView) { - AddAccountView() + AddAccountListView() } .sheet(isPresented: $viewModel.showHelpSheet) { SafariView(url: viewModel.helpSheet.url)