From dde90355921e17d8da3066f5980609c56dc57eab Mon Sep 17 00:00:00 2001 From: Rizwan Mohamed Ibrahim Date: Sat, 4 Jul 2020 13:39:08 +0530 Subject: [PATCH 1/5] Add feeds settings for import and export subscriptions --- .../iOS/Settings/FeedsSettingsModel.swift | 68 ++++++++++++++++ Multiplatform/iOS/Settings/SettingsView.swift | 81 +++++++++++++++---- NetNewsWire.xcodeproj/project.pbxproj | 6 ++ 3 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 Multiplatform/iOS/Settings/FeedsSettingsModel.swift diff --git a/Multiplatform/iOS/Settings/FeedsSettingsModel.swift b/Multiplatform/iOS/Settings/FeedsSettingsModel.swift new file mode 100644 index 000000000..b32c17c71 --- /dev/null +++ b/Multiplatform/iOS/Settings/FeedsSettingsModel.swift @@ -0,0 +1,68 @@ +// +// FeedsSettingsModel.swift +// Multiplatform iOS +// +// Created by Rizwan on 04/07/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import Foundation +import SwiftUI +import Account + +class FeedsSettingsModel: ObservableObject { + @Published var showingImportActionSheet = false + @Published var showingExportActionSheet = false + @Published var exportingFilePath = "" + + func onTapExportOPML(action: ((Account?) -> Void)) { + if AccountManager.shared.accounts.count == 1 { + action(AccountManager.shared.accounts.first) + } + else { + showingExportActionSheet = true + } + } + + func onTapImportOPML(action: ((Account?) -> Void)) { + switch AccountManager.shared.activeAccounts.count { + case 0: + //TODO:- show error + return + case 1: + action(AccountManager.shared.activeAccounts.first) + default: + showingImportActionSheet = true + } + } + + func generateExportURL(for account: Account) -> URL? { + let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces) + let filename = "Subscriptions-\(accountName).opml" + let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename) + let opmlString = OPMLExporter.OPMLString(with: account, title: filename) + do { + try opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8) + } catch { + //TODO:- show error + return nil + } + + return tempFile + } + + func processImportedFiles(_ urls: [URL],_ account: Account?) { + urls.forEach{ + account?.importOPML($0, completion: { result in + switch result { + case .success: + break + case .failure: + //TODO:- show error + break + } + }) + } + } +} + diff --git a/Multiplatform/iOS/Settings/SettingsView.swift b/Multiplatform/iOS/Settings/SettingsView.swift index 9aca56100..95fbd6894 100644 --- a/Multiplatform/iOS/Settings/SettingsView.swift +++ b/Multiplatform/iOS/Settings/SettingsView.swift @@ -8,7 +8,7 @@ import SwiftUI import Account - +import UniformTypeIdentifiers class SettingsViewModel: ObservableObject { @@ -55,10 +55,13 @@ struct SettingsView: View { let sortedAccounts = AccountManager.shared.sortedAccounts @Environment(\.presentationMode) var presentationMode - + @Environment(\.exportFiles) var exportAction + @Environment(\.importFiles) var importAction + @StateObject private var viewModel = SettingsViewModel() + @StateObject private var feedsSettingsModel = FeedsSettingsModel() @StateObject private var settings = AppDefaults.shared - + var body: some View { NavigationView { List { @@ -114,19 +117,50 @@ struct SettingsView: View { var importExport: some View { Section(header: Text("Feeds"), content: { - NavigationLink( - destination: EmptyView(), - label: { - Text("Import Subscriptions") - }) - NavigationLink( - destination: EmptyView(), - label: { - Text("Export Subscriptions") - }) + Button(action:{ + feedsSettingsModel.onTapImportOPML(action: importOPML) + }) { + Text("Import Subscriptions") + .actionSheet(isPresented: $feedsSettingsModel.showingImportActionSheet, content: importActionSheet) + .foregroundColor(.primary) + } + Button(action:{ + feedsSettingsModel.onTapExportOPML(action: exportOPML) + }) { + Text("Export Subscriptions") + .actionSheet(isPresented: $feedsSettingsModel.showingExportActionSheet, content: exportActionSheet) + .foregroundColor(.primary) + } }) + } - + + private func importActionSheet() -> ActionSheet { + var buttons = sortedAccounts.map { (account) -> ActionSheet.Button in + ActionSheet.Button.default(Text(account.nameForDisplay)) { + importOPML(account: account) + } + } + buttons.append(.cancel()) + return ActionSheet( + title: Text("Choose an account to receive the imported feeds and folders"), + buttons: buttons + ) + } + + private func exportActionSheet() -> ActionSheet { + var buttons = sortedAccounts.map { (account) -> ActionSheet.Button in + ActionSheet.Button.default(Text(account.nameForDisplay)) { + exportOPML(account: account) + } + } + buttons.append(.cancel()) + return ActionSheet( + title: Text("Choose an account with the subscriptions to export"), + buttons: buttons + ) + } + var timeline: some View { Section(header: Text("Timeline"), content: { Toggle("Sort Oldest to Newest", isOn: $settings.timelineSortDirection) @@ -202,7 +236,24 @@ struct SettingsView: View { let build = dict?.object(forKey: "CFBundleVersion") as? String ?? "" return "NetNewsWire \(version) (Build \(build))" } - + + private func exportOPML(account: Account?) { + guard let account = account, + let url = feedsSettingsModel.generateExportURL(for: account) else { + return + } + + exportAction(moving: url) { _ in } + } + + private func importOPML(account: Account?) { + let types = [UTType(filenameExtension: "opml"), UTType("public.xml")].compactMap { $0 } + importAction(multipleOfType: types) { (result: Result<[URL], Error>?) in + if let urls = try? result?.get() { + feedsSettingsModel.processImportedFiles(urls, account) + } + } + } } struct SettingsView_Previews: PreviewProvider { diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index bdb2dc841..1e7515704 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -635,6 +635,8 @@ 6581C73D20CED60100F4AD34 /* SafariExtensionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */; }; 6581C74020CED60100F4AD34 /* netnewswire-subscribe-to-feed.js in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73F20CED60100F4AD34 /* netnewswire-subscribe-to-feed.js */; }; 6581C74220CED60100F4AD34 /* ToolbarItemIcon.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */; }; + 6594CA3B24AF6F2A005C7D7C /* OPMLExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8444C8F11FED81840051386C /* OPMLExporter.swift */; }; + 65C2E40124B05D8A000AFDF6 /* FeedsSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65C2E40024B05D8A000AFDF6 /* FeedsSettingsModel.swift */; }; 65CBAD5A24AE03C20006DD91 /* ColorPaletteContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65CBAD5924AE03C20006DD91 /* ColorPaletteContainerView.swift */; }; 65ED3FB7235DEF6C0081F399 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; }; 65ED3FB8235DEF6C0081F399 /* CrashReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848B937121C8C5540038DC0D /* CrashReporter.swift */; }; @@ -2007,6 +2009,7 @@ 6581C73F20CED60100F4AD34 /* netnewswire-subscribe-to-feed.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "netnewswire-subscribe-to-feed.js"; sourceTree = ""; }; 6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ToolbarItemIcon.pdf; sourceTree = ""; }; 6581C74320CED60100F4AD34 /* Subscribe_to_Feed.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Subscribe_to_Feed.entitlements; sourceTree = ""; }; + 65C2E40024B05D8A000AFDF6 /* FeedsSettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedsSettingsModel.swift; sourceTree = ""; }; 65CBAD5924AE03C20006DD91 /* ColorPaletteContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPaletteContainerView.swift; sourceTree = ""; }; 65ED4083235DEF6C0081F399 /* NetNewsWire.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetNewsWire.app; sourceTree = BUILT_PRODUCTS_DIR; }; 65ED409D235DEF770081F399 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2384,6 +2387,7 @@ isa = PBXGroup; children = ( 172199C824AB228900A31D04 /* SettingsView.swift */, + 65C2E40024B05D8A000AFDF6 /* FeedsSettingsModel.swift */, 17B223B924AC24A8001E4592 /* Submenus */, ); path = Settings; @@ -4807,6 +4811,7 @@ 51E498FF24A808BB00B667CB /* SingleFaviconDownloader.swift in Sources */, 51E4997224A8784300B667CB /* DefaultFeedsImporter.swift in Sources */, 514E6C0924AD39AD00AC6F6E /* ArticleIconImageLoader.swift in Sources */, + 6594CA3B24AF6F2A005C7D7C /* OPMLExporter.swift in Sources */, 51919FAF24AA8EFA00541E64 /* SidebarItemView.swift in Sources */, 514E6BDA24ACEA0400AC6F6E /* TimelineItemView.swift in Sources */, 51E4990D24A808C500B667CB /* RSHTMLMetadata+Extension.swift in Sources */, @@ -4827,6 +4832,7 @@ 514E6BFF24AD255D00AC6F6E /* PreviewArticles.swift in Sources */, 51E4993024A8676400B667CB /* ArticleSorter.swift in Sources */, 51408B7E24A9EC6F0073CF4E /* SidebarItem.swift in Sources */, + 65C2E40124B05D8A000AFDF6 /* FeedsSettingsModel.swift in Sources */, 51E4990A24A808C500B667CB /* FeaturedImageDownloader.swift in Sources */, 51E4993824A8680E00B667CB /* Reachability.swift in Sources */, 51E4993224A8676400B667CB /* FetchRequestQueue.swift in Sources */, From c0755fe55bab263fd31615a1522633d9b0042800 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sat, 4 Jul 2020 21:07:18 +0800 Subject: [PATCH 2/5] Add Web Feed Layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2194 • Views are aligned using `LazyVGrid` and some `.padding()` --- Multiplatform/Shared/Add/AddWebFeedView.swift | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/Multiplatform/Shared/Add/AddWebFeedView.swift b/Multiplatform/Shared/Add/AddWebFeedView.swift index 5741e6e1b..f0d745d45 100644 --- a/Multiplatform/Shared/Add/AddWebFeedView.swift +++ b/Multiplatform/Shared/Add/AddWebFeedView.swift @@ -49,23 +49,34 @@ struct AddWebFeedView: View { Form { HStack { Spacer() - Image(systemName: "globe").foregroundColor(.accentColor).font(.title) + Image("FaviconTemplateImage") + .resizable() + .renderingMode(.template) + .frame(width: 30, height: 30) + .foregroundColor(.accentColor).font(.title) Text("Add a Web Feed") .font(.title) Spacer() } - urlTextField - .textFieldStyle(RoundedBorderTextFieldStyle()) - .help("The URL of the feed you want to add.") - providedNameTextField - .textFieldStyle(RoundedBorderTextFieldStyle()) - .help("The name of the feed. (Optional.)") - folderPicker - .help("Pick the folder you want to add the feed to.") + + LazyVGrid(columns: [GridItem(.fixed(75), spacing: 10, alignment: .trailing),GridItem(.fixed(400), spacing: 0, alignment: .leading) ], alignment: .leading, spacing: 10, pinnedViews: [], content:{ + + Text("URL:").bold() + urlTextField + .textFieldStyle(RoundedBorderTextFieldStyle()) + .help("The URL of the feed you want to add.") + Text("Name:").bold() + providedNameTextField + .textFieldStyle(RoundedBorderTextFieldStyle()) + .help("The name of the feed. (Optional.)") + Text("Folder:").bold() + folderPicker + .help("Pick the folder you want to add the feed to.") + }) buttonStack } - .padding() - .frame(minWidth: 450) + .frame(maxWidth: 485) + .padding(12) } #endif @@ -98,8 +109,8 @@ struct AddWebFeedView: View { var urlTextField: some View { HStack { - Text("Feed:") #if os(iOS) + Text("Feed:") TextField("URL", text: $viewModel.providedURL) .disableAutocorrection(true) .autocapitalization(UITextAutocapitalizationType.none) @@ -112,12 +123,15 @@ struct AddWebFeedView: View { var providedNameTextField: some View { HStack(alignment: .lastTextBaseline) { + #if os(iOS) Text("Name:") + #endif TextField("Optional", text: $viewModel.providedName) } } - var folderPicker: some View { + @ViewBuilder var folderPicker: some View { + #if os(iOS) Picker("Folder:", selection: $viewModel.selectedFolderIndex, content: { ForEach(0.. Date: Sat, 4 Jul 2020 21:34:15 +0800 Subject: [PATCH 3/5] Sidebar toolbar no longer an overlay Also moved view model to separate file and removed the view prefix. --- .../Sidebar/CompactSidebarContainerView.swift | 7 +- .../Sidebar/RegularSidebarContainerView.swift | 6 +- .../Shared/Sidebar/SidebarToolbar.swift | 124 ++++++++---------- .../Shared/Sidebar/SidebarToolbarModel.swift | 26 ++++ NetNewsWire.xcodeproj/project.pbxproj | 28 ++-- 5 files changed, 98 insertions(+), 93 deletions(-) create mode 100644 Multiplatform/Shared/Sidebar/SidebarToolbarModel.swift diff --git a/Multiplatform/Shared/Sidebar/CompactSidebarContainerView.swift b/Multiplatform/Shared/Sidebar/CompactSidebarContainerView.swift index a14b01503..686f01ebd 100644 --- a/Multiplatform/Shared/Sidebar/CompactSidebarContainerView.swift +++ b/Multiplatform/Shared/Sidebar/CompactSidebarContainerView.swift @@ -16,6 +16,7 @@ struct CompactSidebarContainerView: View { var body: some View { SidebarView() + .modifier(SidebarToolbar()) .environmentObject(sidebarModel) .navigationBarTitle(Text("Feeds")) .listStyle(PlainListStyle()) @@ -23,11 +24,7 @@ struct CompactSidebarContainerView: View { sceneModel.sidebarModel = sidebarModel sidebarModel.delegate = sceneModel sidebarModel.rebuildSidebarItems() - }.overlay(Group { - #if os(iOS) - SidebarToolbar() - #endif - },alignment: .bottom) + } } } diff --git a/Multiplatform/Shared/Sidebar/RegularSidebarContainerView.swift b/Multiplatform/Shared/Sidebar/RegularSidebarContainerView.swift index f2440a746..bd2e05a42 100644 --- a/Multiplatform/Shared/Sidebar/RegularSidebarContainerView.swift +++ b/Multiplatform/Shared/Sidebar/RegularSidebarContainerView.swift @@ -17,6 +17,7 @@ struct RegularSidebarContainerView: View { @ViewBuilder var body: some View { SidebarView() + .modifier(SidebarToolbar()) .environmentObject(sidebarModel) .navigationTitle(Text("Feeds")) .listStyle(SidebarListStyle()) @@ -25,11 +26,6 @@ struct RegularSidebarContainerView: View { sidebarModel.delegate = sceneModel sidebarModel.rebuildSidebarItems() } - .overlay(Group { - #if os(iOS) - SidebarToolbar() - #endif - },alignment: .bottom) } diff --git a/Multiplatform/Shared/Sidebar/SidebarToolbar.swift b/Multiplatform/Shared/Sidebar/SidebarToolbar.swift index 90d4a7e39..969809e22 100644 --- a/Multiplatform/Shared/Sidebar/SidebarToolbar.swift +++ b/Multiplatform/Shared/Sidebar/SidebarToolbar.swift @@ -8,85 +8,65 @@ import SwiftUI -fileprivate enum ToolbarSheets { - case none, web, twitter, reddit, folder, settings -} -fileprivate class SidebarToolbarViewModel: ObservableObject { - - @Published var showSheet: Bool = false - @Published var sheetToShow: ToolbarSheets = .none { - didSet { - sheetToShow != .none ? (showSheet = true) : (showSheet = false) - } - } - @Published var showActionSheet: Bool = false - @Published var showAddSheet: Bool = false - -} - -struct SidebarToolbar: View { +struct SidebarToolbar: ViewModifier { @EnvironmentObject private var appSettings: AppDefaults - @StateObject private var viewModel = SidebarToolbarViewModel() + @StateObject private var viewModel = SidebarToolbarModel() - var body: some View { - VStack { - Divider() - HStack(alignment: .center) { - Button(action: { - viewModel.sheetToShow = .settings - }, label: { - AppAssets.settingsImage - .font(.title3) - .foregroundColor(.accentColor) - }).help("Settings") + func body(content: Content) -> some View { + content + .toolbar { + ToolbarItem(placement: .automatic) { + Button(action: { + viewModel.sheetToShow = .settings + }, label: { + AppAssets.settingsImage + .font(.title3) + .foregroundColor(.accentColor) + Spacer() + }).help("Settings") + } - Spacer() - - Text("Last updated") - .font(.caption) - .foregroundColor(.secondary) - - Spacer() - - Button(action: { - viewModel.showActionSheet = true - }, label: { - AppAssets.addMenuImage - .font(.title3) - .foregroundColor(.accentColor) + ToolbarItem(placement: .automatic, content: { + Spacer() + Text("Last updated") + .font(.caption) + .foregroundColor(.secondary) + Spacer() }) - .help("Add") - .actionSheet(isPresented: $viewModel.showActionSheet) { - ActionSheet(title: Text("Add"), buttons: [ - .cancel(), - .default(Text("Add Web Feed"), action: { viewModel.sheetToShow = .web }), - .default(Text("Add Twitter Feed")), - .default(Text("Add Reddit Feed")), - .default(Text("Add Folder")) - ]) + + ToolbarItem(placement: .automatic, content: { + Button(action: { + viewModel.showActionSheet = true + }, label: { + Spacer() + AppAssets.addMenuImage + .font(.title3) + .foregroundColor(.accentColor) + }) + .help("Add") + .actionSheet(isPresented: $viewModel.showActionSheet) { + ActionSheet(title: Text("Add"), buttons: [ + .cancel(), + .default(Text("Add Web Feed"), action: { viewModel.sheetToShow = .web }), + .default(Text("Add Twitter Feed")), + .default(Text("Add Reddit Feed")), + .default(Text("Add Folder")) + ]) + } + }) + + } + .sheet(isPresented: $viewModel.showSheet, onDismiss: { viewModel.sheetToShow = .none }) { + if viewModel.sheetToShow == .web { + AddWebFeedView() + } + if viewModel.sheetToShow == .settings { + SettingsView().modifier(PreferredColorSchemeModifier(preferredColorScheme: appSettings.userInterfaceColorPalette)) } } - .padding(.horizontal, 16) - .padding(.bottom, 12) - .padding(.top, 4) - } - .background(VisualEffectBlur(blurStyle: .systemChromeMaterial).edgesIgnoringSafeArea(.bottom)) - .sheet(isPresented: $viewModel.showSheet, onDismiss: { viewModel.sheetToShow = .none }) { - if viewModel.sheetToShow == .web { - AddWebFeedView() - } - if viewModel.sheetToShow == .settings { - SettingsView().modifier(PreferredColorSchemeModifier(preferredColorScheme: appSettings.userInterfaceColorPalette)) - } - } - - } + } } -struct SidebarToolbar_Previews: PreviewProvider { - static var previews: some View { - SidebarToolbar() - } -} + diff --git a/Multiplatform/Shared/Sidebar/SidebarToolbarModel.swift b/Multiplatform/Shared/Sidebar/SidebarToolbarModel.swift new file mode 100644 index 000000000..000066277 --- /dev/null +++ b/Multiplatform/Shared/Sidebar/SidebarToolbarModel.swift @@ -0,0 +1,26 @@ +// +// SidebarToolbarModel.swift +// NetNewsWire +// +// Created by Stuart Breckenridge on 4/7/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import Foundation + +enum ToolbarSheets { + case none, web, twitter, reddit, folder, settings +} + +class SidebarToolbarModel: ObservableObject { + + @Published var showSheet: Bool = false + @Published var sheetToShow: ToolbarSheets = .none { + didSet { + sheetToShow != .none ? (showSheet = true) : (showSheet = false) + } + } + @Published var showActionSheet: Bool = false + @Published var showAddSheet: Bool = false + +} diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index bdb2dc841..ea58b06bd 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ 179DB3CE822BFCC2D774D9F4 /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; }; 17D232A824AFF10A0005F075 /* AddWebFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */; }; 17D232A924AFF10A0005F075 /* AddWebFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */; }; + 17D5F17124B0BC6700375168 /* SidebarToolbarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */; }; + 17D5F17224B0BC6700375168 /* SidebarToolbarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */; }; 3B3A32A5238B820900314204 /* FeedWranglerAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */; }; 3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */; }; 3B826DCC2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; }; @@ -1731,6 +1733,7 @@ 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsNewsBlurWindowController.swift; sourceTree = ""; }; 17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLayoutView.swift; sourceTree = ""; }; 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedModel.swift; sourceTree = ""; }; + 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarToolbarModel.swift; sourceTree = ""; }; 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAccountViewController.swift; sourceTree = ""; }; 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = ""; }; 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsFeedWranglerWindowController.swift; sourceTree = ""; }; @@ -2963,6 +2966,7 @@ 51919FAE24AA8EFA00541E64 /* SidebarItemView.swift */, 51E499FC24A9137600B667CB /* SidebarModel.swift */, 172199F024AB716900A31D04 /* SidebarToolbar.swift */, + 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */, 51919FA524AA64B000541E64 /* SidebarView.swift */, 51919FAB24AA8CCA00541E64 /* UnreadCountView.swift */, ); @@ -3972,46 +3976,46 @@ TargetAttributes = { 51314636235A7BBE00387FDC = { CreatedOnToolsVersion = 11.2; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; LastSwiftMigration = 1120; ProvisioningStyle = Automatic; }; 513C5CE5232571C2003D4054 = { CreatedOnToolsVersion = 11.0; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; }; 518B2ED12351B3DD00400001 = { CreatedOnToolsVersion = 11.2; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; TestTargetID = 840D617B2029031C009BC708; }; 51C0513C24A77DF800194D5E = { CreatedOnToolsVersion = 12.0; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; }; 51C0514324A77DF800194D5E = { CreatedOnToolsVersion = 12.0; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; }; 6581C73220CED60000F4AD34 = { - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; }; 65ED3FA2235DEF6C0081F399 = { - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; }; 65ED4090235DEF770081F399 = { - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -4021,7 +4025,7 @@ }; 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.HardenedRuntime = { @@ -4031,7 +4035,7 @@ }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = FQLBNX3GP7; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -4797,6 +4801,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 17D5F17124B0BC6700375168 /* SidebarToolbarModel.swift in Sources */, 51E4995924A873F900B667CB /* ErrorHandler.swift in Sources */, 51392D1B24AC19A000BE0D35 /* SidebarExpandedContainers.swift in Sources */, 51E4992F24A8676400B667CB /* ArticleArray.swift in Sources */, @@ -4915,6 +4920,7 @@ 51E4990824A808C300B667CB /* RSHTMLMetadata+Extension.swift in Sources */, 51919FF824AB8B7700541E64 /* TimelineView.swift in Sources */, 51E4992B24A8676300B667CB /* ArticleArray.swift in Sources */, + 17D5F17224B0BC6700375168 /* SidebarToolbarModel.swift in Sources */, 514E6C0724AD2B5F00AC6F6E /* Image-Extensions.swift in Sources */, 51E4994D24A8734C00B667CB /* ExtensionPointIdentifer.swift in Sources */, 51E4992224A8095600B667CB /* URL-Extensions.swift in Sources */, From 294591d154d2904a2242613d2bd7af0b39fd702f Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sat, 4 Jul 2020 22:01:01 +0800 Subject: [PATCH 4/5] Fixes compile crash with sidebar toolbar --- Multiplatform/Shared/Add/AddWebFeedView.swift | 3 +-- .../Shared/Sidebar/RegularSidebarContainerView.swift | 1 - Multiplatform/Shared/Sidebar/SidebarToolbar.swift | 11 ++++++++++- NetNewsWire.xcodeproj/project.pbxproj | 2 ++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Multiplatform/Shared/Add/AddWebFeedView.swift b/Multiplatform/Shared/Add/AddWebFeedView.swift index f0d745d45..2ab535457 100644 --- a/Multiplatform/Shared/Add/AddWebFeedView.swift +++ b/Multiplatform/Shared/Add/AddWebFeedView.swift @@ -49,11 +49,10 @@ struct AddWebFeedView: View { Form { HStack { Spacer() - Image("FaviconTemplateImage") + Image(rsImage: AppAssets.faviconTemplateImage) .resizable() .renderingMode(.template) .frame(width: 30, height: 30) - .foregroundColor(.accentColor).font(.title) Text("Add a Web Feed") .font(.title) Spacer() diff --git a/Multiplatform/Shared/Sidebar/RegularSidebarContainerView.swift b/Multiplatform/Shared/Sidebar/RegularSidebarContainerView.swift index bd2e05a42..c5311d586 100644 --- a/Multiplatform/Shared/Sidebar/RegularSidebarContainerView.swift +++ b/Multiplatform/Shared/Sidebar/RegularSidebarContainerView.swift @@ -26,7 +26,6 @@ struct RegularSidebarContainerView: View { sidebarModel.delegate = sceneModel sidebarModel.rebuildSidebarItems() } - } } diff --git a/Multiplatform/Shared/Sidebar/SidebarToolbar.swift b/Multiplatform/Shared/Sidebar/SidebarToolbar.swift index 969809e22..77d6a5799 100644 --- a/Multiplatform/Shared/Sidebar/SidebarToolbar.swift +++ b/Multiplatform/Shared/Sidebar/SidebarToolbar.swift @@ -14,7 +14,8 @@ struct SidebarToolbar: ViewModifier { @EnvironmentObject private var appSettings: AppDefaults @StateObject private var viewModel = SidebarToolbarModel() - func body(content: Content) -> some View { + @ViewBuilder func body(content: Content) -> some View { + #if os(iOS) content .toolbar { ToolbarItem(placement: .automatic) { @@ -66,6 +67,14 @@ struct SidebarToolbar: ViewModifier { SettingsView().modifier(PreferredColorSchemeModifier(preferredColorScheme: appSettings.userInterfaceColorPalette)) } } + #else + content + .toolbar { + ToolbarItem { + Spacer() + } + } + #endif } } diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index ea58b06bd..dd7e01732 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ 17D232A924AFF10A0005F075 /* AddWebFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */; }; 17D5F17124B0BC6700375168 /* SidebarToolbarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */; }; 17D5F17224B0BC6700375168 /* SidebarToolbarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */; }; + 17D5F19524B0C1DD00375168 /* SidebarToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172199F024AB716900A31D04 /* SidebarToolbar.swift */; }; 3B3A32A5238B820900314204 /* FeedWranglerAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */; }; 3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */; }; 3B826DCC2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; }; @@ -4965,6 +4966,7 @@ 51E4995024A8734C00B667CB /* ExtensionPoint.swift in Sources */, 51E4990E24A808CC00B667CB /* HTMLMetadataDownloader.swift in Sources */, 51E498FB24A808BA00B667CB /* FaviconGenerator.swift in Sources */, + 17D5F19524B0C1DD00375168 /* SidebarToolbar.swift in Sources */, 51E4996724A8760B00B667CB /* ArticleStylesManager.swift in Sources */, 1729529B24AA1FD200D65E66 /* MacSearchField.swift in Sources */, 51408B7F24A9EC6F0073CF4E /* SidebarItem.swift in Sources */, From 4bd6b3ac964d3a651f9dc8049813312e8dd55691 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 4 Jul 2020 09:03:35 -0500 Subject: [PATCH 5/5] Refactor SettingsModel into its own swift file --- .../iOS/Settings/SettingsModel.swift | 49 +++++++++++++++++++ Multiplatform/iOS/Settings/SettingsView.swift | 43 +--------------- NetNewsWire.xcodeproj/project.pbxproj | 6 ++- 3 files changed, 55 insertions(+), 43 deletions(-) create mode 100644 Multiplatform/iOS/Settings/SettingsModel.swift diff --git a/Multiplatform/iOS/Settings/SettingsModel.swift b/Multiplatform/iOS/Settings/SettingsModel.swift new file mode 100644 index 000000000..ef37b7f16 --- /dev/null +++ b/Multiplatform/iOS/Settings/SettingsModel.swift @@ -0,0 +1,49 @@ +// +// SettingsModel.swift +// Multiplatform iOS +// +// Created by Maurice Parker on 7/4/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import Foundation + +class SettingsModel: ObservableObject { + + enum HelpSites { + case netNewsWireHelp, netNewsWire, supportNetNewsWire, github, bugTracker, technotes, netNewsWireSlack, none + + var url: URL? { + switch self { + case .netNewsWireHelp: + return URL(string: "https://ranchero.com/netnewswire/help/ios/5.0/en/")! + case .netNewsWire: + return URL(string: "https://ranchero.com/netnewswire/")! + case .supportNetNewsWire: + return URL(string: "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")! + case .github: + return URL(string: "https://github.com/brentsimmons/NetNewsWire")! + case .bugTracker: + return URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")! + case .technotes: + return URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")! + case .netNewsWireSlack: + return URL(string: "https://ranchero.com/netnewswire/slack")! + case .none: + return nil + } + } + } + + @Published var presentSheet: Bool = false + var selectedWebsite: HelpSites = .none { + didSet { + if selectedWebsite == .none { + presentSheet = false + } else { + presentSheet = true + } + } + } + +} diff --git a/Multiplatform/iOS/Settings/SettingsView.swift b/Multiplatform/iOS/Settings/SettingsView.swift index 95fbd6894..2862afa75 100644 --- a/Multiplatform/iOS/Settings/SettingsView.swift +++ b/Multiplatform/iOS/Settings/SettingsView.swift @@ -10,47 +10,6 @@ import SwiftUI import Account import UniformTypeIdentifiers - -class SettingsViewModel: ObservableObject { - - enum HelpSites { - case netNewsWireHelp, netNewsWire, supportNetNewsWire, github, bugTracker, technotes, netNewsWireSlack, none - - var url: URL? { - switch self { - case .netNewsWireHelp: - return URL(string: "https://ranchero.com/netnewswire/help/ios/5.0/en/")! - case .netNewsWire: - return URL(string: "https://ranchero.com/netnewswire/")! - case .supportNetNewsWire: - return URL(string: "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")! - case .github: - return URL(string: "https://github.com/brentsimmons/NetNewsWire")! - case .bugTracker: - return URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")! - case .technotes: - return URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")! - case .netNewsWireSlack: - return URL(string: "https://ranchero.com/netnewswire/slack")! - case .none: - return nil - } - } - } - - @Published var presentSheet: Bool = false - var selectedWebsite: HelpSites = .none { - didSet { - if selectedWebsite == .none { - presentSheet = false - } else { - presentSheet = true - } - } - } - -} - struct SettingsView: View { let sortedAccounts = AccountManager.shared.sortedAccounts @@ -58,7 +17,7 @@ struct SettingsView: View { @Environment(\.exportFiles) var exportAction @Environment(\.importFiles) var importAction - @StateObject private var viewModel = SettingsViewModel() + @StateObject private var viewModel = SettingsModel() @StateObject private var feedsSettingsModel = FeedsSettingsModel() @StateObject private var settings = AppDefaults.shared diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 1e7515704..f85dea27d 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -222,6 +222,7 @@ 517A757C24451C1500B553B9 /* OAuthSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 517A755324451BD500B553B9 /* OAuthSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 5181C5AD24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5181C5AC24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift */; }; 5181C5AE24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5181C5AC24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift */; }; + 5181C66224B0C326002E0F70 /* SettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5181C66124B0C326002E0F70 /* SettingsModel.swift */; }; 5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */; }; 5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */; }; 5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; }; @@ -1850,6 +1851,7 @@ 517A745A2443665000B553B9 /* UIPageViewController-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIPageViewController-Extensions.swift"; sourceTree = ""; }; 517A754424451BD500B553B9 /* OAuthSwift.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OAuthSwift.xcodeproj; path = submodules/OAuthSwift/OAuthSwift.xcodeproj; sourceTree = ""; }; 5181C5AC24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferredColorSchemeModifier.swift; sourceTree = ""; }; + 5181C66124B0C326002E0F70 /* SettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsModel.swift; sourceTree = ""; }; 5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicLabel.swift; sourceTree = ""; }; 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicImageView.swift; sourceTree = ""; }; 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshInterval.swift; sourceTree = ""; }; @@ -2386,8 +2388,9 @@ 172199EB24AB228E00A31D04 /* Settings */ = { isa = PBXGroup; children = ( - 172199C824AB228900A31D04 /* SettingsView.swift */, 65C2E40024B05D8A000AFDF6 /* FeedsSettingsModel.swift */, + 5181C66124B0C326002E0F70 /* SettingsModel.swift */, + 172199C824AB228900A31D04 /* SettingsView.swift */, 17B223B924AC24A8001E4592 /* Submenus */, ); path = Settings; @@ -4849,6 +4852,7 @@ 51E4991D24A8092100B667CB /* NSAttributedString+NetNewsWire.swift in Sources */, 51E499FD24A9137600B667CB /* SidebarModel.swift in Sources */, 51A576BE24AE637400078888 /* ArticleView.swift in Sources */, + 5181C66224B0C326002E0F70 /* SettingsModel.swift in Sources */, 51E4995324A8734D00B667CB /* RedditFeedProvider-Extensions.swift in Sources */, 172199C924AB228900A31D04 /* SettingsView.swift in Sources */, 17D232A824AFF10A0005F075 /* AddWebFeedModel.swift in Sources */,