AddAccount sheets done

This commit is contained in:
Stuart Breckenridge
2022-12-18 15:51:42 +08:00
parent 8108f1ab0f
commit 4ed11c0fc6
26 changed files with 536 additions and 1741 deletions

View File

@@ -41,7 +41,6 @@
176814652564BD7F00D98635 /* WidgetData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 176813B62564B9F800D98635 /* WidgetData.swift */; };
1768146C2564BD8100D98635 /* WidgetDeepLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 176813D82564BA8700D98635 /* WidgetDeepLinks.swift */; };
1768147B2564BE5400D98635 /* widget-sample.json in Resources */ = {isa = PBXBuildFile; fileRef = 1768147A2564BE5400D98635 /* widget-sample.json */; };
177A0C2D25454AAB00D7EAF6 /* ReaderAPIAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 177A0C2C25454AAB00D7EAF6 /* ReaderAPIAccountViewController.swift */; };
178A9F9D2549449F00AB7E9D /* AddAccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 178A9F9C2549449F00AB7E9D /* AddAccountsView.swift */; };
178A9F9E2549449F00AB7E9D /* AddAccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 178A9F9C2549449F00AB7E9D /* AddAccountsView.swift */; };
179C39EA26F76B0500D4E741 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = 179C39E926F76B0500D4E741 /* Zip */; };
@@ -133,7 +132,6 @@
512AF9C2236ED52C0066F8BE /* ImageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512AF9C1236ED52C0066F8BE /* ImageHeaderView.swift */; };
512AF9DD236F05230066F8BE /* InteractiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512AF9DC236F05230066F8BE /* InteractiveLabel.swift */; };
512D554423C804DE0023FFFA /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512D554323C804DE0023FFFA /* OpenInSafariActivity.swift */; };
512DD4C92430086400C17B1F /* CloudKitAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512DD4C82430086400C17B1F /* CloudKitAccountViewController.swift */; };
512E08E62268800D00BDCFDD /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; };
512E08E72268801200BDCFDD /* WebFeedTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97611ED9EB96007D329B /* WebFeedTreeControllerDelegate.swift */; };
512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512E08F722688F7C00BDCFDD /* MasterFeedTableViewSectionHeader.swift */; };
@@ -237,7 +235,6 @@
51627A6923861DED007B3B4B /* MasterFeedViewController+Drop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */; };
51627A93238A3836007B3B4B /* CroppingPreviewParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A92238A3836007B3B4B /* CroppingPreviewParameters.swift */; };
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */; };
516A09402361240900EAE89B /* Account.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 516A093F2361240900EAE89B /* Account.storyboard */; };
516AE9B32371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516AE9B22371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift */; };
516AE9DF2372269A007DEEAA /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516AE9DE2372269A007DEEAA /* IconImage.swift */; };
516AE9E02372269A007DEEAA /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516AE9DE2372269A007DEEAA /* IconImage.swift */; };
@@ -288,7 +285,6 @@
51A052CE244FB9D7006C2024 /* AddFeedWIndowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */; };
51A052CF244FB9D7006C2024 /* AddFeedWIndowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A052CD244FB9D6006C2024 /* AddFeedWIndowController.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 */; };
51A737AE24DB19730015FA66 /* RSCore in Frameworks */ = {isa = PBXBuildFile; productRef = 51A737AD24DB19730015FA66 /* RSCore */; };
51A737AF24DB19730015FA66 /* RSCore in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 51A737AD24DB19730015FA66 /* RSCore */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
@@ -660,7 +656,6 @@
65ED4098235DEF770081F399 /* netnewswire-subscribe-to-feed.js in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73F20CED60100F4AD34 /* netnewswire-subscribe-to-feed.js */; };
65ED40A0235DEFF00081F399 /* container-migration.plist in Resources */ = {isa = PBXBuildFile; fileRef = 65ED409F235DEFF00081F399 /* container-migration.plist */; };
65ED40A1235DEFF00081F399 /* container-migration.plist in Resources */ = {isa = PBXBuildFile; fileRef = 65ED409F235DEFF00081F399 /* container-migration.plist */; };
769F2ED513DA03EE75B993A8 /* NewsBlurAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 769F2D3643779DB02786278E /* NewsBlurAccountViewController.swift */; };
8405DD8A2213E0E3008CE1BF /* DetailContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405DD892213E0E3008CE1BF /* DetailContainerView.swift */; };
8405DD9922153B6B008CE1BF /* TimelineContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405DD9822153B6B008CE1BF /* TimelineContainerView.swift */; };
8405DD9C22153BD7008CE1BF /* NSView-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405DD9B22153BD7008CE1BF /* NSView-Extensions.swift */; };
@@ -842,6 +837,9 @@
DDF9E1D728EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = DDF9E1D628EDF2FC000BC355 /* notificationSoundBlip.mp3 */; };
DDF9E1D828EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = DDF9E1D628EDF2FC000BC355 /* notificationSoundBlip.mp3 */; };
DDF9E1D928EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = DDF9E1D628EDF2FC000BC355 /* notificationSoundBlip.mp3 */; };
DF28B44D294ED52700C4D8CA /* View+DismissOnExternalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF28B44C294ED52700C4D8CA /* View+DismissOnExternalContext.swift */; };
DF28B44F294ED92F00C4D8CA /* NewsBlurAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF28B44E294ED92F00C4D8CA /* NewsBlurAddAccountView.swift */; };
DF28B451294EFC6C00C4D8CA /* View+DismissOnAccountAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF28B450294EFC6C00C4D8CA /* View+DismissOnAccountAdd.swift */; };
DF32ABE829493193008E3A12 /* SettingsComboTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF32ABE629493192008E3A12 /* SettingsComboTableViewCell.swift */; };
DF32ABE929493193008E3A12 /* SettingsComboTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DF32ABE729493193008E3A12 /* SettingsComboTableViewCell.xib */; };
DF32ABEB29494CF1008E3A12 /* Settings.strings in Resources */ = {isa = PBXBuildFile; fileRef = DF32ABEA29494CF0008E3A12 /* Settings.strings */; };
@@ -871,6 +869,8 @@
DFB3499A294C4F1D00BC81AD /* Errors.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34998294C4F1D00BC81AD /* Errors.strings */; };
DFB3499E294C5D5000BC81AD /* CloudKitAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3499D294C5D5000BC81AD /* CloudKitAddAccountView.swift */; };
DFB349A0294E87B700BC81AD /* LocalAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3499F294E87B700BC81AD /* LocalAddAccountView.swift */; };
DFB349A2294E90B500BC81AD /* FeedbinAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB349A1294E90B500BC81AD /* FeedbinAddAccountView.swift */; };
DFB349A4294E914D00BC81AD /* AccountSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB349A3294E914D00BC81AD /* AccountSectionHeader.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 */; };
@@ -1180,7 +1180,6 @@
176814562564BD0600D98635 /* ArticleItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleItemView.swift; sourceTree = "<group>"; };
1768147A2564BE5400D98635 /* widget-sample.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "widget-sample.json"; sourceTree = "<group>"; };
176814822564C02A00D98635 /* NetNewsWire_iOS_WidgetExtension.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = NetNewsWire_iOS_WidgetExtension.entitlements; sourceTree = "<group>"; };
177A0C2C25454AAB00D7EAF6 /* ReaderAPIAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderAPIAccountViewController.swift; sourceTree = "<group>"; };
178A9F9C2549449F00AB7E9D /* AddAccountsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountsView.swift; sourceTree = "<group>"; };
179D280C26F73D83003B2E0A /* ArticleThemePlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemePlist.swift; sourceTree = "<group>"; };
179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsNewsBlurWindowController.swift; sourceTree = "<group>"; };
@@ -1224,7 +1223,6 @@
512AF9C1236ED52C0066F8BE /* ImageHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageHeaderView.swift; sourceTree = "<group>"; };
512AF9DC236F05230066F8BE /* InteractiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractiveLabel.swift; sourceTree = "<group>"; };
512D554323C804DE0023FFFA /* OpenInSafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInSafariActivity.swift; sourceTree = "<group>"; };
512DD4C82430086400C17B1F /* CloudKitAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitAccountViewController.swift; sourceTree = "<group>"; };
512E08F722688F7C00BDCFDD /* MasterFeedTableViewSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterFeedTableViewSectionHeader.swift; sourceTree = "<group>"; };
51314617235A797400387FDC /* NetNewsWire_iOSintentextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSintentextension_target.xcconfig; sourceTree = "<group>"; };
51314637235A7BBE00387FDC /* NetNewsWire iOS Intents Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "NetNewsWire iOS Intents Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1274,7 +1272,6 @@
51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drop.swift"; sourceTree = "<group>"; };
51627A92238A3836007B3B4B /* CroppingPreviewParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CroppingPreviewParameters.swift; sourceTree = "<group>"; };
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = "<group>"; };
516A093F2361240900EAE89B /* Account.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Account.storyboard; sourceTree = "<group>"; };
516AE5FF246AF34100731738 /* RedditAdd.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = RedditAdd.storyboard; sourceTree = "<group>"; };
516AE601246AF36100731738 /* RedditSelectTypeTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditSelectTypeTableViewController.swift; sourceTree = "<group>"; };
516AE603246AF37B00731738 /* RedditSelectAccountTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditSelectAccountTableViewController.swift; sourceTree = "<group>"; };
@@ -1312,7 +1309,6 @@
519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionPointViewController.swift; sourceTree = "<group>"; };
51A052CD244FB9D6006C2024 /* AddFeedWIndowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedWIndowController.swift; path = AddFeed/AddFeedWIndowController.swift; sourceTree = "<group>"; };
51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = "<group>"; };
51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedDefaultContainer.swift; sourceTree = "<group>"; };
51A9A5E32380C8870033AADF /* ShareFolderPickerAccountCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerAccountCell.xib; sourceTree = "<group>"; };
51A9A5E52380C8B20033AADF /* ShareFolderPickerFolderCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerFolderCell.xib; sourceTree = "<group>"; };
@@ -1420,7 +1416,6 @@
65ED409F235DEFF00081F399 /* container-migration.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "container-migration.plist"; sourceTree = "<group>"; };
65ED40F2235DF5E00081F399 /* NetNewsWire_macapp_target_macappstore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_macapp_target_macappstore.xcconfig; sourceTree = "<group>"; };
65ED4186235E045B0081F399 /* NetNewsWire_safariextension_target_macappstore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_safariextension_target_macappstore.xcconfig; sourceTree = "<group>"; };
769F2D3643779DB02786278E /* NewsBlurAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurAccountViewController.swift; sourceTree = "<group>"; };
8405DD892213E0E3008CE1BF /* DetailContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailContainerView.swift; sourceTree = "<group>"; };
8405DD9822153B6B008CE1BF /* TimelineContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineContainerView.swift; sourceTree = "<group>"; };
8405DD9B22153BD7008CE1BF /* NSView-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSView-Extensions.swift"; sourceTree = "<group>"; };
@@ -1609,6 +1604,9 @@
D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = "<group>"; };
DD82AB09231003F6002269DF /* SharingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharingTests.swift; sourceTree = "<group>"; };
DDF9E1D628EDF2FC000BC355 /* notificationSoundBlip.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = notificationSoundBlip.mp3; sourceTree = "<group>"; };
DF28B44C294ED52700C4D8CA /* View+DismissOnExternalContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+DismissOnExternalContext.swift"; sourceTree = "<group>"; };
DF28B44E294ED92F00C4D8CA /* NewsBlurAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsBlurAddAccountView.swift; sourceTree = "<group>"; };
DF28B450294EFC6C00C4D8CA /* View+DismissOnAccountAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+DismissOnAccountAdd.swift"; sourceTree = "<group>"; };
DF32ABE629493192008E3A12 /* SettingsComboTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsComboTableViewCell.swift; sourceTree = "<group>"; };
DF32ABE729493193008E3A12 /* SettingsComboTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsComboTableViewCell.xib; sourceTree = "<group>"; };
DF32ABEA29494CF0008E3A12 /* Settings.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Settings.strings; sourceTree = "<group>"; };
@@ -1633,6 +1631,8 @@
DFB34998294C4F1D00BC81AD /* Errors.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Errors.strings; sourceTree = "<group>"; };
DFB3499D294C5D5000BC81AD /* CloudKitAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitAddAccountView.swift; sourceTree = "<group>"; };
DFB3499F294E87B700BC81AD /* LocalAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAddAccountView.swift; sourceTree = "<group>"; };
DFB349A1294E90B500BC81AD /* FeedbinAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAddAccountView.swift; sourceTree = "<group>"; };
DFB349A3294E914D00BC81AD /* AccountSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSectionHeader.swift; sourceTree = "<group>"; };
DFC14F0E28EA55BD00F6EE86 /* AboutWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutWindowController.swift; sourceTree = "<group>"; };
DFC14F1428EB177000F6EE86 /* AboutNetNewsWireView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutNetNewsWireView.swift; sourceTree = "<group>"; };
DFC14F1628EB17A800F6EE86 /* CreditsNetNewsWireView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsNetNewsWireView.swift; sourceTree = "<group>"; };
@@ -1996,11 +1996,6 @@
children = (
DFB34992294C0B7400BC81AD /* Account.strings */,
DFB3498F294C0B0D00BC81AD /* Views */,
516A093F2361240900EAE89B /* Account.storyboard */,
512DD4C82430086400C17B1F /* CloudKitAccountViewController.swift */,
51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */,
769F2D3643779DB02786278E /* NewsBlurAccountViewController.swift */,
177A0C2C25454AAB00D7EAF6 /* ReaderAPIAccountViewController.swift */,
);
path = Account;
sourceTree = "<group>";
@@ -2998,6 +2993,9 @@
isa = PBXGroup;
children = (
DFB34987294B447F00BC81AD /* InjectedNavigationView.swift */,
DFB349A3294E914D00BC81AD /* AccountSectionHeader.swift */,
DF28B44C294ED52700C4D8CA /* View+DismissOnExternalContext.swift */,
DF28B450294EFC6C00C4D8CA /* View+DismissOnAccountAdd.swift */,
);
path = "SwiftUI Extensions";
sourceTree = "<group>";
@@ -3007,6 +3005,8 @@
children = (
DFB3499F294E87B700BC81AD /* LocalAddAccountView.swift */,
DFB3499D294C5D5000BC81AD /* CloudKitAddAccountView.swift */,
DFB349A1294E90B500BC81AD /* FeedbinAddAccountView.swift */,
DF28B44E294ED92F00C4D8CA /* NewsBlurAddAccountView.swift */,
DFB34990294C0B2200BC81AD /* ReaderAPIAddAccountView.swift */,
);
path = Views;
@@ -3622,7 +3622,6 @@
84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */,
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */,
511D43D1231FA62800FB1562 /* SidebarKeyboardShortcuts.plist in Resources */,
516A09402361240900EAE89B /* Account.storyboard in Resources */,
51C452AB22650DC600C03939 /* template.html in Resources */,
84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */,
B27EEBFB244D15F3000932E6 /* stylesheet.css in Resources */,
@@ -4197,7 +4196,6 @@
buildActionMask = 2147483647;
files = (
51E36E71239D6610006F47A5 /* AddFeedSelectFolderTableViewCell.swift in Sources */,
512DD4C92430086400C17B1F /* CloudKitAccountViewController.swift in Sources */,
840D617F2029031C009BC708 /* AppDelegate.swift in Sources */,
512E08E72268801200BDCFDD /* WebFeedTreeControllerDelegate.swift in Sources */,
51C452A422650A2D00C03939 /* ArticleUtilities.swift in Sources */,
@@ -4208,6 +4206,7 @@
5195C1DA2720205F00888867 /* ShadowTableChanges.swift in Sources */,
5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */,
B2B80778239C4C7000F191E0 /* RSImage-AppIcons.swift in Sources */,
DFB349A2294E90B500BC81AD /* FeedbinAddAccountView.swift in Sources */,
518ED21D23D0F26000E0A862 /* UIViewController-Extensions.swift in Sources */,
DFD406FF291FDC0C00C02962 /* DisplayAndBehaviorsView.swift in Sources */,
51A9A5F52380F6A60033AADF /* ModalNavigationController.swift in Sources */,
@@ -4265,7 +4264,6 @@
51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */,
51F85BF52273625800C787DC /* Bundle-Extensions.swift in Sources */,
DF790D6228E990A900455FC7 /* AboutData.swift in Sources */,
177A0C2D25454AAB00D7EAF6 /* ReaderAPIAccountViewController.swift in Sources */,
51C452A622650A3500C03939 /* Node-Extensions.swift in Sources */,
DF394F0029357A180081EB6E /* NewArticleNotificationsView.swift in Sources */,
51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */,
@@ -4289,6 +4287,7 @@
51C4529D22650A1000C03939 /* FaviconURLFinder.swift in Sources */,
5142192A23522B5500E07E2C /* ImageViewController.swift in Sources */,
51C45258226508CF00C03939 /* AppAssets.swift in Sources */,
DF28B451294EFC6C00C4D8CA /* View+DismissOnAccountAdd.swift in Sources */,
51FA73A82332BE880090D516 /* ExtractedArticle.swift in Sources */,
51C4527C2265091600C03939 /* MasterTimelineDefaultCellLayout.swift in Sources */,
51E4398023805EBC00015C31 /* AddComboTableViewCell.swift in Sources */,
@@ -4343,7 +4342,6 @@
DF3630EF293618A900326FB8 /* SettingsViewModel.swift in Sources */,
51C452882265093600C03939 /* AddFeedViewController.swift in Sources */,
51B5C8C023F3866C00032075 /* ExtensionFeedAddRequestFile.swift in Sources */,
51A169A0235E10D700EB091F /* FeedbinAccountViewController.swift in Sources */,
51934CCE2310792F006127BE /* ActivityManager.swift in Sources */,
DFB34980294B085100BC81AD /* AccountInspectorView.swift in Sources */,
5108F6B72375E612001ABC45 /* CacheCleaner.swift in Sources */,
@@ -4356,6 +4354,7 @@
516AE9B32371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift in Sources */,
51DC370B2405BC9A0095D371 /* PreloadedWebView.swift in Sources */,
D3555BF524664566005E48C3 /* ArticleSearchBar.swift in Sources */,
DF28B44D294ED52700C4D8CA /* View+DismissOnExternalContext.swift in Sources */,
8454C3F3263F2D8700E3F9C7 /* IconImageCache.swift in Sources */,
DFD406F5291F79C900C02962 /* SettingsView.swift in Sources */,
B24E9ADE245AB88400DA5718 /* NSAttributedString+NetNewsWire.swift in Sources */,
@@ -4363,11 +4362,13 @@
17071EF126F8137400F5E71D /* ArticleTheme+Notifications.swift in Sources */,
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */,
84DEE56622C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */,
DF28B44F294ED92F00C4D8CA /* NewsBlurAddAccountView.swift in Sources */,
512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */,
512392BF24E33A3C00F11704 /* RedditSelectSortTableViewController.swift in Sources */,
516AE9E02372269A007DEEAA /* IconImage.swift in Sources */,
519ED456244828C3007F8E94 /* AddExtensionPointViewController.swift in Sources */,
51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */,
DFB349A4294E914D00BC81AD /* AccountSectionHeader.swift in Sources */,
D3A39865246505DF00F9A366 /* FindInArticleActivity.swift in Sources */,
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */,
5137C2EA26F63AE6009EFEDB /* ArticleThemeImporter.swift in Sources */,
@@ -4387,7 +4388,6 @@
511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */,
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */,
769F2ED513DA03EE75B993A8 /* NewsBlurAccountViewController.swift in Sources */,
51BC4B01247277E0000A6ED8 /* URL-Extensions.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -9,4 +9,6 @@
"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.";
"USERNAME_AND_PASSWORD_REQUIRED" = "Username and password required.";
"INVALID_USERNAME_PASSWORD" = "Invalid email or password combination.";
"KEYCHAIN_ERROR" = "Keychain error while storing credentials.";

View File

@@ -16,8 +16,14 @@ public enum LocalizedNetNewsWireError: LocalizedError {
/// Displayed when the user attempts to add a
/// iCloud account but iCloud and/or iCloud Drive
/// are not enabled/
/// are not enabled.
case iCloudDriveMissing
case userNameAndPasswordRequired
case invalidUsernameOrPassword
case keychainError
public var errorDescription: String? {
switch self {
@@ -25,6 +31,12 @@ public enum LocalizedNetNewsWireError: LocalizedError {
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")
case .userNameAndPasswordRequired:
return Bundle.main.localizedString(forKey: "USERNAME_AND_PASSWORD_REQUIRED", value: nil, table: "Errors")
case .invalidUsernameOrPassword:
return Bundle.main.localizedString(forKey: "INVALID_USERNAME_PASSWORD", value: nil, table: "Errors")
case .keychainError:
return Bundle.main.localizedString(forKey: "KEYCHAIN_ERROR", value: nil, table: "Errors")
}
}
}

View File

@@ -143,9 +143,9 @@ public final class WidgetDataEncoder {
let latestData = WidgetData(currentUnreadCount: SmartFeedsController.shared.unreadFeed.unreadCount,
currentTodayCount: SmartFeedsController.shared.todayFeed.unreadCount,
currentStarredCount: (try? AccountManager.shared.fetchCountForStarredArticles()) ?? 0,
unreadArticles: unread,
starredArticles: starred,
todayArticles:today,
unreadArticles: unread.sorted(by: { $0.pubDate > $1.pubDate }),
starredArticles: starred.sorted(by: { $0.pubDate > $1.pubDate }),
todayArticles:today.sorted(by: { $0.pubDate > $1.pubDate }),
lastUpdateTime: Date())
completion(latestData)
}

View File

@@ -1,810 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Modal Navigation Controller-->
<scene sceneID="98f-PW-S1C">
<objects>
<navigationController storyboardIdentifier="LocalAccountNavigationViewController" id="TMY-HB-vAu" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="p8g-7e-3f4">
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="vi3-jb-8XS" kind="relationship" relationship="rootViewController" id="dIe-7d-ZQX"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6sV-68-OXu" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1880" y="-528"/>
</scene>
<!--Modal Navigation Controller-->
<scene sceneID="6i4-ho-e4F">
<objects>
<navigationController storyboardIdentifier="FeedbinAccountNavigationViewController" id="sFg-MZ-PqJ" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="wq6-np-tNn">
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="ECy-jg-Kyc" kind="relationship" relationship="rootViewController" id="usT-8C-GGf"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Lfz-4s-0Vn" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3177" y="-528"/>
</scene>
<!--On My Device-->
<scene sceneID="J93-FN-Yey">
<objects>
<tableViewController id="vi3-jb-8XS" customClass="LocalAccountViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="YLa-nM-G7t">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<view key="tableFooterView" contentMode="scaleToFill" id="Vxr-5V-V6R">
<rect key="frame" x="0.0" y="159" width="414" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Local accounts do not sync your feeds across devices." textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5ce-ZL-glQ">
<rect key="frame" x="20" y="8" width="373" height="13.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="5ce-ZL-glQ" secondAttribute="trailing" constant="21" id="YLV-d0-1us"/>
<constraint firstItem="5ce-ZL-glQ" firstAttribute="leading" secondItem="Vxr-5V-V6R" secondAttribute="leading" constant="20" symbolic="YES" id="dmE-Zi-5FR"/>
<constraint firstItem="5ce-ZL-glQ" firstAttribute="top" secondItem="Vxr-5V-V6R" secondAttribute="top" constant="8" id="z4G-hO-VUE"/>
</constraints>
</view>
<sections>
<tableViewSection id="TfM-Jc-Fr0">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="uFU-j6-qP1">
<rect key="frame" x="20" y="18" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uFU-j6-qP1" id="fr4-mL-3Yf">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Yl1-R6-xZi">
<rect key="frame" x="20" y="12.5" width="334" height="18.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" autocapitalizationType="words"/>
</textField>
</subviews>
<constraints>
<constraint firstItem="Yl1-R6-xZi" firstAttribute="leading" secondItem="fr4-mL-3Yf" secondAttribute="leading" constant="20" id="HJ4-VN-e9Y"/>
<constraint firstAttribute="trailing" secondItem="Yl1-R6-xZi" secondAttribute="trailing" constant="20" id="vbZ-dD-yZM"/>
<constraint firstItem="Yl1-R6-xZi" firstAttribute="centerY" secondItem="fr4-mL-3Yf" secondAttribute="centerY" id="zsZ-z6-IFh"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection id="Sgf-NV-3Di">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="pTk-WJ-j5h" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="97.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pTk-WJ-j5h" id="ahe-yz-PGg">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mQv-3O-Y2d" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="EEL-8n-nHO"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<state key="normal" title="Add Account">
<color key="titleColor" name="secondaryAccentColor"/>
</state>
<connections>
<action selector="add:" destination="vi3-jb-8XS" eventType="touchUpInside" id="lCb-LW-xZ0"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="mQv-3O-Y2d" firstAttribute="centerY" secondItem="ahe-yz-PGg" secondAttribute="centerY" id="6bl-bA-qYE"/>
<constraint firstItem="mQv-3O-Y2d" firstAttribute="leading" secondItem="ahe-yz-PGg" secondAttribute="leading" id="7gZ-8n-bWs"/>
<constraint firstAttribute="trailing" secondItem="mQv-3O-Y2d" secondAttribute="trailing" id="FQu-yU-a4k"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="vi3-jb-8XS" id="U1Z-Kw-46j"/>
<outlet property="delegate" destination="vi3-jb-8XS" id="4El-ci-jdg"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="On My Device" id="AOA-LS-PIB">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="b2H-re-cgN">
<connections>
<action selector="cancel:" destination="vi3-jb-8XS" id="gRE-sR-r4Z"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="footerLabel" destination="5ce-ZL-glQ" id="V50-Yc-hD6"/>
<outlet property="nameTextField" destination="Yl1-R6-xZi" id="jcl-vI-Rde"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="XJD-sO-MSq" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1879.7101449275365" y="144.64285714285714"/>
</scene>
<!--Feedbin-->
<scene sceneID="IDj-HA-olN">
<objects>
<tableViewController id="ECy-jg-Kyc" customClass="FeedbinAccountViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Y0x-RC-7ln">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<view key="tableFooterView" contentMode="scaleToFill" id="3KO-DU-JXG">
<rect key="frame" x="0.0" y="202.5" width="414" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sgL-0C-JZa">
<rect key="frame" x="20" y="8" width="373" height="65.5"/>
<string key="text">Sign in to your Feedbin account to sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.
Dont have a Feedbin account?</string>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Xhf-bK-vzm">
<rect key="frame" x="172" y="72" width="70" height="26"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<state key="normal" title="Sign Up Here"/>
<connections>
<action selector="signUpWithProvider:" destination="ECy-jg-Kyc" eventType="touchUpInside" id="fIY-hq-q3H"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
<constraints>
<constraint firstItem="sgL-0C-JZa" firstAttribute="top" secondItem="3KO-DU-JXG" secondAttribute="top" constant="8" id="BgR-gH-qHf"/>
<constraint firstItem="sgL-0C-JZa" firstAttribute="leading" secondItem="3KO-DU-JXG" secondAttribute="leading" constant="20" symbolic="YES" id="PLI-kz-MMq"/>
<constraint firstItem="Xhf-bK-vzm" firstAttribute="top" secondItem="sgL-0C-JZa" secondAttribute="bottom" constant="-1.5" id="R9l-5y-aMr"/>
<constraint firstAttribute="trailing" secondItem="sgL-0C-JZa" secondAttribute="trailing" constant="21" id="ddS-HE-f1J"/>
<constraint firstItem="Xhf-bK-vzm" firstAttribute="centerX" secondItem="3KO-DU-JXG" secondAttribute="centerX" id="xs6-P4-4Vj"/>
</constraints>
</view>
<sections>
<tableViewSection id="xBN-Pb-KAy">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="lsa-Fl-Pc7">
<rect key="frame" x="20" y="18" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lsa-Fl-Pc7" id="Lpd-D1-1PQ">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Email" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="vJa-NN-yjR">
<rect key="frame" x="20" y="12.5" width="334" height="18.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" spellCheckingType="no" keyboardType="emailAddress" textContentType="username"/>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="vJa-NN-yjR" secondAttribute="trailing" constant="20" id="7xY-Mz-Szf"/>
<constraint firstItem="vJa-NN-yjR" firstAttribute="centerY" secondItem="Lpd-D1-1PQ" secondAttribute="centerY" id="E8M-nD-KIN"/>
<constraint firstItem="vJa-NN-yjR" firstAttribute="leading" secondItem="Lpd-D1-1PQ" secondAttribute="leading" constant="20" id="Lgm-1L-4xL"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Hwv-Q0-zT0">
<rect key="frame" x="20" y="61.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Hwv-Q0-zT0" id="jIT-5L-d8d">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Password" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="YC2-RH-QoV">
<rect key="frame" x="20" y="13.5" width="290" height="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/>
</textField>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" pointerInteraction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TfW-wf-V06">
<rect key="frame" x="318" y="7.5" width="36" height="29"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<state key="normal" title="Show"/>
<connections>
<action selector="showHidePassword:" destination="ECy-jg-Kyc" eventType="touchUpInside" id="QcS-lr-SPG"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="TfW-wf-V06" firstAttribute="leading" secondItem="YC2-RH-QoV" secondAttribute="trailing" constant="8" symbolic="YES" id="MHu-Ut-Kox"/>
<constraint firstItem="TfW-wf-V06" firstAttribute="centerY" secondItem="jIT-5L-d8d" secondAttribute="centerY" id="O3Y-Jd-n9t"/>
<constraint firstItem="YC2-RH-QoV" firstAttribute="leading" secondItem="jIT-5L-d8d" secondAttribute="leading" constant="20" id="W79-WW-Buk"/>
<constraint firstItem="YC2-RH-QoV" firstAttribute="centerY" secondItem="jIT-5L-d8d" secondAttribute="centerY" id="iDt-ym-Qjf"/>
<constraint firstAttribute="trailing" secondItem="TfW-wf-V06" secondAttribute="trailing" constant="20" symbolic="YES" id="rMZ-af-tPg"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection id="Kkf-rn-qdv">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="hWd-EN-p7e">
<rect key="frame" x="20" y="141" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hWd-EN-p7e" id="S8S-1a-vVf">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gv7-yG-aE3" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="pt0-rn-0JI"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<state key="normal" title="Action">
<color key="titleColor" name="secondaryAccentColor"/>
</state>
<connections>
<action selector="action:" destination="ECy-jg-Kyc" eventType="touchUpInside" id="79h-R2-s0C"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="gv7-yG-aE3" firstAttribute="leading" secondItem="S8S-1a-vVf" secondAttribute="leading" id="cbE-1E-Mem"/>
<constraint firstAttribute="trailing" secondItem="gv7-yG-aE3" secondAttribute="trailing" id="tQC-jk-evr"/>
<constraint firstItem="gv7-yG-aE3" firstAttribute="centerY" secondItem="S8S-1a-vVf" secondAttribute="centerY" id="xSU-R7-XQ8"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="ECy-jg-Kyc" id="hUr-Xx-9Ho"/>
<outlet property="delegate" destination="ECy-jg-Kyc" id="DKA-Lp-mNb"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Feedbin" id="tYg-9f-kyd">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="pfF-Of-5NT">
<connections>
<action selector="cancel:" destination="ECy-jg-Kyc" id="ZKI-gV-ylg"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" id="Xwp-LO-qff">
<view key="customView" contentMode="scaleToFill" id="cn4-b1-uZa">
<rect key="frame" x="374" y="12" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="YvV-hB-lzT">
<rect key="frame" x="36" y="6" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="actionButton" destination="gv7-yG-aE3" id="ENc-5A-hQc"/>
<outlet property="activityIndicator" destination="YvV-hB-lzT" id="n1F-tV-5ZV"/>
<outlet property="cancelBarButtonItem" destination="pfF-Of-5NT" id="Zr3-qD-1Yi"/>
<outlet property="emailTextField" destination="vJa-NN-yjR" id="nCF-9W-YsF"/>
<outlet property="footerLabel" destination="sgL-0C-JZa" id="b6I-Mk-2K3"/>
<outlet property="passwordTextField" destination="YC2-RH-QoV" id="qaX-0i-7jq"/>
<outlet property="showHideButton" destination="TfW-wf-V06" id="PbL-67-Nrg"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="L24-0i-kyr" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3177" y="145"/>
</scene>
<!--Modal Navigation Controller-->
<scene sceneID="j4N-ax-exh">
<objects>
<navigationController storyboardIdentifier="NewsBlurAccountNavigationViewController" id="eE3-pu-HdL" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Fsp-NG-hoR">
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="Cge-ND-NpD" kind="relationship" relationship="rootViewController" id="1D5-CN-liN"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="8t3-0U-5vL" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4562" y="-528"/>
</scene>
<!--NewsBlur-->
<scene sceneID="tfA-kz-P6O">
<objects>
<tableViewController id="Cge-ND-NpD" customClass="NewsBlurAccountViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="fLL-7i-HdK">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<view key="tableFooterView" contentMode="scaleToFill" id="mgO-Iq-dEg">
<rect key="frame" x="0.0" y="202.5" width="414" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fal-e8-3BB">
<rect key="frame" x="20" y="8" width="373" height="65.5"/>
<string key="text">Sign in to your NewsBlur account to sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.
Dont have a NewsBlur account?</string>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="YhB-G0-eeJ">
<rect key="frame" x="172" y="72" width="70" height="26"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<state key="normal" title="Sign Up Here"/>
<connections>
<action selector="signUpWithProvider:" destination="Cge-ND-NpD" eventType="touchUpInside" id="Vfz-DD-Kwm"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
<constraints>
<constraint firstItem="YhB-G0-eeJ" firstAttribute="centerX" secondItem="mgO-Iq-dEg" secondAttribute="centerX" id="7r5-l6-NNv"/>
<constraint firstItem="fal-e8-3BB" firstAttribute="leading" secondItem="mgO-Iq-dEg" secondAttribute="leading" constant="20" symbolic="YES" id="O4q-GI-2AO"/>
<constraint firstItem="YhB-G0-eeJ" firstAttribute="top" secondItem="fal-e8-3BB" secondAttribute="bottom" constant="-1.5" id="UHc-sh-Xq4"/>
<constraint firstAttribute="trailing" secondItem="fal-e8-3BB" secondAttribute="trailing" constant="21" id="V0d-ny-GRE"/>
<constraint firstItem="fal-e8-3BB" firstAttribute="top" secondItem="mgO-Iq-dEg" secondAttribute="top" constant="8" id="h5B-kg-rZj"/>
</constraints>
</view>
<sections>
<tableViewSection id="I5T-12-2jC">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="gAY-Bo-c0L">
<rect key="frame" x="20" y="18" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="gAY-Bo-c0L" id="mqD-6S-DIl">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Username or Email" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="S4v-fs-DIO">
<rect key="frame" x="20" y="12.5" width="334" height="18.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" spellCheckingType="no" keyboardType="emailAddress" textContentType="username"/>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="S4v-fs-DIO" secondAttribute="trailing" constant="20" id="Upe-dm-4DP"/>
<constraint firstItem="S4v-fs-DIO" firstAttribute="leading" secondItem="mqD-6S-DIl" secondAttribute="leading" constant="20" id="pQc-Fh-6T3"/>
<constraint firstItem="S4v-fs-DIO" firstAttribute="centerY" secondItem="mqD-6S-DIl" secondAttribute="centerY" id="s9a-ew-C5W"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="iCK-kn-Au6">
<rect key="frame" x="20" y="61.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="iCK-kn-Au6" id="9Ej-wB-9Tr">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Password" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="fct-XR-fEa">
<rect key="frame" x="20" y="13.5" width="290" height="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/>
</textField>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GY9-nr-jFb">
<rect key="frame" x="318" y="7.5" width="36" height="29"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<state key="normal" title="Show"/>
<connections>
<action selector="showHidePassword:" destination="Cge-ND-NpD" eventType="touchUpInside" id="8JH-LX-URH"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="GY9-nr-jFb" firstAttribute="centerY" secondItem="9Ej-wB-9Tr" secondAttribute="centerY" id="3jf-KC-nd8"/>
<constraint firstItem="GY9-nr-jFb" firstAttribute="leading" secondItem="fct-XR-fEa" secondAttribute="trailing" constant="8" symbolic="YES" id="Ibr-pt-eGr"/>
<constraint firstAttribute="trailing" secondItem="GY9-nr-jFb" secondAttribute="trailing" constant="20" symbolic="YES" id="mcZ-cl-knP"/>
<constraint firstItem="fct-XR-fEa" firstAttribute="leading" secondItem="9Ej-wB-9Tr" secondAttribute="leading" constant="20" id="u5f-tJ-8ce"/>
<constraint firstItem="fct-XR-fEa" firstAttribute="centerY" secondItem="9Ej-wB-9Tr" secondAttribute="centerY" id="z5e-jg-0nm"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection id="L37-iZ-GVj">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="fyQ-K8-byV">
<rect key="frame" x="20" y="141" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fyQ-K8-byV" id="CtR-ZJ-FG5">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E1I-C4-JdL" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="yoo-36-msf"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<state key="normal" title="Action">
<color key="titleColor" name="secondaryAccentColor"/>
</state>
<connections>
<action selector="action:" destination="Cge-ND-NpD" eventType="touchUpInside" id="YQw-1k-e8G"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="E1I-C4-JdL" firstAttribute="centerY" secondItem="CtR-ZJ-FG5" secondAttribute="centerY" id="2vc-Ys-4Cj"/>
<constraint firstAttribute="trailing" secondItem="E1I-C4-JdL" secondAttribute="trailing" id="SLX-wc-QR7"/>
<constraint firstItem="E1I-C4-JdL" firstAttribute="leading" secondItem="CtR-ZJ-FG5" secondAttribute="leading" id="Veu-Wo-GSZ"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="Cge-ND-NpD" id="u8B-p4-Vlv"/>
<outlet property="delegate" destination="Cge-ND-NpD" id="RIw-V2-EJC"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="NewsBlur" id="jCQ-pH-6AD">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="bl6-Y1-wQ8">
<connections>
<action selector="cancel:" destination="Cge-ND-NpD" id="9zR-LJ-IWk"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" id="4yi-H0-B9J">
<view key="customView" contentMode="scaleToFill" id="8DU-L0-P6c">
<rect key="frame" x="374" y="12" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="HfW-jV-MjK">
<rect key="frame" x="36" y="6" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="actionButton" destination="E1I-C4-JdL" id="q2T-4o-c8i"/>
<outlet property="activityIndicator" destination="HfW-jV-MjK" id="AIV-uG-9uC"/>
<outlet property="cancelBarButtonItem" destination="bl6-Y1-wQ8" id="ohR-gW-5J2"/>
<outlet property="footerLabel" destination="fal-e8-3BB" id="7Fq-Oz-aEx"/>
<outlet property="passwordTextField" destination="fct-XR-fEa" id="fGL-4k-gZ6"/>
<outlet property="showHideButton" destination="GY9-nr-jFb" id="1p9-9F-GMY"/>
<outlet property="usernameTextField" destination="S4v-fs-DIO" id="B7I-yz-M0T"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="8Ku-6P-yPg" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4561" y="145"/>
</scene>
<!--Reader-->
<scene sceneID="3fU-9I-RDp">
<objects>
<tableViewController id="MzG-hS-TpF" customClass="ReaderAPIAccountViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="bQC-XA-xWP">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<view key="tableFooterView" contentMode="scaleToFill" id="6sa-hD-iAT">
<rect key="frame" x="0.0" y="246" width="414" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7Jj-p8-lYw">
<rect key="frame" x="20" y="8" width="373" height="39.5"/>
<string key="text">Use your Reader account to sync your feeds across your devices.
Dont have a Reader account?</string>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3Fq-U4-PS5">
<rect key="frame" x="172" y="46" width="70" height="26"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<state key="normal" title="Sign Up Here"/>
<connections>
<action selector="signUpWithProvider:" destination="MzG-hS-TpF" eventType="touchUpInside" id="pMC-f8-E4G"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
<constraints>
<constraint firstItem="3Fq-U4-PS5" firstAttribute="top" secondItem="7Jj-p8-lYw" secondAttribute="bottom" constant="-1.5" id="6ma-pw-DSZ"/>
<constraint firstItem="7Jj-p8-lYw" firstAttribute="leading" secondItem="6sa-hD-iAT" secondAttribute="leading" constant="20" symbolic="YES" id="CnY-UA-F3a"/>
<constraint firstItem="3Fq-U4-PS5" firstAttribute="centerX" secondItem="6sa-hD-iAT" secondAttribute="centerX" id="Rgg-oG-KOZ"/>
<constraint firstAttribute="trailing" secondItem="7Jj-p8-lYw" secondAttribute="trailing" constant="21" id="mgT-t6-6c8"/>
<constraint firstItem="7Jj-p8-lYw" firstAttribute="top" secondItem="6sa-hD-iAT" secondAttribute="top" constant="8" id="syN-5x-dbM"/>
</constraints>
</view>
<sections>
<tableViewSection id="Rju-xt-yUY">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="kW8-SV-Byq">
<rect key="frame" x="20" y="18" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="kW8-SV-Byq" id="4mV-Au-W6t">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Username or Email" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="CZg-x8-936">
<rect key="frame" x="14" y="12.5" width="347" height="18.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" spellCheckingType="no" keyboardType="emailAddress" textContentType="username"/>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="CZg-x8-936" secondAttribute="trailing" constant="13" id="7BW-D6-ZAW"/>
<constraint firstItem="CZg-x8-936" firstAttribute="centerY" secondItem="4mV-Au-W6t" secondAttribute="centerY" id="Fii-Qu-oXf"/>
<constraint firstItem="CZg-x8-936" firstAttribute="leading" secondItem="4mV-Au-W6t" secondAttribute="leading" constant="14" id="MKL-Nm-1Po"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="pNe-n6-tVf">
<rect key="frame" x="20" y="61.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pNe-n6-tVf" id="yQJ-L0-qVZ">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Password" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="KgN-kQ-Cyc">
<rect key="frame" x="14" y="13.5" width="300" height="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/>
</textField>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cFF-qt-WLs">
<rect key="frame" x="322" y="7.5" width="36" height="29"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<state key="normal" title="Show"/>
<connections>
<action selector="showHidePassword:" destination="MzG-hS-TpF" eventType="touchUpInside" id="IsF-iJ-oxT"/>
<action selector="showHidePassword:" destination="Cge-ND-NpD" eventType="touchUpInside" id="b9p-LX-Wk7"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="cFF-qt-WLs" firstAttribute="leading" secondItem="KgN-kQ-Cyc" secondAttribute="trailing" constant="8" symbolic="YES" id="Cwh-XX-m2G"/>
<constraint firstItem="cFF-qt-WLs" firstAttribute="centerY" secondItem="yQJ-L0-qVZ" secondAttribute="centerY" id="GDc-9f-afL"/>
<constraint firstAttribute="trailing" secondItem="cFF-qt-WLs" secondAttribute="trailing" constant="16" id="K93-X3-UuK"/>
<constraint firstItem="KgN-kQ-Cyc" firstAttribute="centerY" secondItem="yQJ-L0-qVZ" secondAttribute="centerY" id="UpQ-pU-DYv"/>
<constraint firstItem="KgN-kQ-Cyc" firstAttribute="leading" secondItem="yQJ-L0-qVZ" secondAttribute="leading" constant="14" id="fam-16-kf6"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="mCx-af-pd3">
<rect key="frame" x="20" y="105" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="mCx-af-pd3" id="o1U-Qv-4gz">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="API URL" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="iPv-M2-U8Q">
<rect key="frame" x="14" y="11" width="340" height="18.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="URL" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no" textContentType="url"/>
</textField>
</subviews>
<constraints>
<constraint firstItem="iPv-M2-U8Q" firstAttribute="leading" secondItem="o1U-Qv-4gz" secondAttribute="leading" constant="14" id="4UP-GO-kmh"/>
<constraint firstItem="iPv-M2-U8Q" firstAttribute="top" secondItem="o1U-Qv-4gz" secondAttribute="topMargin" id="Gap-aN-LP7"/>
<constraint firstItem="iPv-M2-U8Q" firstAttribute="trailing" secondItem="o1U-Qv-4gz" secondAttribute="trailingMargin" id="npR-r8-mpF"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection id="UWZ-Vu-0Pp">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" rowHeight="43.5" id="d3E-Ds-Thm">
<rect key="frame" x="20" y="184.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="d3E-Ds-Thm" id="Frb-uH-Sff">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7L9-X7-1Oc">
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
<state key="normal" title="Action"/>
<connections>
<action selector="action:" destination="MzG-hS-TpF" eventType="touchUpInside" id="d10-4f-ZUn"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="7L9-X7-1Oc" firstAttribute="centerY" secondItem="Frb-uH-Sff" secondAttribute="centerY" id="NVm-XD-zND"/>
<constraint firstItem="7L9-X7-1Oc" firstAttribute="centerX" secondItem="Frb-uH-Sff" secondAttribute="centerX" id="YB9-O8-Z15"/>
<constraint firstItem="7L9-X7-1Oc" firstAttribute="height" secondItem="Frb-uH-Sff" secondAttribute="height" multiplier="0.689655" constant="13.999999999999996" id="iNV-NE-jhW"/>
<constraint firstItem="7L9-X7-1Oc" firstAttribute="width" secondItem="Frb-uH-Sff" secondAttribute="width" multiplier="0.893048" constant="40" id="lfQ-KQ-9nR"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="MzG-hS-TpF" id="QvG-cd-Q3P"/>
<outlet property="delegate" destination="MzG-hS-TpF" id="o4Z-RV-8uW"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Reader" id="z3N-XM-EXU">
<barButtonItem key="leftBarButtonItem" title="Cancel" id="n8H-ai-4Df">
<connections>
<action selector="cancel:" destination="MzG-hS-TpF" id="a49-Fh-i1S"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" id="Ih6-jI-jFg">
<view key="customView" contentMode="scaleToFill" id="gSl-PT-7DH">
<rect key="frame" x="374" y="12" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="pdn-6v-d9a">
<rect key="frame" x="36" y="6" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="actionButton" destination="7L9-X7-1Oc" id="VnP-sl-Cmd"/>
<outlet property="activityIndicator" destination="pdn-6v-d9a" id="vgt-C6-fy6"/>
<outlet property="apiURLTextField" destination="iPv-M2-U8Q" id="8kn-Xk-a8w"/>
<outlet property="cancelBarButtonItem" destination="n8H-ai-4Df" id="u86-HH-HYC"/>
<outlet property="footerLabel" destination="7Jj-p8-lYw" id="Tqv-qR-WBR"/>
<outlet property="passwordTextField" destination="KgN-kQ-Cyc" id="A0K-JL-CEW"/>
<outlet property="showHideButton" destination="cFF-qt-WLs" id="AxI-Gl-NdM"/>
<outlet property="signUpButton" destination="3Fq-U4-PS5" id="Wuj-5g-vDH"/>
<outlet property="usernameTextField" destination="CZg-x8-936" id="nUT-WL-fKD"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Fj8-E0-Aeh" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="5260.8695652173919" y="144.64285714285714"/>
</scene>
<!--Modal Navigation Controller-->
<scene sceneID="gfi-2F-rht">
<objects>
<navigationController storyboardIdentifier="CloudKitAccountNavigationViewController" id="LhW-Dq-qqj" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="MVG-BZ-ALL">
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="qj9-Vr-VIU" kind="relationship" relationship="rootViewController" id="n8n-iF-qeC"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="z9f-5I-8GC" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2533" y="-528"/>
</scene>
<!--iCloud-->
<scene sceneID="ULt-VE-viU">
<objects>
<tableViewController id="qj9-Vr-VIU" customClass="CloudKitAccountViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="j6U-sh-M9y">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<view key="tableFooterView" contentMode="scaleToFill" id="iYz-ri-yys">
<rect key="frame" x="0.0" y="79.5" width="414" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Your iCloud account syncs your feeds across your Mac and iOS devices" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aFS-Y0-2MH">
<rect key="frame" x="20" y="8" width="373" height="36"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vXG-7q-4qg">
<rect key="frame" x="75" y="50.5" width="264" height="30"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="iCloud Syncing Limitations &amp; Solutions"/>
<connections>
<action selector="openLimitationsAndSolutions:" destination="qj9-Vr-VIU" eventType="touchUpInside" id="JZ5-hQ-PLl"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
<constraints>
<constraint firstItem="vXG-7q-4qg" firstAttribute="centerX" secondItem="iYz-ri-yys" secondAttribute="centerX" id="8Pg-BU-zIj"/>
<constraint firstItem="aFS-Y0-2MH" firstAttribute="leading" secondItem="iYz-ri-yys" secondAttribute="leading" constant="20" symbolic="YES" id="E97-lo-arw"/>
<constraint firstItem="vXG-7q-4qg" firstAttribute="top" secondItem="aFS-Y0-2MH" secondAttribute="bottom" id="TEh-B3-9Ci"/>
<constraint firstAttribute="trailing" secondItem="aFS-Y0-2MH" secondAttribute="trailing" constant="21" id="XUo-oQ-MbK"/>
<constraint firstItem="aFS-Y0-2MH" firstAttribute="top" secondItem="iYz-ri-yys" secondAttribute="top" constant="8" id="xpj-LW-4l7"/>
</constraints>
</view>
<sections>
<tableViewSection id="bGn-Io-KuQ">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="FSY-KL-m3i" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="18" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FSY-KL-m3i" id="ds7-ib-VgJ">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="T1S-zH-rIp" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="dOv-Gz-h7s"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<state key="normal" title="Use iCloud">
<color key="titleColor" name="secondaryAccentColor"/>
</state>
<connections>
<action selector="add:" destination="qj9-Vr-VIU" eventType="touchUpInside" id="kUm-lW-g62"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="T1S-zH-rIp" firstAttribute="leading" secondItem="ds7-ib-VgJ" secondAttribute="leading" id="7F5-Ym-ew3"/>
<constraint firstAttribute="trailing" secondItem="T1S-zH-rIp" secondAttribute="trailing" id="ON3-nQ-kd8"/>
<constraint firstItem="T1S-zH-rIp" firstAttribute="centerY" secondItem="ds7-ib-VgJ" secondAttribute="centerY" id="dAM-F2-peY"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="qj9-Vr-VIU" id="j7u-Yd-rbe"/>
<outlet property="delegate" destination="qj9-Vr-VIU" id="NhE-Pt-JGp"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="iCloud" id="idp-kp-cGU">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="hKZ-OI-mTV">
<connections>
<action selector="cancel:" destination="qj9-Vr-VIU" id="n5q-9M-3ME"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="footerLabel" destination="aFS-Y0-2MH" id="gDw-R1-HSK"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="weY-OS-9NV" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2533" y="145"/>
</scene>
<!--Modal Navigation Controller-->
<scene sceneID="JBz-7C-wEc">
<objects>
<navigationController storyboardIdentifier="ReaderAPIAccountNavigationViewController" id="Son-xT-GLx" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="sdL-X8-E6K">
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="MzG-hS-TpF" kind="relationship" relationship="rootViewController" id="gsQ-9o-AMJ"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vls-xO-YVi" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="5261" y="-528"/>
</scene>
</scenes>
<resources>
<namedColor name="secondaryAccentColor">
<color red="0.031372549019607843" green="0.41568627450980394" blue="0.93333333333333335" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemGroupedBackgroundColor">
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View File

@@ -6,11 +6,14 @@
Copyright © 2022 Ranchero Software. All rights reserved.
*/
/* Account Names */
/* Account Section */
"ACCOUNT_NAME" = "Name";
"CLOUDKIT" = "iCloud";
"LOCAL_ACCOUNT_NAME_PHONE" = "On My iPhone";
"LOCAL_ACCOUNT_NAME_PAD" = "On My iPad";
"ACCOUNT_EMAIL_ADDRESS_PROMPT" = "Email Address";
"ACCOUNT_USERNAME_PROMPT" = "Username";
"ACCOUNT_PASSWORD_PROMPT" = "Password";
/* Explainers */
@@ -20,5 +23,7 @@
"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\nDont 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\nDont 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.";
"FEEDBIN_FOOTER_EXPLAINER" = "Sign in to your Feedbin account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDont have a Feedbin account? [Sign Up Here](https://feedbin.com/signup)";
"NEWSBLUR_FOOTER_EXPLAINER" = "Sign in to your NewsBlur account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDont have a NewsBlur account? [Sign Up Here](https://newsblur.com)";

View File

@@ -1,73 +0,0 @@
//
// CloudKitAccountViewController.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 3/28/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import UIKit
import SafariServices
import Account
enum CloudKitAccountViewControllerError: LocalizedError {
case iCloudDriveMissing
var errorDescription: String? {
return NSLocalizedString("Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Preferences.", comment: "Unable to add iCloud Account.")
}
}
class CloudKitAccountViewController: UITableViewController {
//weak var delegate: AddAccountDismissDelegate?
@IBOutlet weak var footerLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
setupFooter()
tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
}
private func setupFooter() {
footerLabel.text = NSLocalizedString("NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices.", comment: "iCloud")
}
@IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
//delegate?.dismiss()
}
@IBAction func add(_ sender: Any) {
guard FileManager.default.ubiquityIdentityToken != nil else {
presentError(CloudKitAccountViewControllerError.iCloudDriveMissing)
return
}
let _ = AccountManager.shared.createAccount(type: .cloudKit)
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: .cloudKit)
return headerView
} else {
return super.tableView(tableView, viewForHeaderInSection: section)
}
}
@IBAction func openLimitationsAndSolutions(_ sender: Any) {
let vc = SFSafariViewController(url: CloudKitWebDocumentation.limitationsAndSolutionsURL)
vc.modalPresentationStyle = .pageSheet
present(vc, animated: true)
}
}

View File

@@ -1,194 +0,0 @@
//
// FeedbinAccountViewController.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 5/19/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import UIKit
import Account
import Secrets
import RSWeb
import SafariServices
import RSCore
class FeedbinAccountViewController: UITableViewController, Logging {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var showHideButton: UIButton!
@IBOutlet weak var actionButton: UIButton!
@IBOutlet weak var footerLabel: UILabel!
weak var account: Account?
//weak var delegate: AddAccountDismissDelegate?
override func viewDidLoad() {
super.viewDidLoad()
setupFooter()
activityIndicator.isHidden = true
emailTextField.delegate = self
passwordTextField.delegate = self
if let account = account, let credentials = try? account.retrieveCredentials(type: .basic) {
actionButton.setTitle(NSLocalizedString("Update Credentials", comment: "Update Credentials"), for: .normal)
actionButton.isEnabled = true
emailTextField.text = credentials.username
passwordTextField.text = credentials.secret
} else {
actionButton.setTitle(NSLocalizedString("Add Account", comment: "Add Account"), for: .normal)
}
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: emailTextField)
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: passwordTextField)
tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
}
private func setupFooter() {
footerLabel.text = NSLocalizedString("Sign in to your Feedbin account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDont have a Feedbin account?", comment: "Feedbin")
}
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: .feedbin)
return headerView
} else {
return super.tableView(tableView, viewForHeaderInSection: section)
}
}
@IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
@IBAction func showHidePassword(_ sender: Any) {
if passwordTextField.isSecureTextEntry {
passwordTextField.isSecureTextEntry = false
showHideButton.setTitle("Hide", for: .normal)
} else {
passwordTextField.isSecureTextEntry = true
showHideButton.setTitle("Show", for: .normal)
}
}
@IBAction func action(_ sender: Any) {
guard let email = emailTextField.text, let password = passwordTextField.text else {
showError(NSLocalizedString("Username & password required.", comment: "Credentials Error"))
return
}
// When you fill in the email address via auto-complete it adds extra whitespace
let trimmedEmail = email.trimmingCharacters(in: .whitespaces)
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: trimmedEmail) else {
showError(NSLocalizedString("There is already a Feedbin account with that username created.", comment: "Duplicate Error"))
return
}
resignFirstResponder()
toggleActivityIndicatorAnimation(visible: true)
setNavigationEnabled(to: false)
let credentials = Credentials(type: .basic, username: trimmedEmail, secret: password)
Account.validateCredentials(type: .feedbin, credentials: credentials) { result in
self.toggleActivityIndicatorAnimation(visible: false)
self.setNavigationEnabled(to: true)
switch result {
case .success(let credentials):
if let credentials = credentials {
if self.account == nil {
self.account = AccountManager.shared.createAccount(type: .feedbin)
}
do {
do {
try self.account?.removeCredentials(type: .basic)
} catch {
self.logger.error("Error removing credentials: \(error.localizedDescription, privacy: .public).")
}
try self.account?.storeCredentials(credentials)
self.account?.refreshAll() { result in
switch result {
case .success:
break
case .failure(let error):
self.presentError(error)
}
}
self.dismiss(animated: true, completion: nil)
//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).")
}
} else {
self.showError(NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error"))
}
case .failure:
self.showError(NSLocalizedString("Network error. Try again later.", comment: "Credentials Error"))
}
}
}
@IBAction func signUpWithProvider(_ sender: Any) {
let url = URL(string: "https://feedbin.com/signup")!
let safari = SFSafariViewController(url: url)
safari.modalPresentationStyle = .currentContext
self.present(safari, animated: true, completion: nil)
}
@objc func textDidChange(_ note: Notification) {
actionButton.isEnabled = !(emailTextField.text?.isEmpty ?? false) && !(passwordTextField.text?.isEmpty ?? false)
}
private func showError(_ message: String) {
presentError(title: NSLocalizedString("Error", comment: "Credentials Error"), message: message)
}
private func setNavigationEnabled(to value:Bool){
cancelBarButtonItem.isEnabled = value
actionButton.isEnabled = value
}
private func toggleActivityIndicatorAnimation(visible value: Bool){
activityIndicator.isHidden = !value
if value {
activityIndicator.startAnimating()
} else {
activityIndicator.stopAnimating()
}
}
}
extension FeedbinAccountViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == emailTextField {
passwordTextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
action(self)
}
return true
}
}

View File

@@ -1,197 +0,0 @@
//
// NewsBlurAccountViewController.swift
// NetNewsWire
//
// Created by Anh-Quang Do on 3/9/20.
// Copyright (c) 2020 Ranchero Software. All rights reserved.
//
import UIKit
import Account
import Secrets
import RSWeb
import RSCore
import SafariServices
class NewsBlurAccountViewController: UITableViewController, Logging {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var showHideButton: UIButton!
@IBOutlet weak var actionButton: UIButton!
@IBOutlet weak var footerLabel: UILabel!
weak var account: Account?
//weak var delegate: AddAccountDismissDelegate?
override func viewDidLoad() {
super.viewDidLoad()
setupFooter()
activityIndicator.isHidden = true
usernameTextField.delegate = self
passwordTextField.delegate = self
if let account = account, let credentials = try? account.retrieveCredentials(type: .newsBlurBasic) {
actionButton.setTitle(NSLocalizedString("Update Credentials", comment: "Update Credentials"), for: .normal)
actionButton.isEnabled = true
usernameTextField.text = credentials.username
passwordTextField.text = credentials.secret
} else {
actionButton.setTitle(NSLocalizedString("Add Account", comment: "Add Account"), for: .normal)
}
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: usernameTextField)
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: passwordTextField)
tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
}
private func setupFooter() {
footerLabel.text = NSLocalizedString("Sign in to your NewsBlur account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDont have a NewsBlur account?", comment: "NewsBlur")
}
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: .newsBlur)
return headerView
} else {
return super.tableView(tableView, viewForHeaderInSection: section)
}
}
@IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
@IBAction func showHidePassword(_ sender: Any) {
if passwordTextField.isSecureTextEntry {
passwordTextField.isSecureTextEntry = false
showHideButton.setTitle("Hide", for: .normal)
} else {
passwordTextField.isSecureTextEntry = true
showHideButton.setTitle("Show", for: .normal)
}
}
@IBAction func action(_ sender: Any) {
guard let username = usernameTextField.text else {
showError(NSLocalizedString("Username required.", comment: "Credentials Error"))
return
}
// When you fill in the email address via auto-complete it adds extra whitespace
let trimmedUsername = username.trimmingCharacters(in: .whitespaces)
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: trimmedUsername) else {
showError(NSLocalizedString("There is already a NewsBlur account with that username created.", comment: "Duplicate Error"))
return
}
let password = passwordTextField.text ?? ""
startAnimatingActivityIndicator()
disableNavigation()
let basicCredentials = Credentials(type: .newsBlurBasic, username: trimmedUsername, secret: password)
Account.validateCredentials(type: .newsBlur, credentials: basicCredentials) { result in
self.stopAnimatingActivityIndicator()
self.enableNavigation()
switch result {
case .success(let sessionCredentials):
if let sessionCredentials = sessionCredentials {
if self.account == nil {
self.account = AccountManager.shared.createAccount(type: .newsBlur)
}
do {
do {
try self.account?.removeCredentials(type: .newsBlurBasic)
try self.account?.removeCredentials(type: .newsBlurSessionId)
} catch {
self.logger.error("Error removing credentials: \(error.localizedDescription, privacy: .public).")
}
try self.account?.storeCredentials(basicCredentials)
try self.account?.storeCredentials(sessionCredentials)
self.account?.refreshAll() { result in
switch result {
case .success:
break
case .failure(let error):
self.presentError(error)
}
}
self.dismiss(animated: true, completion: nil)
//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).")
}
} else {
self.showError(NSLocalizedString("Invalid username/password combination.", comment: "Credentials Error"))
}
case .failure(let error):
self.showError(error.localizedDescription)
}
}
}
@IBAction func signUpWithProvider(_ sender: Any) {
let url = URL(string: "https://newsblur.com")!
let safari = SFSafariViewController(url: url)
safari.modalPresentationStyle = .currentContext
self.present(safari, animated: true, completion: nil)
}
@objc func textDidChange(_ note: Notification) {
actionButton.isEnabled = !(usernameTextField.text?.isEmpty ?? false)
}
private func showError(_ message: String) {
presentError(title: "Error", message: message)
}
private func enableNavigation() {
self.cancelBarButtonItem.isEnabled = true
self.actionButton.isEnabled = true
}
private func disableNavigation() {
cancelBarButtonItem.isEnabled = false
actionButton.isEnabled = false
}
private func startAnimatingActivityIndicator() {
activityIndicator.isHidden = false
activityIndicator.startAnimating()
}
private func stopAnimatingActivityIndicator() {
self.activityIndicator.isHidden = true
self.activityIndicator.stopAnimating()
}
}
extension NewsBlurAccountViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

View File

@@ -1,322 +0,0 @@
//
// ReaderAPIAccountViewController.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 25/10/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import UIKit
import Account
import Secrets
import RSWeb
import SafariServices
import RSCore
class ReaderAPIAccountViewController: UITableViewController, Logging {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var apiURLTextField: UITextField!
@IBOutlet weak var showHideButton: UIButton!
@IBOutlet weak var actionButton: UIButton!
@IBOutlet weak var footerLabel: UILabel!
@IBOutlet weak var signUpButton: UIButton!
weak var account: Account?
var accountType: AccountType?
//weak var delegate: AddAccountDismissDelegate?
override func viewDidLoad() {
super.viewDidLoad()
setupFooter()
activityIndicator.isHidden = true
usernameTextField.delegate = self
passwordTextField.delegate = self
if let unwrappedAcount = account,
let credentials = try? retrieveCredentialsForAccount(for: unwrappedAcount) {
actionButton.setTitle(NSLocalizedString("Update Credentials", comment: "Update Credentials"), for: .normal)
actionButton.isEnabled = true
usernameTextField.text = credentials.username
passwordTextField.text = credentials.secret
} else {
actionButton.setTitle(NSLocalizedString("Add Account", comment: "Add Account"), for: .normal)
}
if let unwrappedAccountType = accountType {
switch unwrappedAccountType {
case .freshRSS:
title = NSLocalizedString("FreshRSS", comment: "FreshRSS")
apiURLTextField.placeholder = NSLocalizedString("API URL: fresh.rss.net/api/greader.php", comment: "FreshRSS API Helper")
case .inoreader:
title = NSLocalizedString("InoReader", comment: "InoReader")
case .bazQux:
title = NSLocalizedString("BazQux", comment: "BazQux")
case .theOldReader:
title = NSLocalizedString("The Old Reader", comment: "The Old Reader")
default:
title = ""
}
}
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: usernameTextField)
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: passwordTextField)
tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
}
private func setupFooter() {
switch accountType {
case .bazQux:
footerLabel.text = NSLocalizedString("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\nDont have a BazQux account?", comment: "BazQux")
signUpButton.setTitle(NSLocalizedString("Sign Up Here", comment: "BazQux SignUp"), for: .normal)
case .inoreader:
footerLabel.text = NSLocalizedString("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\nDont have an InoReader account?", comment: "InoReader")
signUpButton.setTitle(NSLocalizedString("Sign Up Here", comment: "InoReader SignUp"), for: .normal)
case .theOldReader:
footerLabel.text = NSLocalizedString("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\nDont have a The Old Reader account?", comment: "TOR")
signUpButton.setTitle(NSLocalizedString("Sign Up Here", comment: "TOR SignUp"), for: .normal)
case .freshRSS:
footerLabel.text = NSLocalizedString("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\nDont have an FreshRSS instance?", comment: "FreshRSS")
signUpButton.setTitle(NSLocalizedString("Find Out More", comment: "FreshRSS SignUp"), for: .normal)
default:
return
}
}
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 = headerViewImage()
return headerView
} else {
return super.tableView(tableView, viewForHeaderInSection: section)
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
switch accountType {
case .freshRSS:
return 3
default:
return 2
}
default:
return 1
}
}
@IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
@IBAction func showHidePassword(_ sender: Any) {
if passwordTextField.isSecureTextEntry {
passwordTextField.isSecureTextEntry = false
showHideButton.setTitle("Hide", for: .normal)
} else {
passwordTextField.isSecureTextEntry = true
showHideButton.setTitle("Show", for: .normal)
}
}
@IBAction func action(_ sender: Any) {
guard validateDataEntry(), let type = accountType else {
return
}
let username = usernameTextField.text!
let password = passwordTextField.text!
let url = apiURL()!
// When you fill in the email address via auto-complete it adds extra whitespace
let trimmedUsername = username.trimmingCharacters(in: .whitespaces)
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: type, username: trimmedUsername) else {
showError(NSLocalizedString("There is already an account of that type with that username created.", comment: "Duplicate Error"))
return
}
startAnimatingActivityIndicator()
disableNavigation()
let credentials = Credentials(type: .readerBasic, username: trimmedUsername, secret: password)
Account.validateCredentials(type: type, credentials: credentials, endpoint: url) { result in
self.stopAnimatingActivityIndicator()
self.enableNavigation()
switch result {
case .success(let validatedCredentials):
if let validatedCredentials = validatedCredentials {
if self.account == nil {
self.account = AccountManager.shared.createAccount(type: type)
}
do {
self.account?.endpointURL = url
try? self.account?.removeCredentials(type: .readerBasic)
try? self.account?.removeCredentials(type: .readerAPIKey)
try self.account?.storeCredentials(credentials)
try self.account?.storeCredentials(validatedCredentials)
self.dismiss(animated: true, completion: nil)
self.account?.refreshAll() { result in
switch result {
case .success:
break
case .failure(let error):
self.showError(NSLocalizedString(error.localizedDescription, comment: "Accoount Refresh Error"))
}
}
//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).")
}
} else {
self.showError(NSLocalizedString("Invalid username/password combination.", comment: "Credentials Error"))
}
case .failure(let error):
self.showError(error.localizedDescription)
}
}
}
private func retrieveCredentialsForAccount(for account: Account) throws -> Credentials? {
switch accountType {
case .bazQux, .inoreader, .theOldReader, .freshRSS:
return try account.retrieveCredentials(type: .readerBasic)
default:
return nil
}
}
private func headerViewImage() -> UIImage? {
if let accountType = accountType {
switch accountType {
case .bazQux:
return AppAssets.accountBazQuxImage
case .inoreader:
return AppAssets.accountInoreaderImage
case .theOldReader:
return AppAssets.accountTheOldReaderImage
case .freshRSS:
return AppAssets.accountFreshRSSImage
default:
return nil
}
}
return nil
}
private func validateDataEntry() -> Bool {
switch accountType {
case .freshRSS:
if !usernameTextField.hasText || !passwordTextField.hasText || !apiURLTextField.hasText {
showError(NSLocalizedString("Username, password, and API URL are required.", comment: "Credentials Error"))
return false
}
guard let _ = URL(string: apiURLTextField.text!) else {
showError(NSLocalizedString("Invalid API URL.", comment: "Invalid API URL"))
return false
}
default:
if !usernameTextField.hasText || !passwordTextField.hasText {
showError(NSLocalizedString("Username and password are required.", comment: "Credentials Error"))
return false
}
}
return true
}
@IBAction func signUpWithProvider(_ sender: Any) {
var url: URL!
switch accountType {
case .bazQux:
url = URL(string: "https://bazqux.com")!
case .inoreader:
url = URL(string: "https://www.inoreader.com")!
case .theOldReader:
url = URL(string: "https://theoldreader.com")!
case .freshRSS:
url = URL(string: "https://freshrss.org")!
default:
return
}
let safari = SFSafariViewController(url: url)
safari.modalPresentationStyle = .currentContext
self.present(safari, animated: true, completion: nil)
}
private func apiURL() -> URL? {
switch accountType {
case .freshRSS:
return URL(string: apiURLTextField.text!)!
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
}
}
@objc func textDidChange(_ note: Notification) {
actionButton.isEnabled = !(usernameTextField.text?.isEmpty ?? false)
}
private func showError(_ message: String) {
presentError(title: "Error", message: message)
}
private func enableNavigation() {
self.cancelBarButtonItem.isEnabled = true
self.actionButton.isEnabled = true
}
private func disableNavigation() {
cancelBarButtonItem.isEnabled = false
actionButton.isEnabled = false
}
private func startAnimatingActivityIndicator() {
activityIndicator.isHidden = false
activityIndicator.startAnimating()
}
private func stopAnimatingActivityIndicator() {
self.activityIndicator.isHidden = true
self.activityIndicator.stopAnimating()
}
}
extension ReaderAPIAccountViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

View File

@@ -17,7 +17,7 @@ struct CloudKitAddAccountView: View {
var body: some View {
NavigationView {
Form {
Section(header: cloudKitHeader) {}
AccountSectionHeader(accountType: .cloudKit)
Section { createCloudKitAccount }
Section(footer: cloudKitExplainer) {}
}
@@ -33,23 +33,11 @@ struct CloudKitAddAccountView: View {
} message: {
Text(addAccountError.0?.localizedDescription ?? "Unknown Error")
}
.onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in
dismiss()
}
.dismissOnExternalContextLaunch()
.dismissOnAccountAdd()
}
}
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 {

View File

@@ -0,0 +1,167 @@
//
// FeedbinAddAccountView.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 18/12/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
import Secrets
import RSWeb
import SafariServices
import RSCore
struct FeedbinAddAccountView: View {
@Environment(\.dismiss) private var dismiss
@State var account: Account? = nil
@State private var accountEmail: String = ""
@State private var accountPassword: String = ""
@State private var showProgressIndicator: Bool = false
@State private var accountError: (Error?, Bool) = (nil, false)
var body: some View {
NavigationView {
Form {
AccountSectionHeader(accountType: .feedbin)
accountDetails
accountButton
Section(footer: feedbinAccountExplainer) {}
}
.task {
retrieveCredentials()
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") })
.disabled(showProgressIndicator)
}
ToolbarItem(placement: .navigationBarTrailing) {
if showProgressIndicator { ProgressView() }
}
}
.alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) {
Button(role: .cancel) {
//
} label: {
Text("DISMISS_BUTTON_TITLE", tableName: "Buttons")
}
} message: {
Text(accountError.0?.localizedDescription ?? "Error")
}
.navigationTitle(Text(account?.type.localizedAccountName() ?? ""))
.navigationBarTitleDisplayMode(.inline)
.dismissOnExternalContextLaunch()
.dismissOnAccountAdd()
}
}
var accountDetails: some View {
Section {
TextField("Email", text: $accountEmail, prompt: Text("ACCOUNT_EMAIL_ADDRESS_PROMPT", tableName: "Account"))
.autocorrectionDisabled()
.autocapitalization(.none)
SecureField("Password", text: $accountPassword, prompt: Text("ACCOUNT_PASSWORD_PROMPT", tableName: "Account"))
}
}
var accountButton: some View {
Section {
Button {
Task {
do {
try await executeAccountCredentials()
dismiss()
} catch {
accountError = (error, true)
}
}
} label: {
HStack{
Spacer()
if account == nil {
Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons")
} else {
Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons")
}
Spacer()
}
}
}
}
var feedbinAccountExplainer: some View {
Text("FEEDBIN_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center)
}
private func retrieveCredentials() {
if let account = account {
do {
if let creds = try account.retrieveCredentials(type: .basic) {
accountEmail = creds.username
accountPassword = creds.secret
}
} catch {
accountError = (error, true)
}
}
}
private func executeAccountCredentials() async throws {
let trimmedEmailAddress = accountEmail.trimmingWhitespace
guard (account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: trimmedEmailAddress)) else {
throw LocalizedNetNewsWireError.duplicateAccount
}
showProgressIndicator = true
let credentials = Credentials(type: .basic, username: trimmedEmailAddress, secret: accountPassword)
return try await withCheckedThrowingContinuation { continuation in
Account.validateCredentials(type: .feedbin, credentials: credentials) { result in
switch result {
case .success(let credentials):
if let validatedCredentials = credentials {
if self.account == nil {
self.account = AccountManager.shared.createAccount(type: .feedbin)
}
do {
try? self.account?.removeCredentials(type: .basic)
try self.account?.storeCredentials(validatedCredentials)
self.account?.refreshAll(completion: { result in
switch result {
case .success(_):
showProgressIndicator = false
continuation.resume()
case .failure(let failure):
showProgressIndicator = false
continuation.resume(throwing: failure)
}
})
} catch {
showProgressIndicator = false
continuation.resume(throwing: LocalizedNetNewsWireError.keychainError)
}
} else {
showProgressIndicator = false
continuation.resume(throwing: LocalizedNetNewsWireError.invalidUsernameOrPassword)
}
case .failure(let failure):
showProgressIndicator = false
continuation.resume(throwing: failure)
}
}
}
}
}
struct FeedbinAddAccountView_Previews: PreviewProvider {
static var previews: some View {
FeedbinAddAccountView()
}
}

View File

@@ -17,7 +17,7 @@ struct LocalAddAccountView: View {
var body: some View {
NavigationView {
Form {
Section(header: accountHeaderView) {}
AccountSectionHeader(accountType: .onMyMac)
Section { accountNameSection }
Section { addAccountButton }
Section(footer: accountFooterView) {}
@@ -29,9 +29,8 @@ struct LocalAddAccountView: View {
}
.navigationTitle(deviceAccountName())
.navigationBarTitleDisplayMode(.inline)
.onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in
dismiss()
}
.dismissOnExternalContextLaunch()
.dismissOnAccountAdd()
}
}
@@ -39,6 +38,8 @@ struct LocalAddAccountView: View {
TextField("Name",
text: $accountName,
prompt: Text("ACCOUNT_NAME", tableName: "Account"))
.autocorrectionDisabled()
.autocapitalization(.none)
}
var addAccountButton: some View {
@@ -54,17 +55,6 @@ struct LocalAddAccountView: View {
}
}
var accountHeaderView: some View {
HStack {
Spacer()
Image(uiImage: accountImage())
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 48, height: 48)
Spacer()
}
}
var accountFooterView: some View {
HStack {
Spacer()

View File

@@ -0,0 +1,173 @@
//
// NewsBlurAddAccountView.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 18/12/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
import Secrets
import RSWeb
import RSCore
struct NewsBlurAddAccountView: View {
@Environment(\.dismiss) private var dismiss
@State var account: Account? = nil
@State private var accountUserName: String = ""
@State private var accountPassword: String = ""
@State private var showProgressIndicator: Bool = false
@State private var accountError: (Error?, Bool) = (nil, false)
var body: some View {
NavigationView {
Form {
AccountSectionHeader(accountType: .newsBlur)
accountDetails
accountButton
Section(footer: newsBlurAccountExplainer) {}
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") })
.disabled(showProgressIndicator)
}
ToolbarItem(placement: .navigationBarTrailing) {
if showProgressIndicator { ProgressView() }
}
}
.navigationTitle(Text(AccountType.newsBlur.localizedAccountName()))
.navigationBarTitleDisplayMode(.inline)
.task {
retreiveCredentials()
}
.alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) {
Button(role: .cancel) {
//
} label: {
Text("DISMISS_BUTTON_TITLE", tableName: "Buttons")
}
} message: {
Text(accountError.0?.localizedDescription ?? "")
}
.dismissOnExternalContextLaunch()
.dismissOnAccountAdd()
}
}
func retreiveCredentials() {
if let account = account {
do {
let credentials = try account.retrieveCredentials(type: .newsBlurBasic)
if let credentials = credentials {
self.accountUserName = credentials.username
self.accountPassword = credentials.secret
} else {
print("No cred")
}
} catch {
print(error.localizedDescription)
}
} else {
print("No account")
}
}
var accountDetails: some View {
Section {
TextField("Email", text: $accountUserName, prompt: Text("ACCOUNT_USERNAME_PROMPT", tableName: "Account"))
.autocorrectionDisabled()
.autocapitalization(.none)
SecureField("Password", text: $accountPassword, prompt: Text("ACCOUNT_PASSWORD_PROMPT", tableName: "Account"))
}
}
var accountButton: some View {
Section {
Button {
Task {
do {
try await executeAccountCredentials()
dismiss()
} catch {
accountError = (error, true)
}
}
} label: {
HStack{
Spacer()
if account == nil {
Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons")
} else {
Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons")
}
Spacer()
}
}
}
}
var newsBlurAccountExplainer: some View {
Text("NEWSBLUR_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center)
}
private func executeAccountCredentials() async throws {
let trimmedEmailAddress = accountUserName.trimmingWhitespace
guard (account != nil || !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: trimmedEmailAddress)) else {
throw LocalizedNetNewsWireError.duplicateAccount
}
showProgressIndicator = true
let basicCredentials = Credentials(type: .newsBlurBasic, username: trimmedEmailAddress, secret: accountPassword)
return try await withCheckedThrowingContinuation { continuation in
Account.validateCredentials(type: .newsBlur, credentials: basicCredentials) { result in
switch result {
case .success(let credentials):
if let sessionsCredentials = credentials {
if self.account == nil {
self.account = AccountManager.shared.createAccount(type: .newsBlur)
}
do {
try self.account?.removeCredentials(type: .newsBlurBasic)
try self.account?.removeCredentials(type: .newsBlurSessionId)
try self.account?.storeCredentials(basicCredentials)
try self.account?.storeCredentials(sessionsCredentials)
self.account?.refreshAll(completion: { result in
switch result {
case .success(_):
showProgressIndicator = false
continuation.resume()
case .failure(let failure):
showProgressIndicator = false
continuation.resume(throwing: failure)
}
})
} catch {
showProgressIndicator = false
continuation.resume(throwing: LocalizedNetNewsWireError.keychainError)
}
} else {
showProgressIndicator = false
continuation.resume(throwing: LocalizedNetNewsWireError.invalidUsernameOrPassword)
}
case .failure(let failure):
showProgressIndicator = false
continuation.resume(throwing: failure)
}
}
}
}
}
struct NewsBlurAddAccountView_Previews: PreviewProvider {
static var previews: some View {
NewsBlurAddAccountView()
}
}

View File

@@ -29,14 +29,16 @@ struct ReaderAPIAddAccountView: View {
var body: some View {
NavigationView {
Form {
Section(header: readerAccountImage) {}
if accountType != nil {
AccountSectionHeader(accountType: accountType!)
}
accountDetailsSection
Section(footer: readerAccountExplainer) {}
}
.navigationTitle(Text(accountType?.localizedAccountName() ?? ""))
.navigationBarTitleDisplayMode(.inline)
.task {
try? retrieveAccountCredentials()
retrieveAccountCredentials()
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
@@ -55,10 +57,8 @@ struct ReaderAPIAddAccountView: View {
} 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
.dismissOnExternalContextLaunch()
.dismissOnAccountAdd()
}
}
@@ -78,47 +78,19 @@ struct ReaderAPIAddAccountView: View {
}
}
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()
.autocapitalization(.none)
SecureField("Password", text: $accountSecret)
if accountType == .freshRSS && accountCredentials == nil {
TextField("FreshRSS URL", text: $accountAPIUrl, prompt: Text("fresh.rss.net/api/greader.php"))
.autocorrectionDisabled()
.autocapitalization(.none)
}
}
@@ -127,6 +99,7 @@ struct ReaderAPIAddAccountView: View {
Task {
do {
try await executeAccountCredentials()
dismiss()
} catch {
accountSetupError = (error, true)
}
@@ -150,7 +123,7 @@ struct ReaderAPIAddAccountView: View {
// MARK: - API
private func retrieveAccountCredentials() throws {
private func retrieveAccountCredentials() {
if let account = account {
do {
if let creds = try account.retrieveCredentials(type: .readerBasic) {

View File

@@ -9,7 +9,7 @@
/* Account Inspector */
"ACCOUNT_NAME_FIELD" = "Account Name";
"ACTIVE" = "Active";
"CLOUDKIT_LIMITATIONS_TITLE" = "[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)";
"CLOUDKIT_LIMITATIONS_LINK" = "[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)";
"REMOVE_ACCOUNT_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.";

View File

@@ -20,7 +20,7 @@ struct AccountInspectorView: View {
var body: some View {
Form {
Section(header: accountHeaderView){}
AccountSectionHeader(accountType: account.type)
accountNameAndActiveSection
if account.type != .onMyMac &&
@@ -46,13 +46,14 @@ struct AccountInspectorView: View {
case .theOldReader, .bazQux, .inoreader, .freshRSS:
ReaderAPIAddAccountView(accountType: account.type, account: account)
case .feedbin:
Text("FEEDBIN")
FeedbinAddAccountView(account: account)
case .newsBlur:
Text("NEWSBLUR")
NewsBlurAddAccountView(account: account)
default:
EmptyView()
}
}
.dismissOnExternalContextLaunch()
}
var accountHeaderView: some View {
@@ -135,7 +136,7 @@ struct AccountInspectorView: View {
var cloudKitLimitations: some View {
HStack {
Spacer()
Text("CLOUDKIT_LIMITATIONS_TITLE", tableName: "Inspector")
Text("CLOUDKIT_LIMITATIONS_LINK", tableName: "Inspector")
Spacer()
}
}

View File

@@ -50,9 +50,9 @@ struct ExtensionInspectorView: View {
}
.navigationTitle(Text(extensionPoint?.title ?? ""))
.edgesIgnoringSafeArea(.bottom)
.dismissOnExternalContextLaunch()
}
var extensionHeader: some View {
HStack {
Spacer()

View File

@@ -63,6 +63,7 @@ struct WebFeedInspectorView: View {
.sheet(isPresented: $showHomePage, onDismiss: nil) {
SafariView(url: URL(string: webFeed.homePageURL!)!)
}
.dismissOnExternalContextLaunch()
}
var webFeedHeaderView: some View {

View File

@@ -66,7 +66,7 @@ struct AccountsManagementView: View {
Button(role: .destructive) {
AccountManager.shared.deleteAccount(accountToRemove!)
} label: {
Text("REMOVE_BUTTON_TITLE", tableName: "Buttons")
Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Buttons")
}
Button(role: .cancel) {

View File

@@ -90,15 +90,15 @@ struct AddAccountListView: View {
LocalAddAccountView()
case .cloudKit:
CloudKitAddAccountView()
case .newsBlur:
NewsBlurAddAccountView()
case .freshRSS, .inoreader, .bazQux, .theOldReader:
ReaderAPIAddAccountView(accountType: viewModel.showAddAccountSheet.accountType, account: nil)
default:
Text(viewModel.showAddAccountSheet.accountType.localizedAccountName())
}
}
.onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in
dismiss()
}
.dismissOnAccountAdd()
}
}

View File

@@ -108,9 +108,7 @@ struct SettingsView: View {
}
}
}
.onReceive(NotificationCenter.default.publisher(for: .LaunchedFromExternalAction), perform: { _ in
dismiss()
})
.dismissOnExternalContextLaunch()
.fileImporter(isPresented: $viewModel.showImportView, allowedContentTypes: OPMLDocument.readableContentTypes) { result in
switch result {
case .success(let url):

View File

@@ -0,0 +1,61 @@
//
// AccountSectionHeader.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 18/12/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
struct AccountSectionHeader: View {
var accountType: AccountType
var body: some View {
Section(header: headerImage) {}
}
var headerImage: some View {
HStack {
Spacer()
Image(uiImage: imageToUse())
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 48, height: 48)
Spacer()
}
}
private func imageToUse() -> UIImage {
switch accountType {
case .onMyMac:
if UIDevice.current.userInterfaceIdiom == .pad { return AppAssets.accountLocalPadImage }
return AppAssets.accountLocalPhoneImage
case .cloudKit:
return AppAssets.accountCloudKitImage
case .feedly:
return AppAssets.accountFeedlyImage
case .feedbin:
return AppAssets.accountFeedbinImage
case .newsBlur:
return AppAssets.accountNewsBlurImage
case .freshRSS:
return AppAssets.accountFreshRSSImage
case .inoreader:
return AppAssets.accountInoreaderImage
case .bazQux:
return AppAssets.accountBazQuxImage
case .theOldReader:
return AppAssets.accountTheOldReaderImage
}
}
}
struct AccountHeader_Previews: PreviewProvider {
static var previews: some View {
AccountSectionHeader(accountType: .onMyMac)
}
}

View File

@@ -0,0 +1,28 @@
//
// View+DismissOnAccountAdd.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 18/12/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import SwiftUI
struct DismissOnAccountAdd: ViewModifier {
@Environment(\.dismiss) private var dismiss
func body(content: Content) -> some View {
content
.onReceive(NotificationCenter.default.publisher(for: .UserDidAddAccount)) { _ in
dismiss()
}
}
}
extension View {
func dismissOnAccountAdd() -> some View {
modifier(DismissOnAccountAdd())
}
}

View File

@@ -0,0 +1,29 @@
//
// View+DismissOnExternalContext.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 18/12/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import SwiftUI
struct DismissOnExternalContext: ViewModifier {
@Environment(\.dismiss) private var dismiss
func body(content: Content) -> some View {
content
.onReceive(NotificationCenter.default.publisher(for: .LaunchedFromExternalAction)) { _ in
dismiss()
}
}
}
extension View {
func dismissOnExternalContextLaunch() -> some View {
modifier(DismissOnExternalContext())
}
}

View File

@@ -13,9 +13,7 @@ import Account
extension UIViewController {
func presentError(_ error: Error, dismiss: (() -> Void)? = nil) {
if let accountError = error as? AccountError, accountError.isCredentialsError {
presentAccountError(accountError, dismiss: dismiss)
} else if let decodingError = error as? DecodingError {
if let decodingError = error as? DecodingError {
let errorTitle = NSLocalizedString("Error", comment: "Error")
var informativeText: String = ""
switch decodingError {
@@ -53,38 +51,3 @@ extension UIViewController {
}
}
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.account?.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.account
self?.present(navController, animated: true)
}
alertController.addAction(credentialsAction)
alertController.preferredAction = 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)
}
}