Simplify Container and its function implementations.
This commit is contained in:
parent
dccb215c76
commit
ca611623aa
|
@ -18,7 +18,7 @@ func markArticles(_ articles: Set<Article>, statusKey: String, flag: Bool) {
|
|||
|
||||
d.keys.forEach { (accountID) in
|
||||
|
||||
guard let accountArticles = d[accountID], let account = accountWithID(accountID) else {
|
||||
guard let accountArticles = d[accountID], let account = AccountManager.shared.existingAccount(with: accountID) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -29,12 +29,11 @@ private extension FolderTreeControllerDelegate {
|
|||
|
||||
var folderNodes = [Node]()
|
||||
|
||||
let _ = AccountManager.shared.localAccount.visitChildren { (oneRepresentedObject) in
|
||||
for oneRepresentedObject in AccountManager.shared.localAccount.children {
|
||||
|
||||
if let folder = oneRepresentedObject as? Folder {
|
||||
folderNodes += [createNode(folder, parent: node)]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return Node.nodesSortedAlphabetically(folderNodes)
|
||||
|
|
|
@ -34,22 +34,19 @@ private extension SidebarTreeControllerDelegate {
|
|||
// This will be expanded later to add synthetic feeds (All Unread, for instance).
|
||||
|
||||
var updatedChildNodes = [Node]()
|
||||
|
||||
let _ = AccountManager.shared.localAccount.visitChildren { (oneRepresentedObject) in
|
||||
|
||||
if let existingNode = node.childNodeRepresentingObject(oneRepresentedObject as AnyObject) {
|
||||
|
||||
for oneRepresentedObject in AccountManager.shared.localAccount.children {
|
||||
|
||||
if let existingNode = node.childNodeRepresentingObject(oneRepresentedObject) {
|
||||
// Reuse nodes.
|
||||
if !updatedChildNodes.contains(existingNode) {
|
||||
updatedChildNodes += [existingNode]
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if let newNode = createNode(representedObject: oneRepresentedObject as AnyObject, parent: node) {
|
||||
updatedChildNodes += [newNode]
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
updatedChildNodes = Node.nodesSortedAlphabeticallyWithFoldersAtEnd(updatedChildNodes)
|
||||
|
@ -60,28 +57,25 @@ private extension SidebarTreeControllerDelegate {
|
|||
|
||||
var updatedChildNodes = [Node]()
|
||||
let folder = node.representedObject as! Folder
|
||||
|
||||
let _ = folder.visitChildren { (oneRepresentedObject) -> Bool in
|
||||
|
||||
|
||||
for oneRepresentedObject in folder.children {
|
||||
|
||||
if let existingNode = node.childNodeRepresentingObject(oneRepresentedObject) {
|
||||
if !updatedChildNodes.contains(existingNode) {
|
||||
updatedChildNodes += [existingNode]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if let newNode = self.createNode(representedObject: oneRepresentedObject, parent: node) {
|
||||
updatedChildNodes += [newNode]
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
updatedChildNodes = Node.nodesSortedAlphabeticallyWithFoldersAtEnd(updatedChildNodes)
|
||||
return updatedChildNodes
|
||||
}
|
||||
|
||||
func createNode(representedObject: AnyObject, parent: Node) -> Node? {
|
||||
func createNode(representedObject: Any, parent: Node) -> Node? {
|
||||
|
||||
if let feed = representedObject as? Feed {
|
||||
return createNode(feed: feed, parent: parent)
|
||||
|
|
|
@ -31,22 +31,22 @@ public enum AccountType: Int {
|
|||
// TODO: more
|
||||
}
|
||||
|
||||
public final class Account: DisplayNameProvider, Hashable {
|
||||
public final class Account: DisplayNameProvider, Container, Hashable {
|
||||
|
||||
public let accountID: String
|
||||
public let type: AccountType
|
||||
public var nameForDisplay = ""
|
||||
public let hashValue: Int
|
||||
public var children = [AnyObject]()
|
||||
let settingsFile: String
|
||||
let dataFolder: String
|
||||
let database: Database
|
||||
let delegate: AccountDelegate
|
||||
var topLevelObjects = [AnyObject]()
|
||||
var feedIDDictionary = [String: Feed]()
|
||||
var username: String?
|
||||
var saveTimer: Timer?
|
||||
|
||||
var dirty = false {
|
||||
public var dirty = false {
|
||||
didSet {
|
||||
|
||||
if refreshInProgress {
|
||||
|
@ -164,7 +164,7 @@ public final class Account: DisplayNameProvider, Hashable {
|
|||
}
|
||||
else {
|
||||
if !topLevelObjectsContainsFeed(uniquedFeed) {
|
||||
topLevelObjects += [uniquedFeed]
|
||||
children += [uniquedFeed]
|
||||
}
|
||||
didAddFeed = true
|
||||
}
|
||||
|
@ -202,8 +202,12 @@ public final class Account: DisplayNameProvider, Hashable {
|
|||
}
|
||||
|
||||
public func importOPML(_ opmlDocument: RSOPMLDocument) {
|
||||
|
||||
// TODO
|
||||
|
||||
guard let children = opmlDocument.children else {
|
||||
return
|
||||
}
|
||||
importOPMLItems(children, parentFolder: nil, foldersAllowed: true)
|
||||
dirty = true
|
||||
}
|
||||
|
||||
// MARK: - Notifications
|
||||
|
@ -262,13 +266,13 @@ private extension Account {
|
|||
guard let childrenArray = d[Key.children] as? [[String: Any]] else {
|
||||
return
|
||||
}
|
||||
topLevelObjects = objects(with: childrenArray)
|
||||
children = objects(with: childrenArray)
|
||||
updateFeedIDDictionary()
|
||||
}
|
||||
|
||||
func diskDictionary() -> NSDictionary {
|
||||
|
||||
let diskObjects = topLevelObjects.flatMap { (object) -> [String: Any]? in
|
||||
let diskObjects = children.flatMap { (object) -> [String: Any]? in
|
||||
|
||||
if let folder = object as? Folder {
|
||||
return folder.dictionary
|
||||
|
@ -341,7 +345,7 @@ private extension Account {
|
|||
|
||||
func topLevelObjectsContainsFeed(_ feed: Feed) -> Bool {
|
||||
|
||||
return topLevelObjects.contains(where: { (object) -> Bool in
|
||||
return children.contains(where: { (object) -> Bool in
|
||||
if let oneFeed = object as? Feed {
|
||||
if oneFeed.feedID == feed.feedID {
|
||||
return true
|
||||
|
@ -350,6 +354,26 @@ private extension Account {
|
|||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func importOPMLItems(_ items: [RSOPMLItem], parentFolder: Folder?, foldersAllowed: Bool) {
|
||||
|
||||
for item in items {
|
||||
|
||||
if let feedSpecifier = item.feedSpecifier {
|
||||
if hasFeed(withURL: feedSpecifier.feedURL) {
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if item.isFolder {
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OPMLRepresentable
|
||||
|
@ -359,7 +383,7 @@ extension Account: OPMLRepresentable {
|
|||
public func OPMLString(indentLevel: Int) -> String {
|
||||
|
||||
var s = ""
|
||||
for oneObject in topLevelObjects {
|
||||
for oneObject in children {
|
||||
if let oneOPMLObject = oneObject as? OPMLRepresentable {
|
||||
s += oneOPMLObject.OPMLString(indentLevel: indentLevel + 1)
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
841973FE1F6DD1BC006346C4 /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841973EF1F6DD19E006346C4 /* RSCore.framework */; };
|
||||
841973FF1F6DD1C5006346C4 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841973FA1F6DD1AC006346C4 /* RSParser.framework */; };
|
||||
841974011F6DD1EC006346C4 /* Folder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841974001F6DD1EC006346C4 /* Folder.swift */; };
|
||||
841974181F6DD535006346C4 /* Folder+Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841974171F6DD535006346C4 /* Folder+Container.swift */; };
|
||||
8419741A1F6DD583006346C4 /* Account+Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841974191F6DD583006346C4 /* Account+Container.swift */; };
|
||||
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841974241F6DDCE4006346C4 /* AccountDelegate.swift */; };
|
||||
8469F8171F6DD0AD0084783E /* Database.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8401A17D1F6DC388002B1BE2 /* Database.framework */; };
|
||||
8469F81C1F6DD15E0084783E /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848935101F62486800CEBD24 /* Account.swift */; };
|
||||
|
@ -20,11 +18,10 @@
|
|||
846E774F1F6EF9C000A165E2 /* LocalAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8419742C1F6DDE84006346C4 /* LocalAccountDelegate.swift */; };
|
||||
846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8419742D1F6DDE96006346C4 /* LocalAccountRefresher.swift */; };
|
||||
846E77541F6F00E300A165E2 /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846E77531F6F00E300A165E2 /* AccountManager.swift */; };
|
||||
846E77571F6F03D600A165E2 /* Article+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846E77561F6F03D600A165E2 /* Article+Account.swift */; };
|
||||
846E77591F6F03E300A165E2 /* Feed+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846E77581F6F03E300A165E2 /* Feed+Account.swift */; };
|
||||
848935001F62484F00CEBD24 /* Account.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 848934F61F62484F00CEBD24 /* Account.framework */; };
|
||||
848935051F62485000CEBD24 /* AccountTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848935041F62485000CEBD24 /* AccountTests.swift */; };
|
||||
84C3654A1F899F3B001EC85C /* CombinedRefreshProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C365491F899F3B001EC85C /* CombinedRefreshProgress.swift */; };
|
||||
84C8B3F41F89DE430053CCA6 /* DataExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C8B3F31F89DE430053CCA6 /* DataExtensions.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -107,14 +104,10 @@
|
|||
841973F41F6DD1AC006346C4 /* RSParser.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSParser.xcodeproj; path = ../RSParser/RSParser.xcodeproj; sourceTree = "<group>"; };
|
||||
841974001F6DD1EC006346C4 /* Folder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Folder.swift; sourceTree = "<group>"; };
|
||||
8419740D1F6DD25F006346C4 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = "<group>"; };
|
||||
841974171F6DD535006346C4 /* Folder+Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Container.swift"; sourceTree = "<group>"; };
|
||||
841974191F6DD583006346C4 /* Account+Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account+Container.swift"; sourceTree = "<group>"; };
|
||||
841974241F6DDCE4006346C4 /* AccountDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDelegate.swift; sourceTree = "<group>"; };
|
||||
8419742C1F6DDE84006346C4 /* LocalAccountDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAccountDelegate.swift; sourceTree = "<group>"; };
|
||||
8419742D1F6DDE96006346C4 /* LocalAccountRefresher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAccountRefresher.swift; sourceTree = "<group>"; };
|
||||
846E77531F6F00E300A165E2 /* AccountManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = "<group>"; };
|
||||
846E77561F6F03D600A165E2 /* Article+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Article+Account.swift"; path = "Extensions/Article+Account.swift"; sourceTree = "<group>"; };
|
||||
846E77581F6F03E300A165E2 /* Feed+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Feed+Account.swift"; path = "Extensions/Feed+Account.swift"; sourceTree = "<group>"; };
|
||||
848934F61F62484F00CEBD24 /* Account.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Account.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
848934FA1F62484F00CEBD24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
848934FF1F62484F00CEBD24 /* AccountTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AccountTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -122,6 +115,7 @@
|
|||
848935061F62485000CEBD24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
848935101F62486800CEBD24 /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = "<group>"; };
|
||||
84C365491F899F3B001EC85C /* CombinedRefreshProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombinedRefreshProgress.swift; sourceTree = "<group>"; };
|
||||
84C8B3F31F89DE430053CCA6 /* DataExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataExtensions.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -184,16 +178,6 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
841974141F6DD4FF006346C4 /* Container */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8419740D1F6DD25F006346C4 /* Container.swift */,
|
||||
841974191F6DD583006346C4 /* Account+Container.swift */,
|
||||
841974171F6DD535006346C4 /* Folder+Container.swift */,
|
||||
);
|
||||
path = Container;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8419742B1F6DDE84006346C4 /* LocalAccount */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -214,15 +198,6 @@
|
|||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
846E77551F6F03B200A165E2 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
846E77581F6F03E300A165E2 /* Feed+Account.swift */,
|
||||
846E77561F6F03D600A165E2 /* Article+Account.swift */,
|
||||
);
|
||||
name = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
848934EC1F62484F00CEBD24 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -231,8 +206,8 @@
|
|||
841974241F6DDCE4006346C4 /* AccountDelegate.swift */,
|
||||
841974001F6DD1EC006346C4 /* Folder.swift */,
|
||||
84C365491F899F3B001EC85C /* CombinedRefreshProgress.swift */,
|
||||
846E77551F6F03B200A165E2 /* Extensions */,
|
||||
841974141F6DD4FF006346C4 /* Container */,
|
||||
84C8B3F31F89DE430053CCA6 /* DataExtensions.swift */,
|
||||
8419740D1F6DD25F006346C4 /* Container.swift */,
|
||||
8419742B1F6DDE84006346C4 /* LocalAccount */,
|
||||
8469F80F1F6DC3C10084783E /* Frameworks */,
|
||||
848934FA1F62484F00CEBD24 /* Info.plist */,
|
||||
|
@ -453,18 +428,15 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
846E77571F6F03D600A165E2 /* Article+Account.swift in Sources */,
|
||||
84C8B3F41F89DE430053CCA6 /* DataExtensions.swift in Sources */,
|
||||
84C3654A1F899F3B001EC85C /* CombinedRefreshProgress.swift in Sources */,
|
||||
8469F81C1F6DD15E0084783E /* Account.swift in Sources */,
|
||||
846E77451F6EF9B900A165E2 /* Container.swift in Sources */,
|
||||
8419741A1F6DD583006346C4 /* Account+Container.swift in Sources */,
|
||||
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */,
|
||||
846E77541F6F00E300A165E2 /* AccountManager.swift in Sources */,
|
||||
846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */,
|
||||
841974011F6DD1EC006346C4 /* Folder.swift in Sources */,
|
||||
846E77591F6F03E300A165E2 /* Feed+Account.swift in Sources */,
|
||||
846E774F1F6EF9C000A165E2 /* LocalAccountDelegate.swift in Sources */,
|
||||
841974181F6DD535006346C4 /* Folder+Container.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -198,12 +198,6 @@ private func accountFilePathWithFolder(_ folderPath: String) -> String {
|
|||
return NSString(string: folderPath).appendingPathComponent(accountDataFileName)
|
||||
}
|
||||
|
||||
public func accountWithID(_ accountID: String) -> Account? {
|
||||
|
||||
// Shortcut.
|
||||
return AccountManager.shared.existingAccount(with: accountID)
|
||||
}
|
||||
|
||||
private struct AccountSpecifier {
|
||||
|
||||
let type: String
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// Container.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 4/17/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import Data
|
||||
|
||||
extension NSNotification.Name {
|
||||
|
||||
public static let ChildrenDidChange = Notification.Name("ChildrenDidChange")
|
||||
}
|
||||
|
||||
public protocol Container {
|
||||
|
||||
var children: [AnyObject] { get }
|
||||
|
||||
//Recursive
|
||||
func flattenedFeeds() -> Set<Feed>
|
||||
func hasFeed(with feedID: String) -> Bool
|
||||
func hasFeed(withURL url: String) -> Bool
|
||||
func existingFeed(with feedID: String) -> Feed?
|
||||
func existingFeed(withURL url: String) -> Feed?
|
||||
func existingFolder(with name: String) -> Folder?
|
||||
|
||||
func postChildrenDidChangeNotification()
|
||||
}
|
||||
|
||||
public extension Container {
|
||||
|
||||
func flattenedFeeds() -> Set<Feed> {
|
||||
|
||||
var feeds = Set<Feed>()
|
||||
|
||||
for object in children {
|
||||
if let feed = object as? Feed {
|
||||
feeds.insert(feed)
|
||||
}
|
||||
else if let container = object as? Container {
|
||||
feeds.formUnion(container.flattenedFeeds())
|
||||
}
|
||||
}
|
||||
|
||||
return feeds
|
||||
}
|
||||
|
||||
func hasFeed(with feedID: String) -> Bool {
|
||||
|
||||
return existingFeed(with: feedID) != nil
|
||||
}
|
||||
|
||||
func hasFeed(withURL url: String) -> Bool {
|
||||
|
||||
return existingFeed(withURL: url) != nil
|
||||
}
|
||||
|
||||
func existingFeed(with feedID: String) -> Feed? {
|
||||
|
||||
for child in children {
|
||||
|
||||
if let feed = child as? Feed, feed.feedID == feedID {
|
||||
return feed
|
||||
}
|
||||
if let container = child as? Container, let feed = container.existingFeed(with: feedID) {
|
||||
return feed
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func existingFeed(withURL url: String) -> Feed? {
|
||||
|
||||
for child in children {
|
||||
|
||||
if let feed = child as? Feed, feed.url == url {
|
||||
return feed
|
||||
}
|
||||
if let container = child as? Container, let feed = container.existingFeed(withURL: url) {
|
||||
return feed
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func existingFolder(with name: String) -> Folder? {
|
||||
|
||||
for child in children {
|
||||
|
||||
if let folder = child as? Folder {
|
||||
if folder.name == name {
|
||||
return folder
|
||||
}
|
||||
if let subFolder = folder.existingFolder(with: name) {
|
||||
return subFolder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func postChildrenDidChangeNotification() {
|
||||
|
||||
NotificationCenter.default.post(name: .ChildrenDidChange, object: self)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
//
|
||||
// Account+Container.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Brent Simmons on 9/16/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Data
|
||||
|
||||
extension Account: Container {
|
||||
|
||||
public func flattenedFeeds() -> Set<Feed> {
|
||||
|
||||
var feeds = Set<Feed>()
|
||||
|
||||
for object in topLevelObjects {
|
||||
if let feed = object as? Feed {
|
||||
feeds.insert(feed)
|
||||
}
|
||||
else if let folder = object as? Folder {
|
||||
feeds.formUnion(folder.flattenedFeeds())
|
||||
}
|
||||
}
|
||||
|
||||
return feeds
|
||||
}
|
||||
|
||||
public func existingFeed(with feedID: String) -> Feed? {
|
||||
|
||||
return feedIDDictionary[feedID]
|
||||
}
|
||||
|
||||
public func canAddItem(_ item: AnyObject) -> Bool {
|
||||
|
||||
return false // TODO
|
||||
}
|
||||
|
||||
public func isChild(_ obj: AnyObject) -> Bool {
|
||||
|
||||
return topLevelObjects.contains(where: { (oneObject) -> Bool in
|
||||
return oneObject === obj
|
||||
})
|
||||
}
|
||||
|
||||
public func visitObjects(_ recurse: Bool, _ visitBlock: VisitBlock) -> Bool {
|
||||
|
||||
for oneObject in topLevelObjects {
|
||||
|
||||
if let oneContainer = oneObject as? Container {
|
||||
if visitBlock(oneObject) {
|
||||
return true
|
||||
}
|
||||
if recurse && oneContainer.visitObjects(recurse, visitBlock) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
else {
|
||||
if visitBlock(oneObject) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
//
|
||||
// Container.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 4/17/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import Data
|
||||
|
||||
public typealias VisitBlock = (_ obj: AnyObject) -> Bool // Return true to stop
|
||||
|
||||
extension NSNotification.Name {
|
||||
|
||||
public static let ChildrenDidChange = Notification.Name("ChildrenDidChangeNotification")
|
||||
}
|
||||
|
||||
public protocol Container {
|
||||
|
||||
//Recursive
|
||||
func flattenedFeeds() -> Set<Feed>
|
||||
func existingFeed(with feedID: String) -> Feed?
|
||||
func existingFeed(withURL url: String) -> Feed?
|
||||
func hasFeed(with feedID: String) -> Bool
|
||||
func hasFeed(withURL url: String) -> Bool
|
||||
|
||||
func isChild(_ obj: AnyObject) -> Bool
|
||||
|
||||
// visitBlock should return true to stop visiting.
|
||||
// visitObjects returns true if a visitBlock returned true.
|
||||
func visitObjects(_ recurse: Bool, _ visitBlock: VisitBlock) -> Bool
|
||||
}
|
||||
|
||||
public extension Container {
|
||||
|
||||
func existingFeed(with feedID: String) -> Feed? {
|
||||
|
||||
let foundObject = findObject(true) { (oneDescendant) -> Bool in
|
||||
if let oneFeed = oneDescendant as? Feed, oneFeed.feedID == feedID {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return foundObject as! Feed?
|
||||
}
|
||||
|
||||
func existingFeed(withURL url: String) -> Feed? {
|
||||
|
||||
let foundObject = findObject(true) { (oneDescendant) -> Bool in
|
||||
if let oneFeed = oneDescendant as? Feed, oneFeed.url == url {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return foundObject as! Feed?
|
||||
}
|
||||
|
||||
func hasFeed(with feedID: String) -> Bool {
|
||||
|
||||
if let _ = existingFeed(with: feedID) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasFeed(withURL url: String) -> Bool {
|
||||
|
||||
if let _ = existingFeed(withURL: url) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func visitChildren(visitBlock: VisitBlock) -> Bool {
|
||||
|
||||
return visitObjects(false, visitBlock)
|
||||
}
|
||||
|
||||
func findObject(_ recurse: Bool, visitBlock: @escaping VisitBlock) -> AnyObject? {
|
||||
|
||||
var foundObject: AnyObject?
|
||||
|
||||
let _ = visitObjects(recurse) { (oneObject) in
|
||||
|
||||
if let _ = foundObject {
|
||||
return true
|
||||
}
|
||||
|
||||
if visitBlock(oneObject) {
|
||||
foundObject = oneObject
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return foundObject
|
||||
}
|
||||
|
||||
func objectIsChild(_ obj: AnyObject) -> Bool {
|
||||
|
||||
return visitObjects(false) { (oneObject) in
|
||||
return obj === oneObject
|
||||
}
|
||||
}
|
||||
|
||||
func objectIsDescendant(_ obj: AnyObject) -> Bool {
|
||||
|
||||
return visitObjects(true) { (oneObject) in
|
||||
return obj === oneObject
|
||||
}
|
||||
}
|
||||
|
||||
func existingFolderWithName(_ name: String) -> Folder? {
|
||||
|
||||
let foundObject = findObject(false) { (oneObject) in
|
||||
if let oneFolder = oneObject as? Folder, oneFolder.nameForDisplay == name {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return foundObject as! Folder?
|
||||
}
|
||||
|
||||
public func postChildrenDidChangeNotification() {
|
||||
|
||||
NotificationCenter.default.post(name: .ChildrenDidChange, object: self)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
//
|
||||
// Folder+Container.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Brent Simmons on 9/16/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Data
|
||||
|
||||
extension Folder: Container {
|
||||
|
||||
public func flattenedFeeds() -> Set<Feed> {
|
||||
|
||||
var feeds = Set<Feed>()
|
||||
for oneChild in children {
|
||||
if let oneFeed = oneChild as? Feed {
|
||||
feeds.insert(oneFeed)
|
||||
}
|
||||
else if let oneContainer = oneChild as? Container {
|
||||
feeds.formUnion(oneContainer.flattenedFeeds())
|
||||
}
|
||||
}
|
||||
return feeds
|
||||
}
|
||||
|
||||
public func isChild(_ obj: AnyObject) -> Bool {
|
||||
|
||||
return children.contains { $0 === obj }
|
||||
}
|
||||
|
||||
public func visitObjects(_ recurse: Bool, _ visitBlock: VisitBlock) -> Bool {
|
||||
|
||||
for oneObject in children {
|
||||
|
||||
if let oneContainer = oneObject as? Container {
|
||||
if visitBlock(oneObject) {
|
||||
return true
|
||||
}
|
||||
if recurse && oneContainer.visitObjects(recurse, visitBlock) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
else {
|
||||
if visitBlock(oneObject) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// DataExtensions.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Brent Simmons on 10/7/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Data
|
||||
|
||||
public extension Feed {
|
||||
|
||||
var account: Account? {
|
||||
get {
|
||||
return AccountManager.shared.existingAccount(with: accountID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Article {
|
||||
|
||||
var account: Account? {
|
||||
get {
|
||||
return AccountManager.shared.existingAccount(with: accountID)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
//
|
||||
// Article+Account.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Brent Simmons on 9/17/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Data
|
||||
|
||||
public extension Article {
|
||||
|
||||
var account: Account? {
|
||||
get {
|
||||
return accountWithID(accountID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
//
|
||||
// Feed+Account.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Brent Simmons on 9/17/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Data
|
||||
|
||||
struct FeedDictionaryKey {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public extension Feed {
|
||||
|
||||
var account: Account? {
|
||||
get {
|
||||
return accountWithID(accountID)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,10 +9,10 @@
|
|||
import Foundation
|
||||
import Data
|
||||
|
||||
public final class Folder: DisplayNameProvider, UnreadCountProvider {
|
||||
public final class Folder: DisplayNameProvider, Container, UnreadCountProvider {
|
||||
|
||||
public let account: Account
|
||||
var children = [AnyObject]()
|
||||
public var children = [AnyObject]()
|
||||
var name: String?
|
||||
static let untitledName = NSLocalizedString("Untitled ƒ", comment: "Folder name")
|
||||
|
||||
|
@ -180,13 +180,11 @@ extension Folder: OPMLRepresentable {
|
|||
|
||||
var hasAtLeastOneChild = false
|
||||
|
||||
let _ = visitChildren { (oneChild) -> Bool in
|
||||
|
||||
if let oneOPMLObject = oneChild as? OPMLRepresentable {
|
||||
s += oneOPMLObject.OPMLString(indentLevel: indentLevel + 1)
|
||||
for child in children {
|
||||
if let opmlObject = child as? OPMLRepresentable {
|
||||
s += opmlObject.OPMLString(indentLevel: indentLevel + 1)
|
||||
hasAtLeastOneChild = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if !hasAtLeastOneChild {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
- (void)addChild:(RSOPMLItem *)child;
|
||||
|
||||
@property (nonatomic, readonly) RSOPMLFeedSpecifier *OPMLFeedSpecifier; //May be nil.
|
||||
@property (nonatomic, readonly) RSOPMLFeedSpecifier *feedSpecifier; //May be nil.
|
||||
|
||||
@property (nonatomic, readonly) NSString *titleFromAttributes; //May be nil.
|
||||
@property (nonatomic, readonly) BOOL isFolder;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
@implementation RSOPMLItem
|
||||
|
||||
@synthesize children = _children;
|
||||
@synthesize OPMLFeedSpecifier = _OPMLFeedSpecifier;
|
||||
@synthesize feedSpecifier = _feedSpecifier;
|
||||
|
||||
|
||||
- (NSArray *)children {
|
||||
|
@ -48,10 +48,10 @@
|
|||
}
|
||||
|
||||
|
||||
- (RSOPMLFeedSpecifier *)OPMLFeedSpecifier {
|
||||
- (RSOPMLFeedSpecifier *)feedSpecifier {
|
||||
|
||||
if (_OPMLFeedSpecifier) {
|
||||
return _OPMLFeedSpecifier;
|
||||
if (_feedSpecifier) {
|
||||
return _feedSpecifier;
|
||||
}
|
||||
|
||||
NSString *feedURL = self.attributes.opml_xmlUrl;
|
||||
|
@ -59,9 +59,9 @@
|
|||
return nil;
|
||||
}
|
||||
|
||||
_OPMLFeedSpecifier = [[RSOPMLFeedSpecifier alloc] initWithTitle:self.attributes.opml_title feedDescription:self.attributes.opml_description homePageURL:self.attributes.opml_htmlUrl feedURL:feedURL];
|
||||
_feedSpecifier = [[RSOPMLFeedSpecifier alloc] initWithTitle:self.attributes.opml_title feedDescription:self.attributes.opml_description homePageURL:self.attributes.opml_htmlUrl feedURL:feedURL];
|
||||
|
||||
return _OPMLFeedSpecifier;
|
||||
return _feedSpecifier;
|
||||
}
|
||||
|
||||
- (NSString *)titleFromAttributes {
|
||||
|
|
Loading…
Reference in New Issue