315 lines
8.9 KiB
Swift
315 lines
8.9 KiB
Swift
//
|
|
// SettingsView.swift
|
|
// NetNewsWire-iOS
|
|
//
|
|
// Created by Maurice Parker on 6/11/19.
|
|
// Copyright © 2019 Ranchero Software. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
import Combine
|
|
import Account
|
|
|
|
struct SettingsView : View {
|
|
|
|
@ObservedObject var viewModel: ViewModel
|
|
|
|
@Environment(\.viewController) private var viewController: UIViewController?
|
|
@Environment(\.sceneCoordinator) private var coordinator: SceneCoordinator?
|
|
|
|
@State private var accountAction: Int? = nil
|
|
@State private var refreshAction: Int? = nil
|
|
@State private var aboutAction: Int? = nil
|
|
|
|
@State private var isWebsitePresented: Bool = false
|
|
@State private var website: String? = nil
|
|
|
|
@State private var isOPMLImportPresented: Bool = false
|
|
@State private var isOPMLImportDocPickerPresented: Bool = false
|
|
@State private var isOPMLExportPresented: Bool = false
|
|
@State private var isOPMLExportDocPickerPresented: Bool = false
|
|
@State private var opmlAccount: Account? = nil
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
Form {
|
|
buildAccountsSection()
|
|
buildTimelineSection()
|
|
buildDatabaseSection()
|
|
buildAboutSection()
|
|
}
|
|
.buttonStyle(VibrantButtonStyle(alignment: .leading))
|
|
.navigationBarTitle(Text("Settings"), displayMode: .inline)
|
|
.navigationBarItems(leading: Button(action: { self.viewController?.dismiss(animated: true) }) { Text("Done") } )
|
|
}
|
|
}
|
|
|
|
func buildAccountsSection() -> some View {
|
|
Section(header: Text("ACCOUNTS").padding(.top, 22.0)) {
|
|
ForEach(0..<viewModel.accounts.count) { index in
|
|
NavigationLink(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(self.viewModel.accounts[index])), tag: index, selection: self.$accountAction) {
|
|
Text(verbatim: self.viewModel.accounts[index].nameForDisplay)
|
|
}
|
|
.modifier(VibrantSelectAction(action: {
|
|
self.accountAction = index
|
|
}))
|
|
}
|
|
NavigationLink(destination: SettingsAddAccountView(), tag: 1000, selection: $accountAction) {
|
|
Text("Add Account")
|
|
}
|
|
.modifier(VibrantSelectAction(action: {
|
|
self.accountAction = 1000
|
|
}))
|
|
}
|
|
}
|
|
|
|
func buildTimelineSection() -> some View {
|
|
Section(header: Text("TIMELINE")) {
|
|
Toggle(isOn: $viewModel.sortOldestToNewest) {
|
|
Text("Sort Newest to Oldest")
|
|
}
|
|
Toggle(isOn: $viewModel.groupByFeed) {
|
|
Text("Group By Feed")
|
|
}
|
|
Stepper(value: $viewModel.timelineNumberOfLines, in: 2...6) {
|
|
Text("Number of Text Lines: \(viewModel.timelineNumberOfLines)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func buildDatabaseSection() -> some View {
|
|
Section(header: Text("DATABASE")) {
|
|
|
|
NavigationLink(destination: SettingsRefreshSelectionView(selectedInterval: $viewModel.refreshInterval), tag: 1, selection: $refreshAction) {
|
|
HStack {
|
|
Text("Refresh Interval")
|
|
Spacer()
|
|
Text(verbatim: self.viewModel.refreshInterval.description()).foregroundColor(.secondary)
|
|
}
|
|
}
|
|
.modifier(VibrantSelectAction(action: {
|
|
self.refreshAction = 1
|
|
}))
|
|
|
|
Button("Import Subscriptions...") {
|
|
if AccountManager.shared.activeAccounts.count == 1 {
|
|
self.opmlAccount = AccountManager.shared.activeAccounts.first
|
|
self.isOPMLImportDocPickerPresented = true
|
|
} else {
|
|
self.isOPMLImportPresented = true
|
|
}
|
|
}.actionSheet(isPresented: $isOPMLImportPresented) {
|
|
buildSubscriptionsImportAccounts()
|
|
}.sheet(isPresented: $isOPMLImportDocPickerPresented) {
|
|
SettingsSubscriptionsImportDocumentPickerView(account: self.opmlAccount!)
|
|
}
|
|
|
|
Button("Export Subscriptions...") {
|
|
if AccountManager.shared.accounts.count == 1 {
|
|
self.opmlAccount = AccountManager.shared.accounts.first
|
|
self.isOPMLImportDocPickerPresented = true
|
|
} else {
|
|
self.isOPMLExportPresented = true
|
|
}
|
|
}.actionSheet(isPresented: $isOPMLExportPresented) {
|
|
buildSubscriptionsExportAccounts()
|
|
}.sheet(isPresented: $isOPMLExportDocPickerPresented) {
|
|
SettingsSubscriptionsExportDocumentPickerView(account: self.opmlAccount!)
|
|
}
|
|
}
|
|
}
|
|
|
|
func buildAboutSection() -> some View {
|
|
Section(header: Text("ABOUT"), footer: buildFooter()) {
|
|
|
|
NavigationLink(destination: SettingsAboutView(viewModel: SettingsAboutView.ViewModel()), tag: 1, selection: $aboutAction) {
|
|
Text("About NetNewsWire")
|
|
}
|
|
.modifier(VibrantSelectAction(action: {
|
|
self.aboutAction = 1
|
|
}))
|
|
|
|
Button(action: {
|
|
self.isWebsitePresented.toggle()
|
|
self.website = "https://ranchero.com/netnewswire/"
|
|
}) {
|
|
Text("Website")
|
|
}
|
|
|
|
Button(action: {
|
|
self.isWebsitePresented.toggle()
|
|
self.website = "https://github.com/brentsimmons/NetNewsWire"
|
|
}) {
|
|
Text("Github Repository")
|
|
}
|
|
|
|
Button(action: {
|
|
self.isWebsitePresented.toggle()
|
|
self.website = "https://github.com/brentsimmons/NetNewsWire/issues"
|
|
}) {
|
|
Text("Bug Tracker")
|
|
}
|
|
|
|
Button(action: {
|
|
self.isWebsitePresented.toggle()
|
|
self.website = "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes"
|
|
}) {
|
|
Text("Technotes")
|
|
}
|
|
|
|
Button(action: {
|
|
self.isWebsitePresented.toggle()
|
|
self.website = "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown"
|
|
}) {
|
|
Text("How To Support NetNewsWire")
|
|
}
|
|
|
|
if !AccountManager.shared.anyAccountHasFeedWithURL("https://nnw.ranchero.com/feed.json") {
|
|
Button(action: {
|
|
self.viewController?.dismiss(animated: true) {
|
|
let feedName = NSLocalizedString("NetNewsWire News", comment: "NetNewsWire News")
|
|
self.coordinator?.showAdd(.feed, initialFeed: "https://nnw.ranchero.com/feed.json", initialFeedName: feedName)
|
|
}
|
|
}) {
|
|
Text("Add NetNewsWire News Feed")
|
|
}
|
|
}
|
|
|
|
}.sheet(isPresented: $isWebsitePresented) {
|
|
SafariView(url: URL(string: self.website!)!)
|
|
}
|
|
}
|
|
|
|
func buildSubscriptionsImportAccounts() -> ActionSheet {
|
|
var buttons = [ActionSheet.Button]()
|
|
|
|
for account in viewModel.activeAccounts {
|
|
if !account.isOPMLImportSupported {
|
|
continue
|
|
}
|
|
|
|
let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
|
|
self.opmlAccount = account
|
|
self.isOPMLImportDocPickerPresented = true
|
|
}
|
|
|
|
buttons.append(button)
|
|
}
|
|
|
|
buttons.append(.cancel())
|
|
return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons)
|
|
}
|
|
|
|
func buildSubscriptionsExportAccounts() -> ActionSheet {
|
|
var buttons = [ActionSheet.Button]()
|
|
|
|
for account in viewModel.accounts {
|
|
let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
|
|
self.opmlAccount = account
|
|
self.isOPMLExportDocPickerPresented = true
|
|
}
|
|
buttons.append(button)
|
|
}
|
|
|
|
buttons.append(.cancel())
|
|
return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons)
|
|
}
|
|
|
|
func buildFooter() -> some View {
|
|
return Text(verbatim: "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))")
|
|
.font(.footnote)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
// MARK: ViewModel
|
|
|
|
class ViewModel: ObservableObject {
|
|
|
|
let objectWillChange = ObservableObjectPublisher()
|
|
|
|
init() {
|
|
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidAddAccount, object: nil)
|
|
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidDeleteAccount, object: nil)
|
|
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil)
|
|
}
|
|
|
|
var accounts: [Account] {
|
|
get {
|
|
return AccountManager.shared.sortedAccounts
|
|
}
|
|
set {
|
|
}
|
|
}
|
|
|
|
var activeAccounts: [Account] {
|
|
get {
|
|
return AccountManager.shared.sortedActiveAccounts
|
|
}
|
|
set {
|
|
}
|
|
}
|
|
|
|
var sortOldestToNewest: Bool {
|
|
get {
|
|
return AppDefaults.timelineSortDirection == .orderedDescending
|
|
}
|
|
set {
|
|
objectWillChange.send()
|
|
if newValue == true {
|
|
AppDefaults.timelineSortDirection = .orderedDescending
|
|
} else {
|
|
AppDefaults.timelineSortDirection = .orderedAscending
|
|
}
|
|
}
|
|
}
|
|
|
|
var groupByFeed: Bool {
|
|
get {
|
|
return AppDefaults.timelineGroupByFeed
|
|
}
|
|
set {
|
|
objectWillChange.send()
|
|
AppDefaults.timelineGroupByFeed = newValue
|
|
}
|
|
}
|
|
|
|
var timelineNumberOfLines: Int {
|
|
get {
|
|
return AppDefaults.timelineNumberOfLines
|
|
}
|
|
set {
|
|
objectWillChange.send()
|
|
AppDefaults.timelineNumberOfLines = newValue
|
|
}
|
|
}
|
|
|
|
var refreshInterval: RefreshInterval {
|
|
get {
|
|
return AppDefaults.refreshInterval
|
|
}
|
|
set {
|
|
objectWillChange.send()
|
|
AppDefaults.refreshInterval = newValue
|
|
}
|
|
}
|
|
|
|
@objc func accountsDidChange(_ notification: Notification) {
|
|
objectWillChange.send()
|
|
}
|
|
|
|
@objc func displayNameDidChange(_ notification: Notification) {
|
|
objectWillChange.send()
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if DEBUG
|
|
struct SettingsView_Previews : PreviewProvider {
|
|
static var previews: some View {
|
|
SettingsView(viewModel: SettingsView.ViewModel())
|
|
}
|
|
}
|
|
#endif
|