Add feeds settings for import and export subscriptions
This commit is contained in:
parent
ba0093e07c
commit
dde9035592
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// FeedsSettingsModel.swift
|
||||
// Multiplatform iOS
|
||||
//
|
||||
// Created by Rizwan on 04/07/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
class FeedsSettingsModel: ObservableObject {
|
||||
@Published var showingImportActionSheet = false
|
||||
@Published var showingExportActionSheet = false
|
||||
@Published var exportingFilePath = ""
|
||||
|
||||
func onTapExportOPML(action: ((Account?) -> Void)) {
|
||||
if AccountManager.shared.accounts.count == 1 {
|
||||
action(AccountManager.shared.accounts.first)
|
||||
}
|
||||
else {
|
||||
showingExportActionSheet = true
|
||||
}
|
||||
}
|
||||
|
||||
func onTapImportOPML(action: ((Account?) -> Void)) {
|
||||
switch AccountManager.shared.activeAccounts.count {
|
||||
case 0:
|
||||
//TODO:- show error
|
||||
return
|
||||
case 1:
|
||||
action(AccountManager.shared.activeAccounts.first)
|
||||
default:
|
||||
showingImportActionSheet = true
|
||||
}
|
||||
}
|
||||
|
||||
func generateExportURL(for account: Account) -> URL? {
|
||||
let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces)
|
||||
let filename = "Subscriptions-\(accountName).opml"
|
||||
let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
|
||||
let opmlString = OPMLExporter.OPMLString(with: account, title: filename)
|
||||
do {
|
||||
try opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8)
|
||||
} catch {
|
||||
//TODO:- show error
|
||||
return nil
|
||||
}
|
||||
|
||||
return tempFile
|
||||
}
|
||||
|
||||
func processImportedFiles(_ urls: [URL],_ account: Account?) {
|
||||
urls.forEach{
|
||||
account?.importOPML($0, completion: { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure:
|
||||
//TODO:- show error
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
|
||||
class SettingsViewModel: ObservableObject {
|
||||
|
@ -55,10 +55,13 @@ struct SettingsView: View {
|
|||
|
||||
let sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
@Environment(\.exportFiles) var exportAction
|
||||
@Environment(\.importFiles) var importAction
|
||||
|
||||
@StateObject private var viewModel = SettingsViewModel()
|
||||
@StateObject private var feedsSettingsModel = FeedsSettingsModel()
|
||||
@StateObject private var settings = AppDefaults.shared
|
||||
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List {
|
||||
|
@ -114,19 +117,50 @@ struct SettingsView: View {
|
|||
|
||||
var importExport: some View {
|
||||
Section(header: Text("Feeds"), content: {
|
||||
NavigationLink(
|
||||
destination: EmptyView(),
|
||||
label: {
|
||||
Text("Import Subscriptions")
|
||||
})
|
||||
NavigationLink(
|
||||
destination: EmptyView(),
|
||||
label: {
|
||||
Text("Export Subscriptions")
|
||||
})
|
||||
Button(action:{
|
||||
feedsSettingsModel.onTapImportOPML(action: importOPML)
|
||||
}) {
|
||||
Text("Import Subscriptions")
|
||||
.actionSheet(isPresented: $feedsSettingsModel.showingImportActionSheet, content: importActionSheet)
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
Button(action:{
|
||||
feedsSettingsModel.onTapExportOPML(action: exportOPML)
|
||||
}) {
|
||||
Text("Export Subscriptions")
|
||||
.actionSheet(isPresented: $feedsSettingsModel.showingExportActionSheet, content: exportActionSheet)
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
private func importActionSheet() -> 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)
|
||||
|
@ -202,7 +236,24 @@ struct SettingsView: View {
|
|||
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 {
|
||||
|
|
|
@ -635,6 +635,8 @@
|
|||
6581C73D20CED60100F4AD34 /* SafariExtensionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */; };
|
||||
6581C74020CED60100F4AD34 /* netnewswire-subscribe-to-feed.js in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73F20CED60100F4AD34 /* netnewswire-subscribe-to-feed.js */; };
|
||||
6581C74220CED60100F4AD34 /* ToolbarItemIcon.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */; };
|
||||
6594CA3B24AF6F2A005C7D7C /* OPMLExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8444C8F11FED81840051386C /* OPMLExporter.swift */; };
|
||||
65C2E40124B05D8A000AFDF6 /* FeedsSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65C2E40024B05D8A000AFDF6 /* FeedsSettingsModel.swift */; };
|
||||
65CBAD5A24AE03C20006DD91 /* ColorPaletteContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65CBAD5924AE03C20006DD91 /* ColorPaletteContainerView.swift */; };
|
||||
65ED3FB7235DEF6C0081F399 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; };
|
||||
65ED3FB8235DEF6C0081F399 /* CrashReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848B937121C8C5540038DC0D /* CrashReporter.swift */; };
|
||||
|
@ -2007,6 +2009,7 @@
|
|||
6581C73F20CED60100F4AD34 /* netnewswire-subscribe-to-feed.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "netnewswire-subscribe-to-feed.js"; sourceTree = "<group>"; };
|
||||
6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ToolbarItemIcon.pdf; sourceTree = "<group>"; };
|
||||
6581C74320CED60100F4AD34 /* Subscribe_to_Feed.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Subscribe_to_Feed.entitlements; sourceTree = "<group>"; };
|
||||
65C2E40024B05D8A000AFDF6 /* FeedsSettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedsSettingsModel.swift; sourceTree = "<group>"; };
|
||||
65CBAD5924AE03C20006DD91 /* ColorPaletteContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPaletteContainerView.swift; sourceTree = "<group>"; };
|
||||
65ED4083235DEF6C0081F399 /* NetNewsWire.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetNewsWire.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
65ED409D235DEF770081F399 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -2384,6 +2387,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
172199C824AB228900A31D04 /* SettingsView.swift */,
|
||||
65C2E40024B05D8A000AFDF6 /* FeedsSettingsModel.swift */,
|
||||
17B223B924AC24A8001E4592 /* Submenus */,
|
||||
);
|
||||
path = Settings;
|
||||
|
@ -4807,6 +4811,7 @@
|
|||
51E498FF24A808BB00B667CB /* SingleFaviconDownloader.swift in Sources */,
|
||||
51E4997224A8784300B667CB /* DefaultFeedsImporter.swift in Sources */,
|
||||
514E6C0924AD39AD00AC6F6E /* ArticleIconImageLoader.swift in Sources */,
|
||||
6594CA3B24AF6F2A005C7D7C /* OPMLExporter.swift in Sources */,
|
||||
51919FAF24AA8EFA00541E64 /* SidebarItemView.swift in Sources */,
|
||||
514E6BDA24ACEA0400AC6F6E /* TimelineItemView.swift in Sources */,
|
||||
51E4990D24A808C500B667CB /* RSHTMLMetadata+Extension.swift in Sources */,
|
||||
|
@ -4827,6 +4832,7 @@
|
|||
514E6BFF24AD255D00AC6F6E /* PreviewArticles.swift in Sources */,
|
||||
51E4993024A8676400B667CB /* ArticleSorter.swift in Sources */,
|
||||
51408B7E24A9EC6F0073CF4E /* SidebarItem.swift in Sources */,
|
||||
65C2E40124B05D8A000AFDF6 /* FeedsSettingsModel.swift in Sources */,
|
||||
51E4990A24A808C500B667CB /* FeaturedImageDownloader.swift in Sources */,
|
||||
51E4993824A8680E00B667CB /* Reachability.swift in Sources */,
|
||||
51E4993224A8676400B667CB /* FetchRequestQueue.swift in Sources */,
|
||||
|
|
Loading…
Reference in New Issue