diff --git a/Multiplatform/macOS/Preferences/MacPreferencesModel.swift b/Multiplatform/macOS/Preferences/MacPreferencesModel.swift index b0df1edd9..12ac839f6 100644 --- a/Multiplatform/macOS/Preferences/MacPreferencesModel.swift +++ b/Multiplatform/macOS/Preferences/MacPreferencesModel.swift @@ -8,28 +8,122 @@ import Foundation -class MacPreferencesModel { +enum PreferencePane: Int, CaseIterable { + case general = 0 + case accounts = 1 + case advanced = 2 - enum PreferencePane: Int, CaseIterable { - case general = 0 - case accounts = 1 - case advanced = 2 - - var description: String { - switch self { - case .general: - return "General" - case .accounts: - return "Accounts" - case .advanced: - return "Advanced" - } + var description: String { + switch self { + case .general: + return "General" + case .accounts: + return "Accounts" + case .advanced: + return "Advanced" } } - var currentPreferencePane: PreferencePane = PreferencePane.general - - // General Preferences - +} + +class MacPreferencesModel: ObservableObject { + @Published var currentPreferencePane: PreferencePane = PreferencePane.general + @Published var rssReaders = Array(RSSReaderInfo.fetchRSSReaders(nil)) + @Published var rssReaderSelection: Set = RSSReaderInfo.fetchRSSReaders(nil) } + +// MARK:- RSS Readers + +private extension MacPreferencesModel { + + func prepareRSSReaders() { + // Top item should always be: NetNewsWire (this app) + // Additional items should be sorted alphabetically. + // Any older versions of NetNewsWire should be listed as: NetNewsWire (old version) + + + + + + } + + func registerAppWithBundleID(_ bundleID: String) { + NSWorkspace.shared.setDefaultAppBundleID(forURLScheme: "feed", to: bundleID) + NSWorkspace.shared.setDefaultAppBundleID(forURLScheme: "feeds", to: bundleID) + objectWillChange.send() + } + +} + + +// MARK: - RSSReaderInfo + +struct RSSReaderInfo { + + let defaultRSSReaderBundleID: String? + let rssReaders: Set + static let feedURLScheme = "feed:" + + init() { + let defaultRSSReaderBundleID = NSWorkspace.shared.defaultAppBundleID(forURLScheme: RSSReaderInfo.feedURLScheme) + self.defaultRSSReaderBundleID = defaultRSSReaderBundleID + self.rssReaders = RSSReaderInfo.fetchRSSReaders(defaultRSSReaderBundleID) + } + + static func fetchRSSReaders(_ defaultRSSReaderBundleID: String?) -> Set { + let rssReaderBundleIDs = NSWorkspace.shared.bundleIDsForApps(forURLScheme: feedURLScheme) + + var rssReaders = Set() + if let defaultRSSReaderBundleID = defaultRSSReaderBundleID, let defaultReader = RSSReader(bundleID: defaultRSSReaderBundleID) { + rssReaders.insert(defaultReader) + } + rssReaderBundleIDs.forEach { (bundleID) in + if let reader = RSSReader(bundleID: bundleID) { + rssReaders.insert(reader) + } + } + return rssReaders + } +} + + +// MARK: - RSSReader + +struct RSSReader: Hashable { + + let bundleID: String + let name: String + let nameMinusAppSuffix: String + let path: String + + init?(bundleID: String) { + guard let path = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleID) else { + return nil + } + + self.path = path.path + self.bundleID = bundleID + + let name = (self.path as NSString).lastPathComponent + self.name = name + if name.hasSuffix(".app") { + self.nameMinusAppSuffix = name.stripping(suffix: ".app") + } + else { + self.nameMinusAppSuffix = name + } + } + + // MARK: - Hashable + + func hash(into hasher: inout Hasher) { + hasher.combine(bundleID) + } + + // MARK: - Equatable + + static func ==(lhs: RSSReader, rhs: RSSReader) -> Bool { + return lhs.bundleID == rhs.bundleID + } +} diff --git a/Multiplatform/macOS/Preferences/MacPreferencesView.swift b/Multiplatform/macOS/Preferences/MacPreferencesView.swift index 94c56849a..ceb8bcdd3 100644 --- a/Multiplatform/macOS/Preferences/MacPreferencesView.swift +++ b/Multiplatform/macOS/Preferences/MacPreferencesView.swift @@ -17,7 +17,7 @@ struct MacPreferencesView: View { VStack { switch viewModel.currentPreferencePane { case .general: - GeneralPreferencesView() + GeneralPreferencesView(preferences: viewModel) .environmentObject(defaults) case .accounts: AccountsPreferencesView() diff --git a/Multiplatform/macOS/Preferences/Preferences Subviews/AccountsPreferencesView.swift b/Multiplatform/macOS/Preferences/Preference Panes/AccountsPreferencesView.swift similarity index 100% rename from Multiplatform/macOS/Preferences/Preferences Subviews/AccountsPreferencesView.swift rename to Multiplatform/macOS/Preferences/Preference Panes/AccountsPreferencesView.swift diff --git a/Multiplatform/macOS/Preferences/Preferences Subviews/AdvancedPreferencesView.swift b/Multiplatform/macOS/Preferences/Preference Panes/AdvancedPreferencesView.swift similarity index 63% rename from Multiplatform/macOS/Preferences/Preferences Subviews/AdvancedPreferencesView.swift rename to Multiplatform/macOS/Preferences/Preference Panes/AdvancedPreferencesView.swift index e493f3582..800514cf0 100644 --- a/Multiplatform/macOS/Preferences/Preferences Subviews/AdvancedPreferencesView.swift +++ b/Multiplatform/macOS/Preferences/Preference Panes/AdvancedPreferencesView.swift @@ -12,37 +12,34 @@ struct AdvancedPreferencesView: View { @EnvironmentObject private var preferences: AppDefaults var body: some View { - VStack { + Form { Toggle("Check for app updates automatically", isOn: $preferences.checkForUpdatesAutomatically) Toggle("Download Test Builds", isOn: $preferences.downloadTestBuilds) - HStack { - Spacer() - Text("If you’re not sure, don't enable test builds. Test builds may have bugs, which may include crashing bugs and data loss.").foregroundColor(.secondary) - Spacer() - } + + Text("If you’re not sure, don't enable test builds. Test builds may have bugs, which may include crashing bugs and data loss.") + .foregroundColor(.secondary) + .lineLimit(3) + .padding(.bottom, 8) HStack { Spacer() Button("Check for Updates", action: {}) Spacer() - }.padding(.vertical, 12) + }.padding(.bottom, 8) Toggle("Send Crash Logs Automatically", isOn: $preferences.sendCrashLogs) - Spacer() HStack { Spacer() - Button("Privacy Policy", action: {}) + Button("Privacy Policy", action: { + NSWorkspace.shared.open(URL(string: "https://ranchero.com/netnewswire/privacypolicy")!) + }) Spacer() }.padding(.top, 12) - - - } - Spacer() - }.frame(width: 300, alignment: .center) + }.frame(width: 400, alignment: .center) } } diff --git a/Multiplatform/macOS/Preferences/Preferences Subviews/GeneralPreferencesView.swift b/Multiplatform/macOS/Preferences/Preference Panes/GeneralPreferencesView.swift similarity index 64% rename from Multiplatform/macOS/Preferences/Preferences Subviews/GeneralPreferencesView.swift rename to Multiplatform/macOS/Preferences/Preference Panes/GeneralPreferencesView.swift index 0eec502b0..e67c91f12 100644 --- a/Multiplatform/macOS/Preferences/Preferences Subviews/GeneralPreferencesView.swift +++ b/Multiplatform/macOS/Preferences/Preference Panes/GeneralPreferencesView.swift @@ -10,11 +10,11 @@ import SwiftUI struct GeneralPreferencesView: View { @EnvironmentObject private var defaults: AppDefaults + @ObservedObject var preferences: MacPreferencesModel var body: some View { - VStack { Form { - Picker("Refresh Feeds", + Picker("Refresh feeds", selection: $defaults.interval, content: { ForEach(RefreshInterval.allCases, content: { interval in @@ -23,12 +23,19 @@ struct GeneralPreferencesView: View { }) .frame(width: 300, alignment: .center) + Picker("Default RSS reader", selection: $preferences.rssReaderSelection, content: { + ForEach(0..