2020-06-30 15:22:23 +02:00
|
|
|
//
|
|
|
|
// 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
|
2020-07-04 10:09:08 +02:00
|
|
|
|
2020-07-04 16:03:35 +02:00
|
|
|
@StateObject private var viewModel = SettingsModel()
|
2020-07-04 10:09:08 +02:00
|
|
|
@StateObject private var feedsSettingsModel = FeedsSettingsModel()
|
2020-07-02 03:57:36 +02:00
|
|
|
@StateObject private var settings = AppDefaults.shared
|
2020-07-04 10:09:08 +02:00
|
|
|
|
2020-06-30 15:22:23 +02:00
|
|
|
var body: some View {
|
|
|
|
NavigationView {
|
|
|
|
List {
|
|
|
|
systemSettings
|
|
|
|
accounts
|
|
|
|
importExport
|
|
|
|
timeline
|
|
|
|
articles
|
|
|
|
appearance
|
|
|
|
help
|
|
|
|
}
|
|
|
|
.listStyle(InsetGroupedListStyle())
|
|
|
|
.navigationBarTitle("Settings", displayMode: .inline)
|
|
|
|
.navigationBarItems(leading:
|
2020-06-30 15:50:53 +02:00
|
|
|
HStack {
|
|
|
|
Button("Done") {
|
|
|
|
presentationMode.wrappedValue.dismiss()
|
|
|
|
}
|
|
|
|
}
|
2020-06-30 15:22:23 +02:00
|
|
|
)
|
|
|
|
}
|
2020-08-28 19:31:58 +02:00
|
|
|
.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 }
|
2020-06-30 15:22:23 +02:00
|
|
|
.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: {
|
2020-07-09 12:44:53 +02:00
|
|
|
ForEach(0..<viewModel.accounts.count, id: \.hashValue , content: { i in
|
2020-06-30 15:22:23 +02:00
|
|
|
NavigationLink(
|
2020-07-09 12:44:53 +02:00
|
|
|
destination: SettingsDetailAccountView(viewModel.accounts[i]),
|
2020-06-30 15:22:23 +02:00
|
|
|
label: {
|
2020-07-09 12:44:53 +02:00
|
|
|
Text(viewModel.accounts[i].nameForDisplay)
|
2020-06-30 15:22:23 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
NavigationLink(
|
2020-07-07 19:15:12 +02:00
|
|
|
destination: SettingsAddAccountView(),
|
2020-06-30 15:22:23 +02:00
|
|
|
label: {
|
|
|
|
Text("Add Account")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var importExport: some View {
|
|
|
|
Section(header: Text("Feeds"), content: {
|
2020-07-24 17:20:43 +02:00
|
|
|
if viewModel.activeAccounts.count > 1 {
|
|
|
|
NavigationLink("Import Subscriptions", destination: importOptions)
|
2020-07-04 10:09:08 +02:00
|
|
|
}
|
2020-07-24 17:20:43 +02:00
|
|
|
else {
|
|
|
|
Button(action:{
|
|
|
|
if feedsSettingsModel.checkForActiveAccount() {
|
2020-08-28 19:31:58 +02:00
|
|
|
feedsSettingsModel.importOPML(account: viewModel.activeAccounts.first)
|
2020-07-24 17:20:43 +02:00
|
|
|
}
|
|
|
|
}) {
|
|
|
|
Text("Import Subscriptions")
|
|
|
|
.foregroundColor(.primary)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if viewModel.accounts.count > 1 {
|
|
|
|
NavigationLink("Export Subscriptions", destination: exportOptions)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Button(action:{
|
2020-08-28 19:31:58 +02:00
|
|
|
feedsSettingsModel.exportOPML(account: viewModel.accounts.first)
|
2020-07-24 17:20:43 +02:00
|
|
|
}) {
|
|
|
|
Text("Export Subscriptions")
|
|
|
|
.foregroundColor(.primary)
|
|
|
|
}
|
2020-07-04 10:09:08 +02:00
|
|
|
}
|
2020-08-15 08:31:56 +02:00
|
|
|
Toggle("Confirm When Deleting", isOn: $settings.sidebarConfirmDelete)
|
2020-08-15 08:34:20 +02:00
|
|
|
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
2020-06-30 15:22:23 +02:00
|
|
|
})
|
2020-07-06 19:13:20 +02:00
|
|
|
.alert(isPresented: $feedsSettingsModel.showError) {
|
|
|
|
Alert(
|
|
|
|
title: Text(feedsSettingsModel.feedsSettingsError!.title ?? "Oops"),
|
|
|
|
message: Text(feedsSettingsModel.feedsSettingsError!.localizedDescription),
|
|
|
|
dismissButton: Alert.Button.cancel({
|
|
|
|
feedsSettingsModel.feedsSettingsError = FeedsSettingsError.none
|
|
|
|
}))
|
|
|
|
}
|
2020-06-30 15:22:23 +02:00
|
|
|
}
|
2020-07-04 10:09:08 +02:00
|
|
|
|
2020-07-24 17:20:43 +02:00
|
|
|
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 {
|
2020-08-28 19:31:58 +02:00
|
|
|
feedsSettingsModel.importOPML(account: viewModel.activeAccounts[i])
|
2020-07-24 17:20:43 +02:00
|
|
|
} label: {
|
|
|
|
Text(viewModel.activeAccounts[i].nameForDisplay)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
2020-07-04 10:09:08 +02:00
|
|
|
}
|
2020-07-24 17:20:43 +02:00
|
|
|
.listStyle(InsetGroupedListStyle())
|
|
|
|
.navigationBarTitle("Import Subscriptions", displayMode: .inline)
|
2020-07-04 10:09:08 +02:00
|
|
|
}
|
|
|
|
|
2020-07-24 17:20:43 +02:00
|
|
|
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 {
|
2020-08-28 19:31:58 +02:00
|
|
|
feedsSettingsModel.exportOPML(account: viewModel.accounts[i])
|
2020-07-24 17:20:43 +02:00
|
|
|
} label: {
|
|
|
|
Text(viewModel.accounts[i].nameForDisplay)
|
|
|
|
}
|
|
|
|
})
|
2020-08-15 08:31:56 +02:00
|
|
|
|
2020-07-24 17:20:43 +02:00
|
|
|
})
|
2020-07-04 10:09:08 +02:00
|
|
|
}
|
2020-07-24 17:20:43 +02:00
|
|
|
.listStyle(InsetGroupedListStyle())
|
|
|
|
.navigationBarTitle("Export Subscriptions", displayMode: .inline)
|
2020-07-04 10:09:08 +02:00
|
|
|
}
|
2020-07-24 17:20:43 +02:00
|
|
|
|
2020-08-15 08:31:56 +02:00
|
|
|
|
2020-08-15 03:37:18 +02:00
|
|
|
|
2020-06-30 15:22:23 +02:00
|
|
|
var timeline: some View {
|
|
|
|
Section(header: Text("Timeline"), content: {
|
2020-07-01 15:06:40 +02:00
|
|
|
Toggle("Sort Oldest to Newest", isOn: $settings.timelineSortDirection)
|
|
|
|
Toggle("Group by Feed", isOn: $settings.timelineGroupByFeed)
|
|
|
|
Toggle("Refresh to Clear Read Articles", isOn: $settings.refreshClearsReadArticles)
|
2020-06-30 15:22:23 +02:00
|
|
|
NavigationLink(
|
2020-07-01 15:06:40 +02:00
|
|
|
destination: TimelineLayoutView().environmentObject(settings),
|
2020-06-30 15:22:23 +02:00
|
|
|
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(
|
2020-07-02 17:31:56 +02:00
|
|
|
destination: ColorPaletteContainerView().environmentObject(settings),
|
2020-06-30 15:22:23 +02:00
|
|
|
label: {
|
|
|
|
HStack {
|
2020-07-02 17:31:56 +02:00
|
|
|
Text("Color Palette")
|
2020-06-30 15:22:23 +02:00
|
|
|
Spacer()
|
2020-07-02 17:31:56 +02:00
|
|
|
Text(settings.userInterfaceColorPalette.description)
|
2020-06-30 15:22:23 +02:00
|
|
|
.foregroundColor(.secondary)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var help: some View {
|
2020-06-30 15:50:53 +02:00
|
|
|
Section(header: Text("Help"), footer: Text(appVersion()).font(.caption2), content: {
|
2020-06-30 15:22:23 +02:00
|
|
|
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)
|
2020-08-13 02:24:54 +02:00
|
|
|
Button("Release Notes", action: {
|
|
|
|
viewModel.selectedWebsite = .releaseNotes
|
|
|
|
}).foregroundColor(.primary)
|
2020-06-30 15:22:23 +02:00
|
|
|
NavigationLink(
|
2020-07-06 22:30:32 +02:00
|
|
|
destination: SettingsAboutView(),
|
2020-06-30 15:22:23 +02:00
|
|
|
label: {
|
|
|
|
Text("About NetNewsWire")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-06-30 15:50:53 +02:00
|
|
|
private func appVersion() -> String {
|
2020-08-13 03:28:11 +02:00
|
|
|
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
|
|
|
|
let build = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? ""
|
2020-06-30 15:50:53 +02:00
|
|
|
return "NetNewsWire \(version) (Build \(build))"
|
|
|
|
}
|
2020-07-04 10:09:08 +02:00
|
|
|
|
2020-06-30 15:22:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct SettingsView_Previews: PreviewProvider {
|
|
|
|
static var previews: some View {
|
|
|
|
SettingsView()
|
|
|
|
}
|
|
|
|
}
|