Restrict OPML import for Account types that don't support it

This commit is contained in:
Maurice Parker 2019-06-19 17:50:32 -05:00
parent b4b80c51bc
commit 74f84dc000
8 changed files with 44 additions and 157 deletions

View File

@ -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
}

View File

@ -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 }

View File

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

View File

@ -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?

View File

@ -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 doesnt have a name, so it wont 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) {

View File

@ -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

View File

@ -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
}

View File

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