diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index f05cd1855..a487953bb 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -468,43 +468,25 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, delegate.accountWillBeDeleted(self) } - func loadOPMLItems(_ items: [RSOPMLItem], parentFolder: Folder?) { - var feedsToAdd = Set() - - items.forEach { (item) in - + func addOPMLItems(_ items: [RSOPMLItem]) { + for item in items { if let feedSpecifier = item.feedSpecifier { - let feed = newWebFeed(with: feedSpecifier) - feedsToAdd.insert(feed) - 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(itemChildren, parentFolder: parentFolder) - } - return - } - - if let folder = ensureFolder(with: folderName) { - folder.externalID = item.attributes?["nnw_externalID"] as? String - if let itemChildren = item.children { - loadOPMLItems(itemChildren, parentFolder: folder) + addWebFeed(newWebFeed(with: feedSpecifier)) + } else { + if let title = item.titleFromAttributes, let folder = ensureFolder(with: title) { + folder.externalID = item.attributes?["nnw_externalID"] as? String + item.children?.forEach { itemChild in + if let feedSpecifier = itemChild.feedSpecifier { + folder.addWebFeed(newWebFeed(with: feedSpecifier)) + } + } } } } - - if let parentFolder = parentFolder { - for feed in feedsToAdd { - parentFolder.addWebFeed(feed) - } - } else { - for feed in feedsToAdd { - addWebFeed(feed) - } - } - + } + + func loadOPMLItems(_ items: [RSOPMLItem]) { + addOPMLItems(OPMLNormalizer.normalize(items)) } public func markArticles(_ articles: Set
, statusKey: ArticleStatus.Key, flag: Bool) -> Set
? { diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index fbc41bc77..fcbb97a28 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -55,6 +55,7 @@ 5165D72A22835F7D00D9D53D /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D71E22835E9800D9D53D /* HTMLFeedFinder.swift */; }; 5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */; }; 5170743C232AEDB500A461A3 /* OPMLFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5170743B232AEDB500A461A3 /* OPMLFile.swift */; }; + 519E84A62433D49000D238B0 /* OPMLNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A52433D49000D238B0 /* OPMLNormalizer.swift */; }; 51BB7B84233531BC008E8144 /* AccountBehaviors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7B83233531BC008E8144 /* AccountBehaviors.swift */; }; 51BC8FCC237EC055004F8B56 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC8FCB237EC055004F8B56 /* Feed.swift */; }; 51BFDECE238B508D00216323 /* ContainerIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BFDECD238B508D00216323 /* ContainerIdentifier.swift */; }; @@ -288,6 +289,7 @@ 5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InitialFeedDownloader.swift; sourceTree = ""; }; 5170743B232AEDB500A461A3 /* OPMLFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLFile.swift; sourceTree = ""; }; 518B2EA52351306200400001 /* Account_project_test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_test.xcconfig; sourceTree = ""; }; + 519E84A52433D49000D238B0 /* OPMLNormalizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLNormalizer.swift; sourceTree = ""; }; 51BB7B83233531BC008E8144 /* AccountBehaviors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountBehaviors.swift; sourceTree = ""; }; 51BC8FCB237EC055004F8B56 /* Feed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = ""; }; 51BFDECD238B508D00216323 /* ContainerIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerIdentifier.swift; sourceTree = ""; }; @@ -670,6 +672,7 @@ 51E3EB40229AF61B00645299 /* AccountError.swift */, 846E77531F6F00E300A165E2 /* AccountManager.swift */, 5170743B232AEDB500A461A3 /* OPMLFile.swift */, + 519E84A52433D49000D238B0 /* OPMLNormalizer.swift */, 84AF4EA3222CFDD100F6A800 /* AccountMetadata.swift */, 510BD110232C3801002692E4 /* AccountMetadataFile.swift */, 84F73CF0202788D80000BCEF /* ArticleFetcher.swift */, @@ -1102,6 +1105,7 @@ 5103A9D92422546800410853 /* CloudKitAccountDelegate.swift in Sources */, 5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */, 9E784EBE237E890600099B1B /* FeedlyLogoutOperation.swift in Sources */, + 519E84A62433D49000D238B0 /* OPMLNormalizer.swift in Sources */, 9EEEF71F23545CB4009E9D80 /* FeedlySendArticleStatusesOperation.swift in Sources */, 9EBD49C223C67784005AD5CD /* FeedlyEntryIdentifierProviding.swift in Sources */, 846E77541F6F00E300A165E2 /* AccountManager.swift in Sources */, diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index 5b1edbabd..b77d4af29 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -128,7 +128,7 @@ final class CloudKitAccountDelegate: AccountDelegate { } BatchUpdate.shared.perform { - account.loadOPMLItems(children, parentFolder: nil) + account.loadOPMLItems(children) } completion(.success(())) diff --git a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift index 2250c0cf4..3483d758e 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift @@ -85,7 +85,7 @@ final class LocalAccountDelegate: AccountDelegate { } BatchUpdate.shared.perform { - account.loadOPMLItems(children, parentFolder: nil) + account.loadOPMLItems(children) } completion(.success(())) diff --git a/Frameworks/Account/OPMLFile.swift b/Frameworks/Account/OPMLFile.swift index eb17aa033..134358c35 100644 --- a/Frameworks/Account/OPMLFile.swift +++ b/Frameworks/Account/OPMLFile.swift @@ -40,7 +40,7 @@ final class OPMLFile { } BatchUpdate.shared.perform { - account.loadOPMLItems(opmlItems, parentFolder: nil) + account.loadOPMLItems(opmlItems) } } diff --git a/Frameworks/Account/OPMLNormalizer.swift b/Frameworks/Account/OPMLNormalizer.swift new file mode 100644 index 000000000..bf540ce2a --- /dev/null +++ b/Frameworks/Account/OPMLNormalizer.swift @@ -0,0 +1,60 @@ +// +// OPMLNormalizer.swift +// Account +// +// Created by Maurice Parker on 3/31/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import Foundation +import RSParser + +final class OPMLNormalizer { + + var normalizedOPMLItems = [RSOPMLItem]() + + static func normalize(_ items: [RSOPMLItem]) -> [RSOPMLItem] { + let opmlNormalizer = OPMLNormalizer() + opmlNormalizer.loadOPMLItems(items) + return opmlNormalizer.normalizedOPMLItems + } + + private func loadOPMLItems(_ items: [RSOPMLItem], parentFolder: RSOPMLItem? = nil) { + var feedsToAdd = [RSOPMLItem]() + + items.forEach { (item) in + + if let _ = item.feedSpecifier { + if !feedsToAdd.contains(where: { $0.feedSpecifier?.feedURL == item.feedSpecifier?.feedURL } ) { + feedsToAdd.append(item) + } + return + } + + guard let _ = 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(itemChildren, parentFolder: parentFolder) + } + return + } + + normalizedOPMLItems.append(item) + if let itemChildren = item.children { + loadOPMLItems(itemChildren, parentFolder: item) + } + } + + if let parentFolder = parentFolder { + for feed in feedsToAdd { + parentFolder.addChild(feed) + } + } else { + for feed in feedsToAdd { + normalizedOPMLItems.append(feed) + } + } + + } + +}