// // SettingsView.swift // Multiplatform iOS // // Created by Stuart Breckenridge on 30/6/20. // Copyright © 2020 Ranchero Software. All rights reserved. // import SwiftUI import Account struct SettingsView: View { @Environment(\.presentationMode) var presentationMode @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() } } ) } .fileImporter( isPresented: $feedsSettingsModel.isImporting, allowedContentTypes: feedsSettingsModel.importingContentTypes, allowsMultipleSelection: true, onCompletion: { result in if let urls = try? result.get() { feedsSettingsModel.processImportedFiles(urls) } } ) .fileMover(isPresented: $feedsSettingsModel.isExporting, file: feedsSettingsModel.generateExportURL()) { _ in } .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..<viewModel.accounts.count, id: \.hashValue , content: { i in NavigationLink( destination: SettingsDetailAccountView(viewModel.accounts[i]), label: { Text(viewModel.accounts[i].nameForDisplay) }) }) NavigationLink( destination: SettingsAddAccountView(), label: { Text("Add Account") }) }) } var importExport: some View { Section(header: Text("Feeds"), content: { if viewModel.activeAccounts.count > 1 { NavigationLink("Import Subscriptions", destination: importOptions) } else { Button(action:{ if feedsSettingsModel.checkForActiveAccount() { feedsSettingsModel.importOPML(account: viewModel.activeAccounts.first) } }) { Text("Import Subscriptions") .foregroundColor(.primary) } } if viewModel.accounts.count > 1 { NavigationLink("Export Subscriptions", destination: exportOptions) } else { Button(action:{ feedsSettingsModel.exportOPML(account: viewModel.accounts.first) }) { Text("Export Subscriptions") .foregroundColor(.primary) } } Toggle("Confirm When Deleting", isOn: $settings.sidebarConfirmDelete) .toggleStyle(SwitchToggleStyle(tint: .accentColor)) }) .alert(isPresented: $feedsSettingsModel.showError) { Alert( title: Text(feedsSettingsModel.feedsSettingsError!.title ?? "Oops"), message: Text(feedsSettingsModel.feedsSettingsError!.localizedDescription), dismissButton: Alert.Button.cancel({ feedsSettingsModel.feedsSettingsError = FeedsSettingsError.none })) } } var importOptions: some View { List { Section(header: Text("Choose an account to receive the imported feeds and folders"), content: { ForEach(0..<viewModel.activeAccounts.count, id: \.hashValue , content: { i in Button { feedsSettingsModel.importOPML(account: viewModel.activeAccounts[i]) } label: { Text(viewModel.activeAccounts[i].nameForDisplay) } }) }) } .listStyle(InsetGroupedListStyle()) .navigationBarTitle("Import Subscriptions", displayMode: .inline) } var exportOptions: some View { List { Section(header: Text("Choose an account with the subscriptions to export"), content: { ForEach(0..<viewModel.accounts.count, id: \.hashValue , content: { i in Button { feedsSettingsModel.exportOPML(account: viewModel.accounts[i]) } label: { Text(viewModel.accounts[i].nameForDisplay) } }) }) } .listStyle(InsetGroupedListStyle()) .navigationBarTitle("Export Subscriptions", displayMode: .inline) } 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) Button("Release Notes", action: { viewModel.selectedWebsite = .releaseNotes }).foregroundColor(.primary) NavigationLink( destination: SettingsAboutView(), label: { Text("About NetNewsWire") }) }) } private func appVersion() -> String { let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "" let build = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "" return "NetNewsWire \(version) (Build \(build))" } } struct SettingsView_Previews: PreviewProvider { static var previews: some View { SettingsView() } }