From 60f13c32a407198ab3f8960e046ef8a43120500b Mon Sep 17 00:00:00 2001
From: Brent Simmons
Date: Fri, 6 Sep 2019 20:00:06 -0700
Subject: [PATCH 01/14] Update to latest RSParser.
---
submodules/RSParser | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/submodules/RSParser b/submodules/RSParser
index 76f4c468c..9e86cf613 160000
--- a/submodules/RSParser
+++ b/submodules/RSParser
@@ -1 +1 @@
-Subproject commit 76f4c468cee1e46544897d44c40e87e2bd05d729
+Subproject commit 9e86cf613b40b6a3389b6248be9427d90debbf9f
From 4184246e42667137a921384f562f073ab23e4897 Mon Sep 17 00:00:00 2001
From: Maurice Parker
Date: Sat, 7 Sep 2019 05:55:30 -0500
Subject: [PATCH 02/14] Updated to latest RSCore
---
submodules/RSCore | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/submodules/RSCore b/submodules/RSCore
index 75b609926..7a4895657 160000
--- a/submodules/RSCore
+++ b/submodules/RSCore
@@ -1 +1 @@
-Subproject commit 75b609926fe64c7c14428a39bda1f301cd968f46
+Subproject commit 7a48956576a242c631d634520748de326d893f9b
From ec4a6f3e0a03ef26eff8410ba5b2fc921c898365 Mon Sep 17 00:00:00 2001
From: Maurice Parker
Date: Sat, 7 Sep 2019 09:01:29 -0500
Subject: [PATCH 03/14] Add search articles keyboard shortcut and make search
work in three panel mode
---
iOS/KeyboardManager.swift | 3 +++
iOS/RootSplitViewController.swift | 4 ++++
iOS/SceneCoordinator.swift | 21 ++++++++++-----------
3 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/iOS/KeyboardManager.swift b/iOS/KeyboardManager.swift
index 0af53e80e..ece86d0b9 100644
--- a/iOS/KeyboardManager.swift
+++ b/iOS/KeyboardManager.swift
@@ -146,6 +146,9 @@ private extension KeyboardManager {
let openInBrowserTitle = NSLocalizedString("Open In Browser", comment: "Open In Browser")
keys.append(createKeyCommand(title: openInBrowserTitle, action: "openInBrowser:", input: UIKeyCommand.inputRightArrow, modifiers: [.command]))
+ let articleSearchTitle = NSLocalizedString("Article Search", comment: "Article Search")
+ keys.append(createKeyCommand(title: articleSearchTitle, action: "articleSearch:", input: "f", modifiers: [.command, .shift]))
+
return keys
}
diff --git a/iOS/RootSplitViewController.swift b/iOS/RootSplitViewController.swift
index c02d23594..2f548b17f 100644
--- a/iOS/RootSplitViewController.swift
+++ b/iOS/RootSplitViewController.swift
@@ -61,6 +61,10 @@ class RootSplitViewController: UISplitViewController {
coordinator.showBrowserForCurrentArticle()
}
+ @objc func articleSearch(_ sender: Any?) {
+ coordinator.showSearch()
+ }
+
@objc func addNewFeed(_ sender: Any?) {
coordinator.showAdd(.feed)
}
diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift
index 46856d69f..cc0cc47f8 100644
--- a/iOS/SceneCoordinator.swift
+++ b/iOS/SceneCoordinator.swift
@@ -327,11 +327,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
func showSearch() {
selectFeed(nil)
-
- masterTimelineViewController = UIStoryboard.main.instantiateController(ofType: MasterTimelineViewController.self)
- masterTimelineViewController!.coordinator = self
- navControllerForTimeline().pushViewController(masterTimelineViewController!, animated: false)
-
+ installTimelineControllerIfNecessary(animated: false)
DispatchQueue.main.asyncAfter(deadline: .now()) {
self.masterTimelineViewController!.showSearchAll()
}
@@ -584,12 +580,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
if let ip = indexPath, let node = nodeFor(ip), let fetcher = node.representedObject as? ArticleFetcher {
timelineFetcher = fetcher
updateSelectingActivity(with: node)
-
- if navControllerForTimeline().viewControllers.filter({ $0 is MasterTimelineViewController }).count < 1 {
- masterTimelineViewController = UIStoryboard.main.instantiateController(ofType: MasterTimelineViewController.self)
- masterTimelineViewController!.coordinator = self
- navControllerForTimeline().pushViewController(masterTimelineViewController!, animated: !automated)
- }
+ installTimelineControllerIfNecessary(animated: !automated)
} else {
timelineFetcher = nil
@@ -1473,6 +1464,14 @@ private extension SceneCoordinator {
// MARK: Double Split
+ func installTimelineControllerIfNecessary(animated: Bool) {
+ if navControllerForTimeline().viewControllers.filter({ $0 is MasterTimelineViewController }).count < 1 {
+ masterTimelineViewController = UIStoryboard.main.instantiateController(ofType: MasterTimelineViewController.self)
+ masterTimelineViewController!.coordinator = self
+ navControllerForTimeline().pushViewController(masterTimelineViewController!, animated: animated)
+ }
+ }
+
// Note about the Shim Controller
// In the root split view controller's secondary (or detail) position we use a view controller that
// only acts as a shim (or wrapper) for the actually desired contents of the second position. This
From 22553b661dbb7e4aa34876a9e2a801015a2d1288 Mon Sep 17 00:00:00 2001
From: Maurice Parker
Date: Sat, 7 Sep 2019 14:00:31 -0500
Subject: [PATCH 04/14] Begin to convert Settings to the latest SwiftUI
---
NetNewsWire.xcodeproj/project.pbxproj | 4 +-
iOS/SceneCoordinator.swift | 18 ++-
iOS/Settings/SettingsView.swift | 208 +++++++++++++++-----------
3 files changed, 131 insertions(+), 99 deletions(-)
diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj
index 289353912..3570e1595 100644
--- a/NetNewsWire.xcodeproj/project.pbxproj
+++ b/NetNewsWire.xcodeproj/project.pbxproj
@@ -59,6 +59,7 @@
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 */; };
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; };
51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -784,8 +785,8 @@
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 = ""; };
- 6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
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; };
6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionHandler.swift; sourceTree = ""; };
6581C73920CED60100F4AD34 /* SafariExtensionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionViewController.swift; sourceTree = ""; };
@@ -2515,6 +2516,7 @@
51934CD023108953006127BE /* ActivityID.swift in Sources */,
51C452782265091600C03939 /* MasterTimelineCellData.swift in Sources */,
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
+ 519D73FB2323FF35008BB345 /* SettingsView.swift in Sources */,
511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */,
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */,
diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift
index cc0cc47f8..e815d9170 100644
--- a/iOS/SceneCoordinator.swift
+++ b/iOS/SceneCoordinator.swift
@@ -6,7 +6,8 @@
// Copyright © 2019 Ranchero Software. All rights reserved.
//
-import Foundation
+import UIKit
+import SwiftUI
import Account
import Articles
import RSCore
@@ -852,14 +853,15 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
func showSettings() {
- let settingsNavViewController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController
- settingsNavViewController.modalPresentationStyle = .formSheet
- let settingsViewController = settingsNavViewController.topViewController as! SettingsViewController
- settingsViewController.presentingParentController = rootSplitViewController
- rootSplitViewController.present(settingsNavViewController, animated: true)
+// let settingsNavViewController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController
+// settingsNavViewController.modalPresentationStyle = .formSheet
+// let settingsViewController = settingsNavViewController.topViewController as! SettingsViewController
+// settingsViewController.presentingParentController = rootSplitViewController
+// rootSplitViewController.present(settingsNavViewController, animated: true)
- // let settings = UIHostingController(rootView: SettingsView(viewModel: SettingsView.ViewModel()))
- // self.present(settings, animated: true)
+ let settings = UIHostingController(rootView: SettingsView(viewModel: SettingsView.ViewModel()))
+ settings.modalPresentationStyle = .formSheet
+ rootSplitViewController.present(settings, animated: true)
}
func showAdd(_ type: AddControllerType) {
diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift
index bf7eacc1a..005f39e4f 100644
--- a/iOS/Settings/SettingsView.swift
+++ b/iOS/Settings/SettingsView.swift
@@ -11,26 +11,29 @@ import Combine
import Account
struct SettingsView : View {
- @ObjectBinding var viewModel: ViewModel
- @State var subscriptionsImportAccounts: ActionSheet? = nil
- @State var subscriptionsImportDocumentPicker: Modal? = nil
- @State var subscriptionsExportAccounts: ActionSheet? = nil
- @State var subscriptionsExportDocumentPicker: Modal? = nil
+
+ @ObservedObject var viewModel: ViewModel
+
+ @State var isWebsitePresented: Bool = false
+ @State var isGithubRepoPresented: Bool = false
+ @State var isBugTrackerPresented: Bool = false
+ @State var isTechnotesPresented: Bool = false
+ @State var isHowToSupportPresented: Bool = false
var body: some View {
NavigationView {
Form {
- Section(header: Text("ACCOUNTS")) {
- ForEach(viewModel.accounts.identified(by: \.self)) { account in
- NavigationButton(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(account)), isDetail: false) {
- Text(verbatim: account.nameForDisplay)
- }
- }
- NavigationButton(destination: SettingsAddAccountView(), isDetail: false) {
- Text("Add Account")
- }
- }
+// Section(header: Text("ACCOUNTS")) {
+// ForEach(viewModel.accounts.identified(by: \.self)) { account in
+// NavigationLink(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(account)), isDetail: false) {
+// Text(verbatim: account.nameForDisplay)
+// }
+// }
+// NavigationLink(destination: SettingsAddAccountView(), isDetail: false) {
+// Text("Add Account")
+// }
+// }
Section(header: Text("TIMELINE")) {
Toggle(isOn: $viewModel.sortOldestToNewest) {
@@ -41,90 +44,115 @@ struct SettingsView : View {
}
}
- Section(header: Text("DATABASE")) {
- Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) {
- ForEach(RefreshInterval.allCases.identified(by: \.self)) { interval in
- Text(interval.description()).tag(interval)
- }
- }
- Button(action: {
- self.subscriptionsImportAccounts = self.createSubscriptionsImportAccounts
- }) {
- Text("Import Subscriptions...")
- }
- .presentation(subscriptionsImportAccounts)
- .presentation(subscriptionsImportDocumentPicker)
- Button(action: {
- self.subscriptionsExportAccounts = self.createSubscriptionsExportAccounts
- }) {
- Text("Export Subscriptions...")
- }
- .presentation(subscriptionsExportAccounts)
- .presentation(subscriptionsExportDocumentPicker)
- }
- .foregroundColor(.primary)
+// Section(header: Text("DATABASE")) {
+// Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) {
+// ForEach(RefreshInterval.allCases.identified(by: \.self)) { interval in
+// Text(interval.description()).tag(interval)
+// }
+// }
+// Button(action: {
+// self.subscriptionsImportAccounts = self.createSubscriptionsImportAccounts
+// }) {
+// Text("Import Subscriptions...")
+// }
+// .presentation(subscriptionsImportAccounts)
+// .presentation(subscriptionsImportDocumentPicker)
+// Button(action: {
+// self.subscriptionsExportAccounts = self.createSubscriptionsExportAccounts
+// }) {
+// Text("Export Subscriptions...")
+// }
+// .presentation(subscriptionsExportAccounts)
+// .presentation(subscriptionsExportDocumentPicker)
+// }
+// .foregroundColor(.primary)
Section(header: Text("ABOUT"), footer: buildFooter) {
Text("About NetNewsWire")
- PresentationButton(destination: SafariView(url: URL(string: "https://ranchero.com/netnewswire/")!)) {
+
+ Button(action: { self.isWebsitePresented.toggle() }) {
Text("Website")
+ }
+ }.sheet(isPresented: $isWebsitePresented) {
+ SafariView(url: URL(string: "https://ranchero.com/netnewswire/")!)
}
- PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire")!)) {
- Text("Github Repository")
+
+ VStack {
+ Button(action: { self.isGithubRepoPresented.toggle() }) {
+ Text("Github Repository")
+ }
+ }.sheet(isPresented: $isGithubRepoPresented) {
+ SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire")!)
}
- PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!)) {
- Text("Bug Tracker")
+
+ VStack {
+ Button(action: { self.isBugTrackerPresented.toggle() }) {
+ Text("Bug Tracker")
+ }
+ }.sheet(isPresented: $isBugTrackerPresented) {
+ SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!)
}
- PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!)) {
- Text("Technotes")
+
+ VStack {
+ Button(action: { self.isTechnotesPresented.toggle() }) {
+ Text("Technotes")
+ }
+ }.sheet(isPresented: $isTechnotesPresented) {
+ SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!)
}
- PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")!)) {
- Text("How to Support NetNewsWire")
+
+ VStack {
+ Button(action: { self.isHowToSupportPresented.toggle() }) {
+ Text("Technotes")
+ }
+ }.sheet(isPresented: $isHowToSupportPresented) {
+ SafariView(url: URL(string: "hhttps://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")!)
}
+
Text("Add NetNewsWire News Feed")
- }
- .foregroundColor(.primary)
+
+ }
+ .foregroundColor(.primary)
}
.navigationBarTitle(Text("Settings"), displayMode: .inline)
-
}
}
- var createSubscriptionsImportAccounts: ActionSheet {
- var buttons = [ActionSheet.Button]()
-
- for account in viewModel.activeAccounts {
- if !account.isOPMLImportSupported {
- continue
- }
-
- let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
- self.subscriptionsImportAccounts = nil
- self.subscriptionsImportDocumentPicker = Modal(SettingsSubscriptionsImportDocumentPickerView(account: account))
- }
-
- buttons.append(button)
- }
-
- buttons.append(.cancel { self.subscriptionsImportAccounts = nil })
- return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons)
- }
-
- var createSubscriptionsExportAccounts: ActionSheet {
- var buttons = [ActionSheet.Button]()
-
- for account in viewModel.accounts {
- let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
- self.subscriptionsExportAccounts = nil
- self.subscriptionsExportDocumentPicker = Modal(SettingsSubscriptionsExportDocumentPickerView(account: account))
- }
- buttons.append(button)
- }
-
- buttons.append(.cancel { self.subscriptionsExportAccounts = nil })
- return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons)
- }
+// var createSubscriptionsImportAccounts: ActionSheet {
+// var buttons = [ActionSheet.Button]()
+//
+// for account in viewModel.activeAccounts {
+// if !account.isOPMLImportSupported {
+// continue
+// }
+//
+// let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
+// self.subscriptionsImportAccounts = nil
+// self.subscriptionsImportDocumentPicker = Modal(SettingsSubscriptionsImportDocumentPickerView(account: account))
+// }
+//
+// buttons.append(button)
+// }
+//
+// buttons.append(.cancel { self.subscriptionsImportAccounts = nil })
+// return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons)
+// }
+//
+// var createSubscriptionsExportAccounts: ActionSheet {
+// var buttons = [ActionSheet.Button]()
+//
+// for account in viewModel.accounts {
+// let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
+// self.subscriptionsExportAccounts = nil
+// self.subscriptionsExportDocumentPicker = Modal(SettingsSubscriptionsExportDocumentPickerView(account: account))
+// }
+// buttons.append(button)
+// }
+//
+// buttons.append(.cancel { self.subscriptionsExportAccounts = nil })
+// return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons)
+// }
var buildFooter: some View {
return Text(verbatim: "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))")
@@ -134,9 +162,9 @@ struct SettingsView : View {
// MARK: ViewModel
- class ViewModel: BindableObject {
+ class ViewModel: ObservableObject {
- let didChange = PassthroughSubject()
+ let objectWillChange = ObservableObjectPublisher()
init() {
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .AccountsDidChange, object: nil)
@@ -164,12 +192,12 @@ struct SettingsView : View {
return AppDefaults.timelineSortDirection == .orderedDescending
}
set {
+ objectWillChange.send()
if newValue == true {
AppDefaults.timelineSortDirection = .orderedDescending
} else {
AppDefaults.timelineSortDirection = .orderedAscending
}
- didChange.send(self)
}
}
@@ -178,8 +206,8 @@ struct SettingsView : View {
return AppDefaults.timelineNumberOfLines
}
set {
+ objectWillChange.send()
AppDefaults.timelineNumberOfLines = newValue
- didChange.send(self)
}
}
@@ -188,17 +216,17 @@ struct SettingsView : View {
return AppDefaults.refreshInterval
}
set {
+ objectWillChange.send()
AppDefaults.refreshInterval = newValue
- didChange.send(self)
}
}
@objc func accountsDidChange(_ notification: Notification) {
- didChange.send(self)
+ objectWillChange.send()
}
@objc func displayNameDidChange(_ notification: Notification) {
- didChange.send(self)
+ objectWillChange.send()
}
}
From 3b4ec7262ff3217d009c6293faa64537e451be2a Mon Sep 17 00:00:00 2001
From: Maurice Parker
Date: Sat, 7 Sep 2019 14:10:20 -0500
Subject: [PATCH 05/14] Refactor website links to make code clearer and more
concise
---
iOS/Settings/SettingsView.swift | 59 +++++++++++++++------------------
1 file changed, 27 insertions(+), 32 deletions(-)
diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift
index 005f39e4f..0bff1287f 100644
--- a/iOS/Settings/SettingsView.swift
+++ b/iOS/Settings/SettingsView.swift
@@ -15,10 +15,7 @@ struct SettingsView : View {
@ObservedObject var viewModel: ViewModel
@State var isWebsitePresented: Bool = false
- @State var isGithubRepoPresented: Bool = false
- @State var isBugTrackerPresented: Bool = false
- @State var isTechnotesPresented: Bool = false
- @State var isHowToSupportPresented: Bool = false
+ @State var website: String? = nil
var body: some View {
NavigationView {
@@ -70,47 +67,45 @@ struct SettingsView : View {
Section(header: Text("ABOUT"), footer: buildFooter) {
Text("About NetNewsWire")
- Button(action: { self.isWebsitePresented.toggle() }) {
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://ranchero.com/netnewswire/"
+ }) {
Text("Website")
- }
- }.sheet(isPresented: $isWebsitePresented) {
- SafariView(url: URL(string: "https://ranchero.com/netnewswire/")!)
}
- VStack {
- Button(action: { self.isGithubRepoPresented.toggle() }) {
- Text("Github Repository")
- }
- }.sheet(isPresented: $isGithubRepoPresented) {
- SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire")!)
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://github.com/brentsimmons/NetNewsWire"
+ }) {
+ Text("Github Repository")
}
- VStack {
- Button(action: { self.isBugTrackerPresented.toggle() }) {
- Text("Bug Tracker")
- }
- }.sheet(isPresented: $isBugTrackerPresented) {
- SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!)
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://github.com/brentsimmons/NetNewsWire/issues"
+ }) {
+ Text("Bug Tracker")
}
- VStack {
- Button(action: { self.isTechnotesPresented.toggle() }) {
- Text("Technotes")
- }
- }.sheet(isPresented: $isTechnotesPresented) {
- SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!)
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes"
+ }) {
+ Text("Technotes")
}
- VStack {
- Button(action: { self.isHowToSupportPresented.toggle() }) {
- Text("Technotes")
- }
- }.sheet(isPresented: $isHowToSupportPresented) {
- SafariView(url: URL(string: "hhttps://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")!)
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown"
+ }) {
+ Text("How To Support NetNewsWire")
}
Text("Add NetNewsWire News Feed")
+ }.sheet(isPresented: $isWebsitePresented) {
+ SafariView(url: URL(string: self.website!)!)
}
.foregroundColor(.primary)
From a4b30c2f0e03c918e61a6013886eb6b2eb68d413 Mon Sep 17 00:00:00 2001
From: Maurice Parker
Date: Sat, 7 Sep 2019 14:33:15 -0500
Subject: [PATCH 06/14] Add refresh interval to settings again
---
NetNewsWire.xcodeproj/project.pbxproj | 12 ++++++++++++
.../RefreshInterval-Extensions.swift | 15 +++++++++++++++
iOS/Settings/SettingsView.swift | 16 ++++++++--------
3 files changed, 35 insertions(+), 8 deletions(-)
create mode 100644 iOS/Model Extensions/RefreshInterval-Extensions.swift
diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj
index 3570e1595..3d2e406a5 100644
--- a/NetNewsWire.xcodeproj/project.pbxproj
+++ b/NetNewsWire.xcodeproj/project.pbxproj
@@ -60,6 +60,7 @@
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 */; };
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; };
51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -727,6 +728,7 @@
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 = ""; };
51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = ""; };
51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = ""; };
@@ -1129,6 +1131,14 @@
path = Wrappers;
sourceTree = "";
};
+ 519D740423243C68008BB345 /* Model Extensions */ = {
+ isa = PBXGroup;
+ children = (
+ 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */,
+ );
+ path = "Model Extensions";
+ sourceTree = "";
+ };
51C45245226506C800C03939 /* Extensions */ = {
isa = PBXGroup;
children = (
@@ -1788,6 +1798,7 @@
5183CCEB227117C70010922C /* Settings */,
5183CCDB226F1EEB0010922C /* Progress */,
51C45245226506C800C03939 /* Extensions */,
+ 519D740423243C68008BB345 /* Model Extensions */,
5F3237FF231DF9D000706F6B /* Views */,
5194B5E222B693EC00144881 /* Wrappers */,
84C9FC9A2262A1A900D921D6 /* Resources */,
@@ -2505,6 +2516,7 @@
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */,
84DEE56622C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */,
512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */,
+ 519D740623243CC0008BB345 /* RefreshInterval-Extensions.swift in Sources */,
51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */,
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */,
51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */,
diff --git a/iOS/Model Extensions/RefreshInterval-Extensions.swift b/iOS/Model Extensions/RefreshInterval-Extensions.swift
new file mode 100644
index 000000000..5906d10a2
--- /dev/null
+++ b/iOS/Model Extensions/RefreshInterval-Extensions.swift
@@ -0,0 +1,15 @@
+//
+// RefreshInterval-Extensions.swift
+// NetNewsWire-iOS
+//
+// Created by Maurice Parker on 9/7/19.
+// Copyright © 2019 Ranchero Software. All rights reserved.
+//
+
+import Foundation
+
+extension RefreshInterval: Identifiable {
+ var id: Int {
+ return rawValue
+ }
+}
diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift
index 0bff1287f..b5fbfa3ab 100644
--- a/iOS/Settings/SettingsView.swift
+++ b/iOS/Settings/SettingsView.swift
@@ -41,12 +41,12 @@ struct SettingsView : View {
}
}
-// Section(header: Text("DATABASE")) {
-// Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) {
-// ForEach(RefreshInterval.allCases.identified(by: \.self)) { interval in
-// Text(interval.description()).tag(interval)
-// }
-// }
+ Section(header: Text("DATABASE")) {
+ Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) {
+ ForEach(RefreshInterval.allCases) { interval in
+ Text(interval.description()).tag(interval)
+ }
+ }
// Button(action: {
// self.subscriptionsImportAccounts = self.createSubscriptionsImportAccounts
// }) {
@@ -61,8 +61,8 @@ struct SettingsView : View {
// }
// .presentation(subscriptionsExportAccounts)
// .presentation(subscriptionsExportDocumentPicker)
-// }
-// .foregroundColor(.primary)
+ }
+ .foregroundColor(.primary)
Section(header: Text("ABOUT"), footer: buildFooter) {
Text("About NetNewsWire")
From dcd2a9c4f61929b7277792414413332c3e5570dc Mon Sep 17 00:00:00 2001
From: Maurice Parker
Date: Sat, 7 Sep 2019 15:30:54 -0500
Subject: [PATCH 07/14] Reimplement Import and Export OPML
---
NetNewsWire.xcodeproj/project.pbxproj | 4 +
iOS/Settings/SettingsView.swift | 108 ++++++++++++++------------
2 files changed, 64 insertions(+), 48 deletions(-)
diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj
index 3d2e406a5..a3db452f2 100644
--- a/NetNewsWire.xcodeproj/project.pbxproj
+++ b/NetNewsWire.xcodeproj/project.pbxproj
@@ -61,6 +61,8 @@
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 */; };
51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; };
51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -2464,9 +2466,11 @@
51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */,
51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */,
51C4525A226508D600C03939 /* UIStoryboard-Extensions.swift in Sources */,
+ 519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */,
5183CCEF227125970010922C /* SettingsViewController.swift in Sources */,
51F85BE5227217D000C787DC /* RefreshIntervalViewController.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 */,
diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift
index b5fbfa3ab..8ddb5dfdd 100644
--- a/iOS/Settings/SettingsView.swift
+++ b/iOS/Settings/SettingsView.swift
@@ -16,6 +16,12 @@ struct SettingsView : View {
@State var isWebsitePresented: Bool = false
@State var website: String? = nil
+
+ @State var isOPMLImportPresented: Bool = false
+ @State var isOPMLImportDocPickerPresented: Bool = false
+ @State var isOPMLExportPresented: Bool = false
+ @State var isOPMLExportDocPickerPresented: Bool = false
+ @State var opmlAccount: Account? = nil
var body: some View {
NavigationView {
@@ -47,20 +53,26 @@ struct SettingsView : View {
Text(interval.description()).tag(interval)
}
}
-// Button(action: {
-// self.subscriptionsImportAccounts = self.createSubscriptionsImportAccounts
-// }) {
-// Text("Import Subscriptions...")
-// }
-// .presentation(subscriptionsImportAccounts)
-// .presentation(subscriptionsImportDocumentPicker)
-// Button(action: {
-// self.subscriptionsExportAccounts = self.createSubscriptionsExportAccounts
-// }) {
-// Text("Export Subscriptions...")
-// }
-// .presentation(subscriptionsExportAccounts)
-// .presentation(subscriptionsExportDocumentPicker)
+
+ VStack {
+ Button("Import Subscriptions...") {
+ self.isOPMLImportPresented = true
+ }
+ }.actionSheet(isPresented: $isOPMLImportPresented) {
+ createSubscriptionsImportAccounts
+ }.sheet(isPresented: $isOPMLImportDocPickerPresented) {
+ SettingsSubscriptionsImportDocumentPickerView(account: self.opmlAccount!)
+ }
+
+ VStack {
+ Button("Export Subscriptions...") {
+ self.isOPMLExportPresented = true
+ }
+ }.actionSheet(isPresented: $isOPMLExportPresented) {
+ createSubscriptionsExportAccounts
+ }.sheet(isPresented: $isOPMLExportDocPickerPresented) {
+ SettingsSubscriptionsExportDocumentPickerView(account: self.opmlAccount!)
+ }
}
.foregroundColor(.primary)
@@ -114,40 +126,40 @@ struct SettingsView : View {
}
}
-// var createSubscriptionsImportAccounts: ActionSheet {
-// var buttons = [ActionSheet.Button]()
-//
-// for account in viewModel.activeAccounts {
-// if !account.isOPMLImportSupported {
-// continue
-// }
-//
-// let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
-// self.subscriptionsImportAccounts = nil
-// self.subscriptionsImportDocumentPicker = Modal(SettingsSubscriptionsImportDocumentPickerView(account: account))
-// }
-//
-// buttons.append(button)
-// }
-//
-// buttons.append(.cancel { self.subscriptionsImportAccounts = nil })
-// return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons)
-// }
-//
-// var createSubscriptionsExportAccounts: ActionSheet {
-// var buttons = [ActionSheet.Button]()
-//
-// for account in viewModel.accounts {
-// let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
-// self.subscriptionsExportAccounts = nil
-// self.subscriptionsExportDocumentPicker = Modal(SettingsSubscriptionsExportDocumentPickerView(account: account))
-// }
-// buttons.append(button)
-// }
-//
-// buttons.append(.cancel { self.subscriptionsExportAccounts = nil })
-// return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons)
-// }
+ var createSubscriptionsImportAccounts: ActionSheet {
+ var buttons = [ActionSheet.Button]()
+
+ for account in viewModel.activeAccounts {
+ if !account.isOPMLImportSupported {
+ continue
+ }
+
+ let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
+ self.opmlAccount = account
+ self.isOPMLImportDocPickerPresented = true
+ }
+
+ buttons.append(button)
+ }
+
+ buttons.append(.cancel())
+ return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons)
+ }
+
+ var createSubscriptionsExportAccounts: ActionSheet {
+ var buttons = [ActionSheet.Button]()
+
+ for account in viewModel.accounts {
+ let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
+ self.opmlAccount = account
+ self.isOPMLExportDocPickerPresented = true
+ }
+ buttons.append(button)
+ }
+
+ buttons.append(.cancel())
+ return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons)
+ }
var buildFooter: some View {
return Text(verbatim: "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))")
From a01b9ebe73a373b96776b5a3ebda31417ebcd700 Mon Sep 17 00:00:00 2001
From: Maurice Parker
Date: Sat, 7 Sep 2019 16:43:44 -0500
Subject: [PATCH 08/14] Add Done button to Setting modal
---
iOS/SceneCoordinator.swift | 6 +++---
iOS/Settings/SettingsView.swift | 18 +++++++++++-------
submodules/RSCore | 2 +-
3 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift
index e815d9170..2953669ac 100644
--- a/iOS/SceneCoordinator.swift
+++ b/iOS/SceneCoordinator.swift
@@ -859,9 +859,9 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
// settingsViewController.presentingParentController = rootSplitViewController
// rootSplitViewController.present(settingsNavViewController, animated: true)
- let settings = UIHostingController(rootView: SettingsView(viewModel: SettingsView.ViewModel()))
- settings.modalPresentationStyle = .formSheet
- rootSplitViewController.present(settings, animated: true)
+ rootSplitViewController.present(style: .formSheet) {
+ SettingsView(viewModel: SettingsView.ViewModel())
+ }
}
func showAdd(_ type: AddControllerType) {
diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift
index 8ddb5dfdd..a80462769 100644
--- a/iOS/Settings/SettingsView.swift
+++ b/iOS/Settings/SettingsView.swift
@@ -10,18 +10,21 @@ import SwiftUI
import Combine
import Account
+
struct SettingsView : View {
@ObservedObject var viewModel: ViewModel
+
+ @Environment(\.viewController) private var viewController: UIViewController?
- @State var isWebsitePresented: Bool = false
- @State var website: String? = nil
+ @State private var isWebsitePresented: Bool = false
+ @State private var website: String? = nil
- @State var isOPMLImportPresented: Bool = false
- @State var isOPMLImportDocPickerPresented: Bool = false
- @State var isOPMLExportPresented: Bool = false
- @State var isOPMLExportDocPickerPresented: Bool = false
- @State var opmlAccount: Account? = 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 {
@@ -123,6 +126,7 @@ struct SettingsView : View {
}
.navigationBarTitle(Text("Settings"), displayMode: .inline)
+ .navigationBarItems(leading: Button(action: { self.viewController?.dismiss(animated: true) }) { Text("Done") } )
}
}
diff --git a/submodules/RSCore b/submodules/RSCore
index 7a4895657..3e0dbb1c0 160000
--- a/submodules/RSCore
+++ b/submodules/RSCore
@@ -1 +1 @@
-Subproject commit 7a48956576a242c631d634520748de326d893f9b
+Subproject commit 3e0dbb1c0a88697e7be510da0226fe1e3e7ef195
From 5b15285e8ec3a7f87f0ee5500ed9ee5ec6c74947 Mon Sep 17 00:00:00 2001
From: Brent Simmons
Date: Sat, 7 Sep 2019 18:27:48 -0700
Subject: [PATCH 09/14] =?UTF-8?q?Make=20sure=20that=20article.preferredLin?=
=?UTF-8?q?k=20doesn=E2=80=99t=20return=20an=20empty=20string.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Shared/Data/ArticleUtilities.swift | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/Shared/Data/ArticleUtilities.swift b/Shared/Data/ArticleUtilities.swift
index be7bc3df8..12df236ec 100644
--- a/Shared/Data/ArticleUtilities.swift
+++ b/Shared/Data/ArticleUtilities.swift
@@ -46,7 +46,13 @@ extension Article {
}
var preferredLink: String? {
- return url ?? externalURL
+ if let url = url, !url.isEmpty {
+ return url
+ }
+ if let externalURL = externalURL, !externalURL.isEmpty {
+ return externalURL
+ }
+ return nil
}
var body: String? {
From c05e55af4168f5db09bdd931586be81bd806a799 Mon Sep 17 00:00:00 2001
From: Brent Simmons
Date: Sat, 7 Sep 2019 18:31:07 -0700
Subject: [PATCH 10/14] Update to latest RSCore.
---
submodules/RSCore | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/submodules/RSCore b/submodules/RSCore
index 290b79dcd..3e0dbb1c0 160000
--- a/submodules/RSCore
+++ b/submodules/RSCore
@@ -1 +1 @@
-Subproject commit 290b79dcd6156210dedddeb767164436ef9481e0
+Subproject commit 3e0dbb1c0a88697e7be510da0226fe1e3e7ef195
From fe874f3ca7734f70a6a3ba9b07a309a5b27756f7 Mon Sep 17 00:00:00 2001
From: Maurice Parker
Date: Sat, 7 Sep 2019 20:50:57 -0500
Subject: [PATCH 11/14] Update Settings for Accounts to work with the latest
SwiftUI
---
NetNewsWire.xcodeproj/project.pbxproj | 14 ++
iOS/Model Extensions/Account-Extensions.swift | 16 ++
iOS/Settings/SettingsAccountLabelView.swift | 18 +-
iOS/Settings/SettingsAddAccountView.swift | 29 ++-
iOS/Settings/SettingsDetailAccountView.swift | 50 ++---
iOS/Settings/SettingsFeedbinAccountView.swift | 45 ++--
iOS/Settings/SettingsLocalAccountView.swift | 8 +-
iOS/Settings/SettingsView.swift | 205 +++++++++---------
8 files changed, 215 insertions(+), 170 deletions(-)
create mode 100644 iOS/Model Extensions/Account-Extensions.swift
diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj
index a3db452f2..1b91251a2 100644
--- a/NetNewsWire.xcodeproj/project.pbxproj
+++ b/NetNewsWire.xcodeproj/project.pbxproj
@@ -34,6 +34,8 @@
5144EA52227B8E4500D19003 /* AccountsFeedbin.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */; };
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 */; };
51543685228F6753005E1CDF /* DetailAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51543684228F6753005E1CDF /* DetailAccountViewController.swift */; };
515436882291D75D005E1CDF /* AddLocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515436872291D75D005E1CDF /* AddLocalAccountViewController.swift */; };
5154368A2291FED9005E1CDF /* FeedbinAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515436892291FED9005E1CDF /* FeedbinAccountViewController.swift */; };
@@ -64,6 +66,10 @@
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 */; };
+ 51AF460E232488C6001742EF /* Account-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51AF460D232488C6001742EF /* Account-Extensions.swift */; };
51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; };
51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
51C451B9226377C900C03939 /* Articles.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 840716732262A60F00344432 /* Articles.framework */; };
@@ -732,6 +738,7 @@
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 = ""; };
+ 51AF460D232488C6001742EF /* Account-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account-Extensions.swift"; sourceTree = ""; };
51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = ""; };
51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = ""; };
51C45254226507D200C03939 /* AppAssets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppAssets.swift; sourceTree = ""; };
@@ -1137,6 +1144,7 @@
isa = PBXGroup;
children = (
519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */,
+ 51AF460D232488C6001742EF /* Account-Extensions.swift */,
);
path = "Model Extensions";
sourceTree = "";
@@ -2443,6 +2451,7 @@
5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */,
51EAED96231363EF00A9EEE3 /* NonIntrinsicButton.swift in Sources */,
51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */,
+ 5152E1022324900D00E5C7AD /* SettingsAddAccountView.swift in Sources */,
515ADE4022E11FAE006B2460 /* SystemMessageViewController.swift in Sources */,
51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */,
51C45296226509D300C03939 /* OPMLExporter.swift in Sources */,
@@ -2451,12 +2460,15 @@
51C45269226508F600C03939 /* MasterFeedTableViewCell.swift in Sources */,
51F85BFD2275DCA800C787DC /* SingleLineUILabelSizer.swift in Sources */,
51C4528F226509BD00C03939 /* UnreadFeed.swift in Sources */,
+ 51AF460E232488C6001742EF /* Account-Extensions.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 */,
514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */,
+ 5152E0F923248F6200E5C7AD /* SettingsLocalAccountView.swift in Sources */,
51C4525C226508DF00C03939 /* String-Extensions.swift in Sources */,
51C452792265091600C03939 /* MasterTimelineTableViewCell.swift in Sources */,
51C452852265093600C03939 /* AddFeedFolderPickerData.swift in Sources */,
@@ -2469,6 +2481,7 @@
519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */,
5183CCEF227125970010922C /* SettingsViewController.swift in Sources */,
51F85BE5227217D000C787DC /* RefreshIntervalViewController.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 */,
@@ -2506,6 +2519,7 @@
51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.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 */,
diff --git a/iOS/Model Extensions/Account-Extensions.swift b/iOS/Model Extensions/Account-Extensions.swift
new file mode 100644
index 000000000..9574f3553
--- /dev/null
+++ b/iOS/Model Extensions/Account-Extensions.swift
@@ -0,0 +1,16 @@
+//
+// Account-Extensions.swift
+// NetNewsWire-iOS
+//
+// Created by Maurice Parker on 9/7/19.
+// Copyright © 2019 Ranchero Software. All rights reserved.
+//
+
+import Foundation
+import Account
+
+extension Account: Identifiable {
+ public var id: String {
+ return accountID
+ }
+}
diff --git a/iOS/Settings/SettingsAccountLabelView.swift b/iOS/Settings/SettingsAccountLabelView.swift
index 8c7f6d41f..0d5ead2e3 100644
--- a/iOS/Settings/SettingsAccountLabelView.swift
+++ b/iOS/Settings/SettingsAccountLabelView.swift
@@ -14,19 +14,13 @@ struct SettingsAccountLabelView : View {
var body: some View {
HStack {
- Spacer()
- HStack {
- Image(accountImage)
- .resizable()
- .aspectRatio(1, contentMode: .fit)
- .frame(height: 32)
- Text(verbatim: accountLabel).font(.title)
-
- }
- .layoutPriority(1)
- Spacer()
+ Image(accountImage)
+ .resizable()
+ .aspectRatio(1, contentMode: .fit)
+ .frame(height: 32)
+ Text(verbatim: accountLabel).font(.title)
}
- .foregroundColor(.primary)
+ .foregroundColor(.primary).padding(4.0)
}
}
diff --git a/iOS/Settings/SettingsAddAccountView.swift b/iOS/Settings/SettingsAddAccountView.swift
index 22fd2ca7c..10f1b7108 100644
--- a/iOS/Settings/SettingsAddAccountView.swift
+++ b/iOS/Settings/SettingsAddAccountView.swift
@@ -10,17 +10,38 @@ import SwiftUI
import Account
struct SettingsAddAccountView : View {
+
+ @State private var isAddPresented = false
+ @State private var selectedAccountType: AccountType = nil
+
var body: some View {
Form {
- PresentationButton(destination: SettingsLocalAccountView(name: "")) {
+
+ Button(action: {
+ self.selectedAccountType = AccountType.onMyMac
+ self.isAddPresented.toggle()
+ }) {
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName)
}
- .padding(4)
- PresentationButton(destination: SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel())) {
+
+ Button(action: {
+ self.selectedAccountType = AccountType.feedbin
+ self.isAddPresented.toggle()
+ }) {
SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin")
}
- .padding(4)
+
+
}
+ .sheet(isPresented: $isAddPresented) {
+ if self.selectedAccountType == .onMyMac {
+ SettingsLocalAccountView(name: "")
+ }
+ if self.selectedAccountType == .feedbin {
+ SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel())
+ }
+ }
+
.navigationBarTitle(Text("Add Account"), displayMode: .inline)
}
}
diff --git a/iOS/Settings/SettingsDetailAccountView.swift b/iOS/Settings/SettingsDetailAccountView.swift
index 8bc4d5edf..b97177f35 100644
--- a/iOS/Settings/SettingsDetailAccountView.swift
+++ b/iOS/Settings/SettingsDetailAccountView.swift
@@ -12,17 +12,15 @@ import Account
import RSWeb
struct SettingsDetailAccountView : View {
- @ObjectBinding var viewModel: ViewModel
- @State private var verifyDelete = false
- @State private var showFeedbinCredentials = false
-
+ @ObservedObject var viewModel: ViewModel
+ @State private var isFeedbinCredentialsPresented = false
+ @State private var isDeleteAlertPresented = false
+
var body: some View {
Form {
Section {
HStack {
- Text("Name")
- Divider()
- TextField($viewModel.name, placeholder: Text("(Optional)"))
+ TextField("Name", text: $viewModel.name)
}
Toggle(isOn: $viewModel.isActive) {
Text("Active")
@@ -33,33 +31,33 @@ struct SettingsDetailAccountView : View {
HStack {
Spacer()
Button(action: {
- self.showFeedbinCredentials = true
+ self.isFeedbinCredentialsPresented.toggle()
}) {
Text("Credentials")
}
- .presentation(showFeedbinCredentials ? feedbinCredentialsModal : nil)
- .onDisappear() { self.showFeedbinCredentials = false }
Spacer()
}
}
+ .sheet(isPresented: $isFeedbinCredentialsPresented) {
+ self.settingsFeedbinAccountView
+ }
}
if viewModel.isDeletable {
Section {
HStack {
Spacer()
Button(action: {
- self.verifyDelete = true
+ self.isDeleteAlertPresented.toggle()
}) {
- Text("Delete Account")
- .foregroundColor(.red)
- }
- .presentation($verifyDelete) {
- Alert(title: Text("Are you sure you want to delete \"\(viewModel.nameForDisplay)\"?"),
- primaryButton: Alert.Button.default(Text("Delete"), onTrigger: { self.viewModel.delete() }),
- secondaryButton: Alert.Button.cancel())
+ Text("Delete Account").foregroundColor(.red)
}
Spacer()
}
+ .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() }),
+ secondaryButton: Alert.Button.cancel())
+ }
}
}
}
@@ -67,13 +65,15 @@ struct SettingsDetailAccountView : View {
}
- var feedbinCredentialsModal: Modal {
+ var settingsFeedbinAccountView: SettingsFeedbinAccountView {
let feedbinViewModel = SettingsFeedbinAccountView.ViewModel(account: viewModel.account)
- return Modal(SettingsFeedbinAccountView(viewModel: feedbinViewModel))
+ return SettingsFeedbinAccountView(viewModel: feedbinViewModel)
}
- class ViewModel: BindableObject {
- let didChange = PassthroughSubject()
+ class ViewModel: ObservableObject {
+
+ let objectWillChange = ObservableObjectPublisher()
+
let account: Account
init(_ account: Account) {
@@ -89,8 +89,8 @@ struct SettingsDetailAccountView : View {
account.name ?? ""
}
set {
+ objectWillChange.send()
account.name = newValue.isEmpty ? nil : newValue
- didChange.send(self)
}
}
@@ -99,8 +99,8 @@ struct SettingsDetailAccountView : View {
account.isActive
}
set {
+ objectWillChange.send()
account.isActive = newValue
- didChange.send(self)
}
}
@@ -114,7 +114,7 @@ struct SettingsDetailAccountView : View {
func delete() {
AccountManager.shared.deleteAccount(account)
- ActivityManager.shared.cleanUp(account)
+// ActivityManager.shared.cleanUp(account)
}
}
}
diff --git a/iOS/Settings/SettingsFeedbinAccountView.swift b/iOS/Settings/SettingsFeedbinAccountView.swift
index 62e88069a..00ef485b7 100644
--- a/iOS/Settings/SettingsFeedbinAccountView.swift
+++ b/iOS/Settings/SettingsFeedbinAccountView.swift
@@ -12,10 +12,10 @@ import Account
import RSWeb
struct SettingsFeedbinAccountView : View {
- @Environment(\.isPresented) private var isPresented
- @ObjectBinding var viewModel: ViewModel
+ @Environment(\.presentationMode) var presentation
+ @ObservedObject var viewModel: ViewModel
@State var busy: Bool = false
- @State var error: Text = Text("")
+ @State var error: String = ""
var body: some View {
NavigationView {
@@ -23,22 +23,13 @@ struct SettingsFeedbinAccountView : View {
Section(header:
SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin").padding()
) {
- HStack {
- Text("Email:")
- Divider()
- TextField($viewModel.email)
- .textContentType(.username)
- }
- HStack {
- Text("Password:")
- Divider()
- SecureField($viewModel.password)
- }
+ TextField("Email", text: $viewModel.email).textContentType(.emailAddress)
+ SecureField("Password", text: $viewModel.password)
}
Section(footer:
HStack {
Spacer()
- error.color(.red)
+ Text(verbatim: error).foregroundColor(.red)
Spacer()
}
) {
@@ -67,7 +58,7 @@ struct SettingsFeedbinAccountView : View {
private func addAccount() {
busy = true
- error = Text("")
+ error = ""
let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces)
let credentials = Credentials.basic(username: emailAddress, password: viewModel.password)
@@ -104,15 +95,15 @@ struct SettingsFeedbinAccountView : View {
self.dismiss()
} catch {
- self.error = Text("Keychain error while storing credentials.")
+ self.error = "Keychain error while storing credentials."
}
} else {
- self.error = Text("Invalid email/password combination.")
+ self.error = "Invalid email/password combination."
}
case .failure:
- self.error = Text("Network error. Try again later.")
+ self.error = "Network error. Try again later."
}
}
@@ -120,11 +111,12 @@ struct SettingsFeedbinAccountView : View {
}
private func dismiss() {
- isPresented?.value = false
+ presentation.wrappedValue.dismiss()
}
- class ViewModel: BindableObject {
- let didChange = PassthroughSubject()
+ class ViewModel: ObservableObject {
+
+ let objectWillChange = ObservableObjectPublisher()
var account: Account? = nil
init() {
@@ -139,13 +131,14 @@ struct SettingsFeedbinAccountView : View {
}
var email: String = "" {
- didSet {
- didChange.send(self)
+ willSet {
+ objectWillChange.send()
}
}
+
var password: String = "" {
- didSet {
- didChange.send(self)
+ willSet {
+ objectWillChange.send()
}
}
diff --git a/iOS/Settings/SettingsLocalAccountView.swift b/iOS/Settings/SettingsLocalAccountView.swift
index 0c2b68569..c54e65cf8 100644
--- a/iOS/Settings/SettingsLocalAccountView.swift
+++ b/iOS/Settings/SettingsLocalAccountView.swift
@@ -10,7 +10,7 @@ import SwiftUI
import Account
struct SettingsLocalAccountView : View {
- @Environment(\.isPresented) private var isPresented
+ @Environment(\.presentationMode) var presentation
@State var name: String
var body: some View {
@@ -20,9 +20,7 @@ struct SettingsLocalAccountView : View {
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName).padding()
) {
HStack {
- Text("Name")
- Divider()
- TextField($name, placeholder: Text("(Optional)"))
+ TextField("Name", text: $name)
}
}
Section {
@@ -47,7 +45,7 @@ struct SettingsLocalAccountView : View {
}
private func dismiss() {
- isPresented?.value = false
+ presentation.wrappedValue.dismiss()
}
}
diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift
index a80462769..c6b927476 100644
--- a/iOS/Settings/SettingsView.swift
+++ b/iOS/Settings/SettingsView.swift
@@ -29,108 +29,117 @@ struct SettingsView : View {
var body: some View {
NavigationView {
Form {
-
-// Section(header: Text("ACCOUNTS")) {
-// ForEach(viewModel.accounts.identified(by: \.self)) { account in
-// NavigationLink(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(account)), isDetail: false) {
-// Text(verbatim: account.nameForDisplay)
-// }
-// }
-// NavigationLink(destination: SettingsAddAccountView(), isDetail: false) {
-// Text("Add Account")
-// }
-// }
-
- Section(header: Text("TIMELINE")) {
- Toggle(isOn: $viewModel.sortOldestToNewest) {
- Text("Sort Oldest to Newest")
- }
- Stepper(value: $viewModel.timelineNumberOfLines, in: 2...6) {
- Text("Number of Text Lines: \(viewModel.timelineNumberOfLines)")
- }
- }
-
- Section(header: Text("DATABASE")) {
- Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) {
- ForEach(RefreshInterval.allCases) { interval in
- Text(interval.description()).tag(interval)
- }
- }
-
- VStack {
- Button("Import Subscriptions...") {
- self.isOPMLImportPresented = true
- }
- }.actionSheet(isPresented: $isOPMLImportPresented) {
- createSubscriptionsImportAccounts
- }.sheet(isPresented: $isOPMLImportDocPickerPresented) {
- SettingsSubscriptionsImportDocumentPickerView(account: self.opmlAccount!)
- }
-
- VStack {
- Button("Export Subscriptions...") {
- self.isOPMLExportPresented = true
- }
- }.actionSheet(isPresented: $isOPMLExportPresented) {
- createSubscriptionsExportAccounts
- }.sheet(isPresented: $isOPMLExportDocPickerPresented) {
- SettingsSubscriptionsExportDocumentPickerView(account: self.opmlAccount!)
- }
- }
- .foregroundColor(.primary)
-
- Section(header: Text("ABOUT"), footer: buildFooter) {
- Text("About NetNewsWire")
-
- 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")
- }
-
- Text("Add NetNewsWire News Feed")
-
- }.sheet(isPresented: $isWebsitePresented) {
- SafariView(url: URL(string: self.website!)!)
- }
- .foregroundColor(.primary)
-
+ buildAccountsSection()
+ buildTimelineSection()
+ buildDatabaseSection()
+ buildAboutSection()
}
.navigationBarTitle(Text("Settings"), displayMode: .inline)
.navigationBarItems(leading: Button(action: { self.viewController?.dismiss(animated: true) }) { Text("Done") } )
}
}
- var createSubscriptionsImportAccounts: ActionSheet {
+ func buildAccountsSection() -> some View {
+ Section(header: Text("ACCOUNTS")) {
+ ForEach(viewModel.accounts) { account in
+ NavigationLink(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(account))) {
+ Text(verbatim: account.nameForDisplay)
+ }
+ }
+ NavigationLink(destination: SettingsAddAccountView()) {
+ Text("Add Account")
+ }
+ }
+ }
+
+ func buildTimelineSection() -> some View {
+ Section(header: Text("TIMELINE")) {
+ Toggle(isOn: $viewModel.sortOldestToNewest) {
+ Text("Sort Oldest to Newest")
+ }
+ Stepper(value: $viewModel.timelineNumberOfLines, in: 2...6) {
+ Text("Number of Text Lines: \(viewModel.timelineNumberOfLines)")
+ }
+ }
+ }
+
+ func buildDatabaseSection() -> some View {
+ Section(header: Text("DATABASE")) {
+ Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) {
+ ForEach(RefreshInterval.allCases) { interval in
+ Text(interval.description()).tag(interval)
+ }
+ }
+
+ VStack {
+ Button("Import Subscriptions...") {
+ self.isOPMLImportPresented = true
+ }
+ }.actionSheet(isPresented: $isOPMLImportPresented) {
+ buildSubscriptionsImportAccounts()
+ }.sheet(isPresented: $isOPMLImportDocPickerPresented) {
+ SettingsSubscriptionsImportDocumentPickerView(account: self.opmlAccount!)
+ }.foregroundColor(.primary)
+
+ VStack {
+ Button("Export Subscriptions...") {
+ self.isOPMLExportPresented = true
+ }
+ }.actionSheet(isPresented: $isOPMLExportPresented) {
+ buildSubscriptionsExportAccounts()
+ }.sheet(isPresented: $isOPMLExportDocPickerPresented) {
+ SettingsSubscriptionsExportDocumentPickerView(account: self.opmlAccount!)
+ }.foregroundColor(.primary)
+ }
+ }
+
+ func buildAboutSection() -> some View {
+ Section(header: Text("ABOUT"), footer: buildFooter()) {
+ Text("About NetNewsWire")
+
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://ranchero.com/netnewswire/"
+ }) {
+ Text("Website")
+ }.foregroundColor(.primary)
+
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://github.com/brentsimmons/NetNewsWire"
+ }) {
+ Text("Github Repository")
+ }.foregroundColor(.primary)
+
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://github.com/brentsimmons/NetNewsWire/issues"
+ }) {
+ Text("Bug Tracker")
+ }.foregroundColor(.primary)
+
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes"
+ }) {
+ Text("Technotes")
+ }.foregroundColor(.primary)
+
+ Button(action: {
+ self.isWebsitePresented.toggle()
+ self.website = "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown"
+ }) {
+ Text("How To Support NetNewsWire")
+ }.foregroundColor(.primary)
+
+ Text("Add NetNewsWire News Feed")
+
+ }.sheet(isPresented: $isWebsitePresented) {
+ SafariView(url: URL(string: self.website!)!)
+ }
+ }
+
+ func buildSubscriptionsImportAccounts() -> ActionSheet {
var buttons = [ActionSheet.Button]()
for account in viewModel.activeAccounts {
@@ -150,7 +159,7 @@ struct SettingsView : View {
return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons)
}
- var createSubscriptionsExportAccounts: ActionSheet {
+ func buildSubscriptionsExportAccounts() -> ActionSheet {
var buttons = [ActionSheet.Button]()
for account in viewModel.accounts {
@@ -165,7 +174,7 @@ struct SettingsView : View {
return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons)
}
- var buildFooter: some View {
+ func buildFooter() -> some View {
return Text(verbatim: "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))")
.font(.footnote)
.foregroundColor(.secondary)
From 53370ff0d30b87bf6e436416b8f1e48b1d56a1e6 Mon Sep 17 00:00:00 2001
From: Brent Simmons
Date: Sat, 7 Sep 2019 20:45:10 -0700
Subject: [PATCH 12/14] Bump version to 5.0.1d2.
---
xcconfig/NetNewsWire_target.xcconfig | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/xcconfig/NetNewsWire_target.xcconfig b/xcconfig/NetNewsWire_target.xcconfig
index 0e9e1a2e2..397010614 100644
--- a/xcconfig/NetNewsWire_target.xcconfig
+++ b/xcconfig/NetNewsWire_target.xcconfig
@@ -29,8 +29,8 @@ PROVISIONING_PROFILE_SPECIFIER =
#include? "../../SharedXcodeSettings/DeveloperSettings.xcconfig"
// High Level Settings common to both the Mac application and any extensions we bundle with it
-MARKETING_VERSION = 5.0.1d1
-CURRENT_PROJECT_VERSION = 2611
+MARKETING_VERSION = 5.0.1d2
+CURRENT_PROJECT_VERSION = 2612
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
COMBINE_HIDPI_IMAGES = YES
From 0f753a66ccef2c57b06a3439d236542e6747c7dd Mon Sep 17 00:00:00 2001
From: Brent Simmons
Date: Sat, 7 Sep 2019 20:55:47 -0700
Subject: [PATCH 13/14] Update to latest RSCore and RSParser.
---
submodules/RSCore | 2 +-
submodules/RSParser | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/submodules/RSCore b/submodules/RSCore
index 7a4895657..3e0dbb1c0 160000
--- a/submodules/RSCore
+++ b/submodules/RSCore
@@ -1 +1 @@
-Subproject commit 7a48956576a242c631d634520748de326d893f9b
+Subproject commit 3e0dbb1c0a88697e7be510da0226fe1e3e7ef195
diff --git a/submodules/RSParser b/submodules/RSParser
index 76f4c468c..9e86cf613 160000
--- a/submodules/RSParser
+++ b/submodules/RSParser
@@ -1 +1 @@
-Subproject commit 76f4c468cee1e46544897d44c40e87e2bd05d729
+Subproject commit 9e86cf613b40b6a3389b6248be9427d90debbf9f
From 721d81f5f7705c1865d1efbe7bd3705e04739bb5 Mon Sep 17 00:00:00 2001
From: Brent Simmons
Date: Sat, 7 Sep 2019 21:25:48 -0700
Subject: [PATCH 14/14] Update appcast for 5.0.1d2.
---
Appcasts/netnewswire-beta.xml | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/Appcasts/netnewswire-beta.xml b/Appcasts/netnewswire-beta.xml
index 120e5399e..7834e2a83 100755
--- a/Appcasts/netnewswire-beta.xml
+++ b/Appcasts/netnewswire-beta.xml
@@ -6,7 +6,25 @@
Most recent NetNewsWire changes with links to updates.
en
- -
+
-
+ NetNewsWire 5.0.1d2
+ Crash fix: when the app is renamed or moved on disk while running, alert the user and quit the app. This prevents crashes that will happen due to renaming/moving. See Daniel Jalkut on App Movement Monitoring for more info.
+
+Timeline: update UI more quickly when a feed icon is downloaded.
+
+Article pane: stop blocking links containing the string “feedburner” — there are legitimate cases where that string appears.
+
+Timeline and article pane: make sure the link for an article isn’t an empty string.
+
+RSS parser: check for bad permalinks more aggressively. If they don’t contain a / character, then do not consider them permalinks.
+ ]]>
+ Sat, 07 Sep 2019 21:00:00 -0700
+
+ 10.14.4
+
+
+ -
NetNewsWire 5.0.1d1
(Probably) fix a crashing bug having to do with a callback being called more than once, when it was designed to be called just once.