diff --git a/Frameworks/FeedProvider/Twitter/TwitterFeedProvider.swift b/Frameworks/FeedProvider/Twitter/TwitterFeedProvider.swift index 1905eae83..f71d43f87 100644 --- a/Frameworks/FeedProvider/Twitter/TwitterFeedProvider.swift +++ b/Frameworks/FeedProvider/Twitter/TwitterFeedProvider.swift @@ -132,6 +132,11 @@ private extension TwitterFeedProvider { // TODO: Update to retrieve the full user func fetchIconURL(screenName: String, completion: @escaping (Result) -> Void) { + guard screenName != "search" else { + completion(.failure(TwitterFeedProviderError.unknown)) + return + } + let url = "\(Self.apiBase)users/show.json" let parameters = ["screen_name": screenName] diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 4602adc46..dc73b5941 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -140,8 +140,6 @@ 515A5178243E90200089E588 /* ExtensionPointIdentifer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515A5176243E90200089E588 /* ExtensionPointIdentifer.swift */; }; 515A517B243E90260089E588 /* ExtensionPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43F6243D035C009F70C3 /* ExtensionPoint.swift */; }; 515A517C243E90260089E588 /* ExtensionPointManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515A50E5243D07A90089E588 /* ExtensionPointManager.swift */; }; - 515A517E243E90260089E588 /* SendToMarsEditCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A1500420048DDF0046AD9A /* SendToMarsEditCommand.swift */; }; - 515A517F243E90260089E588 /* SendToMicroBlogCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A14FF220048CA70046AD9A /* SendToMicroBlogCommand.swift */; }; 515A5180243E90260089E588 /* TwitterFeedProvider-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515A5106243D0CCD0089E588 /* TwitterFeedProvider-Extensions.swift */; }; 515A5181243E90260089E588 /* ExtensionPointIdentifer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515A5176243E90200089E588 /* ExtensionPointIdentifer.swift */; }; 515D4FC123257A3200EE1167 /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; }; @@ -152,8 +150,8 @@ 51627A6923861DED007B3B4B /* MasterFeedViewController+Drop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */; }; 51627A6B238629D8007B3B4B /* MasterFeedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6A238629D8007B3B4B /* MasterFeedDataSource.swift */; }; 51627A93238A3836007B3B4B /* CroppingPreviewParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A92238A3836007B3B4B /* CroppingPreviewParameters.swift */; }; - 516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */; }; - 516A09392360A2AE00EAE89B /* SettingsAccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */; }; + 516A093723609A3600EAE89B /* SettingsComboTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A091D23609A3600EAE89B /* SettingsComboTableViewCell.xib */; }; + 516A09392360A2AE00EAE89B /* SettingsComboTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516A09382360A2AE00EAE89B /* SettingsComboTableViewCell.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 */; }; @@ -189,6 +187,11 @@ 51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; }; 519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; }; 519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; }; + 519ED43F24482629007F8E94 /* FeedProvider.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51107672243BCE0500D97C8C /* FeedProvider.framework */; }; + 519ED44024482629007F8E94 /* FeedProvider.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 51107672243BCE0500D97C8C /* FeedProvider.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 519ED456244828C3007F8E94 /* AddExtensionPointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */; }; + 519ED47A24482AEB007F8E94 /* EnableExtensionPointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */; }; + 519ED47C24488C6F007F8E94 /* ExtensionInspectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519ED47B24488C6F007F8E94 /* ExtensionInspectorViewController.swift */; }; 51A16999235E10D700EB091F /* LocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698F235E10D600EB091F /* LocalAccountViewController.swift */; }; 51A1699A235E10D700EB091F /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51A16990235E10D600EB091F /* Settings.storyboard */; }; 51A1699B235E10D700EB091F /* AccountInspectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16991235E10D600EB091F /* AccountInspectorViewController.swift */; }; @@ -1277,6 +1280,7 @@ 51C451F92264C83E00C03939 /* Account.framework in Embed Frameworks */, 51C451F12264C83100C03939 /* ArticlesDatabase.framework in Embed Frameworks */, 517A757C24451C1500B553B9 /* OAuthSwift.framework in Embed Frameworks */, + 519ED44024482629007F8E94 /* FeedProvider.framework in Embed Frameworks */, 51C451F52264C83900C03939 /* Articles.framework in Embed Frameworks */, 51C451E92264C81000C03939 /* RSDatabase.framework in Embed Frameworks */, 51554C31228B71A10055115A /* SyncDatabase.framework in Embed Frameworks */, @@ -1463,8 +1467,8 @@ 51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drop.swift"; sourceTree = ""; }; 51627A6A238629D8007B3B4B /* MasterFeedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterFeedDataSource.swift; sourceTree = ""; }; 51627A92238A3836007B3B4B /* CroppingPreviewParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CroppingPreviewParameters.swift; 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 = ""; }; + 516A091D23609A3600EAE89B /* SettingsComboTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsComboTableViewCell.xib; sourceTree = ""; }; + 516A09382360A2AE00EAE89B /* SettingsComboTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsComboTableViewCell.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 = ""; }; @@ -1490,6 +1494,9 @@ 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTimelineFeedDelegate.swift; sourceTree = ""; }; 519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = ""; }; 519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExtensionPointViewController.swift; sourceTree = ""; }; + 519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionPointViewController.swift; sourceTree = ""; }; + 519ED47B24488C6F007F8E94 /* ExtensionInspectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionInspectorViewController.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 /* AccountInspectorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountInspectorViewController.swift; sourceTree = ""; }; @@ -1885,6 +1892,7 @@ 51C451E82264C81000C03939 /* RSDatabase.framework in Frameworks */, 51E4DB082425F9EB0091EB5B /* CloudKit.framework in Frameworks */, 51C451EC2264C81B00C03939 /* RSCore.framework in Frameworks */, + 519ED43F24482629007F8E94 /* FeedProvider.framework in Frameworks */, 51554C30228B71A10055115A /* SyncDatabase.framework in Frameworks */, 51C451E42264C80600C03939 /* RSParser.framework in Frameworks */, ); @@ -1982,8 +1990,9 @@ children = ( 516A09412361248000EAE89B /* Inspector.storyboard */, 51A16991235E10D600EB091F /* AccountInspectorViewController.swift */, - 5141E7382373C18B0013FF27 /* WebFeedInspectorViewController.swift */, + 519ED47B24488C6F007F8E94 /* ExtensionInspectorViewController.swift */, 5110C37C2373A8D100A9C04F /* InspectorIconHeaderView.swift */, + 5141E7382373C18B0013FF27 /* WebFeedInspectorViewController.swift */, ); path = Inspector; sourceTree = ""; @@ -2111,9 +2120,11 @@ 51A16990235E10D600EB091F /* Settings.storyboard */, 51A16995235E10D600EB091F /* AboutViewController.swift */, 51A16992235E10D600EB091F /* AddAccountViewController.swift */, + 519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */, + 519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */, 516244E2241E19F000B61C47 /* ColorPaletteTableViewController.swift */, - 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */, - 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */, + 516A09382360A2AE00EAE89B /* SettingsComboTableViewCell.swift */, + 516A091D23609A3600EAE89B /* SettingsComboTableViewCell.xib */, 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */, 51A16993235E10D600EB091F /* SettingsViewController.swift */, 5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */, @@ -3824,7 +3835,7 @@ 511D43D2231FA62C00FB1562 /* GlobalKeyboardShortcuts.plist in Resources */, 84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */, 51BB7C312335ACDE008E8144 /* page.html in Resources */, - 516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */, + 516A093723609A3600EAE89B /* SettingsComboTableViewCell.xib in Resources */, 51F85BF32272531500C787DC /* Dedication.rtf in Resources */, 516A09422361248000EAE89B /* Inspector.storyboard in Resources */, 5103A9B424216A4200410853 /* blank.html in Resources */, @@ -4254,6 +4265,7 @@ 51F85BFD2275DCA800C787DC /* SingleLineUILabelSizer.swift in Sources */, 517630232336657E00E15FFF /* WebViewProvider.swift in Sources */, 51E43962238037C400015C31 /* AddWebFeedFolderViewController.swift in Sources */, + 519ED47A24482AEB007F8E94 /* EnableExtensionPointViewController.swift in Sources */, 51C4528F226509BD00C03939 /* UnreadFeed.swift in Sources */, 51FD413B2342BD0500880194 /* MasterTimelineUnreadCountView.swift in Sources */, 513146B2235A81A400387FDC /* AddWebFeedIntentHandler.swift in Sources */, @@ -4261,7 +4273,7 @@ 51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */, 51C452A522650A2D00C03939 /* SmallIconProvider.swift in Sources */, 51AB8AB323B7F4C6008F147D /* WebViewController.swift in Sources */, - 516A09392360A2AE00EAE89B /* SettingsAccountTableViewCell.swift in Sources */, + 516A09392360A2AE00EAE89B /* SettingsComboTableViewCell.swift in Sources */, 3B3A32A5238B820900314204 /* FeedWranglerAccountViewController.swift in Sources */, 51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */, 51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */, @@ -4286,6 +4298,7 @@ 51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */, 5F323809231DF9F000706F6B /* VibrantTableViewCell.swift in Sources */, 51FE10042345529D0056195D /* UserNotificationManager.swift in Sources */, + 519ED47C24488C6F007F8E94 /* ExtensionInspectorViewController.swift in Sources */, 51C452A022650A1900C03939 /* WebFeedIconDownloader.swift in Sources */, 51C4529E22650A1900C03939 /* ImageDownloader.swift in Sources */, 51A66685238075AE00CB272D /* AddWebFeedDefaultContainer.swift in Sources */, @@ -4358,11 +4371,11 @@ 51DC370B2405BC9A0095D371 /* PreloadedWebView.swift in Sources */, C5A6ED5223C9AF4300AB6BE2 /* TitleActivityItemSource.swift in Sources */, 51DC37092402F1470095D371 /* MasterFeedDataSourceOperation.swift in Sources */, - 515A517E243E90260089E588 /* SendToMarsEditCommand.swift in Sources */, 51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */, 84DEE56622C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */, 512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */, 516AE9E02372269A007DEEAA /* IconImage.swift in Sources */, + 519ED456244828C3007F8E94 /* AddExtensionPointViewController.swift in Sources */, 51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */, 5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */, 51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */, @@ -4372,7 +4385,6 @@ 51C452A322650A1E00C03939 /* HTMLMetadataDownloader.swift in Sources */, 51C4528D2265095F00C03939 /* AddFolderViewController.swift in Sources */, 51DC37072402153E0095D371 /* UpdateSelectionOperation.swift in Sources */, - 515A517F243E90260089E588 /* SendToMicroBlogCommand.swift in Sources */, 51C452782265091600C03939 /* MasterTimelineCellData.swift in Sources */, 5148F4552336DB7000F8CD8B /* MasterTimelineTitleView.swift in Sources */, 515A517C243E90260089E588 /* ExtensionPointManager.swift in Sources */, diff --git a/Shared/ExtensionPoints/ExtensionPoint.swift b/Shared/ExtensionPoints/ExtensionPoint.swift index 8d8355b30..86a84557d 100644 --- a/Shared/ExtensionPoints/ExtensionPoint.swift +++ b/Shared/ExtensionPoints/ExtensionPoint.swift @@ -6,7 +6,11 @@ // Copyright © 2020 Ranchero Software. All rights reserved. // -import Foundation +#if os(macOS) +import AppKit +#else +import UIKit +#endif import RSCore protocol ExtensionPoint { @@ -35,11 +39,19 @@ extension ExtensionPoint { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .center + #if os(macOS) let attrs = [ NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font: NSFont.systemFont(ofSize: NSFont.systemFontSize), NSAttributedString.Key.foregroundColor: NSColor.textColor ] + #else + let attrs = [ + NSAttributedString.Key.paragraphStyle: paragraphStyle, + NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .body), + NSAttributedString.Key.foregroundColor: UIColor.label + ] + #endif return NSMutableAttributedString(string: text, attributes: attrs) } diff --git a/Shared/ExtensionPoints/ExtensionPointIdentifer.swift b/Shared/ExtensionPoints/ExtensionPointIdentifer.swift index 97c471656..0ecbe5ff7 100644 --- a/Shared/ExtensionPoints/ExtensionPointIdentifer.swift +++ b/Shared/ExtensionPoints/ExtensionPointIdentifer.swift @@ -11,16 +11,20 @@ import FeedProvider import RSCore enum ExtensionPointIdentifer: Hashable { + #if os(macOS) case marsEdit case microblog + #endif case twitter(String, String) var extensionPointType: ExtensionPoint.Type { switch self { + #if os(macOS) case .marsEdit: return SendToMarsEditCommand.self case .microblog: return SendToMicroBlogCommand.self + #endif case .twitter: return TwitterFeedProvider.self } @@ -28,6 +32,7 @@ enum ExtensionPointIdentifer: Hashable { public var userInfo: [AnyHashable: AnyHashable] { switch self { + #if os(macOS) case .marsEdit: return [ "type": "marsEdit" @@ -36,6 +41,7 @@ enum ExtensionPointIdentifer: Hashable { return [ "type": "microblog" ] + #endif case .twitter(let userID, let screenName): return [ "type": "twitter", @@ -49,10 +55,12 @@ enum ExtensionPointIdentifer: Hashable { guard let type = userInfo["type"] as? String else { return nil } switch type { + #if os(macOS) case "marsEdit": self = ExtensionPointIdentifer.marsEdit case "microblog": self = ExtensionPointIdentifer.microblog + #endif case "twitter": guard let userID = userInfo["userID"] as? String, let screenName = userInfo["screenName"] as? String else { return nil } self = ExtensionPointIdentifer.twitter(userID, screenName) @@ -63,10 +71,12 @@ enum ExtensionPointIdentifer: Hashable { public func hash(into hasher: inout Hasher) { switch self { + #if os(macOS) case .marsEdit: hasher.combine("marsEdit") case .microblog: hasher.combine("microblog") + #endif case .twitter(let userID, let screenName): hasher.combine("twitter") hasher.combine(userID) diff --git a/Shared/ExtensionPoints/ExtensionPointManager.swift b/Shared/ExtensionPoints/ExtensionPointManager.swift index 6a9467365..4cc3b7a41 100644 --- a/Shared/ExtensionPoints/ExtensionPointManager.swift +++ b/Shared/ExtensionPoints/ExtensionPointManager.swift @@ -103,10 +103,12 @@ private extension ExtensionPointManager { func extensionPoint(for extensionPointType: ExtensionPoint.Type, tokenSuccess: OAuthSwift.TokenSuccess?) -> ExtensionPoint? { switch extensionPointType { + #if os(macOS) case is SendToMarsEditCommand.Type: return SendToMarsEditCommand() case is SendToMicroBlogCommand.Type: return SendToMicroBlogCommand() + #endif case is TwitterFeedProvider.Type: if let tokenSuccess = tokenSuccess { return TwitterFeedProvider(tokenSuccess: tokenSuccess) @@ -121,10 +123,12 @@ private extension ExtensionPointManager { func extensionPoint(for extensionPointID: ExtensionPointIdentifer) -> ExtensionPoint? { switch extensionPointID { + #if os(macOS) case .marsEdit: return SendToMarsEditCommand() case .microblog: return SendToMicroBlogCommand() + #endif case .twitter(let userID, let screenName): return TwitterFeedProvider(userID: userID, screenName: screenName) } diff --git a/Shared/ExtensionPoints/TwitterFeedProvider-Extensions.swift b/Shared/ExtensionPoints/TwitterFeedProvider-Extensions.swift index 92699be06..eee2b444a 100644 --- a/Shared/ExtensionPoints/TwitterFeedProvider-Extensions.swift +++ b/Shared/ExtensionPoints/TwitterFeedProvider-Extensions.swift @@ -18,13 +18,7 @@ extension TwitterFeedProvider: ExtensionPoint { static var title = NSLocalizedString("Twitter", comment: "Twitter") static var templateImage = AppAssets.extensionPointTwitter static var description: NSAttributedString = { - let attrString = TwitterFeedProvider.makeAttrString("This extension enables you to subscribe to Twitter URL's as if they were RSS feeds.") - let range = NSRange(location: 43, length: 7) - attrString.beginEditing() - attrString.addAttribute(NSAttributedString.Key.link, value: "https://twitter.com", range: range) - attrString.addAttribute(NSAttributedString.Key.foregroundColor, value: NSColor.systemBlue, range: range) - attrString.endEditing() - return attrString + return TwitterFeedProvider.makeAttrString("This extension enables you to subscribe to Twitter URL's as if they were RSS feeds.") }() var extensionPointID: ExtensionPointIdentifer { diff --git a/Shared/Extensions/CacheCleaner.swift b/Shared/Extensions/CacheCleaner.swift index 9f36787cd..5c629052f 100644 --- a/Shared/Extensions/CacheCleaner.swift +++ b/Shared/Extensions/CacheCleaner.swift @@ -28,10 +28,11 @@ struct CacheCleaner { let tempDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! let faviconsFolderURL = tempDir.appendingPathComponent("Favicons") let imagesFolderURL = tempDir.appendingPathComponent("Images") + let feedURLToIconURL = tempDir.appendingPathComponent("FeedURLToIconURLCache.plist") let homePageToIconURL = tempDir.appendingPathComponent("HomePageToIconURLCache.plist") let homePagesWithNoIconURL = tempDir.appendingPathComponent("HomePagesWithNoIconURLCache.plist") - for tempItem in [faviconsFolderURL, imagesFolderURL, homePageToIconURL, homePagesWithNoIconURL] { + for tempItem in [faviconsFolderURL, imagesFolderURL, feedURLToIconURL, homePageToIconURL, homePagesWithNoIconURL] { do { os_log(.info, log: self.log, "Removing cache file: %@", tempItem.absoluteString) try FileManager.default.removeItem(at: tempItem) diff --git a/iOS/Account/Account.storyboard b/iOS/Account/Account.storyboard index 9de54a8dd..b4f4a227d 100644 --- a/iOS/Account/Account.storyboard +++ b/iOS/Account/Account.storyboard @@ -2,7 +2,7 @@ - + diff --git a/iOS/Account/FeedWranglerAccountViewController.swift b/iOS/Account/FeedWranglerAccountViewController.swift index b6c297df8..90990343b 100644 --- a/iOS/Account/FeedWranglerAccountViewController.swift +++ b/iOS/Account/FeedWranglerAccountViewController.swift @@ -9,6 +9,7 @@ import UIKit import Account import RSWeb +import Secrets class FeedWranglerAccountViewController: UITableViewController { diff --git a/iOS/Account/FeedbinAccountViewController.swift b/iOS/Account/FeedbinAccountViewController.swift index df6110b64..eb271b0ed 100644 --- a/iOS/Account/FeedbinAccountViewController.swift +++ b/iOS/Account/FeedbinAccountViewController.swift @@ -8,6 +8,7 @@ import UIKit import Account +import Secrets import RSWeb class FeedbinAccountViewController: UITableViewController { diff --git a/iOS/Account/NewsBlurAccountViewController.swift b/iOS/Account/NewsBlurAccountViewController.swift index 9810f8196..0babecde3 100644 --- a/iOS/Account/NewsBlurAccountViewController.swift +++ b/iOS/Account/NewsBlurAccountViewController.swift @@ -8,6 +8,7 @@ import UIKit import Account +import Secrets import RSWeb class NewsBlurAccountViewController: UITableViewController { diff --git a/iOS/AppAssets.swift b/iOS/AppAssets.swift index 58d600aef..ffb89d13e 100644 --- a/iOS/AppAssets.swift +++ b/iOS/AppAssets.swift @@ -101,6 +101,10 @@ struct AppAssets { UIImage(systemName: "square.and.pencil")! }() + static var extensionPointTwitter: UIImage = { + return UIImage(named: "extensionPointTwitter")! + }() + static var faviconTemplateImage: RSImage = { return RSImage(named: "faviconTemplateImage")! }() diff --git a/iOS/AppDefaults.swift b/iOS/AppDefaults.swift index 986378b00..ad7833afb 100644 --- a/iOS/AppDefaults.swift +++ b/iOS/AppDefaults.swift @@ -36,6 +36,7 @@ struct AppDefaults { struct Key { static let userInterfaceColorPalette = "userInterfaceColorPalette" + static let activeExtensionPointIDs = "activeExtensionPointIDs" static let lastImageCacheFlushDate = "lastImageCacheFlushDate" static let firstRunDate = "firstRunDate" static let timelineGroupByFeed = "timelineGroupByFeed" @@ -106,6 +107,15 @@ struct AppDefaults { } } + static var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? { + get { + return UserDefaults.standard.object(forKey: Key.activeExtensionPointIDs) as? [[AnyHashable : AnyHashable]] + } + set { + UserDefaults.standard.set(newValue, forKey: Key.activeExtensionPointIDs) + } + } + static var lastImageCacheFlushDate: Date? { get { return date(for: Key.lastImageCacheFlushDate) diff --git a/iOS/Inspector/ExtensionInspectorViewController.swift b/iOS/Inspector/ExtensionInspectorViewController.swift new file mode 100644 index 000000000..f667bae29 --- /dev/null +++ b/iOS/Inspector/ExtensionInspectorViewController.swift @@ -0,0 +1,63 @@ +// +// ExtensionPointInspectorViewController.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 4/16/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import UIKit + +class ExtensionPointInspectorViewController: UITableViewController { + + @IBOutlet weak var extensionDescription: UILabel! + var extensionPoint: ExtensionPoint? + + override func viewDidLoad() { + super.viewDidLoad() + guard let extensionPoint = extensionPoint else { return } + navigationItem.title = extensionPoint.title + extensionDescription.attributedText = extensionPoint.description + tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader") + } + + @IBAction func disable(_ sender: Any) { + guard let extensionPoint = extensionPoint else { return } + ExtensionPointManager.shared.deactivateExtensionPoint(extensionPoint.extensionPointID) + self.navigationController?.popViewController(animated: true) + } +} + +// MARK: Table View + +extension ExtensionPointInspectorViewController { + + override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: section) + } + + override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let extensionPoint = extensionPoint else { return nil } + + if section == 0 { + let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView + headerView.imageView.image = extensionPoint.templateImage + return headerView + } else { + return super.tableView(tableView, viewForHeaderInSection: section) + } + } + + 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) { + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) + } + +} + diff --git a/iOS/Inspector/Inspector.storyboard b/iOS/Inspector/Inspector.storyboard index 52533c98f..7603d0fab 100644 --- a/iOS/Inspector/Inspector.storyboard +++ b/iOS/Inspector/Inspector.storyboard @@ -1,8 +1,8 @@ - + - + @@ -25,10 +25,10 @@ - + - + @@ -176,7 +176,7 @@ - + @@ -377,10 +377,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/iOS/Resources/Assets.xcassets/extensionPointTwitter.imageset/Contents.json b/iOS/Resources/Assets.xcassets/extensionPointTwitter.imageset/Contents.json new file mode 100644 index 000000000..834100cda --- /dev/null +++ b/iOS/Resources/Assets.xcassets/extensionPointTwitter.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "twitter.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/iOS/Resources/Assets.xcassets/extensionPointTwitter.imageset/twitter.pdf b/iOS/Resources/Assets.xcassets/extensionPointTwitter.imageset/twitter.pdf new file mode 100644 index 000000000..e50de4443 Binary files /dev/null and b/iOS/Resources/Assets.xcassets/extensionPointTwitter.imageset/twitter.pdf differ diff --git a/iOS/Settings/AddAccountViewController.swift b/iOS/Settings/AddAccountViewController.swift index db24c743f..921969cbc 100644 --- a/iOS/Settings/AddAccountViewController.swift +++ b/iOS/Settings/AddAccountViewController.swift @@ -40,27 +40,27 @@ class AddAccountViewController: UITableViewController, AddAccountDismissDelegate } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "SettingsAccountTableViewCell", for: indexPath) as! SettingsAccountTableViewCell + let cell = tableView.dequeueReusableCell(withIdentifier: "SettingsAccountTableViewCell", for: indexPath) as! SettingsComboTableViewCell switch addableAccountTypes[indexPath.row] { case .onMyMac: - cell.accountNameLabel?.text = Account.defaultLocalAccountName - cell.accountImage?.image = AppAssets.image(for: .onMyMac) + cell.comboNameLabel?.text = Account.defaultLocalAccountName + cell.comboImage?.image = AppAssets.image(for: .onMyMac) case .cloudKit: - cell.accountNameLabel?.text = NSLocalizedString("iCloud", comment: "iCloud") - cell.accountImage?.image = AppAssets.accountCloudKitImage + cell.comboNameLabel?.text = NSLocalizedString("iCloud", comment: "iCloud") + cell.comboImage?.image = AppAssets.accountCloudKitImage case .feedbin: - cell.accountNameLabel?.text = NSLocalizedString("Feedbin", comment: "Feedbin") - cell.accountImage?.image = AppAssets.accountFeedbinImage + cell.comboNameLabel?.text = NSLocalizedString("Feedbin", comment: "Feedbin") + cell.comboImage?.image = AppAssets.accountFeedbinImage case .feedWrangler: - cell.accountNameLabel?.text = NSLocalizedString("Feed Wrangler", comment: "Feed Wrangler") - cell.accountImage?.image = AppAssets.accountFeedWranglerImage + cell.comboNameLabel?.text = NSLocalizedString("Feed Wrangler", comment: "Feed Wrangler") + cell.comboImage?.image = AppAssets.accountFeedWranglerImage case .feedly: - cell.accountNameLabel?.text = NSLocalizedString("Feedly", comment: "Feedly") - cell.accountImage?.image = AppAssets.accountFeedlyImage + cell.comboNameLabel?.text = NSLocalizedString("Feedly", comment: "Feedly") + cell.comboImage?.image = AppAssets.accountFeedlyImage case .newsBlur: - cell.accountNameLabel?.text = NSLocalizedString("NewsBlur", comment: "NewsBlur") - cell.accountImage?.image = AppAssets.accountNewsBlurImage + cell.comboNameLabel?.text = NSLocalizedString("NewsBlur", comment: "NewsBlur") + cell.comboImage?.image = AppAssets.accountNewsBlurImage default: break } diff --git a/iOS/Settings/AddExtensionPointViewController.swift b/iOS/Settings/AddExtensionPointViewController.swift new file mode 100644 index 000000000..a9b44fce0 --- /dev/null +++ b/iOS/Settings/AddExtensionPointViewController.swift @@ -0,0 +1,59 @@ +// +// AddExtensionPointViewController.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 4/16/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import UIKit + +protocol AddExtensionPointDismissDelegate: UIViewController { + func dismiss() +} + +class AddExtensionPointViewController: UITableViewController, AddExtensionPointDismissDelegate { + + private var availableExtensionPointTypes = [ExtensionPoint.Type]() + + override func viewDidLoad() { + super.viewDidLoad() + availableExtensionPointTypes = ExtensionPointManager.shared.availableExtensionPointTypes + } + + override func numberOfSections(in tableView: UITableView) -> Int { + 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return availableExtensionPointTypes.count + } + + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 52.0 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "SettingsExtensionTableViewCell", for: indexPath) as! SettingsComboTableViewCell + + let extensionPointType = availableExtensionPointTypes[indexPath.row] + cell.comboNameLabel?.text = extensionPointType.title + cell.comboImage?.image = extensionPointType.templateImage + + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "EnableExtensionPointNavigationViewController") as! UINavigationController + navController.modalPresentationStyle = .currentContext + let enableViewController = navController.topViewController as! EnableExtensionPointViewController + enableViewController.delegate = self + enableViewController.extensionPointType = availableExtensionPointTypes[indexPath.row] + present(navController, animated: true) + } + + func dismiss() { + navigationController?.popViewController(animated: false) + } + +} diff --git a/iOS/Settings/AddExtensionViewContrller.swift b/iOS/Settings/AddExtensionViewContrller.swift new file mode 100644 index 000000000..91125f159 --- /dev/null +++ b/iOS/Settings/AddExtensionViewContrller.swift @@ -0,0 +1,59 @@ +// +// AddExtensionViewContrller.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 4/16/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import UIKit + +protocol AddExtensionDismissDelegate: UIViewController { + func dismiss() +} + +class AddExtensionViewController: UITableViewController, AddExtensionDismissDelegate { + + private var availableExtensionPointTypes = [ExtensionPoint.Type]() + + override func viewDidLoad() { + super.viewDidLoad() + availableExtensionPointTypes = ExtensionPointManager.shared.availableExtensionPointTypes + } + + override func numberOfSections(in tableView: UITableView) -> Int { + 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return availableExtensionPointTypes.count + } + + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 52.0 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "SettingsExtensionTableViewCell", for: indexPath) as! SettingsComboTableViewCell + + let extensionPointType = availableExtensionPointTypes[indexPath.row] + cell.comboNameLabel?.text = extensionPointType.title + cell.comboImage?.image = extensionPointType.templateImage + + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "EnableExtensiontNavigationViewController") as! UINavigationController + navController.modalPresentationStyle = .currentContext + let enableViewController = navController.topViewController as! EnableExtensionViewController + enableViewController.delegate = self + enableViewController.extensionPointType = availableExtensionPointTypes[indexPath.row] + present(navController, animated: true) + } + + func dismiss() { + navigationController?.popViewController(animated: false) + } + +} diff --git a/iOS/Settings/EnableExtensionPointViewController.swift b/iOS/Settings/EnableExtensionPointViewController.swift new file mode 100644 index 000000000..a3099f3df --- /dev/null +++ b/iOS/Settings/EnableExtensionPointViewController.swift @@ -0,0 +1,132 @@ +// +// EnableExtensionPointViewController.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 4/16/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import UIKit +import AuthenticationServices +import Account +import OAuthSwift +import Secrets + +class EnableExtensionPointViewController: UITableViewController { + + @IBOutlet weak var extensionDescription: UILabel! + + private let callbackURL = URL(string: "vincodennw://")! + private var oauth: OAuthSwift? + + weak var delegate: AddExtensionPointDismissDelegate? + var extensionPointType: ExtensionPoint.Type? + + override func viewDidLoad() { + super.viewDidLoad() + navigationItem.title = extensionPointType?.title + extensionDescription.attributedText = extensionPointType?.description + tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader") + } + + @IBAction func cancel(_ sender: Any) { + dismiss(animated: true, completion: nil) + delegate?.dismiss() + } + + @IBAction func enable(_ sender: Any) { + guard let extensionPointType = extensionPointType else { return } + + if let oauth1 = extensionPointType as? OAuth1SwiftProvider.Type { + enableOauth1(oauth1) + } else { + ExtensionPointManager.shared.activateExtensionPoint(extensionPointType) + dismiss(animated: true, completion: nil) + delegate?.dismiss() + } + } + + override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: section) + } + + override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + if section == 0 { + let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView + headerView.imageView.image = extensionPointType?.templateImage + return headerView + } else { + return super.tableView(tableView, viewForHeaderInSection: section) + } + } + +} + +extension EnableExtensionPointViewController: OAuthSwiftURLHandlerType { + + public func handle(_ url: URL) { + let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURL.scheme, completionHandler: { (url, error) in + if let callbackedURL = url { + OAuth1Swift.handle(url: callbackedURL) + } + + guard let error = error else { return } + + self.oauth?.cancel() + self.oauth = nil + + DispatchQueue.main.async { + self.dismiss(animated: true, completion: nil) + self.delegate?.dismiss() + } + + if case ASWebAuthenticationSessionError.canceledLogin = error { + print("Login cancelled.") + } else { + self.presentError(error) + } + }) + + session.presentationContextProvider = self + if !session.start() { + print("Session failed to start!!!") + } + + } +} + +extension EnableExtensionPointViewController: ASWebAuthenticationPresentationContextProviding { + + public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { + return view.window! + } + +} + +private extension EnableExtensionPointViewController { + + func enableOauth1(_ provider: OAuth1SwiftProvider.Type) { + + let oauth1 = provider.oauth1Swift + self.oauth = oauth1 + oauth1.authorizeURLHandler = self + + oauth1.authorize(withCallbackURL: callbackURL) { [weak self] result in + guard let self = self, let extensionPointType = self.extensionPointType else { return } + + switch result { + case .success(let tokenSuccess): + ExtensionPointManager.shared.activateExtensionPoint(extensionPointType, tokenSuccess: tokenSuccess) + self.dismiss(animated: true, completion: nil) + self.delegate?.dismiss() + case .failure(let oauthSwiftError): + self.presentError(oauthSwiftError) + } + + self.oauth?.cancel() + self.oauth = nil + } + + } + +} diff --git a/iOS/Settings/EnableExtensionViewController.swift b/iOS/Settings/EnableExtensionViewController.swift new file mode 100644 index 000000000..0732c43a2 --- /dev/null +++ b/iOS/Settings/EnableExtensionViewController.swift @@ -0,0 +1,132 @@ +// +// EnableExtensionViewController.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 4/16/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import UIKit +import AuthenticationServices +import Account +import OAuthSwift +import Secrets + +class EnableExtensionPointViewController: UITableViewController { + + @IBOutlet weak var extensionDescription: UILabel! + + private let callbackURL = URL(string: "vincodennw://")! + private var oauth: OAuthSwift? + + weak var delegate: AddExtensionPointDismissDelegate? + var extensionPointType: ExtensionPoint.Type? + + override func viewDidLoad() { + super.viewDidLoad() + navigationItem.title = extensionPointType?.title ?? "" + extensionDescription = extensionPointType?.extensionDescription ?? "" + tableView.register(ImageHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader") + } + + @IBAction func cancel(_ sender: Any) { + dismiss(animated: true, completion: nil) + delegate?.dismiss() + } + + @IBAction func enable(_ sender: Any) { + guard let extensionPointType = extensionPointType else { return } + + if let oauth1 = extensionPointType as? OAuth1SwiftProvider.Type { + enableOauth1(oauth1) + } else { + ExtensionPointManager.shared.activateExtensionPoint(extensionPointType) + dismiss(animated: true, completion: nil) + delegate?.dismiss() + } + } + + override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: section) + } + + override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + if section == 0 { + let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView + headerView.imageView.image = extensionPointType?.templateImage + return headerView + } else { + return super.tableView(tableView, viewForHeaderInSection: section) + } + } + +} + +extension EnableExtensionPointViewController: OAuthSwiftURLHandlerType { + + public func handle(_ url: URL) { + let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURL.scheme, completionHandler: { (url, error) in + if let callbackedURL = url { + OAuth1Swift.handle(url: callbackedURL) + } + + guard let error = error else { return } + + self.oauth?.cancel() + self.oauth = nil + + DispatchQueue.main.async { + self.dismiss(animated: true, completion: nil) + self.delegate?.dismiss() + } + + if case ASWebAuthenticationSessionError.canceledLogin = error { + print("Login cancelled.") + } else { + self.presentError(error) + } + }) + + session.presentationContextProvider = self + if !session.start() { + print("Session failed to start!!!") + } + + } +} + +extension EnableExtensionPointViewController: ASWebAuthenticationPresentationContextProviding { + + public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { + return view.window! + } + +} + +private extension EnableExtensionPointViewController { + + func enableOauth1(_ provider: OAuth1SwiftProvider.Type) { + + let oauth1 = provider.oauth1Swift + self.oauth = oauth1 + oauth1.authorizeURLHandler = self + + oauth1.authorize(withCallbackURL: callbackURL) { [weak self] result in + guard let self = self, let extensionPointType = self.extensionPointType else { return } + + switch result { + case .success(let tokenSuccess): + ExtensionPointManager.shared.activateExtensionPoint(extensionPointType, tokenSuccess: tokenSuccess) + self.dismiss(animated: true, completion: nil) + self.delegate?.dismiss() + case .failure(let oauthSwiftError): + self.presentError(oauthSwiftError) + } + + self.oauth?.cancel() + self.oauth = nil + } + + } + +} diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 7d7c0f0f6..04a998ca9 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -2,7 +2,7 @@ - + @@ -58,10 +58,31 @@ + + + + + + + + + + + + + + + - + @@ -78,7 +99,7 @@ - + @@ -95,7 +116,7 @@ - + @@ -116,7 +137,7 @@ - + @@ -149,7 +170,7 @@ - + @@ -182,7 +203,7 @@ - + @@ -215,7 +236,7 @@ - + @@ -242,7 +263,7 @@ - + @@ -275,7 +296,7 @@ - + @@ -320,7 +341,7 @@ - + @@ -356,14 +377,14 @@ - + - + - + @@ -407,7 +428,7 @@ - + @@ -424,7 +445,7 @@ - + @@ -441,7 +462,7 @@ - + @@ -458,7 +479,7 @@ - + @@ -475,7 +496,7 @@ - + @@ -527,7 +548,7 @@ - + @@ -560,8 +581,8 @@ - - + + @@ -726,7 +747,7 @@ - + @@ -829,7 +850,7 @@ - + @@ -885,7 +906,7 @@ - + @@ -926,9 +947,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Settings/SettingsAccountTableViewCell.swift b/iOS/Settings/SettingsComboTableViewCell.swift similarity index 61% rename from iOS/Settings/SettingsAccountTableViewCell.swift rename to iOS/Settings/SettingsComboTableViewCell.swift index 2d5ba352d..ec1775600 100644 --- a/iOS/Settings/SettingsAccountTableViewCell.swift +++ b/iOS/Settings/SettingsComboTableViewCell.swift @@ -8,18 +8,18 @@ import UIKit -class SettingsAccountTableViewCell: VibrantTableViewCell { +class SettingsComboTableViewCell: VibrantTableViewCell { - @IBOutlet weak var accountImage: UIImageView! - @IBOutlet weak var accountNameLabel: UILabel! + @IBOutlet weak var comboImage: UIImageView! + @IBOutlet weak var comboNameLabel: UILabel! override func updateVibrancy(animated: Bool) { super.updateVibrancy(animated: animated) - updateLabelVibrancy(accountNameLabel, color: labelColor, animated: animated) + updateLabelVibrancy(comboNameLabel, color: labelColor, animated: animated) let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : UIColor.label UIView.animate(withDuration: duration(animated: animated)) { - self.accountImage?.tintColor = tintColor + self.comboImage?.tintColor = tintColor } } diff --git a/iOS/Settings/SettingsAccountTableViewCell.xib b/iOS/Settings/SettingsComboTableViewCell.xib similarity index 90% rename from iOS/Settings/SettingsAccountTableViewCell.xib rename to iOS/Settings/SettingsComboTableViewCell.xib index 9eafae53f..4cf193f27 100644 --- a/iOS/Settings/SettingsAccountTableViewCell.xib +++ b/iOS/Settings/SettingsComboTableViewCell.xib @@ -1,14 +1,14 @@ - + - + - + @@ -39,8 +39,8 @@ - - + + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 0f3c47505..3814e3ddc 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -34,8 +34,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) NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange), name: .DisplayNameDidChange, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(activeExtensionPointsDidChange), name: .ActiveExtensionPointsDidChange, object: nil) - tableView.register(UINib(nibName: "SettingsAccountTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsAccountTableViewCell") + tableView.register(UINib(nibName: "SettingsComboTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsComboTableViewCell") tableView.register(UINib(nibName: "SettingsTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsTableViewCell") tableView.rowHeight = UITableView.automaticDimension @@ -110,12 +111,14 @@ class SettingsViewController: UITableViewController { case 1: return AccountManager.shared.accounts.count + 1 case 2: + return ExtensionPointManager.shared.activeExtensionPoints.count + 1 + case 3: let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section) if AccountManager.shared.activeAccounts.isEmpty || AccountManager.shared.anyAccountHasFeedWithURL(appNewsURLString) { return defaultNumberOfRows - 1 } return defaultNumberOfRows - case 4: + case 5: return traitCollection.userInterfaceIdiom == .phone ? 2 : 1 default: return super.tableView(tableView, numberOfRowsInSection: section) @@ -133,11 +136,26 @@ class SettingsViewController: UITableViewController { cell = tableView.dequeueReusableCell(withIdentifier: "SettingsTableViewCell", for: indexPath) cell.textLabel?.text = NSLocalizedString("Add Account", comment: "Accounts") } else { - let acctCell = tableView.dequeueReusableCell(withIdentifier: "SettingsAccountTableViewCell", for: indexPath) as! SettingsAccountTableViewCell + let acctCell = tableView.dequeueReusableCell(withIdentifier: "SettingsComboTableViewCell", for: indexPath) as! SettingsComboTableViewCell acctCell.applyThemeProperties() let account = sortedAccounts[indexPath.row] - acctCell.accountImage?.image = AppAssets.image(for: account.type) - acctCell.accountNameLabel?.text = account.nameForDisplay + acctCell.comboImage?.image = AppAssets.image(for: account.type) + acctCell.comboNameLabel?.text = account.nameForDisplay + cell = acctCell + } + + case 2: + + let extensionPoints = Array(ExtensionPointManager.shared.activeExtensionPoints.values) + if indexPath.row == extensionPoints.count { + cell = tableView.dequeueReusableCell(withIdentifier: "SettingsTableViewCell", for: indexPath) + cell.textLabel?.text = NSLocalizedString("Add Extension", comment: "Extensions") + } else { + let acctCell = tableView.dequeueReusableCell(withIdentifier: "SettingsComboTableViewCell", for: indexPath) as! SettingsComboTableViewCell + acctCell.applyThemeProperties() + let extensionPoint = extensionPoints[indexPath.row] + acctCell.comboImage?.image = extensionPoint.templateImage + acctCell.comboNameLabel?.text = extensionPoint.title cell = acctCell } @@ -166,6 +184,16 @@ class SettingsViewController: UITableViewController { self.navigationController?.pushViewController(controller, animated: true) } case 2: + let extensionPoints = Array(ExtensionPointManager.shared.activeExtensionPoints.values) + if indexPath.row == extensionPoints.count { + let controller = UIStoryboard.settings.instantiateController(ofType: AddExtensionPointViewController.self) + self.navigationController?.pushViewController(controller, animated: true) + } else { + let controller = UIStoryboard.inspector.instantiateController(ofType: ExtensionPointInspectorViewController.self) + controller.extensionPoint = extensionPoints[indexPath.row] + self.navigationController?.pushViewController(controller, animated: true) + } + case 3: switch indexPath.row { case 0: tableView.selectRow(at: nil, animated: true, scrollPosition: .none) @@ -185,7 +213,7 @@ class SettingsViewController: UITableViewController { default: break } - case 3: + case 4: switch indexPath.row { case 3: let timeline = UIStoryboard.settings.instantiateController(ofType: TimelineCustomizerViewController.self) @@ -193,10 +221,10 @@ class SettingsViewController: UITableViewController { default: break } - case 5: + case 6: let colorPalette = UIStoryboard.settings.instantiateController(ofType: ColorPaletteTableViewController.self) self.navigationController?.pushViewController(colorPalette, animated: true) - case 6: + case 7: switch indexPath.row { case 0: openURL("https://ranchero.com/netnewswire/help/ios/5.0/en/") @@ -310,6 +338,10 @@ class SettingsViewController: UITableViewController { tableView.reloadData() } + @objc func activeExtensionPointsDidChange() { + tableView.reloadData() + } + } // MARK: OPML Document Picker