From effec2467438ba66d7347e76b34cd1ce49e6d7ab Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 11:51:33 -0500 Subject: [PATCH 01/50] Change settings from using SwiftUI to using UIKit --- NetNewsWire.xcodeproj/project.pbxproj | 100 +- iOS/SceneCoordinator.swift | 7 +- iOS/Settings/AboutViewController.swift | 56 + .../Account/SettingsAccountLabelView.swift | 34 - .../Account/SettingsAddAccountView.swift | 52 - .../Account/SettingsDetailAccountView.swift | 137 --- .../Account/SettingsFeedbinAccountView.swift | 161 --- .../Account/SettingsLocalAccountView.swift | 59 - .../SettingsReaderAPIAccountView.swift | 178 --- iOS/Settings/AddAccountViewController.swift | 49 + .../AddLocalAccountViewController.swift | 48 + .../DetailAccountViewController.swift | 135 +++ .../FeedbinAccountViewController.swift | 137 +++ .../RefreshIntervalViewController.swift | 111 ++ iOS/Settings/Settings.storyboard | 1003 +++++++++++++++++ iOS/Settings/SettingsAboutView.swift | 67 -- .../SettingsRefreshSelectionView.swift | 35 - ...SubscriptionsExportAccountPickerView.swift | 34 - ...ubscriptionsExportDocumentPickerView.swift | 31 - ...SubscriptionsImportAccountPickerView.swift | 34 - ...ubscriptionsImportDocumentPickerView.swift | 43 - iOS/Settings/SettingsTableViewCell.xib | 24 + iOS/Settings/SettingsView.swift | 270 ----- iOS/Settings/SettingsViewController.swift | 266 +++++ .../TimelineNumberOfLinesViewController.swift | 41 + .../UIStoryboard-Extensions.swift | 2 + 26 files changed, 1916 insertions(+), 1198 deletions(-) create mode 100644 iOS/Settings/AboutViewController.swift delete mode 100644 iOS/Settings/Account/SettingsAccountLabelView.swift delete mode 100644 iOS/Settings/Account/SettingsAddAccountView.swift delete mode 100644 iOS/Settings/Account/SettingsDetailAccountView.swift delete mode 100644 iOS/Settings/Account/SettingsFeedbinAccountView.swift delete mode 100644 iOS/Settings/Account/SettingsLocalAccountView.swift delete mode 100644 iOS/Settings/Account/SettingsReaderAPIAccountView.swift create mode 100644 iOS/Settings/AddAccountViewController.swift create mode 100644 iOS/Settings/AddLocalAccountViewController.swift create mode 100644 iOS/Settings/DetailAccountViewController.swift create mode 100644 iOS/Settings/FeedbinAccountViewController.swift create mode 100644 iOS/Settings/RefreshIntervalViewController.swift create mode 100644 iOS/Settings/Settings.storyboard delete mode 100644 iOS/Settings/SettingsAboutView.swift delete mode 100644 iOS/Settings/SettingsRefreshSelectionView.swift delete mode 100644 iOS/Settings/SettingsSubscriptionsExportAccountPickerView.swift delete mode 100644 iOS/Settings/SettingsSubscriptionsExportDocumentPickerView.swift delete mode 100644 iOS/Settings/SettingsSubscriptionsImportAccountPickerView.swift delete mode 100644 iOS/Settings/SettingsSubscriptionsImportDocumentPickerView.swift create mode 100644 iOS/Settings/SettingsTableViewCell.xib delete mode 100644 iOS/Settings/SettingsView.swift create mode 100644 iOS/Settings/SettingsViewController.swift create mode 100644 iOS/Settings/TimelineNumberOfLinesViewController.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index cfb318ad1..dcaaca48f 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -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 = ""; }; - 510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAddAccountView.swift; sourceTree = ""; }; - 510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLocalAccountView.swift; sourceTree = ""; }; - 510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFeedbinAccountView.swift; sourceTree = ""; }; - 510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAccountLabelView.swift; sourceTree = ""; }; 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = ""; }; 51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = ""; }; 51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContainerViewController.swift; sourceTree = ""; }; @@ -754,14 +747,10 @@ 513146B1235A81A400387FDC /* AddFeedIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFeedIntentHandler.swift; sourceTree = ""; }; 51314706235C41FC00387FDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = ""; }; 51314714235C420900387FDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = ""; }; - 51314715235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportAccountPickerView.swift; sourceTree = ""; }; - 51314717235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportAccountPickerView.swift; sourceTree = ""; }; 51322854232EED360033D4ED /* VibrantSelectAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantSelectAction.swift; sourceTree = ""; }; 51322858232FDDB80033D4ED /* VibrantButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantButtonStyle.swift; sourceTree = ""; }; - 5132285A232FF2C40033D4ED /* SettingsRefreshSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRefreshSelectionView.swift; sourceTree = ""; }; 513228F2233037620033D4ED /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; 513229302330523F0033D4ED /* AttributedStringView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringView.swift; sourceTree = ""; }; - 5132293A23305D4C0033D4ED /* SettingsAboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAboutView.swift; sourceTree = ""; }; 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 = ""; }; 513C5CEB232571C2003D4054 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; @@ -803,11 +792,19 @@ 51934CC1230F5963006127BE /* ThemedNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedNavigationController.swift; sourceTree = ""; }; 51934CCD2310792F006127BE /* ActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityManager.swift; sourceTree = ""; }; 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTimelineFeedDelegate.swift; sourceTree = ""; }; - 5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportDocumentPickerView.swift; sourceTree = ""; }; - 5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportDocumentPickerView.swift; sourceTree = ""; }; 519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = ""; }; 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RefreshInterval-Extensions.swift"; sourceTree = ""; }; 519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshIntervalViewController.swift; sourceTree = ""; }; + 51A1698E235E10D600EB091F /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = ""; }; + 51A1698F235E10D600EB091F /* AddLocalAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocalAccountViewController.swift; sourceTree = ""; }; + 51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; + 51A16991235E10D600EB091F /* DetailAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailAccountViewController.swift; sourceTree = ""; }; + 51A16992235E10D600EB091F /* AddAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddAccountViewController.swift; sourceTree = ""; }; + 51A16993235E10D600EB091F /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; + 51A16994235E10D600EB091F /* TimelineNumberOfLinesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineNumberOfLinesViewController.swift; sourceTree = ""; }; + 51A16995235E10D600EB091F /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; + 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = ""; }; 51AF460D232488C6001742EF /* Account-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account-Extensions.swift"; sourceTree = ""; }; 51B62E67233186730085F949 /* MasterTimelineAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineAvatarView.swift; sourceTree = ""; }; 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleActivityItemSource.swift; sourceTree = ""; }; @@ -852,8 +849,6 @@ 51EF0F8D2279C9260050506E /* AccountsAdd.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountsAdd.xib; sourceTree = ""; }; 51EF0F8F2279C9500050506E /* AccountsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsAddViewController.swift; sourceTree = ""; }; 51EF0F912279CA620050506E /* AccountsAddTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsAddTableCellView.swift; sourceTree = ""; }; - 51F35D0822AFD4760003CE1B /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; - 51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDetailAccountView.swift; sourceTree = ""; }; 51F85BEA22724CB600C787DC /* About.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = About.rtf; sourceTree = ""; }; 51F85BEC227251DF00C787DC /* Acknowledgments.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Acknowledgments.rtf; sourceTree = ""; }; 51F85BEE2272520B00C787DC /* Thanks.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Thanks.rtf; sourceTree = ""; }; @@ -871,7 +866,6 @@ 51FD40BD2341555600880194 /* UIImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage-Extensions.swift"; sourceTree = ""; }; 51FD413A2342BD0500880194 /* MasterTimelineUnreadCountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineUnreadCountView.swift; sourceTree = ""; }; 51FE10022345529D0056195D /* UserNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationManager.swift; sourceTree = ""; }; - 557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsReaderAPIAccountView.swift; sourceTree = ""; }; 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsReaderAPI.xib; sourceTree = ""; }; 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsReaderAPIWindowController.swift; sourceTree = ""; }; 5F323808231DF9F000706F6B /* NNWTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNWTableViewCell.swift; sourceTree = ""; }; @@ -1265,19 +1259,6 @@ name = Products; sourceTree = ""; }; - 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 = ""; - }; 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 = ""; @@ -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; }; diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index 7d9598c8c..e4ac6208e 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -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() { diff --git a/iOS/Settings/AboutViewController.swift b/iOS/Settings/AboutViewController.swift new file mode 100644 index 000000000..31da63e9e --- /dev/null +++ b/iOS/Settings/AboutViewController.swift @@ -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) + } + +} diff --git a/iOS/Settings/Account/SettingsAccountLabelView.swift b/iOS/Settings/Account/SettingsAccountLabelView.swift deleted file mode 100644 index 0d5ead2e3..000000000 --- a/iOS/Settings/Account/SettingsAccountLabelView.swift +++ /dev/null @@ -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 diff --git a/iOS/Settings/Account/SettingsAddAccountView.swift b/iOS/Settings/Account/SettingsAddAccountView.swift deleted file mode 100644 index f26769971..000000000 --- a/iOS/Settings/Account/SettingsAddAccountView.swift +++ /dev/null @@ -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 diff --git a/iOS/Settings/Account/SettingsDetailAccountView.swift b/iOS/Settings/Account/SettingsDetailAccountView.swift deleted file mode 100644 index f67b29833..000000000 --- a/iOS/Settings/Account/SettingsDetailAccountView.swift +++ /dev/null @@ -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 diff --git a/iOS/Settings/Account/SettingsFeedbinAccountView.swift b/iOS/Settings/Account/SettingsFeedbinAccountView.swift deleted file mode 100644 index a685a8ada..000000000 --- a/iOS/Settings/Account/SettingsFeedbinAccountView.swift +++ /dev/null @@ -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 diff --git a/iOS/Settings/Account/SettingsLocalAccountView.swift b/iOS/Settings/Account/SettingsLocalAccountView.swift deleted file mode 100644 index d92cd6f9b..000000000 --- a/iOS/Settings/Account/SettingsLocalAccountView.swift +++ /dev/null @@ -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 diff --git a/iOS/Settings/Account/SettingsReaderAPIAccountView.swift b/iOS/Settings/Account/SettingsReaderAPIAccountView.swift deleted file mode 100644 index 9e4218e9a..000000000 --- a/iOS/Settings/Account/SettingsReaderAPIAccountView.swift +++ /dev/null @@ -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 diff --git a/iOS/Settings/AddAccountViewController.swift b/iOS/Settings/AddAccountViewController.swift new file mode 100644 index 000000000..d1587758a --- /dev/null +++ b/iOS/Settings/AddAccountViewController.swift @@ -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) + } + +} diff --git a/iOS/Settings/AddLocalAccountViewController.swift b/iOS/Settings/AddLocalAccountViewController.swift new file mode 100644 index 000000000..c89569eaf --- /dev/null +++ b/iOS/Settings/AddLocalAccountViewController.swift @@ -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 + } + +} diff --git a/iOS/Settings/DetailAccountViewController.swift b/iOS/Settings/DetailAccountViewController.swift new file mode 100644 index 000000000..6f8c45636 --- /dev/null +++ b/iOS/Settings/DetailAccountViewController.swift @@ -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 + } + +} diff --git a/iOS/Settings/FeedbinAccountViewController.swift b/iOS/Settings/FeedbinAccountViewController.swift new file mode 100644 index 000000000..6a8ba2129 --- /dev/null +++ b/iOS/Settings/FeedbinAccountViewController.swift @@ -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 + } + +} diff --git a/iOS/Settings/RefreshIntervalViewController.swift b/iOS/Settings/RefreshIntervalViewController.swift new file mode 100644 index 000000000..8f3d89ba9 --- /dev/null +++ b/iOS/Settings/RefreshIntervalViewController.swift @@ -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) + + } + +} diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard new file mode 100644 index 000000000..3dfa1b16a --- /dev/null +++ b/iOS/Settings/Settings.storyboard @@ -0,0 +1,1003 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Settings/SettingsAboutView.swift b/iOS/Settings/SettingsAboutView.swift deleted file mode 100644 index e76fce4ec..000000000 --- a/iOS/Settings/SettingsAboutView.swift +++ /dev/null @@ -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()) - } -} diff --git a/iOS/Settings/SettingsRefreshSelectionView.swift b/iOS/Settings/SettingsRefreshSelectionView.swift deleted file mode 100644 index 22fcce129..000000000 --- a/iOS/Settings/SettingsRefreshSelectionView.swift +++ /dev/null @@ -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)) - } - } - } - -} diff --git a/iOS/Settings/SettingsSubscriptionsExportAccountPickerView.swift b/iOS/Settings/SettingsSubscriptionsExportAccountPickerView.swift deleted file mode 100644 index 33bc3ea40..000000000 --- a/iOS/Settings/SettingsSubscriptionsExportAccountPickerView.swift +++ /dev/null @@ -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) - } - -} diff --git a/iOS/Settings/SettingsSubscriptionsExportDocumentPickerView.swift b/iOS/Settings/SettingsSubscriptionsExportDocumentPickerView.swift deleted file mode 100644 index a471c7d27..000000000 --- a/iOS/Settings/SettingsSubscriptionsExportDocumentPickerView.swift +++ /dev/null @@ -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) -> 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) { - // - } - -} diff --git a/iOS/Settings/SettingsSubscriptionsImportAccountPickerView.swift b/iOS/Settings/SettingsSubscriptionsImportAccountPickerView.swift deleted file mode 100644 index 0ce3fbdc6..000000000 --- a/iOS/Settings/SettingsSubscriptionsImportAccountPickerView.swift +++ /dev/null @@ -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) - } - -} diff --git a/iOS/Settings/SettingsSubscriptionsImportDocumentPickerView.swift b/iOS/Settings/SettingsSubscriptionsImportDocumentPickerView.swift deleted file mode 100644 index 4f4043a92..000000000 --- a/iOS/Settings/SettingsSubscriptionsImportDocumentPickerView.swift +++ /dev/null @@ -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) -> UIDocumentPickerViewController { - let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import) - docPicker.delegate = context.coordinator - return docPicker - } - - func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext) { - // - } - - 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} - } - } - - } -} diff --git a/iOS/Settings/SettingsTableViewCell.xib b/iOS/Settings/SettingsTableViewCell.xib new file mode 100644 index 000000000..aca59947e --- /dev/null +++ b/iOS/Settings/SettingsTableViewCell.xib @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift deleted file mode 100644 index 40b6929f0..000000000 --- a/iOS/Settings/SettingsView.swift +++ /dev/null @@ -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 diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift new file mode 100644 index 000000000..84a177d27 --- /dev/null +++ b/iOS/Settings/SettingsViewController.swift @@ -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) + + } + +} diff --git a/iOS/Settings/TimelineNumberOfLinesViewController.swift b/iOS/Settings/TimelineNumberOfLinesViewController.swift new file mode 100644 index 000000000..5c0453b2e --- /dev/null +++ b/iOS/Settings/TimelineNumberOfLinesViewController.swift @@ -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) + } + +} diff --git a/iOS/UIKit Extensions/UIStoryboard-Extensions.swift b/iOS/UIKit Extensions/UIStoryboard-Extensions.swift index f0b67d6ff..4d309a729 100644 --- a/iOS/UIKit Extensions/UIStoryboard-Extensions.swift +++ b/iOS/UIKit Extensions/UIStoryboard-Extensions.swift @@ -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) } From ea267e80dd5151d268658b116e498c8b2f9e1748 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 13:44:47 -0500 Subject: [PATCH 02/50] Add show hide password button and delete unused SwiftUI extensions --- NetNewsWire.xcodeproj/project.pbxproj | 36 ------------ .../FeedbinAccountViewController.swift | 19 ++++-- iOS/Settings/Settings.storyboard | 58 +++++++++++-------- .../AttributedStringView.swift | 45 -------------- iOS/SwiftUI Extensions/PasswordField.swift | 25 -------- iOS/SwiftUI Extensions/SafariView.swift | 52 ----------------- .../ShowHidePasswordView.swift | 51 ---------------- .../ShowHidePasswordView.xib | 46 --------------- .../VibrantButtonStyle.swift | 25 -------- .../VibrantSelectAction.swift | 40 ------------- 10 files changed, 48 insertions(+), 349 deletions(-) delete mode 100644 iOS/SwiftUI Extensions/AttributedStringView.swift delete mode 100644 iOS/SwiftUI Extensions/PasswordField.swift delete mode 100644 iOS/SwiftUI Extensions/SafariView.swift delete mode 100644 iOS/SwiftUI Extensions/ShowHidePasswordView.swift delete mode 100644 iOS/SwiftUI Extensions/ShowHidePasswordView.xib delete mode 100644 iOS/SwiftUI Extensions/VibrantButtonStyle.swift delete mode 100644 iOS/SwiftUI Extensions/VibrantSelectAction.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index dcaaca48f..a25414c57 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -42,11 +42,8 @@ 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 */; }; - 51322855232EED360033D4ED /* VibrantSelectAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51322854232EED360033D4ED /* VibrantSelectAction.swift */; }; - 51322859232FDDB80033D4ED /* VibrantButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51322858232FDDB80033D4ED /* VibrantButtonStyle.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 */; }; 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, ); }; }; @@ -196,9 +193,6 @@ 51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; }; 51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */; }; 51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D87EE02311D34700E63F03 /* ActivityType.swift */; }; - 51E149B3234D82E40004F7A5 /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E149B2234D82E40004F7A5 /* PasswordField.swift */; }; - 51E149C0234D839E0004F7A5 /* ShowHidePasswordView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51E149BF234D839E0004F7A5 /* ShowHidePasswordView.xib */; }; - 51E149C2234D852F0004F7A5 /* ShowHidePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E149C1234D852F0004F7A5 /* ShowHidePasswordView.swift */; }; 51E3EB33229AB02C00645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB32229AB02C00645299 /* ErrorHandler.swift */; }; 51E3EB3D229AB08300645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB3C229AB08300645299 /* ErrorHandler.swift */; }; 51E595A5228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; }; @@ -417,7 +411,6 @@ D5F4EDB720074D6500B9E363 /* Feed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */; }; D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */; }; DD82AB0A231003F6002269DF /* SharingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD82AB09231003F6002269DF /* SharingTests.swift */; }; - DF999FF722B5AEFA0064B687 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF999FF622B5AEFA0064B687 /* SafariView.swift */; }; FF3ABF13232599810074C542 /* ArticleSorterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF09232599450074C542 /* ArticleSorterTests.swift */; }; FF3ABF1523259DDB0074C542 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; }; FF3ABF162325AF5D0074C542 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; }; @@ -747,10 +740,7 @@ 513146B1235A81A400387FDC /* AddFeedIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFeedIntentHandler.swift; sourceTree = ""; }; 51314706235C41FC00387FDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = ""; }; 51314714235C420900387FDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = ""; }; - 51322854232EED360033D4ED /* VibrantSelectAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantSelectAction.swift; sourceTree = ""; }; - 51322858232FDDB80033D4ED /* VibrantButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantButtonStyle.swift; sourceTree = ""; }; 513228F2233037620033D4ED /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; - 513229302330523F0033D4ED /* AttributedStringView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringView.swift; sourceTree = ""; }; 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 = ""; }; 513C5CEB232571C2003D4054 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; @@ -833,9 +823,6 @@ 51CC9B3D231720B2000E842F /* MasterFeedDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterFeedDataSource.swift; sourceTree = ""; }; 51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineDataSource.swift; sourceTree = ""; }; 51D87EE02311D34700E63F03 /* ActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityType.swift; sourceTree = ""; }; - 51E149B2234D82E40004F7A5 /* PasswordField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordField.swift; sourceTree = ""; }; - 51E149BF234D839E0004F7A5 /* ShowHidePasswordView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShowHidePasswordView.xib; sourceTree = ""; }; - 51E149C1234D852F0004F7A5 /* ShowHidePasswordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowHidePasswordView.swift; sourceTree = ""; }; 51E3EB32229AB02C00645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = ""; }; 51E3EB3C229AB08300645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = ""; }; 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleStatusSyncTimer.swift; sourceTree = ""; }; @@ -1066,7 +1053,6 @@ D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Feed+Scriptability.swift"; sourceTree = ""; }; D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = ""; }; DD82AB09231003F6002269DF /* SharingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharingTests.swift; sourceTree = ""; }; - DF999FF622B5AEFA0064B687 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; }; FF3ABF09232599450074C542 /* ArticleSorterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorterTests.swift; sourceTree = ""; }; FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorter.swift; sourceTree = ""; }; FFD43E372340F320009E5CA3 /* UndoAvailableAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UndoAvailableAlertController.swift; sourceTree = ""; }; @@ -1315,20 +1301,6 @@ path = Activity; sourceTree = ""; }; - 5194B5E222B693EC00144881 /* SwiftUI Extensions */ = { - isa = PBXGroup; - children = ( - 513229302330523F0033D4ED /* AttributedStringView.swift */, - DF999FF622B5AEFA0064B687 /* SafariView.swift */, - 51322858232FDDB80033D4ED /* VibrantButtonStyle.swift */, - 51322854232EED360033D4ED /* VibrantSelectAction.swift */, - 51E149B2234D82E40004F7A5 /* PasswordField.swift */, - 51E149C1234D852F0004F7A5 /* ShowHidePasswordView.swift */, - 51E149BF234D839E0004F7A5 /* ShowHidePasswordView.xib */, - ); - path = "SwiftUI Extensions"; - sourceTree = ""; - }; 519D740423243C68008BB345 /* Model Extensions */ = { isa = PBXGroup; children = ( @@ -2021,7 +1993,6 @@ 5183CCDB226F1EEB0010922C /* Progress */, 5183CCEB227117C70010922C /* Settings */, 519D740423243C68008BB345 /* Model Extensions */, - 5194B5E222B693EC00144881 /* SwiftUI Extensions */, 51C45245226506C800C03939 /* UIKit Extensions */, 513C5CE7232571C2003D4054 /* ShareExtension */, 51314643235A7C2300387FDC /* IntentsExtension */, @@ -2640,7 +2611,6 @@ 51F85BED227251DF00C787DC /* Acknowledgments.rtf in Resources */, 511D43D1231FA62800FB1562 /* SidebarKeyboardShortcuts.plist in Resources */, 51C452AB22650DC600C03939 /* template.html in Resources */, - 51E149C0234D839E0004F7A5 /* ShowHidePasswordView.xib in Resources */, 51F85BF12272524100C787DC /* Credits.rtf in Resources */, 84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */, 511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */, @@ -2891,7 +2861,6 @@ buildActionMask = 2147483647; files = ( 840D617F2029031C009BC708 /* AppDelegate.swift in Sources */, - 51E149B3234D82E40004F7A5 /* PasswordField.swift in Sources */, 512E08E72268801200BDCFDD /* FeedTreeControllerDelegate.swift in Sources */, 51C452A422650A2D00C03939 /* ArticleUtilities.swift in Sources */, 51EF0F79227716380050506E /* ColorHash.swift in Sources */, @@ -2915,7 +2884,6 @@ 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 */, @@ -2956,7 +2924,6 @@ 51C4529A22650A0400C03939 /* ArticleStyle.swift in Sources */, 51C4527F2265092C00C03939 /* ArticleViewController.swift in Sources */, 51C4526A226508F600C03939 /* MasterFeedTableViewCellLayout.swift in Sources */, - 51E149C2234D852F0004F7A5 /* ShowHidePasswordView.swift in Sources */, 51C452AE2265104D00C03939 /* ArticleStringFormatter.swift in Sources */, 512E08E62268800D00BDCFDD /* FolderTreeControllerDelegate.swift in Sources */, 51C4529922650A0000C03939 /* ArticleStylesManager.swift in Sources */, @@ -2987,12 +2954,10 @@ 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 */, 512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */, 519D740623243CC0008BB345 /* RefreshInterval-Extensions.swift in Sources */, - 51322855232EED360033D4ED /* VibrantSelectAction.swift in Sources */, 51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */, 5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */, 51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */, @@ -3009,7 +2974,6 @@ 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 */, ); diff --git a/iOS/Settings/FeedbinAccountViewController.swift b/iOS/Settings/FeedbinAccountViewController.swift index 6a8ba2129..e7218bfe1 100644 --- a/iOS/Settings/FeedbinAccountViewController.swift +++ b/iOS/Settings/FeedbinAccountViewController.swift @@ -1,5 +1,5 @@ // -// AddFeedbinAccountViewController.swift +// FeedbinAccountViewController.swift // NetNewsWire-iOS // // Created by Maurice Parker on 5/19/19. @@ -16,6 +16,7 @@ class FeedbinAccountViewController: UIViewController { @IBOutlet weak var cancelBarButtonItem: UIBarButtonItem! @IBOutlet weak var emailTextField: UITextField! @IBOutlet weak var passwordTextField: UITextField! + @IBOutlet weak var showHideButton: UIButton! @IBOutlet weak var actionButton: UIButton! @IBOutlet weak var errorMessageLabel: UILabel! @@ -44,10 +45,20 @@ class FeedbinAccountViewController: UIViewController { delegate?.dismiss() } + @IBAction func showHidePassword(_ sender: Any) { + if passwordTextField.isSecureTextEntry { + passwordTextField.isSecureTextEntry = false + showHideButton.setTitle("Hide", for: .normal) + } else { + passwordTextField.isSecureTextEntry = true + showHideButton.setTitle("Show", for: .normal) + } + } + @IBAction func action(_ sender: Any) { self.errorMessageLabel.text = nil - guard emailTextField.text != nil && passwordTextField.text != nil else { + guard let email = emailTextField.text, let password = passwordTextField.text else { self.errorMessageLabel.text = NSLocalizedString("Username & password required.", comment: "Credentials Error") return } @@ -56,8 +67,8 @@ class FeedbinAccountViewController: UIViewController { 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 ?? "") + let trimmedEmail = email.trimmingCharacters(in: .whitespaces) + let credentials = Credentials(type: .basic, username: trimmedEmail, secret: password) Account.validateCredentials(type: .feedbin, credentials: credentials) { result in self.stopAnimtatingActivityIndicator() diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 3dfa1b16a..9b7b19ed1 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -437,7 +437,7 @@ - + @@ -457,11 +457,10 @@ + - - @@ -471,7 +470,7 @@ - + @@ -492,10 +491,9 @@ - + - @@ -660,14 +658,12 @@ - - - - - + + + + - + + + + + - - - + - + - + + @@ -733,12 +740,13 @@ - + + - + diff --git a/iOS/SwiftUI Extensions/AttributedStringView.swift b/iOS/SwiftUI Extensions/AttributedStringView.swift deleted file mode 100644 index 1cfe55719..000000000 --- a/iOS/SwiftUI Extensions/AttributedStringView.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// AttributedStringView.swift -// NetNewsWire-iOS -// -// Created by Maurice Parker on 9/16/19. -// Copyright © 2019 Ranchero Software. All rights reserved. -// - -import SwiftUI - -struct AttributedStringView: UIViewRepresentable { - - let string: NSAttributedString - let preferredMaxLayoutWidth: CGFloat - - func makeUIView(context: Context) -> HackedTextView { - return HackedTextView() - } - - func updateUIView(_ view: HackedTextView, context: Context) { - view.attributedText = string - - view.preferredMaxLayoutWidth = preferredMaxLayoutWidth - view.isScrollEnabled = false - view.textContainer.lineBreakMode = .byWordWrapping - - view.isUserInteractionEnabled = true - view.adjustsFontForContentSizeCategory = true - view.font = .preferredFont(forTextStyle: .body) - view.textColor = UIColor.label - view.tintColor = AppAssets.secondaryAccentColor - view.backgroundColor = UIColor.secondarySystemGroupedBackground - - view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - view.setContentCompressionResistancePriority(.required, for: .vertical) - } - -} - -class HackedTextView: UITextView { - var preferredMaxLayoutWidth = CGFloat.zero - override var intrinsicContentSize: CGSize { - return sizeThatFits(CGSize(width: preferredMaxLayoutWidth, height: .infinity)) - } -} diff --git a/iOS/SwiftUI Extensions/PasswordField.swift b/iOS/SwiftUI Extensions/PasswordField.swift deleted file mode 100644 index 8875837ae..000000000 --- a/iOS/SwiftUI Extensions/PasswordField.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// PasswordField.swift -// NetNewsWire-iOS -// -// Created by Maurice Parker on 10/8/19. -// Copyright © 2019 Ranchero Software. All rights reserved. -// - -import SwiftUI - -struct PasswordField: UIViewRepresentable { - - let password: Binding - - func makeUIView(context: Context) -> ShowHidePasswordView { - let showHideView = Bundle.main.loadNibNamed("ShowHidePasswordView", owner: Self.self, options: nil)?[0] as! ShowHidePasswordView - showHideView.passwordTextField.bindingString = password - return showHideView - } - - func updateUIView(_ showHideView: ShowHidePasswordView, context: Context) { - showHideView.passwordTextField.bindingString = password - } - -} diff --git a/iOS/SwiftUI Extensions/SafariView.swift b/iOS/SwiftUI Extensions/SafariView.swift deleted file mode 100644 index 6b5d78334..000000000 --- a/iOS/SwiftUI Extensions/SafariView.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// SafariView.swift -// NetNewsWire-iOS -// -// Created by Stuart Breckenridge on 16/6/19. -// Copyright © 2019 Ranchero Software. All rights reserved. -// - -import SwiftUI -import SafariServices - -struct SafariView : UIViewControllerRepresentable { - - let url: URL - - func makeUIViewController(context: UIViewControllerRepresentableContext) -> SFSafariViewController { - let safari = SFSafariViewController(url: url) - safari.delegate = context.coordinator - return safari - } - - func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext) { - // - } - - func makeCoordinator() -> Coordinator { - return Coordinator(self) - } - - class Coordinator : NSObject, SFSafariViewControllerDelegate { - var parent: SafariView - - init(_ safariView: SafariView) { - self.parent = safariView - } - - // MARK: SFSafariViewControllerDelegate - func safariViewControllerDidFinish(_ controller: SFSafariViewController) { - - } - - func safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo URL: URL) { - - } - - func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) { - - } - } -} - - diff --git a/iOS/SwiftUI Extensions/ShowHidePasswordView.swift b/iOS/SwiftUI Extensions/ShowHidePasswordView.swift deleted file mode 100644 index fe6b10d02..000000000 --- a/iOS/SwiftUI Extensions/ShowHidePasswordView.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// ShowHidePasswordView.swift -// NetNewsWire-iOS -// -// Created by Maurice Parker on 10/8/19. -// Copyright © 2019 Ranchero Software. All rights reserved. -// - -import UIKit -import SwiftUI - -class ShowHidePasswordView: UIView { - - @IBOutlet weak var passwordTextField: BindingTextField! - @IBOutlet weak var showHideButton: UIButton! - - @IBAction func toggleShowHideButton(_ sender: Any) { - if passwordTextField.isSecureTextEntry { - passwordTextField.isSecureTextEntry = false - showHideButton.setTitle(NSLocalizedString("Hide", comment: "Hide"), for: .normal) - } else { - passwordTextField.isSecureTextEntry = true - showHideButton.setTitle(NSLocalizedString("Show", comment: "Show"), for: .normal) - } - } - -} - -class BindingTextField: UITextField, UITextFieldDelegate { - - var bindingString: Binding? = nil - - override init(frame: CGRect) { - super.init(frame: frame) - delegate = self - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - delegate = self - } - - func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { - if let currentValue = textField.text as NSString? { - let proposedValue = currentValue.replacingCharacters(in: range, with: string) - bindingString?.wrappedValue = proposedValue - } - return true - } - -} diff --git a/iOS/SwiftUI Extensions/ShowHidePasswordView.xib b/iOS/SwiftUI Extensions/ShowHidePasswordView.xib deleted file mode 100644 index d68686200..000000000 --- a/iOS/SwiftUI Extensions/ShowHidePasswordView.xib +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/iOS/SwiftUI Extensions/VibrantButtonStyle.swift b/iOS/SwiftUI Extensions/VibrantButtonStyle.swift deleted file mode 100644 index 61ffd9517..000000000 --- a/iOS/SwiftUI Extensions/VibrantButtonStyle.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// VibrantButtonStyle.swift -// NetNewsWire-iOS -// -// Created by Maurice Parker on 9/16/19. -// Copyright © 2019 Ranchero Software. All rights reserved. -// - -import SwiftUI - -struct VibrantButtonStyle: ButtonStyle { - - let alignment: Alignment - - func makeBody(configuration: Configuration) -> some View { - GeometryReader { geometry in - configuration.label - .frame(width: geometry.size.width, height: geometry.size.height, alignment: self.alignment) - } - .foregroundColor(configuration.isPressed ? Color(AppAssets.tableViewCellHighlightedTextColor) : .primary) - .listRowBackground(configuration.isPressed ? Color(AppAssets.primaryAccentColor) : Color(.secondarySystemGroupedBackground)) - .background(configuration.isPressed ? Color(AppAssets.primaryAccentColor) : Color(.secondarySystemGroupedBackground)) - } - -} diff --git a/iOS/SwiftUI Extensions/VibrantSelectAction.swift b/iOS/SwiftUI Extensions/VibrantSelectAction.swift deleted file mode 100644 index 45471d5e1..000000000 --- a/iOS/SwiftUI Extensions/VibrantSelectAction.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// VibrantSelectAction.swift -// NetNewsWire-iOS -// -// Created by Maurice Parker on 9/15/19. -// Copyright © 2019 Ranchero Software. All rights reserved. -// - -import SwiftUI - -struct VibrantSelectAction: ViewModifier { - - let action: () -> Void - @State var isTapped = false - @GestureState var isLongPressed = false - - func body(content: Content) -> some View { - GeometryReader { geometry in - content - .frame(width: geometry.size.width, height: geometry.size.height, alignment: .leading) - .background(self.isLongPressed || self.isTapped ? Color(AppAssets.primaryAccentColor) : Color(.secondarySystemGroupedBackground)) - } - .foregroundColor(isLongPressed || isTapped ? Color(AppAssets.tableViewCellHighlightedTextColor) : .primary) - .listRowBackground(isLongPressed || isTapped ? Color(AppAssets.primaryAccentColor) : nil) - .gesture( - LongPressGesture().onEnded( { _ in self.action() }) - .updating($isLongPressed) { value, state, transcation in state = value } - .simultaneously(with: - TapGesture().onEnded( { - self.isTapped = true - self.action() - DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { - self.isTapped = false - } - } - )) - ) - } - -} From d54ba11c3155d9b0a6b75797733a2f65f8c17fec Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 13:49:05 -0500 Subject: [PATCH 03/50] Fixed about page text view layout --- iOS/Settings/Settings.storyboard | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 9b7b19ed1..c927b46ad 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -440,7 +440,7 @@ - + @@ -555,7 +555,7 @@ - + @@ -874,17 +874,17 @@ - + - + - + @@ -900,16 +900,16 @@ - + - + - + @@ -926,7 +926,7 @@ - + @@ -934,8 +934,8 @@ - - + + @@ -952,7 +952,7 @@ - + @@ -961,8 +961,8 @@ - - + + From 3116c632588f4c554a2f0080e9cb1bf76d200dc2 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 16:14:00 -0500 Subject: [PATCH 04/50] Move done button to the leading side. --- iOS/Settings/Settings.storyboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index c927b46ad..550a4a54f 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -270,7 +270,7 @@ - + From 872054e7d6fb7e58844976904b8ec18dd65f1790 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 16:18:18 -0500 Subject: [PATCH 05/50] Change to use vibrant cell selection --- iOS/Settings/Settings.storyboard | 4 ++-- iOS/Settings/SettingsTableViewCell.xib | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 550a4a54f..a571f849c 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -440,7 +440,7 @@ - + @@ -555,7 +555,7 @@ - + diff --git a/iOS/Settings/SettingsTableViewCell.xib b/iOS/Settings/SettingsTableViewCell.xib index aca59947e..18048b1de 100644 --- a/iOS/Settings/SettingsTableViewCell.xib +++ b/iOS/Settings/SettingsTableViewCell.xib @@ -1,21 +1,18 @@ - - - - + + - - + - + - + From c3a8c9a5abbb00ea782fc361a6d7c46ea63a4e3b Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 16:27:54 -0500 Subject: [PATCH 06/50] Change settings order --- iOS/Settings/Settings.storyboard | 230 +++++++++++----------- iOS/Settings/SettingsViewController.swift | 34 ++-- 2 files changed, 132 insertions(+), 132 deletions(-) diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index a571f849c..3cafee91b 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -37,116 +37,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -176,7 +70,7 @@ - + @@ -201,10 +95,10 @@ - + - + @@ -228,7 +122,7 @@ - + @@ -245,7 +139,7 @@ - + @@ -263,6 +157,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -440,7 +440,7 @@ - + @@ -555,7 +555,7 @@ - + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 84a177d27..ebd7d52d9 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -109,6 +109,23 @@ class SettingsViewController: UITableViewController { self.navigationController?.pushViewController(controller, animated: true) } case 1: + if indexPath.row == 1 { + let timeline = UIStoryboard.settings.instantiateController(ofType: TimelineNumberOfLinesViewController.self) + self.navigationController?.pushViewController(timeline, animated: true) + } + case 2: + 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") + } + case 3: switch indexPath.row { case 0: let timeline = UIStoryboard.settings.instantiateController(ofType: AboutViewController.self) @@ -126,23 +143,6 @@ class SettingsViewController: UITableViewController { 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 } From 48db3d6d85ddffc1bf753f3ce28ce34208684245 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 16:42:58 -0500 Subject: [PATCH 07/50] Add group by feed setting --- iOS/Settings/Settings.storyboard | 55 ++++++++++++++++++----- iOS/Settings/SettingsViewController.swift | 19 +++++++- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 3cafee91b..2c71e6cc9 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -69,9 +69,39 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -98,7 +128,7 @@ - + @@ -122,7 +152,7 @@ - + @@ -139,7 +169,7 @@ - + @@ -160,7 +190,7 @@ - + @@ -177,7 +207,7 @@ - + @@ -194,7 +224,7 @@ - + @@ -211,7 +241,7 @@ - + @@ -228,7 +258,7 @@ - + @@ -245,7 +275,7 @@ - + @@ -277,6 +307,7 @@ + @@ -440,7 +471,7 @@ - + @@ -555,7 +586,7 @@ - + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index ebd7d52d9..857251d37 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -15,6 +15,7 @@ class SettingsViewController: UITableViewController { @IBOutlet weak var refreshIntervalLabel: UILabel! @IBOutlet weak var timelineSortOrderSwitch: UISwitch! + @IBOutlet weak var groupByFeedSwitch: UISwitch! @IBOutlet weak var timelineNumberOfLinesLabel: UILabel! weak var presentingParentController: UIViewController? @@ -37,6 +38,12 @@ class SettingsViewController: UITableViewController { timelineSortOrderSwitch.isOn = false } + if AppDefaults.timelineGroupByFeed { + groupByFeedSwitch.isOn = true + } else { + groupByFeedSwitch.isOn = false + } + refreshIntervalLabel.text = AppDefaults.refreshInterval.description() let numberOfLinesText = NSLocalizedString(" lines", comment: "Lines") @@ -109,7 +116,7 @@ class SettingsViewController: UITableViewController { self.navigationController?.pushViewController(controller, animated: true) } case 1: - if indexPath.row == 1 { + if indexPath.row == 2 { let timeline = UIStoryboard.settings.instantiateController(ofType: TimelineNumberOfLinesViewController.self) self.navigationController?.pushViewController(timeline, animated: true) } @@ -123,7 +130,7 @@ class SettingsViewController: UITableViewController { case 2: exportOPML() default: - print("export") + break } case 3: switch indexPath.row { @@ -192,6 +199,14 @@ class SettingsViewController: UITableViewController { } } + @IBAction func switchGroupByFeed(_ sender: Any) { + if groupByFeedSwitch.isOn { + AppDefaults.timelineGroupByFeed = true + } else { + AppDefaults.timelineGroupByFeed = false + } + } + @objc func contentSizeCategoryDidChange() { tableView.reloadData() } From cbf37d4ee0f0dbd45517bcdd8289636daa51ecd0 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 17:19:22 -0500 Subject: [PATCH 08/50] Change number of lines setting to use a stepper --- NetNewsWire.xcodeproj/project.pbxproj | 4 - iOS/Settings/Settings.storyboard | 78 +++++++------------ iOS/Settings/SettingsViewController.swift | 24 ++++-- .../TimelineNumberOfLinesViewController.swift | 41 ---------- 4 files changed, 44 insertions(+), 103 deletions(-) delete mode 100644 iOS/Settings/TimelineNumberOfLinesViewController.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index a25414c57..ee549188e 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -111,7 +111,6 @@ 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 */; }; @@ -792,7 +791,6 @@ 51A16991235E10D600EB091F /* DetailAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailAccountViewController.swift; sourceTree = ""; }; 51A16992235E10D600EB091F /* AddAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddAccountViewController.swift; sourceTree = ""; }; 51A16993235E10D600EB091F /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; - 51A16994235E10D600EB091F /* TimelineNumberOfLinesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineNumberOfLinesViewController.swift; sourceTree = ""; }; 51A16995235E10D600EB091F /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = ""; }; 51AF460D232488C6001742EF /* Account-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account-Extensions.swift"; sourceTree = ""; }; @@ -1276,7 +1274,6 @@ 51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */, 51A1698E235E10D600EB091F /* SettingsTableViewCell.xib */, 51A16993235E10D600EB091F /* SettingsViewController.swift */, - 51A16994235E10D600EB091F /* TimelineNumberOfLinesViewController.swift */, ); path = Settings; sourceTree = ""; @@ -2972,7 +2969,6 @@ 51C45259226508D300C03939 /* AppDefaults.swift in Sources */, 511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */, 51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */, - 51A1699E235E10D700EB091F /* TimelineNumberOfLinesViewController.swift in Sources */, 51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */, 51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */, 51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */, diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 2c71e6cc9..8bc23dfb0 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -53,7 +53,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -83,44 +83,50 @@ - + - - + - + + - + - - + + - - + + + + + + + + @@ -308,8 +314,9 @@ + + - @@ -779,35 +786,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 857251d37..6e0f3ae05 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -16,7 +16,8 @@ class SettingsViewController: UITableViewController { @IBOutlet weak var refreshIntervalLabel: UILabel! @IBOutlet weak var timelineSortOrderSwitch: UISwitch! @IBOutlet weak var groupByFeedSwitch: UISwitch! - @IBOutlet weak var timelineNumberOfLinesLabel: UILabel! + @IBOutlet weak var numberOfTextLinesLabel: UILabel! + @IBOutlet weak var numberOfTextLinesSteppper: UIStepper! weak var presentingParentController: UIViewController? @@ -46,8 +47,9 @@ class SettingsViewController: UITableViewController { refreshIntervalLabel.text = AppDefaults.refreshInterval.description() - let numberOfLinesText = NSLocalizedString(" lines", comment: "Lines") - timelineNumberOfLinesLabel.text = "\(AppDefaults.timelineNumberOfLines)" + numberOfLinesText + let numberOfTextLines = AppDefaults.timelineNumberOfLines + numberOfTextLinesSteppper.value = Double(numberOfTextLines) + updateNumberOfTextLinesLabel(value: numberOfTextLines) let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 20.0, y: 0.0, width: 0.0, height: 0.0)) buildLabel.font = UIFont.systemFont(ofSize: 11.0) @@ -115,11 +117,6 @@ class SettingsViewController: UITableViewController { controller.account = sortedAccounts[indexPath.row] self.navigationController?.pushViewController(controller, animated: true) } - case 1: - if indexPath.row == 2 { - let timeline = UIStoryboard.settings.instantiateController(ofType: TimelineNumberOfLinesViewController.self) - self.navigationController?.pushViewController(timeline, animated: true) - } case 2: switch indexPath.row { case 0: @@ -207,6 +204,12 @@ class SettingsViewController: UITableViewController { } } + @IBAction func stepNumberOfTextLines(_ sender: UIStepper) { + let numberOfLines = Int(sender.value) + AppDefaults.timelineNumberOfLines = numberOfLines + updateNumberOfTextLinesLabel(value: numberOfLines) + } + @objc func contentSizeCategoryDidChange() { tableView.reloadData() } @@ -231,6 +234,11 @@ extension SettingsViewController: UIDocumentPickerDelegate { private extension SettingsViewController { + func updateNumberOfTextLinesLabel(value: Int) { + let localizedText = NSLocalizedString("Number of Text Lines: %d", comment: "Number of Text Lines") + numberOfTextLinesLabel.text = NSString.localizedStringWithFormat(localizedText as NSString, value) as String + } + func addFeed() { let appNewsURLString = "https://nnw.ranchero.com/feed.json" diff --git a/iOS/Settings/TimelineNumberOfLinesViewController.swift b/iOS/Settings/TimelineNumberOfLinesViewController.swift deleted file mode 100644 index 5c0453b2e..000000000 --- a/iOS/Settings/TimelineNumberOfLinesViewController.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// 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) - } - -} From 3f974c7c2b8faf1de91a42584e10d1f9adf7be4d Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 17:52:29 -0500 Subject: [PATCH 09/50] Change links to open in inline Safari browser. --- iOS/Settings/Settings.storyboard | 29 ++++++++++++++++++----- iOS/Settings/SettingsViewController.swift | 19 +++++++++++---- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 8bc23dfb0..90bf18be9 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -229,9 +229,26 @@ - + + + + + + + + + + + + @@ -247,7 +264,7 @@ - + @@ -264,7 +281,7 @@ - + @@ -281,7 +298,7 @@ - + @@ -478,7 +495,7 @@ - + @@ -593,7 +610,7 @@ - + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 6e0f3ae05..0a703d174 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -8,6 +8,7 @@ import UIKit import Account +import SafariServices class SettingsViewController: UITableViewController { @@ -135,17 +136,19 @@ class SettingsViewController: UITableViewController { 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: [:]) + openURL("https://ranchero.com/netnewswire/") case 2: - UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire")!, options: [:]) + openURL("https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown") case 3: - UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!, options: [:]) + openURL("https://github.com/brentsimmons/NetNewsWire") case 4: - UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!, options: [:]) + openURL("https://github.com/brentsimmons/NetNewsWire/issues") case 5: + openURL("https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes") + case 6: addFeed() default: - UIApplication.shared.open(URL(string: "https://ranchero.com/netnewswire/")!, options: [:]) + break } default: break @@ -286,4 +289,10 @@ private extension SettingsViewController { } + func openURL(_ urlString: String) { + let vc = SFSafariViewController(url: URL(string: urlString)!) + vc.modalPresentationStyle = .pageSheet + present(vc, animated: true) + } + } From 67251da7ac48355e2e315f882424b000cefc8fd5 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 18:02:44 -0500 Subject: [PATCH 10/50] Hide the Add NetNewsWire feed option if already subscribed --- iOS/SceneCoordinator.swift | 4 +++- iOS/Settings/Settings.storyboard | 4 ++-- iOS/Settings/SettingsViewController.swift | 16 ++++++++-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index e4ac6208e..9723f0d17 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -787,9 +787,11 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { func showSettings() { let settingsNavController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController + let settingsViewController = settingsNavController.topViewController as! SettingsViewController settingsNavController.modalPresentationStyle = .formSheet settingsNavController.preferredContentSize = SettingsViewController.preferredContentSizeForFormSheetDisplay - masterFeedViewController.present(settingsNavController, animated: true) + settingsViewController.presentingParentController = rootSplitViewController + rootSplitViewController.present(settingsNavController, animated: true) } func showFeedInspector() { diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 90bf18be9..a9c662f46 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -495,7 +495,7 @@ - + @@ -610,7 +610,7 @@ - + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 0a703d174..bcf83e988 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -12,6 +12,7 @@ import SafariServices class SettingsViewController: UITableViewController { + private let appNewsURLString = "https://nnw.ranchero.com/feed.json" static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 400.0) @IBOutlet weak var refreshIntervalLabel: UILabel! @@ -77,6 +78,12 @@ class SettingsViewController: UITableViewController { return defaultNumberOfRows - 1 } return defaultNumberOfRows + case 3: + let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section) + if AccountManager.shared.anyAccountHasFeedWithURL(appNewsURLString) { + return defaultNumberOfRows - 1 + } + return defaultNumberOfRows default: return super.tableView(tableView, numberOfRowsInSection: section) } @@ -243,24 +250,17 @@ 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.initialControllerType = .feed addViewController.initialFeed = appNewsURLString addViewController.initialFeedName = "NetNewsWire News" presentingParentController?.present(addNavViewController, animated: true) - } func importOPML() { From 19400a5a508244c6854c935fea7644a9a7feaa9c Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 21 Oct 2019 18:21:42 -0500 Subject: [PATCH 11/50] Fix coloring of About scene in dark mode --- iOS/Settings/AboutViewController.swift | 1 + iOS/Settings/Settings.storyboard | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/iOS/Settings/AboutViewController.swift b/iOS/Settings/AboutViewController.swift index 31da63e9e..a472efd63 100644 --- a/iOS/Settings/AboutViewController.swift +++ b/iOS/Settings/AboutViewController.swift @@ -49,6 +49,7 @@ private extension AboutViewController { 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.textColor = UIColor.label textView.adjustsFontForContentSizeCategory = true textView.font = .preferredFont(forTextStyle: .body) } diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index a9c662f46..5f53772e1 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -1,6 +1,6 @@ - + @@ -495,7 +495,7 @@ - + @@ -610,7 +610,7 @@ - + @@ -851,7 +851,7 @@ - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + - - + + - + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - @@ -790,18 +792,17 @@ - - - - - - - + + + + + + - - + + - + From be2823b1a1e309fbdecbca63daf22dfd3620f27e Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Tue, 22 Oct 2019 02:31:07 -0500 Subject: [PATCH 13/50] Improve UI consistency of Account Detail --- .../DetailAccountViewController.swift | 87 ++++++++----------- iOS/Settings/Settings.storyboard | 79 +++++++++++------ 2 files changed, 87 insertions(+), 79 deletions(-) diff --git a/iOS/Settings/DetailAccountViewController.swift b/iOS/Settings/DetailAccountViewController.swift index 9e516bda3..40dd698ee 100644 --- a/iOS/Settings/DetailAccountViewController.swift +++ b/iOS/Settings/DetailAccountViewController.swift @@ -32,6 +32,40 @@ class DetailAccountViewController: UITableViewController { account?.isActive = activeSwitch.isOn } + @IBAction func credentials(_ sender: Any) { + 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 + navController.modalPresentationStyle = .currentContext + present(navController, animated: true) + default: + break + } + } + + @IBAction func deleteAccount(_ sender: Any) { + 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 { @@ -68,64 +102,11 @@ extension DetailAccountViewController { } 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 - navController.modalPresentationStyle = .currentContext - 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 { diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 538fbcf4a..2bf1f6875 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -68,7 +68,7 @@ - + @@ -98,7 +98,7 @@ - + @@ -173,7 +173,7 @@ - + @@ -228,7 +228,7 @@ - + @@ -296,7 +296,7 @@ - + @@ -418,15 +418,23 @@ - - - + + + + @@ -441,16 +449,25 @@ - - - + + + + @@ -631,7 +648,7 @@ - + @@ -639,7 +656,10 @@ - - + + + + @@ -741,7 +763,7 @@ - + @@ -749,7 +771,10 @@ - - + + + + From d9a165151d2d891a80be086640ec16aac56b96d0 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Tue, 22 Oct 2019 02:35:47 -0500 Subject: [PATCH 14/50] Rename to VibrantTableViewCell --- NetNewsWire.xcodeproj/project.pbxproj | 12 +++-- .../Cell/MasterFeedTableViewCell.swift | 2 +- .../Cell/MasterTimelineTableViewCell.swift | 2 +- iOS/Settings/Settings.storyboard | 46 +++++++++---------- iOS/Settings/SettingsTableViewCell.xib | 2 +- ...wCell.swift => VibrantTableViewCell.swift} | 4 +- 6 files changed, 36 insertions(+), 32 deletions(-) rename iOS/UIKit Extensions/{NNWTableViewCell.swift => VibrantTableViewCell.swift} (90%) diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 78e6e82de..d8f86eba7 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -231,7 +231,7 @@ 51FE100A234673A00056195D /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; }; 55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */; }; 55E15BCC229D65A900D6602A /* AccountsReaderAPIWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */; }; - 5F323809231DF9F000706F6B /* NNWTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F323808231DF9F000706F6B /* NNWTableViewCell.swift */; }; + 5F323809231DF9F000706F6B /* VibrantTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */; }; 6581C73820CED60100F4AD34 /* SafariExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */; }; 6581C73A20CED60100F4AD34 /* SafariExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6581C73920CED60100F4AD34 /* SafariExtensionViewController.swift */; }; 6581C73D20CED60100F4AD34 /* SafariExtensionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */; }; @@ -1323,7 +1323,7 @@ 51FE10022345529D0056195D /* UserNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationManager.swift; sourceTree = ""; }; 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsReaderAPI.xib; sourceTree = ""; }; 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsReaderAPIWindowController.swift; sourceTree = ""; }; - 5F323808231DF9F000706F6B /* NNWTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNWTableViewCell.swift; sourceTree = ""; }; + 5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantTableViewCell.swift; sourceTree = ""; }; 6543108B2322D90900658221 /* common */ = {isa = PBXFileReference; lastKnownFileType = folder; path = common; sourceTree = ""; }; 6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 6581C73420CED60100F4AD34 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; @@ -1811,7 +1811,6 @@ children = ( 51F85BFA2275D85000C787DC /* Array-Extensions.swift */, 51F85BF42273625800C787DC /* Bundle-Extensions.swift */, - 5F323808231DF9F000706F6B /* NNWTableViewCell.swift */, 51EAED95231363EF00A9EEE3 /* NonIntrinsicButton.swift */, 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */, 5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */, @@ -1822,6 +1821,7 @@ 51FD40BD2341555600880194 /* UIImage-Extensions.swift */, 512E092B2268B25500BDCFDD /* UISplitViewController-Extensions.swift */, 51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */, + 5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */, ); path = "UIKit Extensions"; sourceTree = ""; @@ -2904,6 +2904,10 @@ DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; }; + 65ED3FA2235DEF6C0081F399 = { + DevelopmentTeam = SHJK2V3AJG; + ProvisioningStyle = Automatic; + }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; DevelopmentTeam = SHJK2V3AJG; @@ -3878,7 +3882,7 @@ 51C452A622650A3500C03939 /* Node-Extensions.swift in Sources */, 5183CCDF226F1FCC0010922C /* UINavigationController+Progress.swift in Sources */, 51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */, - 5F323809231DF9F000706F6B /* NNWTableViewCell.swift in Sources */, + 5F323809231DF9F000706F6B /* VibrantTableViewCell.swift in Sources */, 512E09352268B25900BDCFDD /* UISplitViewController-Extensions.swift in Sources */, 51FE10042345529D0056195D /* UserNotificationManager.swift in Sources */, 51C452A022650A1900C03939 /* FeedIconDownloader.swift in Sources */, diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift index 5f684ce8f..a99e00cfa 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift @@ -15,7 +15,7 @@ protocol MasterFeedTableViewCellDelegate: class { func disclosureSelected(_ sender: MasterFeedTableViewCell, expanding: Bool) } -class MasterFeedTableViewCell : NNWTableViewCell { +class MasterFeedTableViewCell : VibrantTableViewCell { weak var delegate: MasterFeedTableViewCellDelegate? diff --git a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift index 7e55f3dfd..d3514288d 100644 --- a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift +++ b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift @@ -9,7 +9,7 @@ import UIKit import RSCore -class MasterTimelineTableViewCell: NNWTableViewCell { +class MasterTimelineTableViewCell: VibrantTableViewCell { private let titleView = MasterTimelineTableViewCell.multiLineUILabel() private let summaryView = MasterTimelineTableViewCell.multiLineUILabel() diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 2bf1f6875..89e5f0a42 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -17,7 +17,7 @@ - + @@ -38,7 +38,7 @@ - + @@ -68,7 +68,7 @@ - + @@ -98,7 +98,7 @@ - + @@ -132,7 +132,7 @@ - + @@ -156,7 +156,7 @@ - + @@ -173,7 +173,7 @@ - + @@ -194,7 +194,7 @@ - + @@ -211,7 +211,7 @@ - + @@ -228,7 +228,7 @@ - + @@ -245,7 +245,7 @@ - + @@ -262,7 +262,7 @@ - + @@ -279,7 +279,7 @@ - + @@ -296,7 +296,7 @@ - + @@ -351,7 +351,7 @@ - + @@ -382,7 +382,7 @@ - + @@ -411,7 +411,7 @@ - + @@ -442,7 +442,7 @@ - + @@ -499,7 +499,7 @@ - + @@ -532,7 +532,7 @@ - + @@ -648,7 +648,7 @@ - + @@ -763,7 +763,7 @@ - + @@ -840,7 +840,7 @@ - + diff --git a/iOS/Settings/SettingsTableViewCell.xib b/iOS/Settings/SettingsTableViewCell.xib index 18048b1de..71f516107 100644 --- a/iOS/Settings/SettingsTableViewCell.xib +++ b/iOS/Settings/SettingsTableViewCell.xib @@ -8,7 +8,7 @@ - + diff --git a/iOS/UIKit Extensions/NNWTableViewCell.swift b/iOS/UIKit Extensions/VibrantTableViewCell.swift similarity index 90% rename from iOS/UIKit Extensions/NNWTableViewCell.swift rename to iOS/UIKit Extensions/VibrantTableViewCell.swift index 1cea8c90c..258acdd95 100644 --- a/iOS/UIKit Extensions/NNWTableViewCell.swift +++ b/iOS/UIKit Extensions/VibrantTableViewCell.swift @@ -1,5 +1,5 @@ // -// NNWTableViewCell.swift +// VibrantTableViewCell.swift // NetNewsWire-iOS // // Created by Jim Correia on 9/2/19. @@ -8,7 +8,7 @@ import UIKit -class NNWTableViewCell: UITableViewCell { +class VibrantTableViewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) commonInit() From 528284999fc16e7c3949120148ce97ab02590c2d Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Tue, 22 Oct 2019 03:31:25 -0500 Subject: [PATCH 15/50] Correct vibrancy configuration for Settings --- NetNewsWire.xcodeproj/project.pbxproj | 8 +++++ iOS/AppAssets.swift | 8 ++--- .../Cell/MasterFeedTableViewCell.swift | 6 ++-- .../Cell/MasterTimelineTableViewCell.swift | 2 +- .../Cell/MasterUnreadIndicatorView.swift | 2 +- .../Contents.json | 0 iOS/Settings/Settings.storyboard | 26 ++++++++++----- iOS/UIKit Extensions/VibrantButton.swift | 32 +++++++++++++++++++ iOS/UIKit Extensions/VibrantLabel.swift | 27 ++++++++++++++++ .../VibrantTableViewCell.swift | 5 ++- 10 files changed, 98 insertions(+), 18 deletions(-) rename iOS/Resources/Assets.xcassets/{tableViewCellHighlightedTextColor.colorset => vibrantTextColor.colorset}/Contents.json (100%) create mode 100644 iOS/UIKit Extensions/VibrantButton.swift create mode 100644 iOS/UIKit Extensions/VibrantLabel.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index d8f86eba7..dcbae23cd 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -96,6 +96,7 @@ 5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; }; 518651B223555EB20078E021 /* NNW3Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651AB23555EB20078E021 /* NNW3Document.swift */; }; 518651DA235621840078E021 /* ImageTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651D9235621840078E021 /* ImageTransition.swift */; }; + 5186A635235EF3A800C97195 /* VibrantLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186A634235EF3A800C97195 /* VibrantLabel.swift */; }; 518B2EE82351B45600400001 /* NetNewsWire_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D61952029031D009BC708 /* NetNewsWire_iOSTests.swift */; }; 51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CC1230F5963006127BE /* ThemedNavigationController.swift */; }; 51934CCE2310792F006127BE /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; }; @@ -229,6 +230,7 @@ 51FE10042345529D0056195D /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; }; 51FE10092346739D0056195D /* ActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D87EE02311D34700E63F03 /* ActivityType.swift */; }; 51FE100A234673A00056195D /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; }; + 51FFF0C4235EE8E5002762AA /* VibrantButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FFF0C3235EE8E5002762AA /* VibrantButton.swift */; }; 55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */; }; 55E15BCC229D65A900D6602A /* AccountsReaderAPIWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */; }; 5F323809231DF9F000706F6B /* VibrantTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */; }; @@ -1246,6 +1248,7 @@ 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRefreshTimer.swift; sourceTree = ""; }; 518651AB23555EB20078E021 /* NNW3Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Document.swift; sourceTree = ""; }; 518651D9235621840078E021 /* ImageTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageTransition.swift; sourceTree = ""; }; + 5186A634235EF3A800C97195 /* VibrantLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantLabel.swift; sourceTree = ""; }; 518B2ED22351B3DD00400001 /* NetNewsWire-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NetNewsWire-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 518B2EE92351B4C200400001 /* NetNewsWire_iOSTests_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSTests_target.xcconfig; sourceTree = ""; }; 51934CC1230F5963006127BE /* ThemedNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedNavigationController.swift; sourceTree = ""; }; @@ -1321,6 +1324,7 @@ 51FD40BD2341555600880194 /* UIImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage-Extensions.swift"; sourceTree = ""; }; 51FD413A2342BD0500880194 /* MasterTimelineUnreadCountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineUnreadCountView.swift; sourceTree = ""; }; 51FE10022345529D0056195D /* UserNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationManager.swift; sourceTree = ""; }; + 51FFF0C3235EE8E5002762AA /* VibrantButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantButton.swift; sourceTree = ""; }; 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsReaderAPI.xib; sourceTree = ""; }; 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsReaderAPIWindowController.swift; sourceTree = ""; }; 5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantTableViewCell.swift; sourceTree = ""; }; @@ -1821,6 +1825,8 @@ 51FD40BD2341555600880194 /* UIImage-Extensions.swift */, 512E092B2268B25500BDCFDD /* UISplitViewController-Extensions.swift */, 51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */, + 51FFF0C3235EE8E5002762AA /* VibrantButton.swift */, + 5186A634235EF3A800C97195 /* VibrantLabel.swift */, 5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */, ); path = "UIKit Extensions"; @@ -3843,6 +3849,7 @@ 5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */, 51EAED96231363EF00A9EEE3 /* NonIntrinsicButton.swift in Sources */, 51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */, + 5186A635235EF3A800C97195 /* VibrantLabel.swift in Sources */, 51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */, 51B62E68233186730085F949 /* MasterTimelineAvatarView.swift in Sources */, 51C45296226509D300C03939 /* OPMLExporter.swift in Sources */, @@ -3944,6 +3951,7 @@ 51C4528D2265095F00C03939 /* AddFolderViewController.swift in Sources */, 51C452782265091600C03939 /* MasterTimelineCellData.swift in Sources */, 5148F4552336DB7000F8CD8B /* MasterTimelineTitleView.swift in Sources */, + 51FFF0C4235EE8E5002762AA /* VibrantButton.swift in Sources */, 513228FC233037630033D4ED /* Reachability.swift in Sources */, 51C45259226508D300C03939 /* AppDefaults.swift in Sources */, 511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */, diff --git a/iOS/AppAssets.swift b/iOS/AppAssets.swift index 2ca4fda10..55f66027a 100644 --- a/iOS/AppAssets.swift +++ b/iOS/AppAssets.swift @@ -140,10 +140,6 @@ struct AppAssets { return UIImage(systemName: "star.fill")! }() - static var tableViewCellHighlightedTextColor: UIColor = { - return UIColor(named: "tableViewCellHighlightedTextColor")! - }() - static var timelineStarImage: UIImage = { let image = UIImage(systemName: "star.fill")! return image.withTintColor(AppAssets.starColor, renderingMode: .alwaysOriginal) @@ -161,4 +157,8 @@ struct AppAssets { return UIImage(systemName: "largecircle.fill.circle")! }() + static var vibrantTextColor: UIColor = { + return UIColor(named: "vibrantTextColor")! + }() + } diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift index a99e00cfa..3662b829a 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift @@ -128,13 +128,13 @@ class MasterFeedTableViewCell : VibrantTableViewCell { override func applyThemeProperties() { super.applyThemeProperties() - titleView.highlightedTextColor = AppAssets.tableViewCellHighlightedTextColor + titleView.highlightedTextColor = AppAssets.vibrantTextColor } override func setHighlighted(_ highlighted: Bool, animated: Bool) { super.setHighlighted(highlighted, animated: animated) - let tintColor = isHighlighted || isSelected ? AppAssets.tableViewCellHighlightedTextColor : AppAssets.secondaryAccentColor + let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor disclosureButton?.tintColor = tintColor faviconImageView.tintColor = tintColor } @@ -142,7 +142,7 @@ class MasterFeedTableViewCell : VibrantTableViewCell { override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) - let tintColor = isHighlighted || isSelected ? AppAssets.tableViewCellHighlightedTextColor : AppAssets.secondaryAccentColor + let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor disclosureButton?.tintColor = tintColor faviconImageView.tintColor = tintColor } diff --git a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift index d3514288d..24f7b725b 100644 --- a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift +++ b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift @@ -37,7 +37,7 @@ class MasterTimelineTableViewCell: VibrantTableViewCell { override func applyThemeProperties() { super.applyThemeProperties() - let highlightedTextColor = AppAssets.tableViewCellHighlightedTextColor + let highlightedTextColor = AppAssets.vibrantTextColor titleView.highlightedTextColor = highlightedTextColor summaryView.highlightedTextColor = highlightedTextColor diff --git a/iOS/MasterTimeline/Cell/MasterUnreadIndicatorView.swift b/iOS/MasterTimeline/Cell/MasterUnreadIndicatorView.swift index 3c64e084e..88cb190f0 100644 --- a/iOS/MasterTimeline/Cell/MasterUnreadIndicatorView.swift +++ b/iOS/MasterTimeline/Cell/MasterUnreadIndicatorView.swift @@ -32,7 +32,7 @@ class MasterUnreadIndicatorView: UIView { }() override func draw(_ dirtyRect: CGRect) { - let color = isSelected ? AppAssets.tableViewCellHighlightedTextColor : AppAssets.secondaryAccentColor + let color = isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor color.setFill() MasterUnreadIndicatorView.bezierPath.fill() } diff --git a/iOS/Resources/Assets.xcassets/tableViewCellHighlightedTextColor.colorset/Contents.json b/iOS/Resources/Assets.xcassets/vibrantTextColor.colorset/Contents.json similarity index 100% rename from iOS/Resources/Assets.xcassets/tableViewCellHighlightedTextColor.colorset/Contents.json rename to iOS/Resources/Assets.xcassets/vibrantTextColor.colorset/Contents.json diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 89e5f0a42..173252dae 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -3,6 +3,7 @@ + @@ -146,7 +147,7 @@ - From 8e37881ed875f4461d88f44bd2f6912b62ab5862 Mon Sep 17 00:00:00 2001 From: Daniel Jalkut Date: Tue, 22 Oct 2019 19:44:06 -0400 Subject: [PATCH 34/50] Protect against unrecognized protocol errors when building for MAC_APP_STORE or TEST. --- Mac/AppDelegate.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index 9e4c8207e..52eedd75e 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -13,14 +13,21 @@ import RSTree import RSWeb import Account import RSCore -#if !MAC_APP_STORE && !TEST + +// If we're not going to import Sparkle, provide dummy protocols to make it easy +// for AppDelegate to comply +#if MAC_APP_STORE || TEST +protocol SPUStandardUserDriverDelegate {} +protocol SPUUpdaterDelegate {} +#else import Sparkle #endif var appDelegate: AppDelegate! @NSApplicationMain -class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UNUserNotificationCenterDelegate, UnreadCountProvider, SPUStandardUserDriverDelegate, SPUUpdaterDelegate { +class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UNUserNotificationCenterDelegate, UnreadCountProvider, SPUStandardUserDriverDelegate, SPUUpdaterDelegate +{ var userNotificationManager: UserNotificationManager! var faviconDownloader: FaviconDownloader! From 7b51a272b0f21e4089935ae4168ac078d475a873 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Tue, 22 Oct 2019 19:20:35 -0500 Subject: [PATCH 35/50] Only clear Feed/Timeline selection after view begins to appear. Issue #1156 --- iOS/Base.lproj/Main.storyboard | 2 +- iOS/MasterFeed/MasterFeedViewController.swift | 5 ++- .../MasterTimelineViewController.swift | 5 ++- iOS/SceneCoordinator.swift | 1 + iOS/Settings/Settings.storyboard | 2 +- iOS/Settings/SettingsViewController.swift | 31 +++++++++++++++---- submodules/RSCore | 2 +- 7 files changed, 33 insertions(+), 15 deletions(-) diff --git a/iOS/Base.lproj/Main.storyboard b/iOS/Base.lproj/Main.storyboard index df20db9ac..994202c66 100644 --- a/iOS/Base.lproj/Main.storyboard +++ b/iOS/Base.lproj/Main.storyboard @@ -121,7 +121,7 @@ - + diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index 7022f7a7f..65e6092a2 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -68,7 +68,6 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { override func viewWillAppear(_ animated: Bool) { navigationController?.title = NSLocalizedString("Feeds", comment: "Feeds") - clearsSelectionOnViewWillAppear = coordinator.isRootSplitCollapsed applyChanges(animate: false) super.viewWillAppear(animated) } @@ -451,7 +450,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { func restoreSelectionIfNecessary(adjustScroll: Bool) { if let indexPath = coordinator.masterFeedIndexPathForCurrentTimeline() { if adjustScroll { - tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false, deselect: coordinator.isRootSplitCollapsed) + tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false) } else { tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none) } @@ -462,7 +461,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { if dataSource.snapshot().numberOfItems > 0 { if let indexPath = coordinator.currentFeedIndexPath { if tableView.indexPathForSelectedRow != indexPath { - tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true, deselect: coordinator.isRootSplitCollapsed) + tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true) } } else { tableView.selectRow(at: nil, animated: true, scrollPosition: .none) diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index 8c74296ab..2dd9a9a2a 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -76,7 +76,6 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner } override func viewWillAppear(_ animated: Bool) { - clearsSelectionOnViewWillAppear = coordinator.isRootSplitCollapsed applyChanges(animate: false) super.viewWillAppear(animated) } @@ -132,7 +131,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner func restoreSelectionIfNecessary(adjustScroll: Bool) { if let article = coordinator.currentArticle, let indexPath = dataSource.indexPath(for: article) { if adjustScroll { - tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false, deselect: coordinator.isRootSplitCollapsed) + tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false) } else { tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none) } @@ -150,7 +149,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner func updateArticleSelection(animated: Bool) { if let article = coordinator.currentArticle, let indexPath = dataSource.indexPath(for: article) { if tableView.indexPathForSelectedRow != indexPath { - tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true, deselect: coordinator.isRootSplitCollapsed) + tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true) } } else { tableView.selectRow(at: nil, animated: animated, scrollPosition: .none) diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index ab57d6523..e97a63e19 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -950,6 +950,7 @@ extension SceneCoordinator: UINavigationControllerDelegate { if viewController === masterTimelineViewController && !isThreePanelMode && rootSplitViewController.isCollapsed && !isArticleViewControllerPending { stopArticleExtractor() currentArticle = nil + masterTimelineViewController?.updateArticleSelection(animated: animated) activityManager.invalidateReading() return } diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 5079063cf..617b76467 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -9,7 +9,7 @@ - + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 0c268ac1d..b88d37246 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -29,7 +29,10 @@ class SettingsViewController: UITableViewController { // 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) - + + NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidAddAccount, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidDeleteAccount, object: nil) + tableView.register(UINib(nibName: "SettingsTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsTableViewCell") } @@ -63,8 +66,11 @@ class SettingsViewController: UITableViewController { buildLabel.translatesAutoresizingMaskIntoConstraints = false tableView.tableFooterView = buildLabel - tableView.reloadData() - + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + self.tableView.selectRow(at: nil, animated: true, scrollPosition: .none) } // MARK: UITableView @@ -112,6 +118,7 @@ class SettingsViewController: UITableViewController { switch indexPath.section { case 0: UIApplication.shared.open(URL(string: "\(UIApplication.openSettingsURLString)")!) + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) case 1: let sortedAccounts = AccountManager.shared.sortedAccounts if indexPath.row == sortedAccounts.count { @@ -128,11 +135,13 @@ class SettingsViewController: UITableViewController { let timeline = UIStoryboard.settings.instantiateController(ofType: RefreshIntervalViewController.self) self.navigationController?.pushViewController(timeline, animated: true) case 1: + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) if let sourceView = tableView.cellForRow(at: indexPath) { let sourceRect = tableView.rectForRow(at: indexPath) importOPML(sourceView: sourceView, sourceRect: sourceRect) } case 2: + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) if let sourceView = tableView.cellForRow(at: indexPath) { let sourceRect = tableView.rectForRow(at: indexPath) exportOPML(sourceView: sourceView, sourceRect: sourceRect) @@ -147,24 +156,28 @@ class SettingsViewController: UITableViewController { self.navigationController?.pushViewController(timeline, animated: true) case 1: openURL("https://ranchero.com/netnewswire/") + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) case 2: openURL("https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown") + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) case 3: openURL("https://github.com/brentsimmons/NetNewsWire") + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) case 4: openURL("https://github.com/brentsimmons/NetNewsWire/issues") + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) case 5: openURL("https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes") + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) case 6: addFeed() + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) default: break } default: - break + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) } - - tableView.selectRow(at: nil, animated: true, scrollPosition: .none) } override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { @@ -223,10 +236,16 @@ class SettingsViewController: UITableViewController { updateNumberOfTextLinesLabel(value: numberOfLines) } + // MARK: Notifications + @objc func contentSizeCategoryDidChange() { tableView.reloadData() } + @objc func accountsDidChange() { + tableView.reloadData() + } + } // MARK: OPML Document Picker diff --git a/submodules/RSCore b/submodules/RSCore index 29dc34284..fa16a5b1a 160000 --- a/submodules/RSCore +++ b/submodules/RSCore @@ -1 +1 @@ -Subproject commit 29dc34284b64af4a399d1cf3927c3469851ec0ad +Subproject commit fa16a5b1a0bc45bb6aee6145e1095446a84f386b From 17af3f28bf1205820882a7cba880856df68b99a3 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 08:53:09 -0500 Subject: [PATCH 36/50] Hide unread account information unless the Account is collapsed. Issue #1159 --- .../Cell/MasterFeedTableViewSectionHeader.swift | 11 ++++++++++- iOS/MasterFeed/MasterFeedViewController.swift | 1 - 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift index 901184ec6..07d6ab35c 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift @@ -29,7 +29,7 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView { set { if unreadCountView.unreadCount != newValue { unreadCountView.unreadCount = newValue - unreadCountView.isHidden = (newValue < 1) + updateUnreadCountView() setNeedsLayout() } } @@ -51,6 +51,7 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView { var disclosureExpanded = false { didSet { updateDisclosureImage() + updateUnreadCountView() } } @@ -141,6 +142,14 @@ private extension MasterFeedTableViewSectionHeader { } } } + + func updateUnreadCountView() { + if !disclosureExpanded && unreadCount > 0 { + unreadCountView.isHidden = false + } else { + self.unreadCountView.isHidden = true + } + } func addSubviewAtInit(_ view: UIView) { addSubview(view) diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index 65e6092a2..039e5b036 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -11,7 +11,6 @@ import Account import Articles import RSCore import RSTree -import SwiftUI class MasterFeedViewController: UITableViewController, UndoableCommandRunner { From 7448523c66850a99fc50bfb9d7f4dd2d0e53d09b Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 09:03:34 -0500 Subject: [PATCH 37/50] Correct issue where toolbar tint color could get lost in darkmode and transitioning to the background --- iOS/UIKit Extensions/ThemedNavigationController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/UIKit Extensions/ThemedNavigationController.swift b/iOS/UIKit Extensions/ThemedNavigationController.swift index 979d83b78..058e6afa5 100644 --- a/iOS/UIKit Extensions/ThemedNavigationController.swift +++ b/iOS/UIKit Extensions/ThemedNavigationController.swift @@ -34,10 +34,10 @@ class ThemedNavigationController: UINavigationController { if traitCollection.userInterfaceStyle == .dark { navigationBar.standardAppearance = UINavigationBarAppearance() - navigationBar.tintColor = view.tintColor + navigationBar.tintColor = AppAssets.primaryAccentColor toolbar.standardAppearance = UIToolbarAppearance() toolbar.compactAppearance = UIToolbarAppearance() - toolbar.tintColor = view.tintColor + toolbar.tintColor = AppAssets.primaryAccentColor } else { let navigationAppearance = UINavigationBarAppearance() navigationAppearance.backgroundColor = AppAssets.barBackgroundColor From be52fde08b6d8f0d65d4ed5ac5831d24bbdb3a5c Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 09:15:13 -0500 Subject: [PATCH 38/50] Change Timeline Feed icon to 32x32 --- iOS/MasterTimeline/Cell/MasterTimelineDefaultCellLayout.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/MasterTimeline/Cell/MasterTimelineDefaultCellLayout.swift b/iOS/MasterTimeline/Cell/MasterTimelineDefaultCellLayout.swift index b1070a2eb..f7ca315c5 100644 --- a/iOS/MasterTimeline/Cell/MasterTimelineDefaultCellLayout.swift +++ b/iOS/MasterTimeline/Cell/MasterTimelineDefaultCellLayout.swift @@ -21,7 +21,7 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout { static let starDimension = CGFloat(integerLiteral: 16) static let starSize = CGSize(width: MasterTimelineDefaultCellLayout.starDimension, height: MasterTimelineDefaultCellLayout.starDimension) - static let avatarSize = CGSize(width: 48.0, height: 48.0) + static let avatarSize = CGSize(width: 32.0, height: 32.0) static let avatarMarginRight = CGFloat(integerLiteral: 8) static let avatarCornerRadius = CGFloat(integerLiteral: 4) From 430a4a37bfaa9a5366b53969d82177fc9f89fb93 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 10:35:53 -0500 Subject: [PATCH 39/50] Add icon next to accounts in the Settings listing. Issue #1160 --- NetNewsWire.xcodeproj/project.pbxproj | 20 ++++++-- iOS/AppAssets.swift | 26 ++++++++++ .../SettingsAccountTableViewCell.swift | 30 ++++++++++++ iOS/Settings/SettingsAccountTableViewCell.xib | 47 +++++++++++++++++++ iOS/Settings/SettingsViewController.swift | 16 ++++--- 5 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 iOS/Settings/SettingsAccountTableViewCell.swift create mode 100644 iOS/Settings/SettingsAccountTableViewCell.xib diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index dcbae23cd..1a326e674 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -81,6 +81,9 @@ 515D4FC123257A3200EE1167 /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; }; 515D4FCA23257CB500EE1167 /* Node-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97971ED9EFAA007D329B /* Node-Extensions.swift */; }; 515D4FCC2325815A00EE1167 /* SafariExt.js in Resources */ = {isa = PBXBuildFile; fileRef = 515D4FCB2325815A00EE1167 /* SafariExt.js */; }; + 516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */; }; + 516A09392360A2AE00EAE89B /* SettingsAccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */; }; + 516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */; }; 51707439232AA97100A461A3 /* ShareFolderPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51707438232AA97100A461A3 /* ShareFolderPickerController.swift */; }; 5170743A232AABFC00A461A3 /* FlattenedAccountFolderPickerData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452812265093600C03939 /* FlattenedAccountFolderPickerData.swift */; }; 517630042336215100E15FFF /* main.js in Resources */ = {isa = PBXBuildFile; fileRef = 517630032336215100E15FFF /* main.js */; }; @@ -106,7 +109,6 @@ 519D740623243CC0008BB345 /* RefreshInterval-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */; }; 519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.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 */; }; @@ -1237,6 +1239,9 @@ 515D4FCB2325815A00EE1167 /* SafariExt.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = SafariExt.js; sourceTree = ""; }; 515D4FCD2325909200EE1167 /* NetNewsWire_iOS_ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NetNewsWire_iOS_ShareExtension.entitlements; sourceTree = ""; }; 515D4FCE2325B3D000EE1167 /* NetNewsWire_iOSshareextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSshareextension_target.xcconfig; sourceTree = ""; }; + 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsAccountTableViewCell.xib; sourceTree = ""; }; + 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAccountTableViewCell.swift; sourceTree = ""; }; + 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = ""; }; 51707438232AA97100A461A3 /* ShareFolderPickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareFolderPickerController.swift; sourceTree = ""; }; 517630032336215100E15FFF /* main.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = main.js; sourceTree = ""; }; 517630222336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleViewControllerWebViewProvider.swift; sourceTree = ""; }; @@ -1258,7 +1263,6 @@ 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RefreshInterval-Extensions.swift"; sourceTree = ""; }; 519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshIntervalViewController.swift; sourceTree = ""; }; - 51A1698E235E10D600EB091F /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = ""; }; 51A1698F235E10D600EB091F /* AddLocalAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocalAccountViewController.swift; sourceTree = ""; }; 51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; 51A16991235E10D600EB091F /* DetailAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailAccountViewController.swift; sourceTree = ""; }; @@ -1775,7 +1779,9 @@ 51A16991235E10D600EB091F /* DetailAccountViewController.swift */, 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */, 51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */, - 51A1698E235E10D600EB091F /* SettingsTableViewCell.xib */, + 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */, + 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */, + 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */, 51A16993235E10D600EB091F /* SettingsViewController.swift */, ); path = Settings; @@ -2914,6 +2920,10 @@ DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; }; + 65ED4090235DEF770081F399 = { + DevelopmentTeam = SHJK2V3AJG; + ProvisioningStyle = Automatic; + }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; DevelopmentTeam = SHJK2V3AJG; @@ -3361,13 +3371,14 @@ 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 */, + 516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */, 51F85BF32272531500C787DC /* Dedication.rtf in Resources */, 84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */, 51F85BEB22724CB600C787DC /* About.rtf in Resources */, 51F85BED227251DF00C787DC /* Acknowledgments.rtf in Resources */, + 516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */, 511D43D1231FA62800FB1562 /* SidebarKeyboardShortcuts.plist in Resources */, 51C452AB22650DC600C03939 /* template.html in Resources */, 51F85BF12272524100C787DC /* Credits.rtf in Resources */, @@ -3866,6 +3877,7 @@ 51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */, 51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */, 51C452A522650A2D00C03939 /* SmallIconProvider.swift in Sources */, + 516A09392360A2AE00EAE89B /* SettingsAccountTableViewCell.swift in Sources */, 51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */, 51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */, 51A16999235E10D700EB091F /* AddLocalAccountViewController.swift in Sources */, diff --git a/iOS/AppAssets.swift b/iOS/AppAssets.swift index 55f66027a..dcb1c9227 100644 --- a/iOS/AppAssets.swift +++ b/iOS/AppAssets.swift @@ -7,9 +7,22 @@ // import UIKit import RSCore +import Account struct AppAssets { + static var accountLocalImage: UIImage = { + return UIImage(named: "accountLocal")! + }() + + static var accountFeedbinImage: UIImage = { + return UIImage(named: "accountFeedbin")! + }() + + static var accountFreshRSSImage: UIImage = { + return UIImage(named: "accountFreshRSS")! + }() + static var articleExtractorError: UIImage = { return UIImage(named: "articleExtractorError")! }() @@ -161,4 +174,17 @@ struct AppAssets { return UIColor(named: "vibrantTextColor")! }() + static func image(for accountType: AccountType) -> UIImage? { + switch accountType { + case .onMyMac: + return AppAssets.accountLocalImage + case .feedbin: + return AppAssets.accountFeedbinImage + case .freshRSS: + return AppAssets.accountFreshRSSImage + default: + return nil + } + } + } diff --git a/iOS/Settings/SettingsAccountTableViewCell.swift b/iOS/Settings/SettingsAccountTableViewCell.swift new file mode 100644 index 000000000..cc45c59b0 --- /dev/null +++ b/iOS/Settings/SettingsAccountTableViewCell.swift @@ -0,0 +1,30 @@ +// +// SettingsAccountTableViewCell.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 10/23/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import UIKit + +class SettingsAccountTableViewCell: VibrantTableViewCell { + + @IBOutlet weak var accountImage: UIImageView! + @IBOutlet weak var accountNameLabel: UILabel! + + override func setHighlighted(_ highlighted: Bool, animated: Bool) { + super.setHighlighted(highlighted, animated: animated) + let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : UIColor.label + accountImage?.tintColor = tintColor + accountNameLabel?.highlightedTextColor = tintColor + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : UIColor.label + accountImage?.tintColor = tintColor + accountNameLabel?.highlightedTextColor = tintColor + } + +} diff --git a/iOS/Settings/SettingsAccountTableViewCell.xib b/iOS/Settings/SettingsAccountTableViewCell.xib new file mode 100644 index 000000000..1b4d94da3 --- /dev/null +++ b/iOS/Settings/SettingsAccountTableViewCell.xib @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index b88d37246..56fe7f11f 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -33,8 +33,9 @@ class SettingsViewController: UITableViewController { NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidAddAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidDeleteAccount, object: nil) + tableView.register(UINib(nibName: "SettingsAccountTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsAccountTableViewCell") tableView.register(UINib(nibName: "SettingsTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsTableViewCell") - + } override func viewWillAppear(_ animated: Bool) { @@ -94,15 +95,18 @@ class SettingsViewController: UITableViewController { let cell: UITableViewCell switch indexPath.section { case 1: - - cell = tableView.dequeueReusableCell(withIdentifier: "SettingsTableViewCell", for: indexPath) - cell.textLabel?.adjustsFontForContentSizeCategory = true - + let sortedAccounts = AccountManager.shared.sortedAccounts if indexPath.row == sortedAccounts.count { + cell = tableView.dequeueReusableCell(withIdentifier: "SettingsTableViewCell", for: indexPath) + cell.textLabel?.adjustsFontForContentSizeCategory = true cell.textLabel?.text = NSLocalizedString("Add Account", comment: "Accounts") } else { - cell.textLabel?.text = sortedAccounts[indexPath.row].nameForDisplay + let acctCell = tableView.dequeueReusableCell(withIdentifier: "SettingsAccountTableViewCell", for: indexPath) as! SettingsAccountTableViewCell + let account = sortedAccounts[indexPath.row] + acctCell.accountImage?.image = AppAssets.image(for: account.type) + acctCell.accountNameLabel?.text = account.nameForDisplay + cell = acctCell } default: From 2e489d4093c45bcf394a9d1554120fc29df82ff3 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 11:08:34 -0500 Subject: [PATCH 40/50] Rollback timeline unread and star animations --- .../Cell/MasterTimelineTableViewCell.swift | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift index 24f7b725b..0a240370e 100644 --- a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift +++ b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift @@ -187,30 +187,12 @@ private extension MasterTimelineTableViewCell { } func updateUnreadIndicator() { - let hide = cellData.read || cellData.starred - self.unreadIndicatorView.isHidden = hide - self.unreadIndicatorView.frame.size = !hide ? CGSize.zero : MasterTimelineDefaultCellLayout.unreadCircleSize - UIView.animate( - withDuration: 0.5, - delay: 0.0, - usingSpringWithDamping: 0.5, - initialSpringVelocity: 0.2, - animations: { - self.unreadIndicatorView.frame.size = !hide ? MasterTimelineDefaultCellLayout.unreadCircleSize : CGSize.zero - }) + showOrHideView(unreadIndicatorView, cellData.read || cellData.starred) + unreadIndicatorView.setNeedsDisplay() } func updateStarView() { - self.starView.isHidden = !self.cellData.starred - self.starView.frame.size = self.cellData.starred ? CGSize.zero : MasterTimelineDefaultCellLayout.starSize - UIView.animate( - withDuration: 0.5, - delay: 0.0, - usingSpringWithDamping: 0.5, - initialSpringVelocity: 0.2, - animations: { - self.starView.frame.size = self.cellData.starred ? MasterTimelineDefaultCellLayout.starSize : CGSize.zero - }) + showOrHideView(starView, !cellData.starred) } func updateAvatar() { @@ -251,6 +233,10 @@ private extension MasterTimelineTableViewCell { } } + func showOrHideView(_ view: UIView, _ shouldHide: Bool) { + shouldHide ? hideView(view) : showView(view) + } + func updateSubviews() { updateTitleView() updateSummaryView() From eea450bee341d8c57a6450eb48133c5bce0393ec Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 11:47:21 -0500 Subject: [PATCH 41/50] Make icon vibrancy deselections match the deselection animation --- .../Cell/MasterFeedTableViewCell.swift | 19 +++++++++++-------- .../SettingsAccountTableViewCell.swift | 19 ++++++++++++++----- iOS/Settings/SettingsViewController.swift | 1 + 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift index 3662b829a..d128d93f4 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift @@ -133,18 +133,12 @@ class MasterFeedTableViewCell : VibrantTableViewCell { override func setHighlighted(_ highlighted: Bool, animated: Bool) { super.setHighlighted(highlighted, animated: animated) - - let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor - disclosureButton?.tintColor = tintColor - faviconImageView.tintColor = tintColor + updateVibrancy(animated: animated) } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) - - let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor - disclosureButton?.tintColor = tintColor - faviconImageView.tintColor = tintColor + updateVibrancy(animated: animated) } override func willTransition(to state: UITableViewCell.StateMask) { @@ -201,5 +195,14 @@ private extension MasterFeedTableViewCell { disclosureButton?.isHidden = !isDisclosureAvailable separatorInset = layout.separatorInsets } + + func updateVibrancy(animated: Bool) { + let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor + let duration = animated ? 0.5 : 0.0 + UIView.animate(withDuration: duration) { + self.disclosureButton?.tintColor = tintColor + self.faviconImageView.tintColor = tintColor + } + } } diff --git a/iOS/Settings/SettingsAccountTableViewCell.swift b/iOS/Settings/SettingsAccountTableViewCell.swift index cc45c59b0..94b3478ac 100644 --- a/iOS/Settings/SettingsAccountTableViewCell.swift +++ b/iOS/Settings/SettingsAccountTableViewCell.swift @@ -15,16 +15,25 @@ class SettingsAccountTableViewCell: VibrantTableViewCell { override func setHighlighted(_ highlighted: Bool, animated: Bool) { super.setHighlighted(highlighted, animated: animated) - let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : UIColor.label - accountImage?.tintColor = tintColor - accountNameLabel?.highlightedTextColor = tintColor + updateVibrancy(animated: animated) } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) + updateVibrancy(animated: animated) + } + + override func applyThemeProperties() { + super.applyThemeProperties() + accountNameLabel?.highlightedTextColor = AppAssets.vibrantTextColor + } + + func updateVibrancy(animated: Bool) { let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : UIColor.label - accountImage?.tintColor = tintColor - accountNameLabel?.highlightedTextColor = tintColor + let duration = animated ? 0.5 : 0.0 + UIView.animate(withDuration: duration) { + self.accountImage?.tintColor = tintColor + } } } diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 56fe7f11f..be8f7683c 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -103,6 +103,7 @@ class SettingsViewController: UITableViewController { cell.textLabel?.text = NSLocalizedString("Add Account", comment: "Accounts") } else { let acctCell = tableView.dequeueReusableCell(withIdentifier: "SettingsAccountTableViewCell", for: indexPath) as! SettingsAccountTableViewCell + acctCell.applyThemeProperties() let account = sortedAccounts[indexPath.row] acctCell.accountImage?.image = AppAssets.image(for: account.type) acctCell.accountNameLabel?.text = account.nameForDisplay From bac205ef84ef6b41c663327903b7d221ca4c4e9c Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 11:56:36 -0500 Subject: [PATCH 42/50] Tweak the vibrancy icon transition animation --- iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift | 2 +- iOS/Settings/SettingsAccountTableViewCell.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift index d128d93f4..5dc3e3c3f 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift @@ -198,7 +198,7 @@ private extension MasterFeedTableViewCell { func updateVibrancy(animated: Bool) { let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor - let duration = animated ? 0.5 : 0.0 + let duration = animated ? 0.6 : 0.0 UIView.animate(withDuration: duration) { self.disclosureButton?.tintColor = tintColor self.faviconImageView.tintColor = tintColor diff --git a/iOS/Settings/SettingsAccountTableViewCell.swift b/iOS/Settings/SettingsAccountTableViewCell.swift index 94b3478ac..0ae9a6ec2 100644 --- a/iOS/Settings/SettingsAccountTableViewCell.swift +++ b/iOS/Settings/SettingsAccountTableViewCell.swift @@ -30,7 +30,7 @@ class SettingsAccountTableViewCell: VibrantTableViewCell { func updateVibrancy(animated: Bool) { let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : UIColor.label - let duration = animated ? 0.5 : 0.0 + let duration = animated ? 0.6 : 0.0 UIView.animate(withDuration: duration) { self.accountImage?.tintColor = tintColor } From c771bd651c3db2b805eb0a9e374a67c8d300de80 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 12:01:26 -0500 Subject: [PATCH 43/50] Make vibrant button highlight more quickly --- iOS/UIKit Extensions/VibrantButton.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/iOS/UIKit Extensions/VibrantButton.swift b/iOS/UIKit Extensions/VibrantButton.swift index 8e070e4d1..dc311d549 100644 --- a/iOS/UIKit Extensions/VibrantButton.swift +++ b/iOS/UIKit Extensions/VibrantButton.swift @@ -30,4 +30,19 @@ class VibrantButton: UIButton { } } + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + isHighlighted = true + super.touchesBegan(touches, with: event) + } + + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + isHighlighted = false + super.touchesEnded(touches, with: event) + } + + override func touchesCancelled(_ touches: Set, with event: UIEvent?) { + isHighlighted = false + super.touchesCancelled(touches, with: event) + } + } From 0dff95e51f19eb11dfb4d9f073938cc3da2294fe Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 13:33:22 -0500 Subject: [PATCH 44/50] Change number of default lines in timeline to 2 --- iOS/AppDefaults.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/AppDefaults.swift b/iOS/AppDefaults.swift index 81a58a09c..31d006641 100644 --- a/iOS/AppDefaults.swift +++ b/iOS/AppDefaults.swift @@ -106,7 +106,7 @@ struct AppDefaults { let defaults: [String : Any] = [Key.lastImageCacheFlushDate: Date(), Key.refreshInterval: RefreshInterval.everyHour.rawValue, Key.timelineGroupByFeed: false, - Key.timelineNumberOfLines: 3, + Key.timelineNumberOfLines: 2, Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue, Key.displayUndoAvailableTip: true] AppDefaults.shared.register(defaults: defaults) From 4fcd134c6a5859db53cd76e860956c501f725570 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 13:52:07 -0500 Subject: [PATCH 45/50] Change the local account icon. --- iOS/AppAssets.swift | 14 +++++++++++--- .../Contents.json | 2 +- .../localAccountPad.pdf} | Bin 4205 -> 3897 bytes .../accountLocalPhone.imageset/Contents.json | 16 ++++++++++++++++ .../localAccountPhone.pdf | Bin 0 -> 3904 bytes iOS/Settings/AddAccountViewController.swift | 3 ++- iOS/Settings/Settings.storyboard | 1 + 7 files changed, 31 insertions(+), 5 deletions(-) rename iOS/Resources/Assets.xcassets/{accountLocal.imageset => accountLocalPad.imageset}/Contents.json (85%) rename iOS/Resources/Assets.xcassets/{accountLocal.imageset/accountLocal.pdf => accountLocalPad.imageset/localAccountPad.pdf} (75%) create mode 100644 iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/Contents.json create mode 100644 iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/localAccountPhone.pdf diff --git a/iOS/AppAssets.swift b/iOS/AppAssets.swift index dcb1c9227..8f60395dd 100644 --- a/iOS/AppAssets.swift +++ b/iOS/AppAssets.swift @@ -11,8 +11,12 @@ import Account struct AppAssets { - static var accountLocalImage: UIImage = { - return UIImage(named: "accountLocal")! + static var accountLocalPadImage: UIImage = { + return UIImage(named: "accountLocalPad")! + }() + + static var accountLocalPhoneImage: UIImage = { + return UIImage(named: "accountLocalPhone")! }() static var accountFeedbinImage: UIImage = { @@ -177,7 +181,11 @@ struct AppAssets { static func image(for accountType: AccountType) -> UIImage? { switch accountType { case .onMyMac: - return AppAssets.accountLocalImage + if UIDevice.current.userInterfaceIdiom == .pad { + return AppAssets.accountLocalPadImage + } else { + return AppAssets.accountLocalPhoneImage + } case .feedbin: return AppAssets.accountFeedbinImage case .freshRSS: diff --git a/iOS/Resources/Assets.xcassets/accountLocal.imageset/Contents.json b/iOS/Resources/Assets.xcassets/accountLocalPad.imageset/Contents.json similarity index 85% rename from iOS/Resources/Assets.xcassets/accountLocal.imageset/Contents.json rename to iOS/Resources/Assets.xcassets/accountLocalPad.imageset/Contents.json index c48efa3f2..7fe4e483c 100644 --- a/iOS/Resources/Assets.xcassets/accountLocal.imageset/Contents.json +++ b/iOS/Resources/Assets.xcassets/accountLocalPad.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "accountLocal.pdf" + "filename" : "localAccountPad.pdf" } ], "info" : { diff --git a/iOS/Resources/Assets.xcassets/accountLocal.imageset/accountLocal.pdf b/iOS/Resources/Assets.xcassets/accountLocalPad.imageset/localAccountPad.pdf similarity index 75% rename from iOS/Resources/Assets.xcassets/accountLocal.imageset/accountLocal.pdf rename to iOS/Resources/Assets.xcassets/accountLocalPad.imageset/localAccountPad.pdf index c32cc3b897b33e9b26438b96b792ab7b5afd15c7..cfc6ca9f04b3b9098564f21f56debec0ee8ff7ca 100644 GIT binary patch delta 647 zcmaE>uv2bAKz*)%G}mDRp5Ny+OByRZFYnn`-cc3e`cq7GHdV8mHosbx85|NF9R!NsS2=4)JR3irk6QEFldP>)MuNh+6yi0#Y;iAfK>@A(sIhD43a=8k;JlDL}=H zjZMuJz_JQ?FfjvTLj!a%VjS?+Qj1v>hOpJ|F679Hb2r5yC dRj{+;DlP$rbwyEX8ke!DkpY*gs;j>n7XT`t%bEZH delta 966 zcmdlf_f}y-Kz(YkW8NJDo;|-s|2RZ=PVsm(NBqeZb_tEB1H1xn{x|YGHQk%B_dt@x zvJ($G{w?DFr4;;5E!LxD=}gPNs;hIBg{|b>X4rNqz@TqU*p5&e72}WFf>+;(lAR`_ z$&tCPy!X`Nhj*@~EoVwHJFfZC=4fr6YQn||j2d@jbQGs9DU5flUwCcDs#T4?5}I3$ zx)k{c~EBD#j2uHTlBaxJA4bnt~G_Oh0l!TYGCXhlEBw z@3;MD4c?Y-aJK3Rv#|4fp04|$l1Fd)XFHP_hfHm{0)_OSXHQRzE;@DeckF^9`O6EQ zzS)?g@1_-Vy;S_tu}@oUibU#zon?P?^%=%2e8+Qq`cuy9Yb}0TRYm#*RrFn`XsY#I z?XTo>Sg~H}M-Xqm5!1PM>ibe$OJ4HMzWczJ?{ojchlWeeM7#>=SaG36_hZ6i8R^W3 z$y?Jj3fSBw65eFr{H)XV@hS5vwSCt$+idT*pLuOkFfF!vl5GEth2E$3P6^N`Ha=Yc z;=kwo8$nC$zdZiiSX$!{@kh>jFYhW>gW0E<+#9y{`Ip{3Daa|A$KLgKQGdRx)UmUZ z1sSE7O^i)8>oHE@X0(`mm?yg4P{BYUKPih#BR4VGKUg6wwWv5VKTpBXK+n)b&qN_c z!>KegCq==~!qv#gEJjlyur#r#q)H*c#mzZCuOzjiM3XBuF9l?Rp%Kg|7b_zJLrViw z14BbYBLgFgC<6m^AVR2wDzvdt@YD||%1qSQ1l0|i6l$tQVbw9O5T6%0T?Ay0t|%rG!8HL*k&Gd3|Zoh-no zT5oD>fG%fXW`spOHg%Tf=z5J!3^B|zHZ?KBP-kih785{rsIi&3;p91dGK|KPck(I5 zC#4!$rkWd=CtDgD8l@T=Cs`O6nHd`!CmWk4CmWfi*>TwrRH6{801T<(5@3{p!^zm( N!h%ax)z#mP3jhURQ_uha diff --git a/iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/Contents.json b/iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/Contents.json new file mode 100644 index 000000000..05bb5e471 --- /dev/null +++ b/iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "localAccountPhone.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template", + "preserves-vector-representation" : true + } +} \ No newline at end of file diff --git a/iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/localAccountPhone.pdf b/iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/localAccountPhone.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c807edece1d482cc068b120a49f95922cf54ad6a GIT binary patch literal 3904 zcmai%c{r49`^PPlWk@QKrMiAtUzG4?KC#DICQ6iPx-p-Jxv1iN z41e}zrKB_ypV|?Nq)WHOa`a%gT#YTT+y0 z*1r!uWlq*0M}dGFodD@c?p9ju75^rQy|9@B zmQXO+%jwspII~SHHU-!mk#OYZ;*aTXGM&C~jtsy|2rfUqRI(QhK>d`=jO^p>OC^zg z0JWb1thX19v+e_I%51hN->-U`>wn5_LiHw@lWBl0r>LF*U=P5s-X7jm^K%3e8QAm% z>w^H)e--$x#^K*;{IU-7BMWwlb0C<`rhQ%Wr5=WC6&6RG=X6>ge%ZAk!GbP!pc02&kXl76Y;Eik42NpK{}(Wl8#CLYKmc{ettN zCM^x7diw0acz40sQuLRh%=K35;eqi-{R1wu^!^$ylW*amMw%6OsA{g2*r%xmyB#|_ znwNNXi*k!taX)1_JA-h`dOSVf7#bNFw~x0}E`Cx8b*g=DvfNl2ymb@o|Cm zd)1BwLnYWespgf>B#|4S=L>0y$S4F-JXbL9uIxy2xyNGB*umQN>*>(ZTq)~L$tSAW z@wFyHnGjZfJSf-hI^?}e{zZN=g$Fhdrk6!WiOQ zi*hzM_EgrTS#VW(@@cbKck#0T*ZdZ)s-Zu=?2YnpNN)ZhEH4x{J?-Jco#U=@@T2-# zPPq5;`Y6m&M#rZ$%cF%7T44taccIAo?82f#BfS?dghkSsPU}me8zWCKlz+81DgTl4 zRAMgjqlM|mRKbWC*V@QqVOCdFoWKzwy{wSkYV!P&!CCHNuD*_3^hk_JwIP3-w+0vX z0I!^vRv-_3%2pHsZmWCZTAEbulZXubKEAq7$S&54WvnA>KDH>F=tPC&DNE)LM|d>w z?6}HLy9rheQ-Sj+pWP}GiaN(T1aL)!9v9`k#;bCihoTL-ucP^z3#@~9&Fy!|?HpH6 zSl~<0sc^M(e0HESPq&^E5)Q>_gQFUZ;M_xD6oK&1qPy~R?pzfw*p{K&Cn{|tZ~?1h za#)P_88+ssXtMUoOX%(0N|)~BVofv_4$!1(1ne)Z-sFD?sPe6I(<8QD=jkSFea-bS zbfDs>i~z2&AYBB0$Sz}sbDB_KVgr+d{( zj(hul_axZ@o?`vXt0D!`j}wk3ogd}pi_Rrn-!0xEITCFh&mvk9`iLjsz1Ko`&`3b6RzmagL+45}SS=);Ad%Uj+(??}QTAxaOkU1Azz1t4y zg)Rd<+SVUm8#>jrl?9y(c%n2UHXFXJ!RIsa;{G~th)l@d;lzC{_3~E*Z9wkf+xA}r z2Toy5Jl4LTeF1yRZ0l{W z9;-XoYqD(c3_bb@It-VBO8=5R(q5WUlH{H?l^z7ar}n4&r#q_T$UE*2Iq|*lTQ!eq zgLZ>?Sbl|Nt^up6Q6th&KQk|lc$t_I=#)2RjLbhcquw~d%?8@h6e2wt4%P2F+Tk$FJ zweg8xRL34ON0@Kb$|(*TPZZNul)RKKrpcx`r*Wqx-Z8fD?iK6Jy5o5V-vf7px)Iz4 z+}3(kN)nhwO#f_VmZR#%=(p&}ikg_f>Q%R|O_LgvDN>nIbyAX27Ei+lVzA}n9zRG{B_UJ>9aM=DyFdem@8DZ1YO&j zSMko;`mRCWO-$3Vwu|`}Zmb*>(ulK)%Mu?HcT}EHHa|3_yw6(EdW)53)nvtL)0^f@ zcY6}OHOg&-RM}ZLIQF2l_|ULl{S5tRrhUbrc>6|p!LJb=5X}%-dQJ0F zsEwfaZlC(ds_vYNAK{k|lE2q&STvkv-Kp5QwKHExSmLup&!OGauJf#F8ETS>lS;f} zz`5KWi??YbHdCuFPWC5^`!#zu55CqISg&3?xXox=5-zSawJ2p=iYer9IlcT$(wXu{ z$xFmpx7i}iBbqUq37R|W{OU^Ruw}wJ&${30;Ftc-cFQ}=3yP-CGAMBN*oL!GNkM1|DU*s`Cw6OiIRiE7|%Bpvv(-*2O0)hklL5GO? zdf4XLj} zrB{PS(Ie9zz$2ejP=tj{a$mEN~!Sac(1XJawjxNO1ku|FHs zpHdsWmOS2aa6Dnoewq9&36vgjKcY)7z1cp7WLRdn%W#ATuk+x&?|sMlu+iewtEu^l zsN$~Te^DZ;maDpTA%xh9gZ3y5c?PUWH6Ma-Q{ zc;>OoV`k8~vefNux~xOjS7OJ9*bjKfyX2{1Yx5 zVwFL(b{KeB_TIjf{bD|E{!#6j={ie-?fJ9YD2YyqRmj`-87Io!aJ@y71(RV?iJ4=J z$)HCwS#Mh3_SX-4hIDf$@IK?$+$$#YB&2Q0#JyC<^BLhC>uwWsDsQr;-X+8_L~rK! zToMx(iM!R>nCRPkw5MPFwtA{sV?uJvy^-^+-s|tbVuN;;OqihZA1{Lr@5ju2XkFen zn=O27;6g0@&V>AW&f>$DHnBFjeee2W9u}xkf{cTPCqlck_H^z!q;+HC;zImlMrP%6 z7b9j;=dM3)U~>F}=XF~m=i*`{qK}-RY1UMYzRD`AC>!bsOD}hrdrU2U%UJtnkzV3b zhOs|N&!RJCqSh1o_SR-!bDR#QOgxzNWK5i^T)#F_dZlg)eSFn^4K}n`LuX&v$dcxb z<6HftQWv!0@J?fbKD6G&U%bsh>qX$Z)7M^|Xh)?exiL6)r{%|R+t0X7b ze~sZnY;XST-Zhn>bzt>7txrSJ*F2RTF1Z)DV8DRe> z6K_KFpG^E0WBUNGlVm3fLDxF~u!VC{&}aa2_y;8Wa>yG2z>FwPJ{)*P{;c*JAR{)@ z|GOfVKqGi~yZpp=pI_Ym7nUP_+rYtcCtnh$15)rLknrY!C7J3&@%91`a1{g^uv5_W zrFb|22#k&z9BHQrnEDc^v_ODE@IRnGfTqX+cTTHB_}|;PsI&k8 literal 0 HcmV?d00001 diff --git a/iOS/Settings/AddAccountViewController.swift b/iOS/Settings/AddAccountViewController.swift index d1587758a..e0a18a590 100644 --- a/iOS/Settings/AddAccountViewController.swift +++ b/iOS/Settings/AddAccountViewController.swift @@ -15,11 +15,12 @@ protocol AddAccountDismissDelegate: UIViewController { class AddAccountViewController: UITableViewController, AddAccountDismissDelegate { + @IBOutlet private weak var localAccountImageView: UIImageView! @IBOutlet private weak var localAccountNameLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() - + localAccountImageView.image = AppAssets.image(for: .onMyMac) localAccountNameLabel.text = Account.defaultLocalAccountName } diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 617b76467..5aa060786 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -595,6 +595,7 @@ + From 8c8ab39b0ce5f41362d2d9c3b30e7ffb88f31e72 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 14:21:55 -0500 Subject: [PATCH 46/50] Change the local account icon --- .../accountLocal.imageset/Contents.json | 2 +- .../{accountLocal.pdf => localAccountMac.pdf} | Bin 4205 -> 3910 bytes 2 files changed, 1 insertion(+), 1 deletion(-) rename Mac/Resources/Assets.xcassets/accountLocal.imageset/{accountLocal.pdf => localAccountMac.pdf} (75%) diff --git a/Mac/Resources/Assets.xcassets/accountLocal.imageset/Contents.json b/Mac/Resources/Assets.xcassets/accountLocal.imageset/Contents.json index 4b7dc7acc..1d99e9ded 100644 --- a/Mac/Resources/Assets.xcassets/accountLocal.imageset/Contents.json +++ b/Mac/Resources/Assets.xcassets/accountLocal.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "accountLocal.pdf" + "filename" : "localAccountMac.pdf" } ], "info" : { diff --git a/Mac/Resources/Assets.xcassets/accountLocal.imageset/accountLocal.pdf b/Mac/Resources/Assets.xcassets/accountLocal.imageset/localAccountMac.pdf similarity index 75% rename from Mac/Resources/Assets.xcassets/accountLocal.imageset/accountLocal.pdf rename to Mac/Resources/Assets.xcassets/accountLocal.imageset/localAccountMac.pdf index c32cc3b897b33e9b26438b96b792ab7b5afd15c7..5c3292ce04da8aab5e456e6958225e12170fc66f 100644 GIT binary patch delta 664 zcmaE>a7=DOKz(e0qu&t&o;}Y+KQsE=G3hIQ##;S|aiV}nuF|E5&p*FAZ z4SQaNLa=M{t*Gp--G{wu;~QTVERO5)a8~gac4fQdpg$#h?W3+vy(y~MWlsE)W%l2#d#L{Vd*`3& zDcKXcWB;j73fI4FerF#;O-3x!Bqk|lBLj=gvP_e?SuHIU%*`in<&Bu!#3xd(k(-$8 zAFL3TT2!2wpQm7Gpl4{R5ToH#nwgWLU}))RW?&SfsSsG2SX5G_5a8nGoS#>cT2Z2@ zpzmCinpgtVYG58^V4w~}Ky|+PDIg^ZSe3AV)J#6e zC+uO!WdH{XW~QdbrV42aP%&d;a{~pitU?}4%)r>l7+nk~WI0)eUp3v>zyw{+z{CKH zdTi><4bk-i{e)qjv9XcCVjE0ln@+-zC8(Su)m?R||m|B`A8yFi~7$v7BnHyQA rC8kaoG@5q7bWKXUA1s0u1YlqSQ1lV^d>eE>%@me>W}w@v7Gq delta 994 zcmX>m_f}y-Kz(YkW8NJDo;|-s|2RZ=PVsm(NBqeZb_tEB1H1xn{x|YGHQk%B_dt@x zvJ($G{w?DFr4;;5E!LxD=}gPNs;hIBg{|b>X4rNqz@TqU*p5&e72}WFf>+;(lAR`_ z$&tCPy!X`Nhj*@~EoVwHJFfZC=4fr6YQn||j2d@jbQGs9DU5flUwCcDs#T4?5}I3$ zx)k{c~EBD#j2uHTlBaxJA4bnt~G_Oh0l!TYGCXhlEBw z@3;MD4c?Y-aJK3Rv#|4fp04|$l1Fd)XFHP_hfHm{0)_OSXHQRzE;@DeckF^9`O6EQ zzS)?g@1_-Vy;S_tu}@oUibU#zon?P?^%=%2e8+Qq`cuy9Yb}0TRYm#*RrFn`XsY#I z?XTo>Sg~H}M-Xqm5!1PM>ibe$OJ4HMzWczJ?{ojchlWeeM7#>=SaG36_hZ6i8R^W3 z$y?Jj3fSBw65eFr{H)XV@hS5vwSCt$+idT*pLuOkFfF!vl5GEth2E$3P6^N`Ha=Yc z;=kwo8$nC$zdZiiSX$!{@kh>jFYhW>gW0E<+#9y{`Ip{3Daa|A$KLgKQGdRx)UmUZ zKegCq==~!qv#gEJjlyur#r#q)H*c#mzZCuOzjiM3XBuF9l?Rp%Kg|7b_zJ zLrViw14BbYBLgFgC<6m^AVR2wDzvdtsMilD%1qSQ1l0|i54E(17FFf%nZHdRPdfQlKL8yYKsWfk&ZVg{xr zmOwE0qvMir!y{WMQh6XbukOnmM*wk5?W2!U6FwfZ3#0*27sU=uU0L4*e z=7y73^2sn7Pd>({7@w4CWSMGiV4iGgY-p5fY@B3aU}R=&Y@BRtnw)H8mS)FgLr{rA atO76ui%Wn}2@XGFa|;VDRaIAiH!c7JL0F;y From a39aab58e4a4fdbf088da28592d5894176468c1e Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 15:31:54 -0500 Subject: [PATCH 47/50] Fix vertical padding for Feeds that have more than one line. Issue #1150 --- .../Cell/MasterFeedTableViewCellLayout.swift | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift index 2df98fe02..b60177efb 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift @@ -17,6 +17,7 @@ struct MasterFeedTableViewCellLayout { private static let unreadCountMarginLeft = CGFloat(integerLiteral: 8) private static let unreadCountMarginRight = CGFloat(integerLiteral: 16) private static let disclosureButtonSize = CGSize(width: 44, height: 44) + private static let verticalPadding = CGFloat(integerLiteral: 11) private static let minRowHeight = CGFloat(integerLiteral: 44) @@ -53,7 +54,7 @@ struct MasterFeedTableViewCellLayout { var rFavicon = CGRect.zero if !shouldShowDisclosure { let x = bounds.origin.x + ((MasterFeedTableViewCellLayout.disclosureButtonSize.width - MasterFeedTableViewCellLayout.imageSize.width) / 2) - let y = UIFontMetrics.default.scaledValue(for: CGFloat(integerLiteral: 4)) + let y = UIFontMetrics.default.scaledValue(for: MasterFeedTableViewCellLayout.verticalPadding) rFavicon = CGRect(x: x, y: y, width: MasterFeedTableViewCellLayout.imageSize.width, height: MasterFeedTableViewCellLayout.imageSize.height) } @@ -82,31 +83,26 @@ struct MasterFeedTableViewCellLayout { let labelSizeInfo = MultilineUILabelSizer.size(for: label.text ?? "", font: label.font, numberOfLines: 0, width: Int(floor(labelWidth))) let rLabelx = bounds.minX + MasterFeedTableViewCellLayout.disclosureButtonSize.width - var rLabel = CGRect(x: rLabelx, y: 0.0, width: labelSizeInfo.size.width, height: labelSizeInfo.size.height) + let rLabely = UIFontMetrics.default.scaledValue(for: MasterFeedTableViewCellLayout.verticalPadding) + let rLabel = CGRect(x: rLabelx, y: rLabely, width: labelSizeInfo.size.width, height: labelSizeInfo.size.height) // Determine cell height - var cellHeight = [rFavicon, rLabel, rUnread, rDisclosure].maxY() + let paddedLabelHeight = rLabel.maxY + UIFontMetrics.default.scaledValue(for: MasterFeedTableViewCellLayout.verticalPadding) + let maxGraphicsHeight = [rFavicon, rUnread, rDisclosure].maxY() + var cellHeight = max(paddedLabelHeight, maxGraphicsHeight) if cellHeight < MasterFeedTableViewCellLayout.minRowHeight { cellHeight = MasterFeedTableViewCellLayout.minRowHeight } // Center in Cell let newBounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width, height: cellHeight) - - if !shouldShowDisclosure && labelSizeInfo.numberOfLinesUsed == 1 { - rFavicon = MasterFeedTableViewCellLayout.centerVertically(rFavicon, newBounds) - } - if !unreadCountIsHidden { rUnread = MasterFeedTableViewCellLayout.centerVertically(rUnread, newBounds) } - if shouldShowDisclosure { rDisclosure = MasterFeedTableViewCellLayout.centerVertically(rDisclosure, newBounds) } - rLabel = MasterFeedTableViewCellLayout.centerVertically(rLabel, newBounds) - // Assign the properties self.height = cellHeight self.faviconRect = rFavicon From f0d1cf62398023b2f0fee8e300d7786811412187 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 19:58:18 -0500 Subject: [PATCH 48/50] Add Account Inspector context menu item for Accounts --- NetNewsWire.xcodeproj/project.pbxproj | 34 +- iOS/Account/Account.storyboard | 260 +++++++++++ .../FeedbinAccountViewController.swift | 0 .../LocalAccountViewController.swift} | 6 +- .../AccountInspectorViewController.swift} | 27 +- iOS/Inspector/Inspector.storyboard | 169 ++++++++ iOS/MasterFeed/MasterFeedViewController.swift | 32 ++ iOS/SceneCoordinator.swift | 11 + iOS/Settings/AddAccountViewController.swift | 6 +- iOS/Settings/Settings.storyboard | 408 +----------------- iOS/Settings/SettingsViewController.swift | 7 +- .../UIStoryboard-Extensions.swift | 8 + 12 files changed, 542 insertions(+), 426 deletions(-) create mode 100644 iOS/Account/Account.storyboard rename iOS/{Settings => Account}/FeedbinAccountViewController.swift (100%) rename iOS/{Settings/AddLocalAccountViewController.swift => Account/LocalAccountViewController.swift} (83%) rename iOS/{Settings/DetailAccountViewController.swift => Inspector/AccountInspectorViewController.swift} (81%) create mode 100644 iOS/Inspector/Inspector.storyboard diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 1a326e674..4102a510f 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -84,6 +84,8 @@ 516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */; }; 516A09392360A2AE00EAE89B /* SettingsAccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */; }; 516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */; }; + 516A09402361240900EAE89B /* Account.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 516A093F2361240900EAE89B /* Account.storyboard */; }; + 516A09422361248000EAE89B /* Inspector.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 516A09412361248000EAE89B /* Inspector.storyboard */; }; 51707439232AA97100A461A3 /* ShareFolderPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51707438232AA97100A461A3 /* ShareFolderPickerController.swift */; }; 5170743A232AABFC00A461A3 /* FlattenedAccountFolderPickerData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452812265093600C03939 /* FlattenedAccountFolderPickerData.swift */; }; 517630042336215100E15FFF /* main.js in Resources */ = {isa = PBXBuildFile; fileRef = 517630032336215100E15FFF /* main.js */; }; @@ -109,9 +111,9 @@ 519D740623243CC0008BB345 /* RefreshInterval-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */; }; 519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; }; 51A16997235E10D700EB091F /* RefreshIntervalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */; }; - 51A16999235E10D700EB091F /* AddLocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698F235E10D600EB091F /* AddLocalAccountViewController.swift */; }; + 51A16999235E10D700EB091F /* LocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698F235E10D600EB091F /* LocalAccountViewController.swift */; }; 51A1699A235E10D700EB091F /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51A16990235E10D600EB091F /* Settings.storyboard */; }; - 51A1699B235E10D700EB091F /* DetailAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16991235E10D600EB091F /* DetailAccountViewController.swift */; }; + 51A1699B235E10D700EB091F /* AccountInspectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16991235E10D600EB091F /* AccountInspectorViewController.swift */; }; 51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16992235E10D600EB091F /* AddAccountViewController.swift */; }; 51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16993235E10D600EB091F /* SettingsViewController.swift */; }; 51A1699F235E10D700EB091F /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16995235E10D600EB091F /* AboutViewController.swift */; }; @@ -1242,6 +1244,8 @@ 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsAccountTableViewCell.xib; sourceTree = ""; }; 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAccountTableViewCell.swift; sourceTree = ""; }; 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = ""; }; + 516A093F2361240900EAE89B /* Account.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Account.storyboard; sourceTree = ""; }; + 516A09412361248000EAE89B /* Inspector.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Inspector.storyboard; sourceTree = ""; }; 51707438232AA97100A461A3 /* ShareFolderPickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareFolderPickerController.swift; sourceTree = ""; }; 517630032336215100E15FFF /* main.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = main.js; sourceTree = ""; }; 517630222336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleViewControllerWebViewProvider.swift; sourceTree = ""; }; @@ -1263,9 +1267,9 @@ 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RefreshInterval-Extensions.swift"; sourceTree = ""; }; 519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshIntervalViewController.swift; sourceTree = ""; }; - 51A1698F235E10D600EB091F /* AddLocalAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLocalAccountViewController.swift; sourceTree = ""; }; + 51A1698F235E10D600EB091F /* LocalAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalAccountViewController.swift; sourceTree = ""; }; 51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; - 51A16991235E10D600EB091F /* DetailAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailAccountViewController.swift; sourceTree = ""; }; + 51A16991235E10D600EB091F /* AccountInspectorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountInspectorViewController.swift; sourceTree = ""; }; 51A16992235E10D600EB091F /* AddAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddAccountViewController.swift; sourceTree = ""; }; 51A16993235E10D600EB091F /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; 51A16995235E10D600EB091F /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; @@ -1668,6 +1672,8 @@ 5123DB95233EC69300282CC9 /* Inspector */ = { isa = PBXGroup; children = ( + 516A09412361248000EAE89B /* Inspector.storyboard */, + 51A16991235E10D600EB091F /* AccountInspectorViewController.swift */, 5123DB9E233EC6FD00282CC9 /* FeedInspectorView.swift */, ); path = Inspector; @@ -1750,6 +1756,16 @@ name = Products; sourceTree = ""; }; + 516A093E236123A800EAE89B /* Account */ = { + isa = PBXGroup; + children = ( + 516A093F2361240900EAE89B /* Account.storyboard */, + 51A1698F235E10D600EB091F /* LocalAccountViewController.swift */, + 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */, + ); + path = Account; + sourceTree = ""; + }; 5183CCDB226F1EEB0010922C /* Progress */ = { isa = PBXGroup; children = ( @@ -1775,9 +1791,6 @@ 51A16990235E10D600EB091F /* Settings.storyboard */, 51A16995235E10D600EB091F /* AboutViewController.swift */, 51A16992235E10D600EB091F /* AddAccountViewController.swift */, - 51A1698F235E10D600EB091F /* AddLocalAccountViewController.swift */, - 51A16991235E10D600EB091F /* DetailAccountViewController.swift */, - 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */, 51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */, 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */, 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */, @@ -2521,6 +2534,7 @@ 51C4525D226508F600C03939 /* MasterFeed */, 51C4526D2265091600C03939 /* MasterTimeline */, 51C4527D2265092C00C03939 /* Article */, + 516A093E236123A800EAE89B /* Account */, 51C452802265093600C03939 /* Add */, 5123DB95233EC69300282CC9 /* Inspector */, 513145F9235A55A700387FDC /* Intents */, @@ -3375,11 +3389,13 @@ 51BB7C312335ACDE008E8144 /* page.html in Resources */, 516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */, 51F85BF32272531500C787DC /* Dedication.rtf in Resources */, + 516A09422361248000EAE89B /* Inspector.storyboard in Resources */, 84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */, 51F85BEB22724CB600C787DC /* About.rtf in Resources */, 51F85BED227251DF00C787DC /* Acknowledgments.rtf in Resources */, 516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */, 511D43D1231FA62800FB1562 /* SidebarKeyboardShortcuts.plist in Resources */, + 516A09402361240900EAE89B /* Account.storyboard in Resources */, 51C452AB22650DC600C03939 /* template.html in Resources */, 51F85BF12272524100C787DC /* Credits.rtf in Resources */, 84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */, @@ -3880,7 +3896,7 @@ 516A09392360A2AE00EAE89B /* SettingsAccountTableViewCell.swift in Sources */, 51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */, 51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */, - 51A16999235E10D700EB091F /* AddLocalAccountViewController.swift in Sources */, + 51A16999235E10D700EB091F /* LocalAccountViewController.swift in Sources */, 514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */, 51FA73A52332BE110090D516 /* ArticleExtractor.swift in Sources */, 51314704235C41FC00387FDC /* Intents.intentdefinition in Sources */, @@ -3940,7 +3956,7 @@ 51F85BFB2275D85000C787DC /* Array-Extensions.swift in Sources */, 51C452AC22650FD200C03939 /* AppNotifications.swift in Sources */, 51EF0F7E2277A57D0050506E /* MasterTimelineAccessibilityCellLayout.swift in Sources */, - 51A1699B235E10D700EB091F /* DetailAccountViewController.swift in Sources */, + 51A1699B235E10D700EB091F /* AccountInspectorViewController.swift in Sources */, 51C452762265091600C03939 /* MasterTimelineViewController.swift in Sources */, 5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */, 51C452882265093600C03939 /* AddFeedViewController.swift in Sources */, diff --git a/iOS/Account/Account.storyboard b/iOS/Account/Account.storyboard new file mode 100644 index 000000000..b5f05cc65 --- /dev/null +++ b/iOS/Account/Account.storyboard @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Settings/FeedbinAccountViewController.swift b/iOS/Account/FeedbinAccountViewController.swift similarity index 100% rename from iOS/Settings/FeedbinAccountViewController.swift rename to iOS/Account/FeedbinAccountViewController.swift diff --git a/iOS/Settings/AddLocalAccountViewController.swift b/iOS/Account/LocalAccountViewController.swift similarity index 83% rename from iOS/Settings/AddLocalAccountViewController.swift rename to iOS/Account/LocalAccountViewController.swift index b9c092336..5abf1f436 100644 --- a/iOS/Settings/AddLocalAccountViewController.swift +++ b/iOS/Account/LocalAccountViewController.swift @@ -1,5 +1,5 @@ // -// AddLocalAccountViewController.swift +// LocalAccountViewController.swift // NetNewsWire-iOS // // Created by Maurice Parker on 5/19/19. @@ -9,7 +9,7 @@ import UIKit import Account -class AddLocalAccountViewController: UITableViewController { +class LocalAccountViewController: UITableViewController { @IBOutlet weak var nameTextField: UITextField! @@ -35,7 +35,7 @@ class AddLocalAccountViewController: UITableViewController { } -extension AddLocalAccountViewController: UITextFieldDelegate { +extension LocalAccountViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() diff --git a/iOS/Settings/DetailAccountViewController.swift b/iOS/Inspector/AccountInspectorViewController.swift similarity index 81% rename from iOS/Settings/DetailAccountViewController.swift rename to iOS/Inspector/AccountInspectorViewController.swift index 40dd698ee..c08beb28c 100644 --- a/iOS/Settings/DetailAccountViewController.swift +++ b/iOS/Inspector/AccountInspectorViewController.swift @@ -1,5 +1,5 @@ // -// DetailAccountViewController.swift +// AccountInspectorViewController.swift // NetNewsWire-iOS // // Created by Maurice Parker on 5/17/19. @@ -9,11 +9,14 @@ import UIKit import Account -class DetailAccountViewController: UITableViewController { +class AccountInspectorViewController: UITableViewController { + + static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 400.0) @IBOutlet weak var nameTextField: UITextField! @IBOutlet weak var activeSwitch: UISwitch! + var isModal = false weak var account: Account? override func viewDidLoad() { @@ -25,18 +28,28 @@ class DetailAccountViewController: UITableViewController { nameTextField.text = account.name nameTextField.delegate = self activeSwitch.isOn = account.isActive - } - + + if isModal { + let doneBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done)) + navigationItem.leftBarButtonItem = doneBarButtonItem + } + + } + override func viewWillDisappear(_ animated: Bool) { account?.name = nameTextField.text account?.isActive = activeSwitch.isOn } + + @objc func done() { + dismiss(animated: true) + } @IBAction func credentials(_ sender: Any) { guard let account = account else { return } switch account.type { case .feedbin: - let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController + let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController let addViewController = navController.topViewController as! FeedbinAccountViewController addViewController.account = account navController.modalPresentationStyle = .currentContext @@ -68,7 +81,7 @@ class DetailAccountViewController: UITableViewController { } -extension DetailAccountViewController { +extension AccountInspectorViewController { override func numberOfSections(in tableView: UITableView) -> Int { guard let account = account else { return 0 } @@ -107,7 +120,7 @@ extension DetailAccountViewController { } -extension DetailAccountViewController: UITextFieldDelegate { +extension AccountInspectorViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() diff --git a/iOS/Inspector/Inspector.storyboard b/iOS/Inspector/Inspector.storyboard new file mode 100644 index 000000000..e87fb1179 --- /dev/null +++ b/iOS/Inspector/Inspector.storyboard @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index 039e5b036..e9c0d4d65 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -182,6 +182,10 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { let tap = UITapGestureRecognizer(target: self, action:#selector(self.toggleSectionHeader(_:))) headerView.addGestureRecognizer(tap) + if section != 0 { + headerView.addInteraction(UIContextMenuInteraction(delegate: self)) + } + return headerView } @@ -531,6 +535,26 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { } +// MARK: UIContextMenuInteractionDelegate + +extension MasterFeedViewController: UIContextMenuInteractionDelegate { + func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { + + guard let sectionIndex = interaction.view?.tag, + let sectionNode = coordinator.rootNode.childAtIndex(sectionIndex), + let account = sectionNode.representedObject as? Account, + let headerView = interaction.view as? MasterFeedTableViewSectionHeader + else { + return nil + } + + return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in + let accountInfoAction = self.getAccountInfoAction(account: account) + return UIMenu(title: "", children: [accountInfoAction]) + } + } +} + // MARK: MasterTableViewCellDelegate extension MasterFeedViewController: MasterFeedTableViewCellDelegate { @@ -846,6 +870,14 @@ private extension MasterFeedViewController { return action } + func getAccountInfoAction(account: Account) -> UIAction { + let title = NSLocalizedString("Get Info", comment: "Get Info") + let action = UIAction(title: title, image: AppAssets.infoImage) { [weak self] action in + self?.coordinator.showAccountInspector(for: account) + } + return action + } + func getInfoAlertAction(indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? { guard let node = dataSource.itemIdentifier(for: indexPath), let feed = node.representedObject as? Feed else { return nil diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index e97a63e19..bfd636ccd 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -793,6 +793,17 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { rootSplitViewController.present(settingsNavController, animated: true) } + func showAccountInspector(for account: Account) { + let accountInspectorNavController = + UIStoryboard.inspector.instantiateViewController(identifier: "AccountInspectorNavigationViewController") as! UINavigationController + let accountInspectorController = accountInspectorNavController.topViewController as! AccountInspectorViewController + accountInspectorNavController.modalPresentationStyle = .formSheet + accountInspectorNavController.preferredContentSize = AccountInspectorViewController.preferredContentSizeForFormSheetDisplay + accountInspectorController.isModal = true + accountInspectorController.account = account + rootSplitViewController.present(accountInspectorNavController, animated: true) + } + func showFeedInspector() { guard let feed = timelineFetcher as? Feed else { return diff --git a/iOS/Settings/AddAccountViewController.swift b/iOS/Settings/AddAccountViewController.swift index e0a18a590..f53d46a55 100644 --- a/iOS/Settings/AddAccountViewController.swift +++ b/iOS/Settings/AddAccountViewController.swift @@ -27,13 +27,13 @@ class AddAccountViewController: UITableViewController, AddAccountDismissDelegate override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch indexPath.row { case 0: - let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "AddLocalAccountNavigationViewController") as! UINavigationController + let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "AddLocalAccountNavigationViewController") as! UINavigationController navController.modalPresentationStyle = .currentContext - let addViewController = navController.topViewController as! AddLocalAccountViewController + let addViewController = navController.topViewController as! LocalAccountViewController addViewController.delegate = self present(navController, animated: true) case 1: - let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController + let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController navController.modalPresentationStyle = .currentContext let addViewController = navController.topViewController as! FeedbinAccountViewController addViewController.delegate = self diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 5aa060786..3afc9391c 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -359,155 +359,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -601,257 +453,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -881,7 +483,7 @@ - + @@ -1061,7 +663,7 @@ - + @@ -1082,6 +684,6 @@ - + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index be8f7683c..cab947682 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -32,6 +32,7 @@ class SettingsViewController: UITableViewController { 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) tableView.register(UINib(nibName: "SettingsAccountTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsAccountTableViewCell") tableView.register(UINib(nibName: "SettingsTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsTableViewCell") @@ -130,7 +131,7 @@ class SettingsViewController: UITableViewController { let controller = UIStoryboard.settings.instantiateController(ofType: AddAccountViewController.self) self.navigationController?.pushViewController(controller, animated: true) } else { - let controller = UIStoryboard.settings.instantiateController(ofType: DetailAccountViewController.self) + let controller = UIStoryboard.inspector.instantiateController(ofType: AccountInspectorViewController.self) controller.account = sortedAccounts[indexPath.row] self.navigationController?.pushViewController(controller, animated: true) } @@ -251,6 +252,10 @@ class SettingsViewController: UITableViewController { tableView.reloadData() } + @objc func displayNameDidChange() { + tableView.reloadData() + } + } // MARK: OPML Document Picker diff --git a/iOS/UIKit Extensions/UIStoryboard-Extensions.swift b/iOS/UIKit Extensions/UIStoryboard-Extensions.swift index 4d309a729..ef48e061d 100644 --- a/iOS/UIKit Extensions/UIStoryboard-Extensions.swift +++ b/iOS/UIKit Extensions/UIStoryboard-Extensions.swift @@ -24,6 +24,14 @@ extension UIStoryboard { return UIStoryboard(name: "Settings", bundle: nil) } + static var inspector: UIStoryboard { + return UIStoryboard(name: "Inspector", bundle: nil) + } + + static var account: UIStoryboard { + return UIStoryboard(name: "Account", bundle: nil) + } + func instantiateController(ofType type: T.Type = T.self) -> T where T: UIViewController { let storyboardId = String(describing: type) From 9376fcd64aa303854ea2a96e2900ef9445967994 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 20:22:31 -0500 Subject: [PATCH 49/50] Added deactivate context menu for accounts. --- iOS/AppAssets.swift | 4 ++++ iOS/MasterFeed/MasterFeedViewController.swift | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/iOS/AppAssets.swift b/iOS/AppAssets.swift index 8f60395dd..ca3806fd9 100644 --- a/iOS/AppAssets.swift +++ b/iOS/AppAssets.swift @@ -77,6 +77,10 @@ struct AppAssets { return UIImage(systemName: "doc.on.doc")! }() + static var deactivateImage: UIImage = { + UIImage(systemName: "minus.circle")! + }() + static var editImage: UIImage = { UIImage(systemName: "square.and.pencil")! }() diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index e9c0d4d65..15a86ba80 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -542,15 +542,15 @@ extension MasterFeedViewController: UIContextMenuInteractionDelegate { guard let sectionIndex = interaction.view?.tag, let sectionNode = coordinator.rootNode.childAtIndex(sectionIndex), - let account = sectionNode.representedObject as? Account, - let headerView = interaction.view as? MasterFeedTableViewSectionHeader + let account = sectionNode.representedObject as? Account else { return nil } return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in let accountInfoAction = self.getAccountInfoAction(account: account) - return UIMenu(title: "", children: [accountInfoAction]) + let deactivateAction = self.deactivateAccountAction(account: account) + return UIMenu(title: "", children: [accountInfoAction, deactivateAction]) } } } @@ -878,6 +878,14 @@ private extension MasterFeedViewController { return action } + func deactivateAccountAction(account: Account) -> UIAction { + let title = NSLocalizedString("Deactivate", comment: "Deactivate") + let action = UIAction(title: title, image: AppAssets.deactivateImage) { action in + account.isActive = false + } + return action + } + func getInfoAlertAction(indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? { guard let node = dataSource.itemIdentifier(for: indexPath), let feed = node.representedObject as? Feed else { return nil From f538b3304fd13a1f31bc213118f94d3ab3d306de Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Oct 2019 20:29:22 -0500 Subject: [PATCH 50/50] Added account name to account inspector title --- iOS/Inspector/AccountInspectorViewController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iOS/Inspector/AccountInspectorViewController.swift b/iOS/Inspector/AccountInspectorViewController.swift index c08beb28c..853670755 100644 --- a/iOS/Inspector/AccountInspectorViewController.swift +++ b/iOS/Inspector/AccountInspectorViewController.swift @@ -29,6 +29,8 @@ class AccountInspectorViewController: UITableViewController { nameTextField.delegate = self activeSwitch.isOn = account.isActive + navigationItem.title = account.nameForDisplay + if isModal { let doneBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done)) navigationItem.leftBarButtonItem = doneBarButtonItem