Change settings from using SwiftUI to using UIKit
This commit is contained in:
parent
94f31b18bc
commit
effec24674
|
@ -9,7 +9,6 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||
510BD15D232D765D002692E4 /* SettingsReaderAPIAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */; };
|
||||
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */; };
|
||||
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
|
||||
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */; };
|
||||
|
@ -43,15 +42,11 @@
|
|||
513146C5235A8FDB00387FDC /* SyncDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; };
|
||||
51314704235C41FC00387FDC /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 51314707235C41FC00387FDC /* Intents.intentdefinition */; };
|
||||
51314705235C41FC00387FDC /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 51314707235C41FC00387FDC /* Intents.intentdefinition */; };
|
||||
51314716235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51314715235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift */; };
|
||||
51314718235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51314717235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift */; };
|
||||
51322855232EED360033D4ED /* VibrantSelectAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51322854232EED360033D4ED /* VibrantSelectAction.swift */; };
|
||||
51322859232FDDB80033D4ED /* VibrantButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51322858232FDDB80033D4ED /* VibrantButtonStyle.swift */; };
|
||||
5132285B232FF2C40033D4ED /* SettingsRefreshSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5132285A232FF2C40033D4ED /* SettingsRefreshSelectionView.swift */; };
|
||||
513228FB233037630033D4ED /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513228F2233037620033D4ED /* Reachability.swift */; };
|
||||
513228FC233037630033D4ED /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513228F2233037620033D4ED /* Reachability.swift */; };
|
||||
513229312330523F0033D4ED /* AttributedStringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513229302330523F0033D4ED /* AttributedStringView.swift */; };
|
||||
5132293B23305D4C0033D4ED /* SettingsAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5132293A23305D4C0033D4ED /* SettingsAboutView.swift */; };
|
||||
513C5CE9232571C2003D4054 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513C5CE8232571C2003D4054 /* ShareViewController.swift */; };
|
||||
513C5CEC232571C2003D4054 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 513C5CEA232571C2003D4054 /* MainInterface.storyboard */; };
|
||||
513C5CF0232571C2003D4054 /* NetNewsWire iOS Share Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 513C5CE6232571C2003D4054 /* NetNewsWire iOS Share Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
|
@ -81,8 +76,6 @@
|
|||
5148F4552336DB7000F8CD8B /* MasterTimelineTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5148F4542336DB7000F8CD8B /* MasterTimelineTitleView.swift */; };
|
||||
514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514B7C8223205EFB00BAC947 /* RootSplitViewController.swift */; };
|
||||
514B7D1F23219F3C00BAC947 /* AddControllerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514B7D1E23219F3C00BAC947 /* AddControllerType.swift */; };
|
||||
5152E0F923248F6200E5C7AD /* SettingsLocalAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */; };
|
||||
5152E1022324900D00E5C7AD /* SettingsAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */; };
|
||||
5154368B229404D1005E1CDF /* FaviconGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F76227716200050506E /* FaviconGenerator.swift */; };
|
||||
51554C24228B71910055115A /* SyncDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; };
|
||||
51554C25228B71910055115A /* SyncDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
|
@ -112,14 +105,18 @@
|
|||
51938DF2231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; };
|
||||
51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; };
|
||||
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; };
|
||||
519D73FB2323FF35008BB345 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F35D0822AFD4760003CE1B /* SettingsView.swift */; };
|
||||
519D740623243CC0008BB345 /* RefreshInterval-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */; };
|
||||
519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */; };
|
||||
519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */; };
|
||||
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
|
||||
51AF45E123246731001742EF /* SettingsAccountLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */; };
|
||||
51AF460323247321001742EF /* SettingsDetailAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */; };
|
||||
51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */; };
|
||||
51A16997235E10D700EB091F /* RefreshIntervalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */; };
|
||||
51A16998235E10D700EB091F /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51A1698E235E10D600EB091F /* SettingsTableViewCell.xib */; };
|
||||
51A16999235E10D700EB091F /* AddLocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698F235E10D600EB091F /* AddLocalAccountViewController.swift */; };
|
||||
51A1699A235E10D700EB091F /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51A16990235E10D600EB091F /* Settings.storyboard */; };
|
||||
51A1699B235E10D700EB091F /* DetailAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16991235E10D600EB091F /* DetailAccountViewController.swift */; };
|
||||
51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16992235E10D600EB091F /* AddAccountViewController.swift */; };
|
||||
51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16993235E10D600EB091F /* SettingsViewController.swift */; };
|
||||
51A1699E235E10D700EB091F /* TimelineNumberOfLinesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16994235E10D600EB091F /* TimelineNumberOfLinesViewController.swift */; };
|
||||
51A1699F235E10D700EB091F /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16995235E10D600EB091F /* AboutViewController.swift */; };
|
||||
51A169A0235E10D700EB091F /* FeedbinAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */; };
|
||||
51AF460E232488C6001742EF /* Account-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51AF460D232488C6001742EF /* Account-Extensions.swift */; };
|
||||
51B62E68233186730085F949 /* MasterTimelineAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B62E67233186730085F949 /* MasterTimelineAvatarView.swift */; };
|
||||
51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */; };
|
||||
|
@ -730,10 +727,6 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
49F40DEF2335B71000552BF4 /* newsfoot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = newsfoot.js; sourceTree = "<group>"; };
|
||||
510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAddAccountView.swift; sourceTree = "<group>"; };
|
||||
510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLocalAccountView.swift; sourceTree = "<group>"; };
|
||||
510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFeedbinAccountView.swift; sourceTree = "<group>"; };
|
||||
510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAccountLabelView.swift; sourceTree = "<group>"; };
|
||||
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
|
||||
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
|
||||
51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContainerViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -754,14 +747,10 @@
|
|||
513146B1235A81A400387FDC /* AddFeedIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFeedIntentHandler.swift; sourceTree = "<group>"; };
|
||||
51314706235C41FC00387FDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = "<group>"; };
|
||||
51314714235C420900387FDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||
51314715235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportAccountPickerView.swift; sourceTree = "<group>"; };
|
||||
51314717235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportAccountPickerView.swift; sourceTree = "<group>"; };
|
||||
51322854232EED360033D4ED /* VibrantSelectAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantSelectAction.swift; sourceTree = "<group>"; };
|
||||
51322858232FDDB80033D4ED /* VibrantButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantButtonStyle.swift; sourceTree = "<group>"; };
|
||||
5132285A232FF2C40033D4ED /* SettingsRefreshSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRefreshSelectionView.swift; sourceTree = "<group>"; };
|
||||
513228F2233037620033D4ED /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = "<group>"; };
|
||||
513229302330523F0033D4ED /* AttributedStringView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringView.swift; sourceTree = "<group>"; };
|
||||
5132293A23305D4C0033D4ED /* SettingsAboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAboutView.swift; sourceTree = "<group>"; };
|
||||
513C5CE6232571C2003D4054 /* NetNewsWire iOS Share Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "NetNewsWire iOS Share Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
513C5CE8232571C2003D4054 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||
513C5CEB232571C2003D4054 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
|
@ -803,11 +792,19 @@
|
|||
51934CC1230F5963006127BE /* ThemedNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedNavigationController.swift; sourceTree = "<group>"; };
|
||||
51934CCD2310792F006127BE /* ActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityManager.swift; sourceTree = "<group>"; };
|
||||
51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTimelineFeedDelegate.swift; sourceTree = "<group>"; };
|
||||
5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportDocumentPickerView.swift; sourceTree = "<group>"; };
|
||||
5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportDocumentPickerView.swift; sourceTree = "<group>"; };
|
||||
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
|
||||
519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RefreshInterval-Extensions.swift"; sourceTree = "<group>"; };
|
||||
519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshIntervalViewController.swift; sourceTree = "<group>"; };
|
||||
51A1698E235E10D600EB091F /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = "<group>"; };
|
||||
51A1698F235E10D600EB091F /* AddLocalAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocalAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
|
||||
51A16991235E10D600EB091F /* DetailAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51A16992235E10D600EB091F /* AddAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51A16993235E10D600EB091F /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
|
||||
51A16994235E10D600EB091F /* TimelineNumberOfLinesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineNumberOfLinesViewController.swift; sourceTree = "<group>"; };
|
||||
51A16995235E10D600EB091F /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
||||
51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51AF460D232488C6001742EF /* Account-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51B62E67233186730085F949 /* MasterTimelineAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineAvatarView.swift; sourceTree = "<group>"; };
|
||||
51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleActivityItemSource.swift; sourceTree = "<group>"; };
|
||||
|
@ -852,8 +849,6 @@
|
|||
51EF0F8D2279C9260050506E /* AccountsAdd.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountsAdd.xib; sourceTree = "<group>"; };
|
||||
51EF0F8F2279C9500050506E /* AccountsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsAddViewController.swift; sourceTree = "<group>"; };
|
||||
51EF0F912279CA620050506E /* AccountsAddTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsAddTableCellView.swift; sourceTree = "<group>"; };
|
||||
51F35D0822AFD4760003CE1B /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDetailAccountView.swift; sourceTree = "<group>"; };
|
||||
51F85BEA22724CB600C787DC /* About.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = About.rtf; sourceTree = "<group>"; };
|
||||
51F85BEC227251DF00C787DC /* Acknowledgments.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Acknowledgments.rtf; sourceTree = "<group>"; };
|
||||
51F85BEE2272520B00C787DC /* Thanks.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Thanks.rtf; sourceTree = "<group>"; };
|
||||
|
@ -871,7 +866,6 @@
|
|||
51FD40BD2341555600880194 /* UIImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51FD413A2342BD0500880194 /* MasterTimelineUnreadCountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineUnreadCountView.swift; sourceTree = "<group>"; };
|
||||
51FE10022345529D0056195D /* UserNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationManager.swift; sourceTree = "<group>"; };
|
||||
557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsReaderAPIAccountView.swift; sourceTree = "<group>"; };
|
||||
55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsReaderAPI.xib; sourceTree = "<group>"; };
|
||||
55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsReaderAPIWindowController.swift; sourceTree = "<group>"; };
|
||||
5F323808231DF9F000706F6B /* NNWTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNWTableViewCell.swift; sourceTree = "<group>"; };
|
||||
|
@ -1265,19 +1259,6 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
515E4F06232506240057B0E7 /* Account */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */,
|
||||
510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */,
|
||||
51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */,
|
||||
510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */,
|
||||
510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */,
|
||||
557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */,
|
||||
);
|
||||
path = Account;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5183CCDB226F1EEB0010922C /* Progress */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1300,14 +1281,16 @@
|
|||
5183CCEB227117C70010922C /* Settings */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
51F35D0822AFD4760003CE1B /* SettingsView.swift */,
|
||||
5132293A23305D4C0033D4ED /* SettingsAboutView.swift */,
|
||||
5132285A232FF2C40033D4ED /* SettingsRefreshSelectionView.swift */,
|
||||
51314717235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift */,
|
||||
5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */,
|
||||
51314715235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift */,
|
||||
5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */,
|
||||
515E4F06232506240057B0E7 /* Account */,
|
||||
51A16990235E10D600EB091F /* Settings.storyboard */,
|
||||
51A16995235E10D600EB091F /* AboutViewController.swift */,
|
||||
51A16992235E10D600EB091F /* AddAccountViewController.swift */,
|
||||
51A1698F235E10D600EB091F /* AddLocalAccountViewController.swift */,
|
||||
51A16991235E10D600EB091F /* DetailAccountViewController.swift */,
|
||||
51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */,
|
||||
51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */,
|
||||
51A1698E235E10D600EB091F /* SettingsTableViewCell.xib */,
|
||||
51A16993235E10D600EB091F /* SettingsViewController.swift */,
|
||||
51A16994235E10D600EB091F /* TimelineNumberOfLinesViewController.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2648,6 +2631,7 @@
|
|||
51C452862265093600C03939 /* Add.storyboard in Resources */,
|
||||
511D43EF231FBDE900FB1562 /* LaunchScreenPad.storyboard in Resources */,
|
||||
511D43D2231FA62C00FB1562 /* GlobalKeyboardShortcuts.plist in Resources */,
|
||||
51A16998235E10D700EB091F /* SettingsTableViewCell.xib in Resources */,
|
||||
84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */,
|
||||
51BB7C312335ACDE008E8144 /* page.html in Resources */,
|
||||
51F85BF32272531500C787DC /* Dedication.rtf in Resources */,
|
||||
|
@ -2660,6 +2644,7 @@
|
|||
51F85BF12272524100C787DC /* Credits.rtf in Resources */,
|
||||
84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */,
|
||||
511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */,
|
||||
51A1699A235E10D700EB091F /* Settings.storyboard in Resources */,
|
||||
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */,
|
||||
51F85BEF2272520B00C787DC /* Thanks.rtf in Resources */,
|
||||
84C9FC9D2262A1A900D921D6 /* Assets.xcassets in Resources */,
|
||||
|
@ -2913,7 +2898,6 @@
|
|||
5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */,
|
||||
51EAED96231363EF00A9EEE3 /* NonIntrinsicButton.swift in Sources */,
|
||||
51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */,
|
||||
5152E1022324900D00E5C7AD /* SettingsAddAccountView.swift in Sources */,
|
||||
51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */,
|
||||
51B62E68233186730085F949 /* MasterTimelineAvatarView.swift in Sources */,
|
||||
51C45296226509D300C03939 /* OPMLExporter.swift in Sources */,
|
||||
|
@ -2927,36 +2911,30 @@
|
|||
51FD413B2342BD0500880194 /* MasterTimelineUnreadCountView.swift in Sources */,
|
||||
513146B2235A81A400387FDC /* AddFeedIntentHandler.swift in Sources */,
|
||||
5183CCDD226F1F5C0010922C /* NavigationProgressView.swift in Sources */,
|
||||
51AF45E123246731001742EF /* SettingsAccountLabelView.swift in Sources */,
|
||||
51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */,
|
||||
51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */,
|
||||
51C452A522650A2D00C03939 /* SmallIconProvider.swift in Sources */,
|
||||
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */,
|
||||
51322859232FDDB80033D4ED /* VibrantButtonStyle.swift in Sources */,
|
||||
51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */,
|
||||
51A16999235E10D700EB091F /* AddLocalAccountViewController.swift in Sources */,
|
||||
514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */,
|
||||
5152E0F923248F6200E5C7AD /* SettingsLocalAccountView.swift in Sources */,
|
||||
51FA73A52332BE110090D516 /* ArticleExtractor.swift in Sources */,
|
||||
51314704235C41FC00387FDC /* Intents.intentdefinition in Sources */,
|
||||
FF3ABF162325AF5D0074C542 /* ArticleSorter.swift in Sources */,
|
||||
510BD15D232D765D002692E4 /* SettingsReaderAPIAccountView.swift in Sources */,
|
||||
51C4525C226508DF00C03939 /* String-Extensions.swift in Sources */,
|
||||
51C452792265091600C03939 /* MasterTimelineTableViewCell.swift in Sources */,
|
||||
51FA73AB2332C2FD0090D516 /* ArticleExtractorConfig.swift in Sources */,
|
||||
5132285B232FF2C40033D4ED /* SettingsRefreshSelectionView.swift in Sources */,
|
||||
51C452852265093600C03939 /* FlattenedAccountFolderPickerData.swift in Sources */,
|
||||
51C4526B226508F600C03939 /* MasterFeedViewController.swift in Sources */,
|
||||
5126EE97226CB48A00C22AFC /* SceneCoordinator.swift in Sources */,
|
||||
51FD40C72341555A00880194 /* UIImage-Extensions.swift in Sources */,
|
||||
5132293B23305D4C0033D4ED /* SettingsAboutView.swift in Sources */,
|
||||
84CAFCB022BC8C35007694F0 /* FetchRequestOperation.swift in Sources */,
|
||||
51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */,
|
||||
51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */,
|
||||
51C4525A226508D600C03939 /* UIStoryboard-Extensions.swift in Sources */,
|
||||
519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */,
|
||||
51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */,
|
||||
51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */,
|
||||
51F85BF52273625800C787DC /* Bundle-Extensions.swift in Sources */,
|
||||
519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */,
|
||||
51C452A622650A3500C03939 /* Node-Extensions.swift in Sources */,
|
||||
5183CCDF226F1FCC0010922C /* UINavigationController+Progress.swift in Sources */,
|
||||
51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */,
|
||||
|
@ -2991,22 +2969,24 @@
|
|||
84CAFCA522BC8C08007694F0 /* FetchRequestQueue.swift in Sources */,
|
||||
51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */,
|
||||
51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
|
||||
51A1699F235E10D700EB091F /* AboutViewController.swift in Sources */,
|
||||
51C45290226509C100C03939 /* PseudoFeed.swift in Sources */,
|
||||
51C452A922650DC600C03939 /* ArticleRenderer.swift in Sources */,
|
||||
51AF460323247321001742EF /* SettingsDetailAccountView.swift in Sources */,
|
||||
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */,
|
||||
51C45297226509E300C03939 /* DefaultFeedsImporter.swift in Sources */,
|
||||
512E094D2268B8AB00BDCFDD /* DeleteCommand.swift in Sources */,
|
||||
51F85BFB2275D85000C787DC /* Array-Extensions.swift in Sources */,
|
||||
51C452AC22650FD200C03939 /* AppNotifications.swift in Sources */,
|
||||
51EF0F7E2277A57D0050506E /* MasterTimelineAccessibilityCellLayout.swift in Sources */,
|
||||
51A1699B235E10D700EB091F /* DetailAccountViewController.swift in Sources */,
|
||||
51C452762265091600C03939 /* MasterTimelineViewController.swift in Sources */,
|
||||
51314718235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift in Sources */,
|
||||
5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */,
|
||||
51C452882265093600C03939 /* AddFeedViewController.swift in Sources */,
|
||||
51A169A0235E10D700EB091F /* FeedbinAccountViewController.swift in Sources */,
|
||||
51934CCE2310792F006127BE /* ActivityManager.swift in Sources */,
|
||||
518651DA235621840078E021 /* ImageTransition.swift in Sources */,
|
||||
514219372352510100E07E2C /* ImageScrollView.swift in Sources */,
|
||||
51A16997235E10D700EB091F /* RefreshIntervalViewController.swift in Sources */,
|
||||
DF999FF722B5AEFA0064B687 /* SafariView.swift in Sources */,
|
||||
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */,
|
||||
84DEE56622C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */,
|
||||
|
@ -3025,13 +3005,13 @@
|
|||
5148F4552336DB7000F8CD8B /* MasterTimelineTitleView.swift in Sources */,
|
||||
513228FC233037630033D4ED /* Reachability.swift in Sources */,
|
||||
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
|
||||
519D73FB2323FF35008BB345 /* SettingsView.swift in Sources */,
|
||||
511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */,
|
||||
51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */,
|
||||
51A1699E235E10D700EB091F /* TimelineNumberOfLinesViewController.swift in Sources */,
|
||||
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
|
||||
513229312330523F0033D4ED /* AttributedStringView.swift in Sources */,
|
||||
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */,
|
||||
51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */,
|
||||
51314716235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -786,9 +786,10 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||
}
|
||||
|
||||
func showSettings() {
|
||||
rootSplitViewController.present(style: .formSheet) {
|
||||
SettingsView(viewModel: SettingsView.ViewModel()).environment(\.sceneCoordinator, self)
|
||||
}
|
||||
let settingsNavController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController
|
||||
settingsNavController.modalPresentationStyle = .formSheet
|
||||
settingsNavController.preferredContentSize = SettingsViewController.preferredContentSizeForFormSheetDisplay
|
||||
masterFeedViewController.present(settingsNavController, animated: true)
|
||||
}
|
||||
|
||||
func showFeedInspector() {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// AboutViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 4/25/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class AboutViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var aboutTextView: UITextView!
|
||||
@IBOutlet weak var creditsTextView: UITextView!
|
||||
@IBOutlet weak var acknowledgmentsTextView: UITextView!
|
||||
@IBOutlet weak var thanksTextView: UITextView!
|
||||
@IBOutlet weak var dedicationTextView: UITextView!
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad()
|
||||
|
||||
configureCell(file: "About", textView: aboutTextView)
|
||||
configureCell(file: "Credits", textView: creditsTextView)
|
||||
configureCell(file: "Acknowledgments", textView: acknowledgmentsTextView)
|
||||
configureCell(file: "Thanks", textView: thanksTextView)
|
||||
configureCell(file: "Dedication", textView: dedicationTextView)
|
||||
|
||||
let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 20.0, y: 0.0, width: 0.0, height: 0.0))
|
||||
buildLabel.font = UIFont.systemFont(ofSize: 11.0)
|
||||
buildLabel.textColor = UIColor.gray
|
||||
buildLabel.text = NSLocalizedString("Copyright © 2002-2019 Ranchero Software", comment: "Copyright")
|
||||
buildLabel.numberOfLines = 0
|
||||
buildLabel.sizeToFit()
|
||||
buildLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
tableView.tableFooterView = buildLabel
|
||||
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension AboutViewController {
|
||||
|
||||
func configureCell(file: String, textView: UITextView) {
|
||||
let url = Bundle.main.url(forResource: file, withExtension: "rtf")!
|
||||
let string = try! NSAttributedString(url: url, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtf], documentAttributes: nil)
|
||||
textView.attributedText = string
|
||||
textView.adjustsFontForContentSizeCategory = true
|
||||
textView.font = .preferredFont(forTextStyle: .body)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
//
|
||||
// SettingsAccountLabelView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsAccountLabelView : View {
|
||||
let accountImage: String
|
||||
let accountLabel: String
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Image(accountImage)
|
||||
.resizable()
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.frame(height: 32)
|
||||
Text(verbatim: accountLabel).font(.title)
|
||||
}
|
||||
.foregroundColor(.primary).padding(4.0)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsAccountLabelView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: "On My Device")
|
||||
.previewLayout(.fixed(width: 300, height: 44))
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,52 +0,0 @@
|
|||
//
|
||||
// SettingsAddAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsAddAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@State private var accountAddAction: Int? = nil
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
|
||||
NavigationLink(destination: SettingsLocalAccountView(name: ""), tag: 1, selection: $accountAddAction) {
|
||||
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName)
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.accountAddAction = 1
|
||||
})).padding(.vertical, 16)
|
||||
|
||||
NavigationLink(destination: SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel()), tag: 2, selection: $accountAddAction) {
|
||||
SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin")
|
||||
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.accountAddAction = 2
|
||||
})).padding(.vertical, 16)
|
||||
|
||||
// NavigationLink(destination: SettingsReaderAPIAccountView(viewModel: SettingsReaderAPIAccountView.ViewModel(accountType: .freshRSS)), tag: 3, selection: $accountAddAction) {
|
||||
// SettingsAccountLabelView(accountImage: "accountFreshRSS", accountLabel: "Fresh RSS")
|
||||
// }
|
||||
// .modifier(VibrantSelectAction(action: {
|
||||
// self.accountAddAction = 3
|
||||
// }))
|
||||
|
||||
}
|
||||
.navigationBarTitle(Text("Add Account"), displayMode: .inline)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct AddAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsAddAccountView()
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,137 +0,0 @@
|
|||
//
|
||||
// SettingsDetailAccountView.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 6/13/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Account
|
||||
import RSWeb
|
||||
|
||||
struct SettingsDetailAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
@State private var credentialsAction: Int? = nil
|
||||
@State private var isDeleteAlertPresented = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
HStack {
|
||||
TextField("Name", text: $viewModel.name)
|
||||
}
|
||||
Toggle(isOn: $viewModel.isActive) {
|
||||
Text("Active")
|
||||
}
|
||||
}
|
||||
if viewModel.isCreditialsAvailable {
|
||||
if viewModel.account.type == .feedbin {
|
||||
NavigationLink(destination: self.settingsFeedbinAccountView, tag: 1, selection: $credentialsAction) {
|
||||
Text("Credentials")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.credentialsAction = 1
|
||||
}))
|
||||
}
|
||||
if viewModel.account.type == .freshRSS {
|
||||
NavigationLink(destination: self.settingsReaderAPIAccountView, tag: 1, selection: $credentialsAction) {
|
||||
Text("Credentials")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.credentialsAction = 1
|
||||
}))
|
||||
}
|
||||
}
|
||||
if viewModel.isDeletable {
|
||||
Section {
|
||||
Button(action: {
|
||||
self.isDeleteAlertPresented.toggle()
|
||||
}) {
|
||||
Text("Delete Account").foregroundColor(.red)
|
||||
}
|
||||
.alert(isPresented: $isDeleteAlertPresented) {
|
||||
Alert(title: Text("Are you sure you want to delete \"\(viewModel.nameForDisplay)\"?"),
|
||||
primaryButton: Alert.Button.default(Text("Delete"), action: {
|
||||
self.viewModel.delete()
|
||||
self.presentation.wrappedValue.dismiss()
|
||||
}),
|
||||
secondaryButton: Alert.Button.cancel())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .center))
|
||||
.navigationBarTitle(Text(verbatim: viewModel.nameForDisplay), displayMode: .inline)
|
||||
|
||||
}
|
||||
|
||||
var settingsFeedbinAccountView: SettingsFeedbinAccountView {
|
||||
let feedbinViewModel = SettingsFeedbinAccountView.ViewModel(account: viewModel.account)
|
||||
return SettingsFeedbinAccountView(viewModel: feedbinViewModel)
|
||||
}
|
||||
|
||||
var settingsReaderAPIAccountView: SettingsReaderAPIAccountView {
|
||||
let readerAPIModel = SettingsReaderAPIAccountView.ViewModel(account: viewModel.account)
|
||||
return SettingsReaderAPIAccountView(viewModel: readerAPIModel)
|
||||
}
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
|
||||
let account: Account
|
||||
|
||||
init(_ account: Account) {
|
||||
self.account = account
|
||||
}
|
||||
|
||||
var nameForDisplay: String {
|
||||
account.nameForDisplay
|
||||
}
|
||||
|
||||
var name: String {
|
||||
get {
|
||||
account.name ?? ""
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
account.name = newValue.isEmpty ? nil : newValue
|
||||
}
|
||||
}
|
||||
|
||||
var isActive: Bool {
|
||||
get {
|
||||
account.isActive
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
account.isActive = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var isCreditialsAvailable: Bool {
|
||||
return account.type != .onMyMac
|
||||
}
|
||||
|
||||
var isDeletable: Bool {
|
||||
return AccountManager.shared.defaultAccount != account
|
||||
}
|
||||
|
||||
func delete() {
|
||||
AccountManager.shared.deleteAccount(account)
|
||||
ActivityManager.cleanUp(account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsDetailAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
let viewModel = SettingsDetailAccountView.ViewModel(AccountManager.shared.defaultAccount)
|
||||
return SettingsDetailAccountView(viewModel: viewModel)
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,161 +0,0 @@
|
|||
//
|
||||
// SettingsFeedbinAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Account
|
||||
import RSWeb
|
||||
|
||||
struct SettingsFeedbinAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
@State var busy: Bool = false
|
||||
@State var error: String = ""
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header:
|
||||
HStack {
|
||||
Spacer()
|
||||
SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin")
|
||||
.padding()
|
||||
.layoutPriority(1.0)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
TextField("Email", text: $viewModel.email)
|
||||
.keyboardType(.emailAddress)
|
||||
.textContentType(.emailAddress)
|
||||
PasswordField(password: $viewModel.password)
|
||||
}
|
||||
Section(footer:
|
||||
HStack {
|
||||
Spacer()
|
||||
Text(verbatim: error).foregroundColor(.red)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
Button(action: { self.addAccount() }) {
|
||||
if viewModel.isUpdate {
|
||||
Text("Update Account")
|
||||
} else {
|
||||
Text("Add Account")
|
||||
}
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .center))
|
||||
.disabled(!viewModel.isValid)
|
||||
}
|
||||
}
|
||||
// .disabled(busy) // Maybe someday we can do this, but right now it crashes on the iPad
|
||||
.navigationBarTitle(Text(""), displayMode: .inline)
|
||||
}
|
||||
|
||||
private func addAccount() {
|
||||
|
||||
busy = true
|
||||
error = ""
|
||||
|
||||
let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials(type: .basic, username: emailAddress, secret: viewModel.password)
|
||||
|
||||
Account.validateCredentials(type: .feedbin, credentials: credentials) { result in
|
||||
|
||||
self.busy = false
|
||||
|
||||
switch result {
|
||||
case .success(let authenticated):
|
||||
|
||||
if (authenticated != nil) {
|
||||
|
||||
var newAccount = false
|
||||
let workAccount: Account
|
||||
if self.viewModel.account == nil {
|
||||
workAccount = AccountManager.shared.createAccount(type: .feedbin)
|
||||
newAccount = true
|
||||
} else {
|
||||
workAccount = self.viewModel.account!
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
do {
|
||||
try workAccount.removeCredentials(type: .basic)
|
||||
} catch {}
|
||||
try workAccount.storeCredentials(credentials)
|
||||
|
||||
if newAccount {
|
||||
workAccount.refreshAll() { result in }
|
||||
}
|
||||
|
||||
self.dismiss()
|
||||
|
||||
} catch {
|
||||
self.error = "Keychain error while storing credentials."
|
||||
}
|
||||
|
||||
} else {
|
||||
self.error = "Invalid email/password combination."
|
||||
}
|
||||
|
||||
case .failure:
|
||||
self.error = "Network error. Try again later."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
presentation.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
var account: Account? = nil
|
||||
|
||||
init() {
|
||||
}
|
||||
|
||||
init(account: Account) {
|
||||
self.account = account
|
||||
if let credentials = try? account.retrieveCredentials(type: .basic) {
|
||||
self.email = credentials.username
|
||||
}
|
||||
}
|
||||
|
||||
var email: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
var password: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
var isUpdate: Bool {
|
||||
return account != nil
|
||||
}
|
||||
|
||||
var isValid: Bool {
|
||||
return !email.isEmpty && !password.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsFeedbinAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel())
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,59 +0,0 @@
|
|||
//
|
||||
// SettingsLocalAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsLocalAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@State var name: String
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header:
|
||||
HStack {
|
||||
Spacer()
|
||||
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName)
|
||||
.padding()
|
||||
.layoutPriority(1.0)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
HStack {
|
||||
TextField("Name", text: $name)
|
||||
}
|
||||
}
|
||||
Section {
|
||||
Button(action: { self.addAccount() }) {
|
||||
Text("Add Account")
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .center))
|
||||
}
|
||||
}
|
||||
.navigationBarTitle(Text(""), displayMode: .inline)
|
||||
}
|
||||
|
||||
private func addAccount() {
|
||||
let account = AccountManager.shared.createAccount(type: .onMyMac)
|
||||
account.name = name
|
||||
dismiss()
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
presentation.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsLocalAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsLocalAccountView(name: "")
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,178 +0,0 @@
|
|||
//
|
||||
// SettingsReaderAPIAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Jeremy Beker on 5/28/2019.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Account
|
||||
import RSWeb
|
||||
|
||||
struct SettingsReaderAPIAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
|
||||
@State var busy: Bool = false
|
||||
@State var error: String = ""
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header:
|
||||
HStack {
|
||||
Spacer()
|
||||
SettingsAccountLabelView(accountImage: "accountFreshRSS", accountLabel: "FreshRSS")
|
||||
.padding()
|
||||
.layoutPriority(1.0)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
TextField("Email", text: $viewModel.email)
|
||||
.keyboardType(.emailAddress)
|
||||
.textContentType(.emailAddress)
|
||||
SecureField("Password", text: $viewModel.password)
|
||||
TextField("API URL:", text: $viewModel.apiURL).textContentType(.URL)
|
||||
}
|
||||
|
||||
Section(footer:
|
||||
HStack {
|
||||
Spacer()
|
||||
Text(verbatim: error).foregroundColor(.red)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
Button(action: { self.addAccount() }) {
|
||||
if viewModel.isUpdate {
|
||||
Text("Update Account")
|
||||
} else {
|
||||
Text("Add Account")
|
||||
}
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .center))
|
||||
.disabled(!viewModel.isValid)
|
||||
}
|
||||
}
|
||||
// .disabled(busy)
|
||||
}
|
||||
|
||||
private func addAccount() {
|
||||
|
||||
busy = true
|
||||
error = ""
|
||||
|
||||
let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials(type: .readerBasic, username: emailAddress, secret: viewModel.password)
|
||||
guard let apiURL = URL(string: viewModel.apiURL) else {
|
||||
self.error = "Invalid API URL."
|
||||
return
|
||||
}
|
||||
|
||||
Account.validateCredentials(type: viewModel.accountType, credentials: credentials, endpoint: apiURL) { result in
|
||||
|
||||
self.busy = false
|
||||
|
||||
switch result {
|
||||
case .success(let validatedCredentials):
|
||||
|
||||
guard let validatedCredentials = validatedCredentials else {
|
||||
self.error = "Invalid email/password combination."
|
||||
return
|
||||
}
|
||||
|
||||
var newAccount = false
|
||||
let workAccount: Account
|
||||
if self.viewModel.account == nil {
|
||||
workAccount = AccountManager.shared.createAccount(type: self.viewModel.accountType)
|
||||
newAccount = true
|
||||
} else {
|
||||
workAccount = self.viewModel.account!
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
do {
|
||||
try workAccount.removeCredentials(type: .readerBasic)
|
||||
try workAccount.removeCredentials(type: .readerAPIKey)
|
||||
} catch {}
|
||||
|
||||
workAccount.endpointURL = apiURL
|
||||
|
||||
try workAccount.storeCredentials(credentials)
|
||||
try workAccount.storeCredentials(validatedCredentials)
|
||||
|
||||
if newAccount {
|
||||
workAccount.refreshAll() { result in }
|
||||
}
|
||||
|
||||
self.dismiss()
|
||||
|
||||
} catch {
|
||||
self.error = "Keychain error while storing credentials."
|
||||
}
|
||||
|
||||
case .failure:
|
||||
self.error = "Network error. Try again later."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
presentation.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
var accountType: AccountType
|
||||
var account: Account? = nil
|
||||
|
||||
init(accountType: AccountType) {
|
||||
self.accountType = accountType
|
||||
}
|
||||
|
||||
init(account: Account) {
|
||||
self.account = account
|
||||
self.accountType = account.type
|
||||
if let credentials = try? account.retrieveCredentials(type: .readerBasic) {
|
||||
self.email = credentials.username
|
||||
self.apiURL = account.endpointURL?.absoluteString ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
var email: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
var password: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
var apiURL: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
var isUpdate: Bool {
|
||||
return account != nil
|
||||
}
|
||||
|
||||
var isValid: Bool {
|
||||
return !email.isEmpty && !password.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsReaderAPIAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsReaderAPIAccountView(viewModel: SettingsReaderAPIAccountView.ViewModel(accountType: .freshRSS))
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// AddAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 5/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Account
|
||||
import UIKit
|
||||
|
||||
protocol AddAccountDismissDelegate: UIViewController {
|
||||
func dismiss()
|
||||
}
|
||||
|
||||
class AddAccountViewController: UITableViewController, AddAccountDismissDelegate {
|
||||
|
||||
@IBOutlet private weak var localAccountNameLabel: UILabel!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
localAccountNameLabel.text = Account.defaultLocalAccountName
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "AddLocalAccountNavigationViewController") as! UINavigationController
|
||||
navController.modalPresentationStyle = .currentContext
|
||||
let addViewController = navController.topViewController as! AddLocalAccountViewController
|
||||
addViewController.delegate = self
|
||||
present(navController, animated: true)
|
||||
case 1:
|
||||
let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController
|
||||
navController.modalPresentationStyle = .currentContext
|
||||
let addViewController = navController.topViewController as! FeedbinAccountViewController
|
||||
addViewController.delegate = self
|
||||
present(navController, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
navigationController?.popViewController(animated: false)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// AddLocalAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 5/19/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
|
||||
class AddLocalAccountViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var localAccountNameLabel: UILabel!
|
||||
@IBOutlet weak var nameTextField: UITextField!
|
||||
|
||||
weak var delegate: AddAccountDismissDelegate?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
localAccountNameLabel.text = Account.defaultLocalAccountName
|
||||
nameTextField.delegate = self
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
delegate?.dismiss()
|
||||
}
|
||||
|
||||
@IBAction func addAccountTapped(_ sender: Any) {
|
||||
let account = AccountManager.shared.createAccount(type: .onMyMac)
|
||||
account.name = nameTextField.text
|
||||
dismiss(animated: true, completion: nil)
|
||||
delegate?.dismiss()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AddLocalAccountViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// DetailAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 5/17/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
|
||||
class DetailAccountViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var nameTextField: UITextField!
|
||||
@IBOutlet weak var activeSwitch: UISwitch!
|
||||
|
||||
weak var account: Account?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
guard let account = account else { return }
|
||||
|
||||
nameTextField.placeholder = account.defaultName
|
||||
nameTextField.text = account.name
|
||||
nameTextField.delegate = self
|
||||
activeSwitch.isOn = account.isActive
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
account?.name = nameTextField.text
|
||||
account?.isActive = activeSwitch.isOn
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension DetailAccountViewController {
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
guard let account = account else { return 0 }
|
||||
|
||||
if account == AccountManager.shared.defaultAccount {
|
||||
return 1
|
||||
} else if account.type == .onMyMac {
|
||||
return 2
|
||||
} else {
|
||||
return super.numberOfSections(in: tableView)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell: UITableViewCell
|
||||
|
||||
if indexPath.section == 1, let account = account, account.type == .onMyMac {
|
||||
cell = super.tableView(tableView, cellForRowAt: IndexPath(row: 0, section: 2))
|
||||
} else {
|
||||
cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
|
||||
if indexPath.section > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
if let account = account, account.type == .onMyMac {
|
||||
if indexPath.section == 1 {
|
||||
deleteAccount()
|
||||
}
|
||||
} else {
|
||||
switch indexPath.section {
|
||||
case 1:
|
||||
credentials()
|
||||
case 2:
|
||||
deleteAccount()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension DetailAccountViewController {
|
||||
|
||||
func credentials() {
|
||||
guard let account = account else { return }
|
||||
switch account.type {
|
||||
case .feedbin:
|
||||
let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController
|
||||
let addViewController = navController.topViewController as! FeedbinAccountViewController
|
||||
addViewController.account = account
|
||||
present(navController, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func deleteAccount() {
|
||||
let title = NSLocalizedString("Delete Account", comment: "Delete Account")
|
||||
let message = NSLocalizedString("Are you sure you want to delete this account? This can not be undone.", comment: "Delete Account")
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel)
|
||||
alertController.addAction(cancelAction)
|
||||
|
||||
let markTitle = NSLocalizedString("Delete", comment: "Delete")
|
||||
let markAction = UIAlertAction(title: markTitle, style: .default) { [weak self] (action) in
|
||||
guard let account = self?.account else { return }
|
||||
AccountManager.shared.deleteAccount(account)
|
||||
self?.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
alertController.addAction(markAction)
|
||||
|
||||
present(alertController, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension DetailAccountViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// AddFeedbinAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 5/19/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
import RSWeb
|
||||
|
||||
class FeedbinAccountViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
|
||||
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet weak var emailTextField: UITextField!
|
||||
@IBOutlet weak var passwordTextField: UITextField!
|
||||
@IBOutlet weak var actionButton: UIButton!
|
||||
|
||||
@IBOutlet weak var errorMessageLabel: UILabel!
|
||||
|
||||
weak var account: Account?
|
||||
weak var delegate: AddAccountDismissDelegate?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
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)
|
||||
emailTextField.text = credentials.username
|
||||
passwordTextField.text = credentials.secret
|
||||
} else {
|
||||
actionButton.setTitle(NSLocalizedString("Add Account", comment: "Update Credentials"), for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
delegate?.dismiss()
|
||||
}
|
||||
|
||||
@IBAction func action(_ sender: Any) {
|
||||
self.errorMessageLabel.text = nil
|
||||
|
||||
guard emailTextField.text != nil && passwordTextField.text != nil else {
|
||||
self.errorMessageLabel.text = NSLocalizedString("Username & password required.", comment: "Credentials Error")
|
||||
return
|
||||
}
|
||||
|
||||
startAnimatingActivityIndicator()
|
||||
disableNavigation()
|
||||
|
||||
// When you fill in the email address via auto-complete it adds extra whitespace
|
||||
let emailAddress = emailTextField.text?.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials(type: .basic, username: emailAddress ?? "", secret: passwordTextField.text ?? "")
|
||||
Account.validateCredentials(type: .feedbin, credentials: credentials) { result in
|
||||
|
||||
self.stopAnimtatingActivityIndicator()
|
||||
self.enableNavigation()
|
||||
|
||||
switch result {
|
||||
case .success(let credentials):
|
||||
if let credentials = credentials {
|
||||
var newAccount = false
|
||||
if self.account == nil {
|
||||
self.account = AccountManager.shared.createAccount(type: .feedbin)
|
||||
newAccount = true
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
do {
|
||||
try self.account?.removeCredentials(type: .basic)
|
||||
} catch {}
|
||||
try self.account?.storeCredentials(credentials)
|
||||
|
||||
if newAccount {
|
||||
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.errorMessageLabel.text = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")
|
||||
}
|
||||
} else {
|
||||
self.errorMessageLabel.text = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error")
|
||||
}
|
||||
case .failure:
|
||||
self.errorMessageLabel.text = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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 stopAnimtatingActivityIndicator() {
|
||||
self.activityIndicator.isHidden = true
|
||||
self.activityIndicator.stopAnimating()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FeedbinAccountViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
//
|
||||
// RefreshIntervalViewController.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 4/25/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class RefreshIntervalViewController: UITableViewController {
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return 7
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
|
||||
|
||||
cell.textLabel?.adjustsFontForContentSizeCategory = true
|
||||
|
||||
let userRefreshInterval = AppDefaults.refreshInterval
|
||||
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
cell.textLabel?.text = RefreshInterval.manually.description()
|
||||
if userRefreshInterval == RefreshInterval.manually {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
case 1:
|
||||
cell.textLabel?.text = RefreshInterval.every10Minutes.description()
|
||||
if userRefreshInterval == RefreshInterval.every10Minutes {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
case 2:
|
||||
cell.textLabel?.text = RefreshInterval.every30Minutes.description()
|
||||
if userRefreshInterval == RefreshInterval.every30Minutes {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
case 3:
|
||||
cell.textLabel?.text = RefreshInterval.everyHour.description()
|
||||
if userRefreshInterval == RefreshInterval.everyHour {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
case 4:
|
||||
cell.textLabel?.text = RefreshInterval.every2Hours.description()
|
||||
if userRefreshInterval == RefreshInterval.every2Hours {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
case 5:
|
||||
cell.textLabel?.text = RefreshInterval.every4Hours.description()
|
||||
if userRefreshInterval == RefreshInterval.every4Hours {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
default:
|
||||
cell.textLabel?.text = RefreshInterval.every8Hours.description()
|
||||
if userRefreshInterval == RefreshInterval.every8Hours {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
}
|
||||
|
||||
return cell
|
||||
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
|
||||
let refreshInterval: RefreshInterval
|
||||
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
refreshInterval = RefreshInterval.manually
|
||||
case 1:
|
||||
refreshInterval = RefreshInterval.every10Minutes
|
||||
case 2:
|
||||
refreshInterval = RefreshInterval.every30Minutes
|
||||
case 3:
|
||||
refreshInterval = RefreshInterval.everyHour
|
||||
case 4:
|
||||
refreshInterval = RefreshInterval.every2Hours
|
||||
case 5:
|
||||
refreshInterval = RefreshInterval.every4Hours
|
||||
default:
|
||||
refreshInterval = RefreshInterval.every8Hours
|
||||
}
|
||||
|
||||
AppDefaults.refreshInterval = refreshInterval
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,67 +0,0 @@
|
|||
//
|
||||
// SettingsAboutView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
struct SettingsAboutView: View {
|
||||
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
List {
|
||||
Text("NetNewsWire").font(.largeTitle)
|
||||
AttributedStringView(string: self.viewModel.about, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
Section(header: Text("CREDITS")) {
|
||||
AttributedStringView(string: self.viewModel.credits, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
}
|
||||
Section(header: Text("ACKNOWLEDGEMENTS")) {
|
||||
AttributedStringView(string: self.viewModel.acknowledgements, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
}
|
||||
Section(header: Text("THANKS")) {
|
||||
AttributedStringView(string: self.viewModel.thanks, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
}
|
||||
Section(header: Text("DEDICATION"), footer: Text("Copyright © 2002-2019 Ranchero Software").font(.footnote)) {
|
||||
AttributedStringView(string: self.viewModel.dedication, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
|
||||
var about: NSAttributedString
|
||||
var credits: NSAttributedString
|
||||
var acknowledgements: NSAttributedString
|
||||
var thanks: NSAttributedString
|
||||
var dedication: NSAttributedString
|
||||
|
||||
init() {
|
||||
about = ViewModel.loadResource("About")
|
||||
credits = ViewModel.loadResource("Credits")
|
||||
acknowledgements = ViewModel.loadResource("Acknowledgments")
|
||||
thanks = ViewModel.loadResource("Thanks")
|
||||
dedication = ViewModel.loadResource("Dedication")
|
||||
}
|
||||
|
||||
private static func loadResource(_ resource: String) -> NSAttributedString {
|
||||
let url = Bundle.main.url(forResource: resource, withExtension: "rtf")!
|
||||
return try! NSAttributedString(url: url, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtf], documentAttributes: nil)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct SettingsAboutView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsAboutView(viewModel: SettingsAboutView.ViewModel())
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
//
|
||||
// SettingsRefreshSelectionView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsRefreshSelectionView: View {
|
||||
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@Binding var selectedInterval: RefreshInterval
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
ForEach(RefreshInterval.allCases) { interval in
|
||||
Button(action: {
|
||||
self.selectedInterval = interval
|
||||
self.presentation.wrappedValue.dismiss()
|
||||
}) {
|
||||
HStack {
|
||||
Text(interval.description())
|
||||
Spacer()
|
||||
if interval == self.selectedInterval {
|
||||
Image(systemName: "checkmark")
|
||||
}
|
||||
}
|
||||
}.buttonStyle(VibrantButtonStyle(alignment: .leading))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
//
|
||||
// SettingsSubscriptionsExportAccountPickerView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 10/20/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsSubscriptionsExportAccountPickerView: View {
|
||||
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@State private var selectedAccount: Account?
|
||||
@State private var isOPMLExportDocPickerPresented: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
ForEach(AccountManager.shared.sortedAccounts) { account in
|
||||
Button(action: {
|
||||
self.selectedAccount = account
|
||||
self.isOPMLExportDocPickerPresented = true
|
||||
}) {
|
||||
Text(verbatim: account.nameForDisplay)
|
||||
}.buttonStyle(VibrantButtonStyle(alignment: .leading))
|
||||
}
|
||||
}.sheet(isPresented: $isOPMLExportDocPickerPresented, onDismiss: { self.presentation.wrappedValue.dismiss() }) {
|
||||
SettingsSubscriptionsExportDocumentPickerView(account: self.selectedAccount!)
|
||||
}
|
||||
.navigationBarTitle(Text("Select Account"), displayMode: .inline)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
//
|
||||
// SettingsSubscriptionsExportDocumentPickerView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsSubscriptionsExportDocumentPickerView : UIViewControllerRepresentable {
|
||||
var account: Account
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<SettingsSubscriptionsExportDocumentPickerView>) -> UIDocumentPickerViewController {
|
||||
|
||||
let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces)
|
||||
let filename = "Subscriptions-\(accountName).opml"
|
||||
let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
|
||||
|
||||
let opmlString = OPMLExporter.OPMLString(with: account, title: filename)
|
||||
try? opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8)
|
||||
|
||||
return UIDocumentPickerViewController(url: tempFile, in: .exportToService)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<SettingsSubscriptionsExportDocumentPickerView>) {
|
||||
//
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
//
|
||||
// SettingsSubscriptionsImportAccountPickerView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 10/20/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsSubscriptionsImportAccountPickerView: View {
|
||||
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@State private var selectedAccount: Account?
|
||||
@State private var isOPMLImportDocPickerPresented: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
ForEach(AccountManager.shared.sortedActiveAccounts) { account in
|
||||
Button(action: {
|
||||
self.selectedAccount = account
|
||||
self.isOPMLImportDocPickerPresented = true
|
||||
}) {
|
||||
Text(verbatim: account.nameForDisplay)
|
||||
}.buttonStyle(VibrantButtonStyle(alignment: .leading))
|
||||
}
|
||||
}.sheet(isPresented: $isOPMLImportDocPickerPresented, onDismiss: { self.presentation.wrappedValue.dismiss() }) {
|
||||
SettingsSubscriptionsImportDocumentPickerView(account: self.selectedAccount!)
|
||||
}
|
||||
.navigationBarTitle(Text("Select Account"), displayMode: .inline)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// SettingsSubscriptionsImportDocumentPickerView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsSubscriptionsImportDocumentPickerView : UIViewControllerRepresentable {
|
||||
var account: Account
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<SettingsSubscriptionsImportDocumentPickerView>) -> UIDocumentPickerViewController {
|
||||
let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import)
|
||||
docPicker.delegate = context.coordinator
|
||||
return docPicker
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<SettingsSubscriptionsImportDocumentPickerView>) {
|
||||
//
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
return Coordinator(self)
|
||||
}
|
||||
|
||||
class Coordinator : NSObject, UIDocumentPickerDelegate {
|
||||
var parent: SettingsSubscriptionsImportDocumentPickerView
|
||||
|
||||
init(_ view: SettingsSubscriptionsImportDocumentPickerView) {
|
||||
self.parent = view
|
||||
}
|
||||
|
||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||
for url in urls {
|
||||
parent.account.importOPML(url) { result in}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="JCb-QB-CrO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JCb-QB-CrO" id="FzD-t2-JGy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="376" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
<point key="canvasLocation" x="7" y="-9"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
|
@ -1,270 +0,0 @@
|
|||
//
|
||||
// SettingsView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Account
|
||||
|
||||
struct SettingsView : View {
|
||||
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
|
||||
@Environment(\.viewController) private var viewController: UIViewController?
|
||||
@Environment(\.sceneCoordinator) private var coordinator: SceneCoordinator?
|
||||
|
||||
@State private var accountAction: Int? = nil
|
||||
@State private var refreshAction: Int? = nil
|
||||
@State private var importOPMLAction: Int? = nil
|
||||
@State private var exportOPMLAction: Int? = nil
|
||||
@State private var aboutAction: Int? = nil
|
||||
|
||||
@State private var isWebsitePresented: Bool = false
|
||||
@State private var website: String? = nil
|
||||
|
||||
@State private var isOPMLImportPresented: Bool = false
|
||||
@State private var isOPMLImportDocPickerPresented: Bool = false
|
||||
@State private var isOPMLExportPresented: Bool = false
|
||||
@State private var isOPMLExportDocPickerPresented: Bool = false
|
||||
@State private var opmlAccount: Account? = nil
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
buildAccountsSection()
|
||||
buildTimelineSection()
|
||||
buildDatabaseSection()
|
||||
buildAboutSection()
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .leading))
|
||||
.navigationBarTitle(Text("Settings"), displayMode: .inline)
|
||||
.navigationBarItems(leading: Button(action: { self.viewController?.dismiss(animated: true) }) { Text("Done") } )
|
||||
}
|
||||
}
|
||||
|
||||
func buildAccountsSection() -> some View {
|
||||
Section(header: Text("ACCOUNTS").padding(.top, 22.0)) {
|
||||
ForEach(viewModel.accounts.indices, id: \.self) { index in
|
||||
NavigationLink(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(self.viewModel.accounts[index])), tag: index, selection: self.$accountAction) {
|
||||
Text(verbatim: self.viewModel.accounts[index].nameForDisplay)
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.accountAction = index
|
||||
}))
|
||||
}
|
||||
NavigationLink(destination: SettingsAddAccountView(), tag: 1000, selection: $accountAction) {
|
||||
Text("Add Account")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.accountAction = 1000
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func buildTimelineSection() -> some View {
|
||||
Section(header: Text("TIMELINE")) {
|
||||
Toggle(isOn: $viewModel.sortOldestToNewest) {
|
||||
Text("Sort Newest to Oldest")
|
||||
}
|
||||
Toggle(isOn: $viewModel.groupByFeed) {
|
||||
Text("Group By Feed")
|
||||
}
|
||||
Stepper(value: $viewModel.timelineNumberOfLines, in: 2...6) {
|
||||
Text("Number of Text Lines: \(viewModel.timelineNumberOfLines)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildDatabaseSection() -> some View {
|
||||
Section(header: Text("DATABASE")) {
|
||||
|
||||
NavigationLink(destination: SettingsRefreshSelectionView(selectedInterval: $viewModel.refreshInterval), tag: 1, selection: $refreshAction) {
|
||||
HStack {
|
||||
Text("Refresh Interval")
|
||||
Spacer()
|
||||
Text(verbatim: self.viewModel.refreshInterval.description()).foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.refreshAction = 1
|
||||
}))
|
||||
|
||||
NavigationLink(destination: SettingsSubscriptionsImportAccountPickerView(), tag: 1, selection: $importOPMLAction) {
|
||||
Text("Import Subscriptions")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.importOPMLAction = 1
|
||||
}))
|
||||
|
||||
NavigationLink(destination: SettingsSubscriptionsExportAccountPickerView(), tag: 1, selection: $exportOPMLAction) {
|
||||
Text("Export Subscriptions")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.exportOPMLAction = 1
|
||||
}))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func buildAboutSection() -> some View {
|
||||
Section(header: Text("ABOUT"), footer: buildFooter()) {
|
||||
|
||||
NavigationLink(destination: SettingsAboutView(viewModel: SettingsAboutView.ViewModel()), tag: 1, selection: $aboutAction) {
|
||||
Text("About NetNewsWire")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.aboutAction = 1
|
||||
}))
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://ranchero.com/netnewswire/"
|
||||
}) {
|
||||
Text("Website")
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://github.com/brentsimmons/NetNewsWire"
|
||||
}) {
|
||||
Text("Github Repository")
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://github.com/brentsimmons/NetNewsWire/issues"
|
||||
}) {
|
||||
Text("Bug Tracker")
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes"
|
||||
}) {
|
||||
Text("Technotes")
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown"
|
||||
}) {
|
||||
Text("How To Support NetNewsWire")
|
||||
}
|
||||
|
||||
if !AccountManager.shared.anyAccountHasFeedWithURL("https://nnw.ranchero.com/feed.json") {
|
||||
Button(action: {
|
||||
self.viewController?.dismiss(animated: true) {
|
||||
let feedName = NSLocalizedString("NetNewsWire News", comment: "NetNewsWire News")
|
||||
self.coordinator?.showAdd(.feed, initialFeed: "https://nnw.ranchero.com/feed.json", initialFeedName: feedName)
|
||||
}
|
||||
}) {
|
||||
Text("Add NetNewsWire News Feed")
|
||||
}
|
||||
}
|
||||
|
||||
}.sheet(isPresented: $isWebsitePresented) {
|
||||
SafariView(url: URL(string: self.website!)!)
|
||||
}
|
||||
}
|
||||
|
||||
func buildFooter() -> some View {
|
||||
return Text(verbatim: "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
// MARK: ViewModel
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
|
||||
init() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidAddAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidDeleteAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil)
|
||||
}
|
||||
|
||||
var accounts: [Account] {
|
||||
get {
|
||||
return AccountManager.shared.sortedAccounts
|
||||
}
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
var activeAccounts: [Account] {
|
||||
get {
|
||||
return AccountManager.shared.sortedActiveAccounts
|
||||
}
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
var sortOldestToNewest: Bool {
|
||||
get {
|
||||
return AppDefaults.timelineSortDirection == .orderedDescending
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
if newValue == true {
|
||||
AppDefaults.timelineSortDirection = .orderedDescending
|
||||
} else {
|
||||
AppDefaults.timelineSortDirection = .orderedAscending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var groupByFeed: Bool {
|
||||
get {
|
||||
return AppDefaults.timelineGroupByFeed
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
AppDefaults.timelineGroupByFeed = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var timelineNumberOfLines: Int {
|
||||
get {
|
||||
return AppDefaults.timelineNumberOfLines
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
AppDefaults.timelineNumberOfLines = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var refreshInterval: RefreshInterval {
|
||||
get {
|
||||
return AppDefaults.refreshInterval
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
AppDefaults.refreshInterval = newValue
|
||||
}
|
||||
}
|
||||
|
||||
@objc func accountsDidChange(_ notification: Notification) {
|
||||
objectWillChange.send()
|
||||
}
|
||||
|
||||
@objc func displayNameDidChange(_ notification: Notification) {
|
||||
objectWillChange.send()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsView(viewModel: SettingsView.ViewModel())
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,266 @@
|
|||
//
|
||||
// SettingsViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 4/24/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
|
||||
class SettingsViewController: UITableViewController {
|
||||
|
||||
static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 400.0)
|
||||
|
||||
@IBOutlet weak var refreshIntervalLabel: UILabel!
|
||||
@IBOutlet weak var timelineSortOrderSwitch: UISwitch!
|
||||
@IBOutlet weak var timelineNumberOfLinesLabel: UILabel!
|
||||
|
||||
weak var presentingParentController: UIViewController?
|
||||
|
||||
override func viewDidLoad() {
|
||||
// This hack mostly works around a bug in static tables with dynamic type. See: https://spin.atomicobject.com/2018/10/15/dynamic-type-static-uitableview/
|
||||
NotificationCenter.default.removeObserver(tableView!, name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
|
||||
tableView.register(UINib(nibName: "SettingsTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsTableViewCell")
|
||||
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if AppDefaults.timelineSortDirection == .orderedAscending {
|
||||
timelineSortOrderSwitch.isOn = true
|
||||
} else {
|
||||
timelineSortOrderSwitch.isOn = false
|
||||
}
|
||||
|
||||
refreshIntervalLabel.text = AppDefaults.refreshInterval.description()
|
||||
|
||||
let numberOfLinesText = NSLocalizedString(" lines", comment: "Lines")
|
||||
timelineNumberOfLinesLabel.text = "\(AppDefaults.timelineNumberOfLines)" + numberOfLinesText
|
||||
|
||||
let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 20.0, y: 0.0, width: 0.0, height: 0.0))
|
||||
buildLabel.font = UIFont.systemFont(ofSize: 11.0)
|
||||
buildLabel.textColor = UIColor.gray
|
||||
buildLabel.text = "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))"
|
||||
buildLabel.sizeToFit()
|
||||
buildLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
tableView.tableFooterView = buildLabel
|
||||
|
||||
tableView.reloadData()
|
||||
|
||||
}
|
||||
|
||||
// MARK: UITableView
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch section {
|
||||
case 0:
|
||||
return AccountManager.shared.accounts.count + 1
|
||||
case 1:
|
||||
let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section)
|
||||
if AccountManager.shared.activeAccounts.isEmpty {
|
||||
// Hide the add NetNewsWire feed row if they don't have any active accounts
|
||||
return defaultNumberOfRows - 1
|
||||
}
|
||||
return defaultNumberOfRows
|
||||
default:
|
||||
return super.tableView(tableView, numberOfRowsInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell: UITableViewCell
|
||||
switch indexPath.section {
|
||||
case 0:
|
||||
|
||||
cell = tableView.dequeueReusableCell(withIdentifier: "SettingsTableViewCell", for: indexPath)
|
||||
cell.textLabel?.adjustsFontForContentSizeCategory = true
|
||||
|
||||
let sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
if indexPath.row == sortedAccounts.count {
|
||||
cell.textLabel?.text = NSLocalizedString("Add Account", comment: "Accounts")
|
||||
} else {
|
||||
cell.textLabel?.text = sortedAccounts[indexPath.row].nameForDisplay
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
switch indexPath.section {
|
||||
case 0:
|
||||
let sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
if indexPath.row == sortedAccounts.count {
|
||||
let controller = UIStoryboard.settings.instantiateController(ofType: AddAccountViewController.self)
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
} else {
|
||||
let controller = UIStoryboard.settings.instantiateController(ofType: DetailAccountViewController.self)
|
||||
controller.account = sortedAccounts[indexPath.row]
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
case 1:
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
let timeline = UIStoryboard.settings.instantiateController(ofType: AboutViewController.self)
|
||||
self.navigationController?.pushViewController(timeline, animated: true)
|
||||
case 1:
|
||||
UIApplication.shared.open(URL(string: "https://ranchero.com/netnewswire/")!, options: [:])
|
||||
case 2:
|
||||
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire")!, options: [:])
|
||||
case 3:
|
||||
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!, options: [:])
|
||||
case 4:
|
||||
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!, options: [:])
|
||||
case 5:
|
||||
addFeed()
|
||||
default:
|
||||
UIApplication.shared.open(URL(string: "https://ranchero.com/netnewswire/")!, options: [:])
|
||||
}
|
||||
case 2:
|
||||
if indexPath.row == 1 {
|
||||
let timeline = UIStoryboard.settings.instantiateController(ofType: TimelineNumberOfLinesViewController.self)
|
||||
self.navigationController?.pushViewController(timeline, animated: true)
|
||||
}
|
||||
case 3:
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
let timeline = UIStoryboard.settings.instantiateController(ofType: RefreshIntervalViewController.self)
|
||||
self.navigationController?.pushViewController(timeline, animated: true)
|
||||
case 1:
|
||||
importOPML()
|
||||
case 2:
|
||||
exportOPML()
|
||||
default:
|
||||
print("export")
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
|
||||
return .none
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
if indexPath.section == 0 {
|
||||
return super.tableView(tableView, heightForRowAt: IndexPath(row: 0, section: 0))
|
||||
} else {
|
||||
return super.tableView(tableView, heightForRowAt: indexPath)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, indentationLevelForRowAt indexPath: IndexPath) -> Int {
|
||||
if indexPath.section == 0 {
|
||||
return super.tableView(tableView, indentationLevelForRowAt: IndexPath(row: 0, section: 0))
|
||||
} else {
|
||||
return super.tableView(tableView, indentationLevelForRowAt: indexPath)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
@IBAction func done(_ sender: Any) {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
@IBAction func switchTimelineOrder(_ sender: Any) {
|
||||
if timelineSortOrderSwitch.isOn {
|
||||
AppDefaults.timelineSortDirection = .orderedAscending
|
||||
} else {
|
||||
AppDefaults.timelineSortDirection = .orderedDescending
|
||||
}
|
||||
}
|
||||
|
||||
@objc func contentSizeCategoryDidChange() {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: OPML Document Picker
|
||||
|
||||
extension SettingsViewController: UIDocumentPickerDelegate {
|
||||
|
||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||
|
||||
for url in urls {
|
||||
AccountManager.shared.defaultAccount.importOPML(url) { result in}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension SettingsViewController {
|
||||
|
||||
func addFeed() {
|
||||
|
||||
let appNewsURLString = "https://nnw.ranchero.com/feed.json"
|
||||
if AccountManager.shared.anyAccountHasFeedWithURL(appNewsURLString) {
|
||||
presentError(title: "Subscribe", message: "You are already subscribed to the NetNewsWire news feed.")
|
||||
return
|
||||
}
|
||||
|
||||
self.dismiss(animated: true)
|
||||
|
||||
let addNavViewController = UIStoryboard.add.instantiateInitialViewController() as! UINavigationController
|
||||
let addViewController = addNavViewController.topViewController as! AddContainerViewController
|
||||
addNavViewController.modalPresentationStyle = .formSheet
|
||||
addNavViewController.preferredContentSize = AddContainerViewController.preferredContentSizeForFormSheetDisplay
|
||||
addViewController.initialFeed = appNewsURLString
|
||||
addViewController.initialFeedName = "NetNewsWire News"
|
||||
|
||||
presentingParentController?.present(addNavViewController, animated: true)
|
||||
|
||||
}
|
||||
|
||||
func importOPML() {
|
||||
|
||||
let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import)
|
||||
docPicker.delegate = self
|
||||
docPicker.modalPresentationStyle = .formSheet
|
||||
self.present(docPicker, animated: true)
|
||||
|
||||
}
|
||||
|
||||
func exportOPML() {
|
||||
|
||||
let filename = "MySubscriptions.opml"
|
||||
let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
|
||||
let opmlString = OPMLExporter.OPMLString(with: AccountManager.shared.defaultAccount, title: filename)
|
||||
do {
|
||||
try opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8)
|
||||
} catch {
|
||||
self.presentError(title: "OPML Export Error", message: error.localizedDescription)
|
||||
}
|
||||
|
||||
let docPicker = UIDocumentPickerViewController(url: tempFile, in: .exportToService)
|
||||
docPicker.modalPresentationStyle = .formSheet
|
||||
self.present(docPicker, animated: true)
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// TimelineNumberOfLinesViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 4/29/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class TimelineNumberOfLinesViewController: UITableViewController {
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return 5
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
|
||||
cell.textLabel?.adjustsFontForContentSizeCategory = true
|
||||
cell.textLabel?.text = "\(2 + indexPath.row)" + NSLocalizedString(" lines", comment: "Lines")
|
||||
|
||||
let numberOfLines = AppDefaults.timelineNumberOfLines
|
||||
if indexPath.row + 2 == numberOfLines {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
AppDefaults.timelineNumberOfLines = indexPath.row + 2
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
}
|
|
@ -10,6 +10,8 @@ import UIKit
|
|||
|
||||
extension UIStoryboard {
|
||||
|
||||
static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 400.0)
|
||||
|
||||
static var main: UIStoryboard {
|
||||
return UIStoryboard(name: "Main", bundle: nil)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue