// // SettingsView.swift // Multiplatform iOS // // Created by Stuart Breckenridge on 30/6/20. // Copyright © 2020 Ranchero Software. All rights reserved. // import SwiftUI import Account import UniformTypeIdentifiers struct SettingsView: View { let sortedAccounts = AccountManager.shared.sortedAccounts @Environment(\.presentationMode) var presentationMode @Environment(\.exportFiles) var exportAction @Environment(\.importFiles) var importAction @StateObject private var viewModel = SettingsModel() @StateObject private var feedsSettingsModel = FeedsSettingsModel() @StateObject private var settings = AppDefaults.shared var body: some View { NavigationView { List { systemSettings accounts importExport timeline articles appearance help } .listStyle(InsetGroupedListStyle()) .navigationBarTitle("Settings", displayMode: .inline) .navigationBarItems(leading: HStack { Button("Done") { presentationMode.wrappedValue.dismiss() } } ) } .sheet(isPresented: $viewModel.presentSheet, content: { SafariView(url: viewModel.selectedWebsite.url!) }) } var systemSettings: some View { Section(header: Text("Notifications, Badge, Data, & More"), content: { Button(action: { UIApplication.shared.open(URL(string: "\(UIApplication.openSettingsURLString)")!) }, label: { Text("Open System Settings").foregroundColor(.primary) }) }) } var accounts: some View { Section(header: Text("Accounts"), content: { ForEach(0.. 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) Toggle("Group by Feed", isOn: $settings.timelineGroupByFeed) Toggle("Refresh to Clear Read Articles", isOn: $settings.refreshClearsReadArticles) NavigationLink( destination: TimelineLayoutView().environmentObject(settings), label: { Text("Timeline Layout") }) }).toggleStyle(SwitchToggleStyle(tint: .accentColor)) } var articles: some View { Section(header: Text("Articles"), content: { Toggle("Confirm Mark All as Read", isOn: .constant(true)) Toggle(isOn: .constant(true), label: { VStack(alignment: .leading, spacing: 4) { Text("Enable Full Screen Articles") Text("Tap the article top bar to enter Full Screen. Tap the bottom or top to exit.").font(.caption).foregroundColor(.secondary) } }) }).toggleStyle(SwitchToggleStyle(tint: .accentColor)) } var appearance: some View { Section(header: Text("Appearance"), content: { NavigationLink( destination: ColorPaletteContainerView().environmentObject(settings), label: { HStack { Text("Color Palette") Spacer() Text(settings.userInterfaceColorPalette.description) .foregroundColor(.secondary) } }) }) } var help: some View { Section(header: Text("Help"), footer: Text(appVersion()).font(.caption2), content: { Button("NetNewsWire Help", action: { viewModel.selectedWebsite = .netNewsWireHelp }).foregroundColor(.primary) Button("Website", action: { viewModel.selectedWebsite = .netNewsWire }).foregroundColor(.primary) Button("How To Support NetNewsWire", action: { viewModel.selectedWebsite = .supportNetNewsWire }).foregroundColor(.primary) Button("Github Repository", action: { viewModel.selectedWebsite = .github }).foregroundColor(.primary) Button("Bug Tracker", action: { viewModel.selectedWebsite = .bugTracker }).foregroundColor(.primary) Button("NetNewsWire Slack", action: { viewModel.selectedWebsite = .netNewsWireSlack }).foregroundColor(.primary) NavigationLink( destination: SettingsAboutView(), label: { Text("About NetNewsWire") }) }) } private func appVersion() -> String { let dict = NSDictionary(contentsOf: Bundle.main.url(forResource: "Info", withExtension: "plist")!) let version = dict?.object(forKey: "CFBundleShortVersionString") as? String ?? "" 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 { static var previews: some View { SettingsView() } }