From ca4a7f8b0b1edd463ff752ebdd94a94d25a387cb Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Fri, 27 Sep 2019 19:45:09 -0500 Subject: [PATCH] Add Feed Inspector --- NetNewsWire.xcodeproj/project.pbxproj | 12 +++ iOS/Inspector/FeedInspectorView.swift | 99 +++++++++++++++++++ .../MasterTimelineViewController.swift | 21 +++- iOS/SceneCoordinator.swift | 9 ++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 iOS/Inspector/FeedInspectorView.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 2cc3c8173..8bdb83bab 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 511D43D2231FA62C00FB1562 /* GlobalKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 844B5B641FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist */; }; 511D43EF231FBDE900FB1562 /* LaunchScreenPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 511D43ED231FBDE800FB1562 /* LaunchScreenPad.storyboard */; }; 511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511D4410231FC02D00FB1562 /* KeyboardManager.swift */; }; + 5123DB9F233EC6FD00282CC9 /* FeedInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5123DB9E233EC6FD00282CC9 /* FeedInspectorView.swift */; }; 5126EE97226CB48A00C22AFC /* SceneCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5126EE96226CB48A00C22AFC /* SceneCoordinator.swift */; }; 5127B238222B4849006D641D /* DetailKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5127B236222B4849006D641D /* DetailKeyboardDelegate.swift */; }; 5127B23A222B4849006D641D /* DetailKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */; }; @@ -787,6 +788,7 @@ 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RSImage-Extensions.swift"; sourceTree = ""; }; 511D43EE231FBDE800FB1562 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreenPad.storyboard; sourceTree = ""; }; 511D4410231FC02D00FB1562 /* KeyboardManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardManager.swift; sourceTree = ""; }; + 5123DB9E233EC6FD00282CC9 /* FeedInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedInspectorView.swift; sourceTree = ""; }; 5126EE96226CB48A00C22AFC /* SceneCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneCoordinator.swift; sourceTree = ""; }; 5127B236222B4849006D641D /* DetailKeyboardDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailKeyboardDelegate.swift; sourceTree = ""; }; 5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = DetailKeyboardShortcuts.plist; sourceTree = ""; }; @@ -1176,6 +1178,14 @@ path = Resources; sourceTree = ""; }; + 5123DB95233EC69300282CC9 /* Inspector */ = { + isa = PBXGroup; + children = ( + 5123DB9E233EC6FD00282CC9 /* FeedInspectorView.swift */, + ); + path = Inspector; + sourceTree = ""; + }; 5127B235222B4849006D641D /* Keyboard */ = { isa = PBXGroup; children = ( @@ -1968,6 +1978,7 @@ 51C4526D2265091600C03939 /* MasterTimeline */, 51C4527D2265092C00C03939 /* Article */, 51C452802265093600C03939 /* Add */, + 5123DB95233EC69300282CC9 /* Inspector */, 5183CCEB227117C70010922C /* Settings */, 5183CCDB226F1EEB0010922C /* Progress */, 519D740423243C68008BB345 /* Model Extensions */, @@ -2828,6 +2839,7 @@ 51C452AE2265104D00C03939 /* TimelineStringFormatter.swift in Sources */, 512E08E62268800D00BDCFDD /* FolderTreeControllerDelegate.swift in Sources */, 51C4529922650A0000C03939 /* ArticleStylesManager.swift in Sources */, + 5123DB9F233EC6FD00282CC9 /* FeedInspectorView.swift in Sources */, 51EF0F802277A8330050506E /* MasterTimelineCellLayout.swift in Sources */, 51F85BF722749FA100C787DC /* UIFont-Extensions.swift in Sources */, 51C452AF2265108300C03939 /* ArticleArray.swift in Sources */, diff --git a/iOS/Inspector/FeedInspectorView.swift b/iOS/Inspector/FeedInspectorView.swift new file mode 100644 index 000000000..3d15ccac1 --- /dev/null +++ b/iOS/Inspector/FeedInspectorView.swift @@ -0,0 +1,99 @@ +// +// FeedInspector.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 9/27/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Combine +import Account + +struct FeedInspectorView : View { + + @ObservedObject var viewModel: ViewModel + @Environment(\.viewController) private var viewController: UIViewController? + + var body: some View { + NavigationView { + Form { + Section(header: + HStack { + Spacer() + Image(uiImage: self.viewModel.image).resizable().frame(width: 48.0, height: 48.0) + Spacer() + }) { + TextField("Feed Name", text: $viewModel.name) + Toggle(isOn: $viewModel.isArticleExtractorAlwaysOn) { + Text("Reader View is always on") + } + } + Section(header: Text("HOME PAGE")) { + Text(verbatim: self.viewModel.homePageURL) + } + Section(header: Text("FEED URL")) { + Text(verbatim: self.viewModel.feedLinkURL) + } + } + .navigationBarTitle(Text(verbatim: self.viewModel.nameForDisplay), displayMode: .inline) + .navigationBarItems(leading: Button(action: { self.viewController?.dismiss(animated: true) }) { Text("Done") } ) + } + } + + // MARK: ViewModel + + class ViewModel: ObservableObject { + + let objectWillChange = ObservableObjectPublisher() + let feed: Feed + + init(feed: Feed) { + self.feed = feed + } + + var image: UIImage { + if let feedIcon = appDelegate.feedIconDownloader.icon(for: feed) { + return feedIcon + } + if let favicon = appDelegate.faviconDownloader.favicon(for: feed) { + return favicon + } + return FaviconGenerator.favicon(feed) + } + + var nameForDisplay: String { + return feed.nameForDisplay + } + + var name: String { + get { + return feed.editedName ?? feed.name ?? "" + } + set { + objectWillChange.send() + feed.editedName = newValue + } + } + + var isArticleExtractorAlwaysOn: Bool { + get { + return feed.isArticleExtractorAlwaysOn ?? false + } + set { + objectWillChange.send() + feed.isArticleExtractorAlwaysOn = newValue + } + } + + var homePageURL: String { + return feed.homePageURL ?? "" + } + + var feedLinkURL: String { + return feed.url + } + + } + +} diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index ed13b926b..241875218 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -46,6 +46,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange), name: .DisplayNameDidChange, object: nil) + // Setup the Search Controller searchController.delegate = self @@ -129,6 +131,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner coordinator.navigateToDetail() } + @objc func showFeedInspector(_ sender: UITapGestureRecognizer) { + coordinator.showFeedInspector() + } + // MARK: API func restoreSelectionIfNecessary() { @@ -365,6 +371,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner updateProgressIndicatorIfNeeded() } + @objc func displayNameDidChange(_ note: Notification) { + titleView?.label.text = coordinator.timelineName + } + // MARK: Reloading func queueReloadAvailableCells() { @@ -454,12 +464,21 @@ private extension MasterTimelineViewController { } func resetUI() { - title = coordinator.timelineName + if let titleView = Bundle.main.loadNibNamed("MasterTimelineTitleView", owner: self, options: nil)?[0] as? MasterTimelineTitleView { self.titleView = titleView titleView.imageView.image = coordinator.timelineFavicon titleView.label.text = coordinator.timelineName + + if coordinator.timelineFetcher is Feed { + let width = titleView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)).width + titleView.widthAnchor.constraint(equalToConstant: width).isActive = true + titleView.heightAnchor.constraint(equalToConstant: 44.0).isActive = true + let tap = UITapGestureRecognizer(target: self, action:#selector(showFeedInspector(_:))) + titleView.addGestureRecognizer(tap) + } + navigationItem.titleView = titleView } diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index 16f08ec78..2d39f806d 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -788,6 +788,15 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { } } + func showFeedInspector() { + guard let feed = timelineFetcher as? Feed else { + return + } + rootSplitViewController.present(style: .formSheet) { + FeedInspectorView(viewModel: FeedInspectorView.ViewModel(feed: feed)) + } + } + func showAdd(_ type: AddControllerType, initialFeed: String? = nil, initialFeedName: String? = nil) { selectFeed(nil)