From 51c2527da2c14bdb8395581710abb00803024a50 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 11 May 2019 16:07:27 -0500 Subject: [PATCH] Add OPML import for Feedbin. --- Frameworks/Account/Account.swift | 26 ++-- .../Feedbin/FeedbinAccountDelegate.swift | 143 +++++++++++++++++- 2 files changed, 154 insertions(+), 15 deletions(-) diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index 7f25a70c2..4b33dafa3 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -341,6 +341,18 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, return ensureFolder(with: folderName) } + func newFeed(with opmlFeedSpecifier: RSOPMLFeedSpecifier) -> Feed { + let feedURL = opmlFeedSpecifier.feedURL + let metadata = feedMetadata(feedURL: feedURL, feedID: feedURL) + let feed = Feed(account: self, url: opmlFeedSpecifier.feedURL, metadata: metadata) + if let feedTitle = opmlFeedSpecifier.title { + if feed.name == nil { + feed.name = feedTitle + } + } + return feed + } + func addFeed(container: Container, feed: Feed, completion: @escaping (Result) -> Void) { delegate.addFeed(for: self, to: container, with: feed, completion: completion) } @@ -905,18 +917,6 @@ private extension Account { feedDictionaryNeedsUpdate = false } - func ensureFeed(with opmlFeedSpecifier: RSOPMLFeedSpecifier) -> Feed { - let feedURL = opmlFeedSpecifier.feedURL - let metadata = feedMetadata(feedURL: feedURL, feedID: feedURL) - let feed = Feed(account: self, url: opmlFeedSpecifier.feedURL, metadata: metadata) - if let feedTitle = opmlFeedSpecifier.title { - if feed.name == nil { - feed.name = feedTitle - } - } - return feed - } - func loadOPMLItems(_ items: [RSOPMLItem], parentFolder: Folder?) { var feedsToAdd = Set() @@ -924,7 +924,7 @@ private extension Account { items.forEach { (item) in if let feedSpecifier = item.feedSpecifier { - let feed = ensureFeed(with: feedSpecifier) + let feed = newFeed(with: feedSpecifier) feedsToAdd.insert(feed) return } diff --git a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift index 4073476df..4714e3d2a 100644 --- a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift +++ b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift @@ -13,6 +13,7 @@ import UIKit import RSCore #endif import RSCore +import RSParser import RSWeb import os.log @@ -64,8 +65,41 @@ final class FeedbinAccountDelegate: AccountDelegate { func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result) -> 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, let children = loadDocument.children else { + completion(.success(())) + return + } + + importOPMLItems(account, items: children, parentFolder: nil) + completion(.success(())) + + } + func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) { caller.renameTag(oldName: folder.name ?? "", newName: name) { result in @@ -342,6 +376,109 @@ private extension FeedbinAccountDelegate { } + func importOPMLItems(_ account: Account, items: [RSOPMLItem], parentFolder: Folder?) { + + items.forEach { (item) in + + if let feedSpecifier = item.feedSpecifier { + importFeedSpecifier(account, feedSpecifier: feedSpecifier, parentFolder: parentFolder) + 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 { + importOPMLItems(account, items: itemChildren, parentFolder: parentFolder) + } + return + } + + if let folder = account.ensureFolder(with: folderName) { + if let itemChildren = item.children { + importOPMLItems(account, items: itemChildren, parentFolder: folder) + } + } + + } + + } + + func importFeedSpecifier(_ account: Account, feedSpecifier: RSOPMLFeedSpecifier, parentFolder: Folder?) { + + caller.createSubscription(url: feedSpecifier.feedURL) { [weak self] result in + + switch result { + case .success(let subResult): + switch subResult { + case .created(let sub): + + DispatchQueue.main.async { + + let feed = account.createFeed(with: sub.name, url: sub.url, feedID: String(sub.feedID), homePageURL: sub.homePageURL) + feed.subscriptionID = String(sub.subscriptionID) + + self?.importFeedSpecifierPostProcess(account: account, sub: sub, feedSpecifier: feedSpecifier, feed: feed, parentFolder: parentFolder) + + } + + default: + break + } + + case .failure(let error): + guard let self = self else { return } + os_log(.error, log: self.log, "Create feed on OPML import failed: %@.", error.localizedDescription) + } + + } + + } + + func importFeedSpecifierPostProcess(account: Account, sub: FeedbinSubscription, feedSpecifier: RSOPMLFeedSpecifier, feed: Feed, parentFolder: Folder?) { + + // Rename the feed if its name in the OPML file doesn't match the found name + if sub.name != feedSpecifier.title, let newName = feedSpecifier.title { + + self.caller.renameSubscription(subscriptionID: String(sub.subscriptionID), newName: newName) { [weak self] result in + switch result { + case .success: + DispatchQueue.main.async { + feed.editedName = newName + } + case .failure(let error): + guard let self = self else { return } + os_log(.error, log: self.log, "Rename feed on OPML import failed: %@.", error.localizedDescription) + } + } + + } + + // Move the new feed if it is in a folder + if let folder = parentFolder, let feedID = Int(feed.feedID) { + + self.caller.createTagging(feedID: feedID, name: folder.name ?? "") { [weak self] result in + switch result { + case .success(let taggingID): + DispatchQueue.main.async { + self?.saveFolderRelationship(for: feed, withFolderName: folder.name ?? "", id: String(taggingID)) + folder.addFeed(feed) + } + case .failure(let error): + guard let self = self else { return } + os_log(.error, log: self.log, "Move feed to folder on OPML import failed: %@.", error.localizedDescription) + } + } + + } else { + + DispatchQueue.main.async { + account.addFeed(feed) + } + + } + + } + func syncFolders(_ account: Account, _ tags: [FeedbinTag]?) { guard let tags = tags else { return } @@ -606,7 +743,9 @@ private extension FeedbinAccountDelegate { account.addFeed(feed) } - processRestoredFeedName(for: account, feed: feed, editedName: editedName!, completion: completion) + if editedName != nil { + processRestoredFeedName(for: account, feed: feed, editedName: editedName!, completion: completion) + } }