<commit_before>//
//  SettingsView.swift
//  Multiplatform iOS
//
//  Created by <NAME> on 30/6/20.
//  Copyright © 2020 Ranchero Software. All rights reserved.
//

import SwiftUI
import Account
import UniformTypeIdentifiers

struct SettingsView: View {
	
	@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
				sidebarFeedManagement
				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..<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() {
						importOPML(account: viewModel.activeAccounts.first)
					}
				}) {
					Text("Import Subscriptions")
						.foregroundColor(.primary)
				}
			}

			if viewModel.accounts.count > 1 {
				NavigationLink("Export Subscriptions", destination: exportOptions)
			}
			else {
				Button(action:{
					exportOPML(account: viewModel.accounts.first)
				}) {
					Text("Export Subscriptions")
						.foregroundColor(.primary)
				}
			}
		})
		.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 {
						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 {
						exportOPML(account: viewModel.accounts[i])
					} label: {
						Text(viewModel.accounts[i].nameForDisplay)
					}
				})
			})
		}
		.listStyle(InsetGroupedListStyle())
		.navigationBarTitle("Export Subscriptions", displayMode: .inline)
	}
	
	var sidebarFeedManagement: some View {
		Section(header: Text("Feed Management")) {
			Toggle("Confirm When Deleting Feeds and Folders", isOn: $settings.sidebarConfirmDelete)
		}.toggleStyle(SwitchToggleStyle(tint: .accentColor))
	}
	
	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))"
	}

	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()
	}
}
<commit_msg>Moves feed deletion to Feeds section<commit_after>//
//  SettingsView.swift
//  Multiplatform iOS
//
//  Created by <NAME> on 30/6/20.
//  Copyright © 2020 Ranchero Software. All rights reserved.
//

import SwiftUI
import Account
import UniformTypeIdentifiers

struct SettingsView: View {
	
	@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..<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() {
						importOPML(account: viewModel.activeAccounts.first)
					}
				}) {
					Text("Import Subscriptions")
						.foregroundColor(.primary)
				}
			}

			if viewModel.accounts.count > 1 {
				NavigationLink("Export Subscriptions", destination: exportOptions)
			}
			else {
				Button(action:{
					exportOPML(account: viewModel.accounts.first)
				}) {
					Text("Export Subscriptions")
						.foregroundColor(.primary)
				}
			}
			Toggle("Confirm When Deleting", isOn: $settings.sidebarConfirmDelete)
		})
		.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 {
						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 {
						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))"
	}

	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()
	}
}

{'fraction_non_alphanumeric': 0.1238004491934935, 'fraction_numerical': 0.0023140270877288504, 'mean_word_length': 3.8319631700098653, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4143503', 'n_tokens_mistral': 5496, 'n_tokens_neox': 4355, 'n_words': 930}