Restrict OPML import for Account types that don't support it
This commit is contained in:
parent
b4b80c51bc
commit
74f84dc000
|
@ -184,8 +184,12 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||
}
|
||||
}
|
||||
|
||||
public var usesTags: Bool {
|
||||
return delegate.usesTags
|
||||
public var isTagBasedSystem: Bool {
|
||||
return delegate.isTagBasedSystem
|
||||
}
|
||||
|
||||
public var isOPMLImportSupported: Bool {
|
||||
return delegate.isOPMLImportSupported
|
||||
}
|
||||
|
||||
var refreshInProgress = false {
|
||||
|
@ -206,8 +210,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||
return delegate.refreshProgress
|
||||
}
|
||||
|
||||
var supportsSubFolders: Bool {
|
||||
return delegate.supportsSubFolders
|
||||
var isSubfoldersSupported: Bool {
|
||||
return delegate.isSubfoldersSupported
|
||||
}
|
||||
|
||||
init?(dataFolder: String, type: AccountType, accountID: String, transport: Transport? = nil) {
|
||||
|
@ -348,7 +352,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||
|
||||
public func importOPML(_ opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
guard !delegate.opmlImportInProgress else {
|
||||
guard !delegate.isOPMLImportInProgress else {
|
||||
completion(.failure(AccountError.opmlImportInProgress))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -13,9 +13,10 @@ import RSWeb
|
|||
protocol AccountDelegate {
|
||||
|
||||
// Local account does not; some synced accounts might.
|
||||
var supportsSubFolders: Bool { get }
|
||||
var usesTags: Bool { get }
|
||||
var opmlImportInProgress: Bool { get }
|
||||
var isSubfoldersSupported: Bool { get }
|
||||
var isTagBasedSystem: Bool { get }
|
||||
var isOPMLImportSupported: Bool { get }
|
||||
var isOPMLImportInProgress: Bool { get }
|
||||
|
||||
var server: String? { get }
|
||||
var credentials: Credentials? { get set }
|
||||
|
|
|
@ -6,12 +6,6 @@
|
|||
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(macOS)
|
||||
import AppKit
|
||||
#else
|
||||
import UIKit
|
||||
import RSCore
|
||||
#endif
|
||||
import Articles
|
||||
import RSCore
|
||||
import RSParser
|
||||
|
@ -30,10 +24,11 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||
private let caller: FeedbinAPICaller
|
||||
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Feedbin")
|
||||
|
||||
let supportsSubFolders = false
|
||||
let usesTags = true
|
||||
let isSubfoldersSupported = false
|
||||
let isTagBasedSystem = true
|
||||
let isOPMLImportSupported = true
|
||||
let server: String? = "api.feedbin.com"
|
||||
var opmlImportInProgress = false
|
||||
var isOPMLImportInProgress = false
|
||||
|
||||
var credentials: Credentials? {
|
||||
didSet {
|
||||
|
@ -207,7 +202,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||
}
|
||||
|
||||
os_log(.debug, log: log, "Begin importing OPML...")
|
||||
opmlImportInProgress = true
|
||||
isOPMLImportInProgress = true
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||
|
||||
caller.importOPML(opmlData: opmlData) { result in
|
||||
|
@ -216,7 +211,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||
if importResult.complete {
|
||||
os_log(.debug, log: self.log, "Import OPML done.")
|
||||
self.refreshProgress.completeTask()
|
||||
self.opmlImportInProgress = false
|
||||
self.isOPMLImportInProgress = false
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(()))
|
||||
}
|
||||
|
@ -226,7 +221,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||
case .failure(let error):
|
||||
os_log(.debug, log: self.log, "Import OPML failed.")
|
||||
self.refreshProgress.completeTask()
|
||||
self.opmlImportInProgress = false
|
||||
self.isOPMLImportInProgress = false
|
||||
DispatchQueue.main.async {
|
||||
let wrappedError = AccountError.wrappedError(error: error, account: account)
|
||||
completion(.failure(wrappedError))
|
||||
|
@ -569,7 +564,7 @@ private extension FeedbinAccountDelegate {
|
|||
os_log(.debug, log: self.log, "Checking status of OPML import successfully completed.")
|
||||
timer.invalidate()
|
||||
self.refreshProgress.completeTask()
|
||||
self.opmlImportInProgress = false
|
||||
self.isOPMLImportInProgress = false
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(()))
|
||||
}
|
||||
|
@ -578,7 +573,7 @@ private extension FeedbinAccountDelegate {
|
|||
os_log(.debug, log: self.log, "Import OPML check failed.")
|
||||
timer.invalidate()
|
||||
self.refreshProgress.completeTask()
|
||||
self.opmlImportInProgress = false
|
||||
self.isOPMLImportInProgress = false
|
||||
DispatchQueue.main.async {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
|
|
@ -18,9 +18,10 @@ public enum LocalAccountDelegateError: String, Error {
|
|||
|
||||
final class LocalAccountDelegate: AccountDelegate {
|
||||
|
||||
let supportsSubFolders = false
|
||||
let usesTags = false
|
||||
let opmlImportInProgress = false
|
||||
let isSubfoldersSupported = false
|
||||
let isTagBasedSystem = false
|
||||
let isOPMLImportSupported = true
|
||||
let isOPMLImportInProgress = false
|
||||
|
||||
let server: String? = nil
|
||||
var credentials: Credentials?
|
||||
|
|
|
@ -6,12 +6,6 @@
|
|||
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(macOS)
|
||||
import AppKit
|
||||
#else
|
||||
import UIKit
|
||||
import RSCore
|
||||
#endif
|
||||
import Articles
|
||||
import RSCore
|
||||
import RSParser
|
||||
|
@ -31,8 +25,8 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||
private let caller: ReaderAPICaller
|
||||
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "ReaderAPI")
|
||||
|
||||
let supportsSubFolders = false
|
||||
let usesTags = true
|
||||
let isSubfoldersSupported = false
|
||||
let isTagBasedSystem = true
|
||||
|
||||
var server: String? {
|
||||
get {
|
||||
|
@ -40,7 +34,8 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
var opmlImportInProgress = false
|
||||
let isOPMLImportSupported = false
|
||||
var isOPMLImportInProgress = false
|
||||
|
||||
var credentials: Credentials? {
|
||||
didSet {
|
||||
|
@ -195,128 +190,6 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||
}
|
||||
|
||||
func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
var fileData: Data?
|
||||
|
||||
do {
|
||||
fileData = try Data(contentsOf: opmlFile)
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
return
|
||||
}
|
||||
|
||||
guard let opmlData = fileData else {
|
||||
completion(.success(()))
|
||||
return
|
||||
}
|
||||
|
||||
let parserData = ParserData(url: opmlFile.absoluteString, data: opmlData)
|
||||
var opmlDocument: RSOPMLDocument?
|
||||
|
||||
do {
|
||||
opmlDocument = try RSOPMLParser.parseOPML(with: parserData)
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
return
|
||||
}
|
||||
|
||||
guard let loadDocument = opmlDocument else {
|
||||
completion(.success(()))
|
||||
return
|
||||
}
|
||||
|
||||
// We use the same mechanism to load local accounts as we do to load the subscription
|
||||
// OPML all accounts.
|
||||
BatchUpdate.shared.perform {
|
||||
loadOPML(account: account, opmlDocument: loadDocument)
|
||||
}
|
||||
completion(.success(()))
|
||||
|
||||
}
|
||||
|
||||
func loadOPML(account: Account, opmlDocument: RSOPMLDocument) {
|
||||
|
||||
guard let children = opmlDocument.children else {
|
||||
return
|
||||
}
|
||||
loadOPMLItems(account: account, items: children, parentFolder: nil)
|
||||
}
|
||||
|
||||
func loadOPMLItems(account: Account, items: [RSOPMLItem], parentFolder: Folder?) {
|
||||
|
||||
var feedsToAdd = Set<String>()
|
||||
|
||||
items.forEach { (item) in
|
||||
|
||||
if let feedSpecifier = item.feedSpecifier {
|
||||
feedsToAdd.insert(feedSpecifier.feedURL)
|
||||
return
|
||||
}
|
||||
|
||||
guard let folderName = item.titleFromAttributes else {
|
||||
// Folder doesn’t have a name, so it won’t be created, and its items will go one level up.
|
||||
if let itemChildren = item.children {
|
||||
loadOPMLItems(account: account, items: itemChildren, parentFolder: parentFolder)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if let itemChildren = item.children, let folder = account.ensureFolder(with: folderName) {
|
||||
loadOPMLItems(account: account, items: itemChildren, parentFolder: folder)
|
||||
}
|
||||
}
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
if let parentFolder = parentFolder {
|
||||
for url in feedsToAdd {
|
||||
group.enter()
|
||||
caller.createSubscription(url: url) { result in
|
||||
group.leave()
|
||||
switch result {
|
||||
case .success(let subResult):
|
||||
switch subResult {
|
||||
case .created(let subscription):
|
||||
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: String(subscription.feedID), homePageURL: subscription.homePageURL)
|
||||
feed.subscriptionID = String(subscription.feedID)
|
||||
account.addFeed(feed, to: parentFolder) { _ in }
|
||||
default:
|
||||
break
|
||||
}
|
||||
case .failure(_):
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for url in feedsToAdd {
|
||||
group.enter()
|
||||
caller.createSubscription(url: url) { result in
|
||||
group.leave()
|
||||
switch result {
|
||||
case .success(let subResult):
|
||||
switch subResult {
|
||||
case .created(let subscription):
|
||||
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: String(subscription.feedID), homePageURL: subscription.homePageURL)
|
||||
feed.subscriptionID = String(subscription.feedID)
|
||||
account.addFeed(feed)
|
||||
default:
|
||||
break
|
||||
}
|
||||
case .failure(_):
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group.notify(queue: DispatchQueue.main) {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.refreshAll(for: account) { (_) in }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
|
||||
|
|
|
@ -26,6 +26,10 @@ class ImportOPMLWindowController: NSWindowController {
|
|||
|
||||
for oneAccount in AccountManager.shared.sortedActiveAccounts {
|
||||
|
||||
if !oneAccount.isOPMLImportSupported {
|
||||
continue
|
||||
}
|
||||
|
||||
let oneMenuItem = NSMenuItem()
|
||||
oneMenuItem.title = oneAccount.nameForDisplay
|
||||
oneMenuItem.representedObject = oneAccount
|
||||
|
|
|
@ -636,7 +636,7 @@ private extension SidebarOutlineDataSource {
|
|||
}
|
||||
|
||||
func violatesTagSpecificBehavior(_ parentNode: Node, _ draggedFeeds: Set<PasteboardFeed>) -> Bool {
|
||||
guard let parentAccount = nodeAccount(parentNode), parentAccount.usesTags else {
|
||||
guard let parentAccount = nodeAccount(parentNode), parentAccount.isTagBasedSystem else {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -93,19 +93,27 @@ struct SettingsView : View {
|
|||
|
||||
var createSubscriptionsImportAccounts: ActionSheet {
|
||||
var buttons = [ActionSheet.Button]()
|
||||
|
||||
for account in viewModel.activeAccounts {
|
||||
if !account.isOPMLImportSupported {
|
||||
continue
|
||||
}
|
||||
|
||||
let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
|
||||
self.subscriptionsImportAccounts = nil
|
||||
self.subscriptionsImportDocumentPicker = Modal(SettingsSubscriptionsImportDocumentPickerView(account: account))
|
||||
}
|
||||
|
||||
buttons.append(button)
|
||||
}
|
||||
|
||||
buttons.append(.cancel { self.subscriptionsImportAccounts = nil })
|
||||
return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons)
|
||||
}
|
||||
|
||||
var createSubscriptionsExportAccounts: ActionSheet {
|
||||
var buttons = [ActionSheet.Button]()
|
||||
|
||||
for account in viewModel.accounts {
|
||||
let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
|
||||
self.subscriptionsExportAccounts = nil
|
||||
|
@ -113,6 +121,7 @@ struct SettingsView : View {
|
|||
}
|
||||
buttons.append(button)
|
||||
}
|
||||
|
||||
buttons.append(.cancel { self.subscriptionsExportAccounts = nil })
|
||||
return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue