Merge branch 'master' of https://github.com/brentsimmons/NetNewsWire
This commit is contained in:
commit
25270b3d96
|
@ -6,7 +6,25 @@
|
|||
<description>Most recent NetNewsWire changes with links to updates.</description>
|
||||
<language>en</language>
|
||||
|
||||
<item>
|
||||
<item>
|
||||
<title>NetNewsWire 5.0.1d2</title>
|
||||
<description><![CDATA[
|
||||
<p>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 <a href="https://red-sweater.com/blog/3508/app-movement-monitoring">App Movement Monitoring</a> for more info.</p>
|
||||
|
||||
<p>Timeline: update UI more quickly when a feed icon is downloaded.</p>
|
||||
|
||||
<p>Article pane: stop blocking links containing the string “feedburner” — there are legitimate cases where that string appears.</p>
|
||||
|
||||
<p>Timeline and article pane: make sure the link for an article isn’t an empty string.</p>
|
||||
|
||||
<p>RSS parser: check for bad permalinks more aggressively. If they don’t contain a / character, then do not consider them permalinks.</p>
|
||||
]]></description>
|
||||
<pubDate>Sat, 07 Sep 2019 21:00:00 -0700</pubDate>
|
||||
<enclosure url="https://github.com/brentsimmons/NetNewsWire/releases/download/mac-5.0.1d2/NetNewsWire5.0.1d2.zip" sparkle:version="2612" sparkle:shortVersionString="5.0.1d2" length="5095642" type="application/zip" />
|
||||
<sparkle:minimumSystemVersion>10.14.4</sparkle:minimumSystemVersion>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>NetNewsWire 5.0.1d1</title>
|
||||
<description><![CDATA[
|
||||
<p>(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.</p>
|
||||
|
|
|
@ -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 */; };
|
||||
|
@ -59,7 +61,15 @@
|
|||
51938DF2231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; };
|
||||
51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; };
|
||||
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; };
|
||||
519D73FB2323FF35008BB345 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F35D0822AFD4760003CE1B /* SettingsView.swift */; };
|
||||
519D740623243CC0008BB345 /* RefreshInterval-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */; };
|
||||
519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */; };
|
||||
519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */; };
|
||||
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
|
||||
51AF45E123246731001742EF /* SettingsAccountLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */; };
|
||||
51AF460323247321001742EF /* SettingsDetailAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */; };
|
||||
51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */; };
|
||||
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 */; };
|
||||
|
@ -726,7 +736,9 @@
|
|||
5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportDocumentPickerView.swift; sourceTree = "<group>"; };
|
||||
5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportDocumentPickerView.swift; sourceTree = "<group>"; };
|
||||
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
|
||||
519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RefreshInterval-Extensions.swift"; sourceTree = "<group>"; };
|
||||
519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
51AF460D232488C6001742EF /* Account-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51C45254226507D200C03939 /* AppAssets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppAssets.swift; sourceTree = "<group>"; };
|
||||
|
@ -784,8 +796,8 @@
|
|||
55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsReaderAPI.xib; sourceTree = "<group>"; };
|
||||
55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsReaderAPIWindowController.swift; sourceTree = "<group>"; };
|
||||
5F323808231DF9F000706F6B /* NNWTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNWTableViewCell.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
6581C73920CED60100F4AD34 /* SafariExtensionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1128,6 +1140,15 @@
|
|||
path = Wrappers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
519D740423243C68008BB345 /* Model Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */,
|
||||
51AF460D232488C6001742EF /* Account-Extensions.swift */,
|
||||
);
|
||||
path = "Model Extensions";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
51C45245226506C800C03939 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1787,6 +1808,7 @@
|
|||
5183CCEB227117C70010922C /* Settings */,
|
||||
5183CCDB226F1EEB0010922C /* Progress */,
|
||||
51C45245226506C800C03939 /* Extensions */,
|
||||
519D740423243C68008BB345 /* Model Extensions */,
|
||||
5F3237FF231DF9D000706F6B /* Views */,
|
||||
5194B5E222B693EC00144881 /* Wrappers */,
|
||||
84C9FC9A2262A1A900D921D6 /* Resources */,
|
||||
|
@ -2429,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 */,
|
||||
|
@ -2437,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 */,
|
||||
|
@ -2452,9 +2478,12 @@
|
|||
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 */,
|
||||
51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */,
|
||||
51F85BF52273625800C787DC /* Bundle-Extensions.swift in Sources */,
|
||||
519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */,
|
||||
51C452A622650A3500C03939 /* Node-Extensions.swift in Sources */,
|
||||
5183CCDF226F1FCC0010922C /* UINavigationController+Progress.swift in Sources */,
|
||||
51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */,
|
||||
|
@ -2490,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 */,
|
||||
|
@ -2504,6 +2534,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 */,
|
||||
|
@ -2515,6 +2546,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 */,
|
||||
|
|
|
@ -47,7 +47,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? {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -61,6 +61,10 @@ class RootSplitViewController: UISplitViewController {
|
|||
coordinator.showBrowserForCurrentArticle()
|
||||
}
|
||||
|
||||
@objc func articleSearch(_ sender: Any?) {
|
||||
coordinator.showSearch()
|
||||
}
|
||||
|
||||
@objc func addNewFeed(_ sender: Any?) {
|
||||
coordinator.showAdd(.feed)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import Account
|
||||
import Articles
|
||||
import RSCore
|
||||
|
@ -327,11 +328,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 +581,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
|
||||
|
||||
|
@ -861,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)
|
||||
rootSplitViewController.present(style: .formSheet) {
|
||||
SettingsView(viewModel: SettingsView.ViewModel())
|
||||
}
|
||||
}
|
||||
|
||||
func showAdd(_ type: AddControllerType) {
|
||||
|
@ -1473,6 +1466,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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ViewModel, Never>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ViewModel, Never>()
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,88 +10,136 @@ import SwiftUI
|
|||
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
|
||||
|
||||
@Environment(\.viewController) private var viewController: UIViewController?
|
||||
|
||||
@State private var isWebsitePresented: Bool = false
|
||||
@State private var website: String? = nil
|
||||
|
||||
@State private var isOPMLImportPresented: Bool = false
|
||||
@State private var isOPMLImportDocPickerPresented: Bool = false
|
||||
@State private var isOPMLExportPresented: Bool = false
|
||||
@State private var isOPMLExportDocPickerPresented: Bool = false
|
||||
@State private var opmlAccount: Account? = nil
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
|
||||
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("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.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/")!)) {
|
||||
Text("Website")
|
||||
}
|
||||
PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire")!)) {
|
||||
Text("Github Repository")
|
||||
}
|
||||
PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!)) {
|
||||
Text("Bug Tracker")
|
||||
}
|
||||
PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!)) {
|
||||
Text("Technotes")
|
||||
}
|
||||
PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")!)) {
|
||||
Text("How to Support NetNewsWire")
|
||||
}
|
||||
Text("Add NetNewsWire News Feed")
|
||||
}
|
||||
.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 {
|
||||
|
@ -100,33 +148,33 @@ struct SettingsView : View {
|
|||
}
|
||||
|
||||
let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
|
||||
self.subscriptionsImportAccounts = nil
|
||||
self.subscriptionsImportDocumentPicker = Modal(SettingsSubscriptionsImportDocumentPickerView(account: account))
|
||||
self.opmlAccount = account
|
||||
self.isOPMLImportDocPickerPresented = true
|
||||
}
|
||||
|
||||
buttons.append(button)
|
||||
}
|
||||
|
||||
buttons.append(.cancel { self.subscriptionsImportAccounts = nil })
|
||||
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 {
|
||||
func buildSubscriptionsExportAccounts() -> 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))
|
||||
self.opmlAccount = account
|
||||
self.isOPMLExportDocPickerPresented = true
|
||||
}
|
||||
buttons.append(button)
|
||||
}
|
||||
|
||||
buttons.append(.cancel { self.subscriptionsExportAccounts = nil })
|
||||
buttons.append(.cancel())
|
||||
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)
|
||||
|
@ -134,9 +182,9 @@ struct SettingsView : View {
|
|||
|
||||
// MARK: ViewModel
|
||||
|
||||
class ViewModel: BindableObject {
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
let didChange = PassthroughSubject<ViewModel, Never>()
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
|
||||
init() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .AccountsDidChange, object: nil)
|
||||
|
@ -164,12 +212,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 +226,8 @@ struct SettingsView : View {
|
|||
return AppDefaults.timelineNumberOfLines
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
AppDefaults.timelineNumberOfLines = newValue
|
||||
didChange.send(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,17 +236,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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 75b609926fe64c7c14428a39bda1f301cd968f46
|
||||
Subproject commit 3e0dbb1c0a88697e7be510da0226fe1e3e7ef195
|
|
@ -1 +1 @@
|
|||
Subproject commit 76f4c468cee1e46544897d44c40e87e2bd05d729
|
||||
Subproject commit 9e86cf613b40b6a3389b6248be9427d90debbf9f
|
|
@ -38,4 +38,4 @@ CODE_SIGN_ENTITLEMENTS = Mac/Resources/NetNewsWire.entitlements
|
|||
INFOPLIST_FILE = Mac/Resources/Info.plist
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.NetNewsWire-Evergreen
|
||||
PRODUCT_NAME = NetNewsWire
|
||||
PRODUCT_NAME = NetNewsWire
|
Loading…
Reference in New Issue