Refactor to differentiate between loading the Subscriptions.opml file and importing an external OPML file as they now need separate behaviors
This commit is contained in:
parent
5200e49175
commit
6f92cd1a73
|
@ -290,28 +290,10 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||
delegate.refreshAll(for: self, completion: completion)
|
||||
}
|
||||
|
||||
public func update(_ feed: Feed, with parsedFeed: ParsedFeed, _ completion: @escaping (() -> Void)) {
|
||||
|
||||
feed.takeSettings(from: parsedFeed)
|
||||
|
||||
database.update(feedID: feed.feedID, parsedFeed: parsedFeed) { (newArticles, updatedArticles) in
|
||||
|
||||
var userInfo = [String: Any]()
|
||||
if let newArticles = newArticles, !newArticles.isEmpty {
|
||||
self.updateUnreadCounts(for: Set([feed]))
|
||||
userInfo[UserInfoKey.newArticles] = newArticles
|
||||
}
|
||||
if let updatedArticles = updatedArticles, !updatedArticles.isEmpty {
|
||||
userInfo[UserInfoKey.updatedArticles] = updatedArticles
|
||||
}
|
||||
userInfo[UserInfoKey.feeds] = Set([feed])
|
||||
|
||||
completion()
|
||||
|
||||
NotificationCenter.default.post(name: .AccountDidDownloadArticles, object: self, userInfo: userInfo)
|
||||
}
|
||||
public func importOPML(_ opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
delegate.importOPML(for: self, opmlFile: opmlFile, completion: completion)
|
||||
}
|
||||
|
||||
|
||||
public func markArticles(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
|
||||
|
||||
// Returns set of Articles whose statuses did change.
|
||||
|
@ -413,12 +395,12 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||
structureDidChange()
|
||||
}
|
||||
|
||||
public func importOPML(_ opmlDocument: RSOPMLDocument) {
|
||||
func loadOPML(_ opmlDocument: RSOPMLDocument) {
|
||||
|
||||
guard let children = opmlDocument.children else {
|
||||
return
|
||||
}
|
||||
importOPMLItems(children, parentFolder: nil)
|
||||
loadOPMLItems(children, parentFolder: nil)
|
||||
structureDidChange()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
@ -573,6 +555,28 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||
feedDictionaryNeedsUpdate = true
|
||||
}
|
||||
|
||||
func update(_ feed: Feed, with parsedFeed: ParsedFeed, _ completion: @escaping (() -> Void)) {
|
||||
|
||||
feed.takeSettings(from: parsedFeed)
|
||||
|
||||
database.update(feedID: feed.feedID, parsedFeed: parsedFeed) { (newArticles, updatedArticles) in
|
||||
|
||||
var userInfo = [String: Any]()
|
||||
if let newArticles = newArticles, !newArticles.isEmpty {
|
||||
self.updateUnreadCounts(for: Set([feed]))
|
||||
userInfo[UserInfoKey.newArticles] = newArticles
|
||||
}
|
||||
if let updatedArticles = updatedArticles, !updatedArticles.isEmpty {
|
||||
userInfo[UserInfoKey.updatedArticles] = updatedArticles
|
||||
}
|
||||
userInfo[UserInfoKey.feeds] = Set([feed])
|
||||
|
||||
completion()
|
||||
|
||||
NotificationCenter.default.post(name: .AccountDidDownloadArticles, object: self, userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Container
|
||||
|
||||
public func flattenedFeeds() -> Set<Feed> {
|
||||
|
@ -735,12 +739,12 @@ private extension Account {
|
|||
}
|
||||
|
||||
func pullObjectsFromDisk() {
|
||||
importAccountMetadata()
|
||||
importFeedMetadata()
|
||||
importOPMLFile(path: opmlFilePath)
|
||||
loadAccountMetadata()
|
||||
loadFeedMetadata()
|
||||
loadOPMLFile(path: opmlFilePath)
|
||||
}
|
||||
|
||||
func importAccountMetadata() {
|
||||
func loadAccountMetadata() {
|
||||
let url = URL(fileURLWithPath: metadataPath)
|
||||
guard let data = try? Data(contentsOf: url) else {
|
||||
metadata.delegate = self
|
||||
|
@ -751,7 +755,7 @@ private extension Account {
|
|||
metadata.delegate = self
|
||||
}
|
||||
|
||||
func importFeedMetadata() {
|
||||
func loadFeedMetadata() {
|
||||
let url = URL(fileURLWithPath: feedMetadataPath)
|
||||
guard let data = try? Data(contentsOf: url) else {
|
||||
return
|
||||
|
@ -761,7 +765,7 @@ private extension Account {
|
|||
feedMetadata.values.forEach { $0.delegate = self }
|
||||
}
|
||||
|
||||
func importOPMLFile(path: String) {
|
||||
func loadOPMLFile(path: String) {
|
||||
let opmlFileURL = URL(fileURLWithPath: path)
|
||||
var fileData: Data?
|
||||
do {
|
||||
|
@ -794,7 +798,7 @@ private extension Account {
|
|||
}
|
||||
|
||||
BatchUpdate.shared.perform {
|
||||
importOPMLItems(children, parentFolder: nil)
|
||||
loadOPMLItems(children, parentFolder: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -901,7 +905,7 @@ private extension Account {
|
|||
feedDictionaryNeedsUpdate = false
|
||||
}
|
||||
|
||||
func createFeed(with opmlFeedSpecifier: RSOPMLFeedSpecifier) -> Feed {
|
||||
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)
|
||||
|
@ -913,14 +917,14 @@ private extension Account {
|
|||
return feed
|
||||
}
|
||||
|
||||
func importOPMLItems(_ items: [RSOPMLItem], parentFolder: Folder?) {
|
||||
func loadOPMLItems(_ items: [RSOPMLItem], parentFolder: Folder?) {
|
||||
|
||||
var feedsToAdd = Set<Feed>()
|
||||
|
||||
items.forEach { (item) in
|
||||
|
||||
if let feedSpecifier = item.feedSpecifier {
|
||||
let feed = createFeed(with: feedSpecifier)
|
||||
let feed = ensureFeed(with: feedSpecifier)
|
||||
feedsToAdd.insert(feed)
|
||||
return
|
||||
}
|
||||
|
@ -928,14 +932,14 @@ private extension Account {
|
|||
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(itemChildren, parentFolder: parentFolder)
|
||||
loadOPMLItems(itemChildren, parentFolder: parentFolder)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if let folder = ensureFolder(with: folderName) {
|
||||
if let itemChildren = item.children {
|
||||
importOPMLItems(itemChildren, parentFolder: folder)
|
||||
loadOPMLItems(itemChildren, parentFolder: folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ protocol AccountDelegate {
|
|||
var refreshProgress: DownloadProgress { get }
|
||||
|
||||
func refreshAll(for account: Account, completion: (() -> Void)?)
|
||||
|
||||
func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
|
||||
func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
func deleteFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
|
||||
|
|
|
@ -62,6 +62,10 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
}
|
||||
|
||||
func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
caller.renameTag(oldName: folder.name ?? "", newName: name) { result in
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import RSParser
|
||||
import RSWeb
|
||||
|
||||
public enum LocalAccountDelegateError: String, Error {
|
||||
|
@ -35,6 +36,44 @@ final class LocalAccountDelegate: AccountDelegate {
|
|||
refresher.refreshFeeds(account.flattenedFeeds())
|
||||
completion?()
|
||||
}
|
||||
|
||||
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.
|
||||
account.loadOPML(loadDocument)
|
||||
completion(.success(()))
|
||||
|
||||
}
|
||||
|
||||
func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
folder.name = name
|
||||
|
|
|
@ -64,13 +64,13 @@ class ImportOPMLWindowController: NSWindowController {
|
|||
panel.allowedFileTypes = ["opml", "xml"]
|
||||
panel.allowsOtherFileTypes = false
|
||||
|
||||
panel.beginSheetModal(for: hostWindow!) { result in
|
||||
if result == NSApplication.ModalResponse.OK, let url = panel.url {
|
||||
DispatchQueue.main.async {
|
||||
do {
|
||||
try OPMLImporter.parseAndImport(fileURL: url, account: account)
|
||||
}
|
||||
catch let error as NSError {
|
||||
panel.beginSheetModal(for: hostWindow!) { modalResult in
|
||||
if modalResult == NSApplication.ModalResponse.OK, let url = panel.url {
|
||||
account.importOPML(url) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
NSApplication.shared.presentError(error)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,6 @@
|
|||
51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8477ACBD22238E9500DF7F37 /* SearchFeedDelegate.swift */; };
|
||||
51C45296226509D300C03939 /* OPMLExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8444C8F11FED81840051386C /* OPMLExporter.swift */; };
|
||||
51C45297226509E300C03939 /* DefaultFeedsImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97591ED9EB0D007D329B /* DefaultFeedsImporter.swift */; };
|
||||
51C45298226509E600C03939 /* OPMLImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */; };
|
||||
51C4529922650A0000C03939 /* ArticleStylesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */; };
|
||||
51C4529A22650A0400C03939 /* ArticleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleStyle.swift */; };
|
||||
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */; };
|
||||
|
@ -259,7 +258,6 @@
|
|||
84C9FCA42262A1B800D921D6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84C9FCA22262A1B800D921D6 /* LaunchScreen.storyboard */; };
|
||||
84CC88181FE59CBF00644329 /* SmartFeedsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CC88171FE59CBF00644329 /* SmartFeedsController.swift */; };
|
||||
84D52E951FE588BB00D14F5B /* DetailStatusBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D52E941FE588BB00D14F5B /* DetailStatusBarView.swift */; };
|
||||
84DAEE301F86CAFE0058304B /* OPMLImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */; };
|
||||
84E185B3203B74E500F69BFA /* SingleLineTextFieldSizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E185B2203B74E500F69BFA /* SingleLineTextFieldSizer.swift */; };
|
||||
84E185C3203BB12600F69BFA /* MultilineTextFieldSizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E185C2203BB12600F69BFA /* MultilineTextFieldSizer.swift */; };
|
||||
84E46C7D1F75EF7B005ECFB3 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */; };
|
||||
|
@ -834,7 +832,6 @@
|
|||
84CBDDAE1FD3674C005A61AA /* Technotes */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Technotes; sourceTree = "<group>"; };
|
||||
84CC88171FE59CBF00644329 /* SmartFeedsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartFeedsController.swift; sourceTree = "<group>"; };
|
||||
84D52E941FE588BB00D14F5B /* DetailStatusBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailStatusBarView.swift; sourceTree = "<group>"; };
|
||||
84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLImporter.swift; sourceTree = "<group>"; };
|
||||
84E185B2203B74E500F69BFA /* SingleLineTextFieldSizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleLineTextFieldSizer.swift; sourceTree = "<group>"; };
|
||||
84E185C2203BB12600F69BFA /* MultilineTextFieldSizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineTextFieldSizer.swift; sourceTree = "<group>"; };
|
||||
84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDefaults.swift; sourceTree = "<group>"; };
|
||||
|
@ -1654,7 +1651,6 @@
|
|||
84DAEE201F86CAE00058304B /* Importers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84DAEE2F1F86CAFE0058304B /* OPMLImporter.swift */,
|
||||
849A97591ED9EB0D007D329B /* DefaultFeedsImporter.swift */,
|
||||
84A3EE52223B667F00557320 /* DefaultFeeds.opml */,
|
||||
);
|
||||
|
@ -2319,7 +2315,6 @@
|
|||
51C452782265091600C03939 /* MasterTimelineCellData.swift in Sources */,
|
||||
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
|
||||
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
|
||||
51C45298226509E600C03939 /* OPMLImporter.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -2374,7 +2369,6 @@
|
|||
84162A152038C12C00035290 /* MarkCommandValidationStatus.swift in Sources */,
|
||||
84E95D241FB1087500552D99 /* ArticlePasteboardWriter.swift in Sources */,
|
||||
849A975B1ED9EB0D007D329B /* ArticleUtilities.swift in Sources */,
|
||||
84DAEE301F86CAFE0058304B /* OPMLImporter.swift in Sources */,
|
||||
849A975C1ED9EB0D007D329B /* DefaultFeedsImporter.swift in Sources */,
|
||||
84A37CB5201ECD610087C5AF /* RenameWindowController.swift in Sources */,
|
||||
84A14FF320048CA70046AD9A /* SendToMicroBlogCommand.swift in Sources */,
|
||||
|
|
|
@ -19,7 +19,7 @@ struct DefaultFeedsImporter {
|
|||
|
||||
appDelegate.logDebugMessage("Importing default feeds.")
|
||||
let defaultFeedsURL = Bundle.main.url(forResource: "DefaultFeeds", withExtension: "opml")!
|
||||
try! OPMLImporter.parseAndImport(fileURL: defaultFeedsURL, account: AccountManager.shared.defaultAccount)
|
||||
AccountManager.shared.defaultAccount.importOPML(defaultFeedsURL) { result in }
|
||||
}
|
||||
|
||||
private static func shouldImportDefaultFeeds(_ isFirstRun: Bool) -> Bool {
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
//
|
||||
// OPMLImporter.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Brent Simmons on 10/5/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSParser
|
||||
import Account
|
||||
import RSCore
|
||||
|
||||
struct OPMLImporter {
|
||||
|
||||
static func parseAndImport(fileURL: URL, account: Account) throws {
|
||||
|
||||
var fileData: Data?
|
||||
|
||||
do {
|
||||
fileData = try Data(contentsOf: fileURL)
|
||||
} catch {
|
||||
print("Error reading OPML file. \(error)")
|
||||
throw error
|
||||
}
|
||||
|
||||
guard let opmlData = fileData else {
|
||||
return
|
||||
}
|
||||
|
||||
let parserData = ParserData(url: fileURL.absoluteString, data: opmlData)
|
||||
var opmlDocument: RSOPMLDocument?
|
||||
|
||||
do {
|
||||
opmlDocument = try RSOPMLParser.parseOPML(with: parserData)
|
||||
} catch {
|
||||
print("Error parsing OPML file. \(error)")
|
||||
throw error
|
||||
}
|
||||
|
||||
if let opmlDocument = opmlDocument {
|
||||
BatchUpdate.shared.perform {
|
||||
account.importOPML(opmlDocument)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue