Continue renaming webFeed to feed.
This commit is contained in:
parent
fac4eded60
commit
2d3ef95619
@ -143,7 +143,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var topLevelWebFeeds = Set<Feed>()
|
public var topLevelFeeds = Set<Feed>()
|
||||||
public var folders: Set<Folder>? = Set<Folder>()
|
public var folders: Set<Folder>? = Set<Folder>()
|
||||||
|
|
||||||
public var externalID: String? {
|
public var externalID: String? {
|
||||||
@ -179,7 +179,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var flattenedWebFeedURLs: Set<String> {
|
var flattenedWebFeedURLs: Set<String> {
|
||||||
return Set(flattenedWebFeeds().map({ $0.url }))
|
return Set(flattenedFeeds().map({ $0.url }))
|
||||||
}
|
}
|
||||||
|
|
||||||
var username: String? {
|
var username: String? {
|
||||||
@ -339,7 +339,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
self.metadata.performedApril2020RetentionPolicyChange = true
|
self.metadata.performedApril2020RetentionPolicyChange = true
|
||||||
}
|
}
|
||||||
|
|
||||||
self.database.cleanupDatabaseAtStartup(subscribedToWebFeedIDs: self.flattenedWebFeeds().webFeedIDs())
|
self.database.cleanupDatabaseAtStartup(subscribedToWebFeedIDs: self.flattenedFeeds().webFeedIDs())
|
||||||
self.fetchAllUnreadCounts()
|
self.fetchAllUnreadCounts()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,11 +537,11 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
|
|
||||||
func existingContainers(withWebFeed webFeed: Feed) -> [Container] {
|
func existingContainers(withWebFeed webFeed: Feed) -> [Container] {
|
||||||
var containers = [Container]()
|
var containers = [Container]()
|
||||||
if topLevelWebFeeds.contains(webFeed) {
|
if topLevelFeeds.contains(webFeed) {
|
||||||
containers.append(self)
|
containers.append(self)
|
||||||
}
|
}
|
||||||
folders?.forEach { folder in
|
folders?.forEach { folder in
|
||||||
if folder.topLevelWebFeeds.contains(webFeed) {
|
if folder.topLevelFeeds.contains(webFeed) {
|
||||||
containers.append(folder)
|
containers.append(folder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -588,7 +588,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
|
|
||||||
func newWebFeed(with opmlFeedSpecifier: RSOPMLFeedSpecifier) -> Feed {
|
func newWebFeed(with opmlFeedSpecifier: RSOPMLFeedSpecifier) -> Feed {
|
||||||
let feedURL = opmlFeedSpecifier.feedURL
|
let feedURL = opmlFeedSpecifier.feedURL
|
||||||
let metadata = webFeedMetadata(feedURL: feedURL, webFeedID: feedURL)
|
let metadata = webFeedMetadata(feedURL: feedURL, feedID: feedURL)
|
||||||
let feed = Feed(account: self, url: opmlFeedSpecifier.feedURL, metadata: metadata)
|
let feed = Feed(account: self, url: opmlFeedSpecifier.feedURL, metadata: metadata)
|
||||||
if let feedTitle = opmlFeedSpecifier.title {
|
if let feedTitle = opmlFeedSpecifier.title {
|
||||||
if feed.name == nil {
|
if feed.name == nil {
|
||||||
@ -598,16 +598,16 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
return feed
|
return feed
|
||||||
}
|
}
|
||||||
|
|
||||||
public func addWebFeed(_ feed: Feed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
public func addFeed(_ feed: Feed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
delegate.addWebFeed(for: self, with: feed, to: container, completion: completion)
|
delegate.addWebFeed(for: self, with: feed, to: container, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func createWebFeed(url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<Feed, Error>) -> Void) {
|
public func createFeed(url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||||
delegate.createWebFeed(for: self, url: url, name: name, container: container, validateFeed: validateFeed, completion: completion)
|
delegate.createWebFeed(for: self, url: url, name: name, container: container, validateFeed: validateFeed, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWebFeed(with name: String?, url: String, webFeedID: String, homePageURL: String?) -> Feed {
|
func createWebFeed(with name: String?, url: String, webFeedID: String, homePageURL: String?) -> Feed {
|
||||||
let metadata = webFeedMetadata(feedURL: url, webFeedID: webFeedID)
|
let metadata = webFeedMetadata(feedURL: url, feedID: webFeedID)
|
||||||
let feed = Feed(account: self, url: url, metadata: metadata)
|
let feed = Feed(account: self, url: url, metadata: metadata)
|
||||||
feed.name = name
|
feed.name = name
|
||||||
feed.homePageURL = homePageURL
|
feed.homePageURL = homePageURL
|
||||||
@ -618,7 +618,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
delegate.removeWebFeed(for: self, with: feed, from: container, completion: completion)
|
delegate.removeWebFeed(for: self, with: feed, from: container, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func moveWebFeed(_ feed: Feed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
public func moveFeed(_ feed: Feed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
delegate.moveWebFeed(for: self, with: feed, from: from, to: to, completion: completion)
|
delegate.moveWebFeed(for: self, with: feed, from: from, to: to, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,15 +711,15 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func fetchUnreadCountForToday(_ completion: @escaping SingleUnreadCountCompletionBlock) {
|
public func fetchUnreadCountForToday(_ completion: @escaping SingleUnreadCountCompletionBlock) {
|
||||||
database.fetchUnreadCountForToday(for: flattenedWebFeeds().webFeedIDs(), completion: completion)
|
database.fetchUnreadCountForToday(for: flattenedFeeds().webFeedIDs(), completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchUnreadCountForStarredArticles(_ completion: @escaping SingleUnreadCountCompletionBlock) {
|
public func fetchUnreadCountForStarredArticles(_ completion: @escaping SingleUnreadCountCompletionBlock) {
|
||||||
database.fetchStarredAndUnreadCount(for: flattenedWebFeeds().webFeedIDs(), completion: completion)
|
database.fetchStarredAndUnreadCount(for: flattenedFeeds().webFeedIDs(), completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchCountForStarredArticles() throws -> Int {
|
public func fetchCountForStarredArticles() throws -> Int {
|
||||||
return try database.fetchStarredArticlesCount(flattenedWebFeeds().webFeedIDs())
|
return try database.fetchStarredArticlesCount(flattenedFeeds().webFeedIDs())
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchUnreadArticleIDs(_ completion: @escaping ArticleIDsCompletionBlock) {
|
public func fetchUnreadArticleIDs(_ completion: @escaping ArticleIDsCompletionBlock) {
|
||||||
@ -736,11 +736,11 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func unreadCount(for webFeed: Feed) -> Int {
|
public func unreadCount(for webFeed: Feed) -> Int {
|
||||||
return unreadCounts[webFeed.webFeedID] ?? 0
|
return unreadCounts[webFeed.feedID] ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setUnreadCount(_ unreadCount: Int, for webFeed: Feed) {
|
public func setUnreadCount(_ unreadCount: Int, for webFeed: Feed) {
|
||||||
unreadCounts[webFeed.webFeedID] = unreadCount
|
unreadCounts[webFeed.feedID] = unreadCount
|
||||||
}
|
}
|
||||||
|
|
||||||
public func structureDidChange() {
|
public func structureDidChange() {
|
||||||
@ -763,7 +763,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
update(webFeed.webFeedID, with: parsedItems, completion: completion)
|
update(webFeed.feedID, with: parsedItems, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(_ webFeedID: String, with parsedItems: Set<ParsedItem>, deleteOlder: Bool = true, completion: @escaping UpdateArticlesCompletionBlock) {
|
func update(_ webFeedID: String, with parsedItems: Set<ParsedItem>, deleteOlder: Bool = true, completion: @escaping UpdateArticlesCompletionBlock) {
|
||||||
@ -899,7 +899,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
|
|
||||||
// MARK: - Container
|
// MARK: - Container
|
||||||
|
|
||||||
public func flattenedWebFeeds() -> Set<Feed> {
|
public func flattenedFeeds() -> Set<Feed> {
|
||||||
assert(Thread.isMainThread)
|
assert(Thread.isMainThread)
|
||||||
if flattenedWebFeedsNeedUpdate {
|
if flattenedWebFeedsNeedUpdate {
|
||||||
updateFlattenedWebFeeds()
|
updateFlattenedWebFeeds()
|
||||||
@ -908,7 +908,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func removeWebFeed(_ webFeed: Feed) {
|
public func removeWebFeed(_ webFeed: Feed) {
|
||||||
topLevelWebFeeds.remove(webFeed)
|
topLevelFeeds.remove(webFeed)
|
||||||
structureDidChange()
|
structureDidChange()
|
||||||
postChildrenDidChangeNotification()
|
postChildrenDidChangeNotification()
|
||||||
}
|
}
|
||||||
@ -917,19 +917,19 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
guard !webFeeds.isEmpty else {
|
guard !webFeeds.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
topLevelWebFeeds.subtract(webFeeds)
|
topLevelFeeds.subtract(webFeeds)
|
||||||
structureDidChange()
|
structureDidChange()
|
||||||
postChildrenDidChangeNotification()
|
postChildrenDidChangeNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func addWebFeed(_ webFeed: Feed) {
|
public func addWebFeed(_ webFeed: Feed) {
|
||||||
topLevelWebFeeds.insert(webFeed)
|
topLevelFeeds.insert(webFeed)
|
||||||
structureDidChange()
|
structureDidChange()
|
||||||
postChildrenDidChangeNotification()
|
postChildrenDidChangeNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFeedIfNotInAnyFolder(_ webFeed: Feed) {
|
func addFeedIfNotInAnyFolder(_ webFeed: Feed) {
|
||||||
if !flattenedWebFeeds().contains(webFeed) {
|
if !flattenedFeeds().contains(webFeed) {
|
||||||
addWebFeed(webFeed)
|
addWebFeed(webFeed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -944,7 +944,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
|
|
||||||
public func debugDropConditionalGetInfo() {
|
public func debugDropConditionalGetInfo() {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
flattenedWebFeeds().forEach{ $0.dropConditionalGetInfo() }
|
flattenedFeeds().forEach{ $0.dropConditionalGetInfo() }
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1027,7 +1027,7 @@ extension Account: WebFeedMetadataDelegate {
|
|||||||
|
|
||||||
func valueDidChange(_ feedMetadata: WebFeedMetadata, key: WebFeedMetadata.CodingKeys) {
|
func valueDidChange(_ feedMetadata: WebFeedMetadata, key: WebFeedMetadata.CodingKeys) {
|
||||||
webFeedMetadataFile.markAsDirty()
|
webFeedMetadataFile.markAsDirty()
|
||||||
guard let feed = existingWebFeed(withWebFeedID: feedMetadata.webFeedID) else {
|
guard let feed = existingWebFeed(withWebFeedID: feedMetadata.feedID) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
feed.postFeedSettingDidChangeNotification(key)
|
feed.postFeedSettingDidChangeNotification(key)
|
||||||
@ -1039,11 +1039,11 @@ extension Account: WebFeedMetadataDelegate {
|
|||||||
private extension Account {
|
private extension Account {
|
||||||
|
|
||||||
func fetchStarredArticles(limit: Int?) throws -> Set<Article> {
|
func fetchStarredArticles(limit: Int?) throws -> Set<Article> {
|
||||||
return try database.fetchStarredArticles(flattenedWebFeeds().webFeedIDs(), limit)
|
return try database.fetchStarredArticles(flattenedFeeds().webFeedIDs(), limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchStarredArticlesAsync(limit: Int?, _ completion: @escaping ArticleSetResultBlock) {
|
func fetchStarredArticlesAsync(limit: Int?, _ completion: @escaping ArticleSetResultBlock) {
|
||||||
database.fetchedStarredArticlesAsync(flattenedWebFeeds().webFeedIDs(), limit, completion)
|
database.fetchedStarredArticlesAsync(flattenedFeeds().webFeedIDs(), limit, completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUnreadArticles(limit: Int?) throws -> Set<Article> {
|
func fetchUnreadArticles(limit: Int?) throws -> Set<Article> {
|
||||||
@ -1055,11 +1055,11 @@ private extension Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchTodayArticles(limit: Int?) throws -> Set<Article> {
|
func fetchTodayArticles(limit: Int?) throws -> Set<Article> {
|
||||||
return try database.fetchTodayArticles(flattenedWebFeeds().webFeedIDs(), limit)
|
return try database.fetchTodayArticles(flattenedFeeds().webFeedIDs(), limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchTodayArticlesAsync(limit: Int?, _ completion: @escaping ArticleSetResultBlock) {
|
func fetchTodayArticlesAsync(limit: Int?, _ completion: @escaping ArticleSetResultBlock) {
|
||||||
database.fetchTodayArticlesAsync(flattenedWebFeeds().webFeedIDs(), limit, completion)
|
database.fetchTodayArticlesAsync(flattenedFeeds().webFeedIDs(), limit, completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticles(folder: Folder) throws -> Set<Article> {
|
func fetchArticles(folder: Folder) throws -> Set<Article> {
|
||||||
@ -1079,13 +1079,13 @@ private extension Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticles(webFeed: Feed) throws -> Set<Article> {
|
func fetchArticles(webFeed: Feed) throws -> Set<Article> {
|
||||||
let articles = try database.fetchArticles(webFeed.webFeedID)
|
let articles = try database.fetchArticles(webFeed.feedID)
|
||||||
validateUnreadCount(webFeed, articles)
|
validateUnreadCount(webFeed, articles)
|
||||||
return articles
|
return articles
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticlesAsync(webFeed: Feed, _ completion: @escaping ArticleSetResultBlock) {
|
func fetchArticlesAsync(webFeed: Feed, _ completion: @escaping ArticleSetResultBlock) {
|
||||||
database.fetchArticlesAsync(webFeed.webFeedID) { [weak self] articleSetResult in
|
database.fetchArticlesAsync(webFeed.feedID) { [weak self] articleSetResult in
|
||||||
switch articleSetResult {
|
switch articleSetResult {
|
||||||
case .success(let articles):
|
case .success(let articles):
|
||||||
self?.validateUnreadCount(webFeed, articles)
|
self?.validateUnreadCount(webFeed, articles)
|
||||||
@ -1097,7 +1097,7 @@ private extension Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticlesMatching(_ searchString: String) throws -> Set<Article> {
|
func fetchArticlesMatching(_ searchString: String) throws -> Set<Article> {
|
||||||
return try database.fetchArticlesMatching(searchString, flattenedWebFeeds().webFeedIDs())
|
return try database.fetchArticlesMatching(searchString, flattenedFeeds().webFeedIDs())
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticlesMatchingWithArticleIDs(_ searchString: String, _ articleIDs: Set<String>) throws -> Set<Article> {
|
func fetchArticlesMatchingWithArticleIDs(_ searchString: String, _ articleIDs: Set<String>) throws -> Set<Article> {
|
||||||
@ -1105,7 +1105,7 @@ private extension Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticlesMatchingAsync(_ searchString: String, _ completion: @escaping ArticleSetResultBlock) {
|
func fetchArticlesMatchingAsync(_ searchString: String, _ completion: @escaping ArticleSetResultBlock) {
|
||||||
database.fetchArticlesMatchingAsync(searchString, flattenedWebFeeds().webFeedIDs(), completion)
|
database.fetchArticlesMatchingAsync(searchString, flattenedFeeds().webFeedIDs(), completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticlesMatchingWithArticleIDsAsync(_ searchString: String, _ articleIDs: Set<String>, _ completion: @escaping ArticleSetResultBlock) {
|
func fetchArticlesMatchingWithArticleIDsAsync(_ searchString: String, _ articleIDs: Set<String>, _ completion: @escaping ArticleSetResultBlock) {
|
||||||
@ -1121,20 +1121,20 @@ private extension Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchUnreadArticles(webFeed: Feed) throws -> Set<Article> {
|
func fetchUnreadArticles(webFeed: Feed) throws -> Set<Article> {
|
||||||
let articles = try database.fetchUnreadArticles(Set([webFeed.webFeedID]), nil)
|
let articles = try database.fetchUnreadArticles(Set([webFeed.feedID]), nil)
|
||||||
validateUnreadCount(webFeed, articles)
|
validateUnreadCount(webFeed, articles)
|
||||||
return articles
|
return articles
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticles(forContainer container: Container) throws -> Set<Article> {
|
func fetchArticles(forContainer container: Container) throws -> Set<Article> {
|
||||||
let feeds = container.flattenedWebFeeds()
|
let feeds = container.flattenedFeeds()
|
||||||
let articles = try database.fetchArticles(feeds.webFeedIDs())
|
let articles = try database.fetchArticles(feeds.webFeedIDs())
|
||||||
validateUnreadCountsAfterFetchingUnreadArticles(feeds, articles)
|
validateUnreadCountsAfterFetchingUnreadArticles(feeds, articles)
|
||||||
return articles
|
return articles
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchArticlesAsync(forContainer container: Container, _ completion: @escaping ArticleSetResultBlock) {
|
func fetchArticlesAsync(forContainer container: Container, _ completion: @escaping ArticleSetResultBlock) {
|
||||||
let webFeeds = container.flattenedWebFeeds()
|
let webFeeds = container.flattenedFeeds()
|
||||||
database.fetchArticlesAsync(webFeeds.webFeedIDs()) { [weak self] (articleSetResult) in
|
database.fetchArticlesAsync(webFeeds.webFeedIDs()) { [weak self] (articleSetResult) in
|
||||||
switch articleSetResult {
|
switch articleSetResult {
|
||||||
case .success(let articles):
|
case .success(let articles):
|
||||||
@ -1147,7 +1147,7 @@ private extension Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchUnreadArticles(forContainer container: Container, limit: Int?) throws -> Set<Article> {
|
func fetchUnreadArticles(forContainer container: Container, limit: Int?) throws -> Set<Article> {
|
||||||
let feeds = container.flattenedWebFeeds()
|
let feeds = container.flattenedFeeds()
|
||||||
let articles = try database.fetchUnreadArticles(feeds.webFeedIDs(), limit)
|
let articles = try database.fetchUnreadArticles(feeds.webFeedIDs(), limit)
|
||||||
|
|
||||||
// We don't validate limit queries because they, by definition, won't correctly match the
|
// We don't validate limit queries because they, by definition, won't correctly match the
|
||||||
@ -1160,7 +1160,7 @@ private extension Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchUnreadArticlesAsync(forContainer container: Container, limit: Int?, _ completion: @escaping ArticleSetResultBlock) {
|
func fetchUnreadArticlesAsync(forContainer container: Container, limit: Int?, _ completion: @escaping ArticleSetResultBlock) {
|
||||||
let webFeeds = container.flattenedWebFeeds()
|
let webFeeds = container.flattenedFeeds()
|
||||||
database.fetchUnreadArticlesAsync(webFeeds.webFeedIDs(), limit) { [weak self] (articleSetResult) in
|
database.fetchUnreadArticlesAsync(webFeeds.webFeedIDs(), limit) { [weak self] (articleSetResult) in
|
||||||
switch articleSetResult {
|
switch articleSetResult {
|
||||||
case .success(let articles):
|
case .success(let articles):
|
||||||
@ -1189,7 +1189,7 @@ private extension Account {
|
|||||||
unreadCountStorage[article.webFeedID, default: 0] += 1
|
unreadCountStorage[article.webFeedID, default: 0] += 1
|
||||||
}
|
}
|
||||||
webFeeds.forEach { (webFeed) in
|
webFeeds.forEach { (webFeed) in
|
||||||
let unreadCount = unreadCountStorage[webFeed.webFeedID, default: 0]
|
let unreadCount = unreadCountStorage[webFeed.feedID, default: 0]
|
||||||
webFeed.unreadCount = unreadCount
|
webFeed.unreadCount = unreadCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1213,12 +1213,12 @@ private extension Account {
|
|||||||
|
|
||||||
private extension Account {
|
private extension Account {
|
||||||
|
|
||||||
func webFeedMetadata(feedURL: String, webFeedID: String) -> WebFeedMetadata {
|
func webFeedMetadata(feedURL: String, feedID: String) -> WebFeedMetadata {
|
||||||
if let d = webFeedMetadata[feedURL] {
|
if let d = webFeedMetadata[feedURL] {
|
||||||
assert(d.delegate === self)
|
assert(d.delegate === self)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
let d = WebFeedMetadata(webFeedID: webFeedID)
|
let d = WebFeedMetadata(feedID: feedID)
|
||||||
d.delegate = self
|
d.delegate = self
|
||||||
webFeedMetadata[feedURL] = d
|
webFeedMetadata[feedURL] = d
|
||||||
return d
|
return d
|
||||||
@ -1226,9 +1226,9 @@ private extension Account {
|
|||||||
|
|
||||||
func updateFlattenedWebFeeds() {
|
func updateFlattenedWebFeeds() {
|
||||||
var feeds = Set<Feed>()
|
var feeds = Set<Feed>()
|
||||||
feeds.formUnion(topLevelWebFeeds)
|
feeds.formUnion(topLevelFeeds)
|
||||||
for folder in folders! {
|
for folder in folders! {
|
||||||
feeds.formUnion(folder.flattenedWebFeeds())
|
feeds.formUnion(folder.flattenedFeeds())
|
||||||
}
|
}
|
||||||
|
|
||||||
_flattenedWebFeeds = feeds
|
_flattenedWebFeeds = feeds
|
||||||
@ -1239,8 +1239,8 @@ private extension Account {
|
|||||||
var idDictionary = [String: Feed]()
|
var idDictionary = [String: Feed]()
|
||||||
var externalIDDictionary = [String: Feed]()
|
var externalIDDictionary = [String: Feed]()
|
||||||
|
|
||||||
flattenedWebFeeds().forEach { (feed) in
|
flattenedFeeds().forEach { (feed) in
|
||||||
idDictionary[feed.webFeedID] = feed
|
idDictionary[feed.feedID] = feed
|
||||||
if let externalID = feed.externalID {
|
if let externalID = feed.externalID {
|
||||||
externalIDDictionary[externalID] = feed
|
externalIDDictionary[externalID] = feed
|
||||||
}
|
}
|
||||||
@ -1256,7 +1256,7 @@ private extension Account {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var updatedUnreadCount = 0
|
var updatedUnreadCount = 0
|
||||||
for feed in flattenedWebFeeds() {
|
for feed in flattenedFeeds() {
|
||||||
updatedUnreadCount += feed.unreadCount
|
updatedUnreadCount += feed.unreadCount
|
||||||
}
|
}
|
||||||
unreadCount = updatedUnreadCount
|
unreadCount = updatedUnreadCount
|
||||||
@ -1304,7 +1304,7 @@ private extension Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchUnreadCount(_ feed: Feed, _ completion: VoidCompletionBlock?) {
|
func fetchUnreadCount(_ feed: Feed, _ completion: VoidCompletionBlock?) {
|
||||||
database.fetchUnreadCount(feed.webFeedID) { result in
|
database.fetchUnreadCount(feed.feedID) { result in
|
||||||
if let unreadCount = try? result.get() {
|
if let unreadCount = try? result.get() {
|
||||||
feed.unreadCount = unreadCount
|
feed.unreadCount = unreadCount
|
||||||
}
|
}
|
||||||
@ -1313,7 +1313,7 @@ private extension Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchUnreadCounts(_ feeds: Set<Feed>, _ completion: VoidCompletionBlock?) {
|
func fetchUnreadCounts(_ feeds: Set<Feed>, _ completion: VoidCompletionBlock?) {
|
||||||
let webFeedIDs = Set(feeds.map { $0.webFeedID })
|
let webFeedIDs = Set(feeds.map { $0.feedID })
|
||||||
database.fetchUnreadCounts(for: webFeedIDs) { result in
|
database.fetchUnreadCounts(for: webFeedIDs) { result in
|
||||||
if let unreadCountDictionary = try? result.get() {
|
if let unreadCountDictionary = try? result.get() {
|
||||||
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds)
|
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds)
|
||||||
@ -1329,7 +1329,7 @@ private extension Account {
|
|||||||
completion?()
|
completion?()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedWebFeeds())
|
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedFeeds())
|
||||||
|
|
||||||
self.fetchingAllUnreadCounts = false
|
self.fetchingAllUnreadCounts = false
|
||||||
self.updateUnreadCount()
|
self.updateUnreadCount()
|
||||||
@ -1345,7 +1345,7 @@ private extension Account {
|
|||||||
func processUnreadCounts(unreadCountDictionary: UnreadCountDictionary, feeds: Set<Feed>) {
|
func processUnreadCounts(unreadCountDictionary: UnreadCountDictionary, feeds: Set<Feed>) {
|
||||||
for feed in feeds {
|
for feed in feeds {
|
||||||
// When the unread count is zero, it won’t appear in unreadCountDictionary.
|
// When the unread count is zero, it won’t appear in unreadCountDictionary.
|
||||||
let unreadCount = unreadCountDictionary[feed.webFeedID] ?? 0
|
let unreadCount = unreadCountDictionary[feed.feedID] ?? 0
|
||||||
feed.unreadCount = unreadCount
|
feed.unreadCount = unreadCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1410,7 +1410,7 @@ extension Account: OPMLRepresentable {
|
|||||||
|
|
||||||
public func OPMLString(indentLevel: Int, allowCustomAttributes: Bool) -> String {
|
public func OPMLString(indentLevel: Int, allowCustomAttributes: Bool) -> String {
|
||||||
var s = ""
|
var s = ""
|
||||||
for feed in topLevelWebFeeds.sorted() {
|
for feed in topLevelFeeds.sorted() {
|
||||||
s += feed.OPMLString(indentLevel: indentLevel + 1, allowCustomAttributes: allowCustomAttributes)
|
s += feed.OPMLString(indentLevel: indentLevel + 1, allowCustomAttributes: allowCustomAttributes)
|
||||||
}
|
}
|
||||||
for folder in folders!.sorted() {
|
for folder in folders!.sorted() {
|
||||||
|
@ -344,7 +344,7 @@ public final class AccountManager: UnreadCountProvider {
|
|||||||
|
|
||||||
public func anyAccountHasFeedWithURL(_ urlString: String) -> Bool {
|
public func anyAccountHasFeedWithURL(_ urlString: String) -> Bool {
|
||||||
for account in activeAccounts {
|
for account in activeAccounts {
|
||||||
if let _ = account.existingWebFeed(withURL: urlString) {
|
if let _ = account.existingFeed(withURL: urlString) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -358,7 +358,7 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let feedsToRestore = folder.topLevelWebFeeds
|
let feedsToRestore = folder.topLevelFeeds
|
||||||
refreshProgress.addToNumberOfTasksAndRemaining(1 + feedsToRestore.count)
|
refreshProgress.addToNumberOfTasksAndRemaining(1 + feedsToRestore.count)
|
||||||
|
|
||||||
accountZone.createFolder(name: name) { result in
|
accountZone.createFolder(name: name) { result in
|
||||||
@ -371,7 +371,7 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
for feed in feedsToRestore {
|
for feed in feedsToRestore {
|
||||||
|
|
||||||
folder.topLevelWebFeeds.remove(feed)
|
folder.topLevelFeeds.remove(feed)
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
self.restoreWebFeed(for: account, feed: feed, container: folder) { result in
|
self.restoreWebFeed(for: account, feed: feed, container: folder) { result in
|
||||||
@ -485,7 +485,7 @@ private extension CloudKitAccountDelegate {
|
|||||||
accountZone.fetchChangesInZone() { result in
|
accountZone.fetchChangesInZone() { result in
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
|
|
||||||
let webFeeds = account.flattenedWebFeeds()
|
let webFeeds = account.flattenedFeeds()
|
||||||
self.refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
|
self.refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
@ -518,7 +518,7 @@ private extension CloudKitAccountDelegate {
|
|||||||
|
|
||||||
func standardRefreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
func standardRefreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
|
|
||||||
let intialWebFeedsCount = account.flattenedWebFeeds().count
|
let intialWebFeedsCount = account.flattenedFeeds().count
|
||||||
refreshProgress.addToNumberOfTasksAndRemaining(3 + intialWebFeedsCount)
|
refreshProgress.addToNumberOfTasksAndRemaining(3 + intialWebFeedsCount)
|
||||||
|
|
||||||
func fail(_ error: Error) {
|
func fail(_ error: Error) {
|
||||||
@ -532,7 +532,7 @@ private extension CloudKitAccountDelegate {
|
|||||||
case .success:
|
case .success:
|
||||||
|
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
let webFeeds = account.flattenedWebFeeds()
|
let webFeeds = account.flattenedFeeds()
|
||||||
self.refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count - intialWebFeedsCount)
|
self.refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count - intialWebFeedsCount)
|
||||||
|
|
||||||
self.refreshArticleStatus(for: account) { result in
|
self.refreshArticleStatus(for: account) { result in
|
||||||
@ -617,7 +617,7 @@ private extension CloudKitAccountDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.hasWebFeed(withURL: bestFeedSpecifier.urlString) {
|
if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
|
||||||
self.refreshProgress.completeTasks(4)
|
self.refreshProgress.completeTasks(4)
|
||||||
completion(.failure(AccountError.createErrorAlreadySubscribed))
|
completion(.failure(AccountError.createErrorAlreadySubscribed))
|
||||||
return
|
return
|
||||||
@ -706,7 +706,7 @@ private extension CloudKitAccountDelegate {
|
|||||||
|
|
||||||
func processAccountError(_ account: Account, _ error: Error) {
|
func processAccountError(_ account: Account, _ error: Error) {
|
||||||
if case CloudKitZoneError.userDeletedZone = error {
|
if case CloudKitZoneError.userDeletedZone = error {
|
||||||
account.removeFeeds(account.topLevelWebFeeds)
|
account.removeFeeds(account.topLevelFeeds)
|
||||||
for folder in account.folders ?? Set<Folder>() {
|
for folder in account.folders ?? Set<Folder>() {
|
||||||
account.removeFolder(folder)
|
account.removeFolder(folder)
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ private extension CloudKitSendStatusOperation {
|
|||||||
|
|
||||||
func processAccountError(_ account: Account, _ error: Error) {
|
func processAccountError(_ account: Account, _ error: Error) {
|
||||||
if case CloudKitZoneError.userDeletedZone = error {
|
if case CloudKitZoneError.userDeletedZone = error {
|
||||||
account.removeFeeds(account.topLevelWebFeeds)
|
account.removeFeeds(account.topLevelFeeds)
|
||||||
for folder in account.folders ?? Set<Folder>() {
|
for folder in account.folders ?? Set<Folder>() {
|
||||||
account.removeFolder(folder)
|
account.removeFolder(folder)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ extension Notification.Name {
|
|||||||
public protocol Container: AnyObject, ContainerIdentifiable {
|
public protocol Container: AnyObject, ContainerIdentifiable {
|
||||||
|
|
||||||
var account: Account? { get }
|
var account: Account? { get }
|
||||||
var topLevelWebFeeds: Set<Feed> { get set }
|
var topLevelFeeds: Set<Feed> { get set }
|
||||||
var folders: Set<Folder>? { get set }
|
var folders: Set<Folder>? { get set }
|
||||||
var externalID: String? { get set }
|
var externalID: String? { get set }
|
||||||
|
|
||||||
@ -33,12 +33,12 @@ public protocol Container: AnyObject, ContainerIdentifiable {
|
|||||||
func addWebFeed(_ webFeed: Feed)
|
func addWebFeed(_ webFeed: Feed)
|
||||||
|
|
||||||
//Recursive — checks subfolders
|
//Recursive — checks subfolders
|
||||||
func flattenedWebFeeds() -> Set<Feed>
|
func flattenedFeeds() -> Set<Feed>
|
||||||
func has(_ webFeed: Feed) -> Bool
|
func has(_ webFeed: Feed) -> Bool
|
||||||
func hasWebFeed(with webFeedID: String) -> Bool
|
func hasWebFeed(with webFeedID: String) -> Bool
|
||||||
func hasWebFeed(withURL url: String) -> Bool
|
func hasFeed(withURL url: String) -> Bool
|
||||||
func existingWebFeed(withWebFeedID: String) -> Feed?
|
func existingWebFeed(withWebFeedID: String) -> Feed?
|
||||||
func existingWebFeed(withURL url: String) -> Feed?
|
func existingFeed(withURL url: String) -> Feed?
|
||||||
func existingWebFeed(withExternalID externalID: String) -> Feed?
|
func existingWebFeed(withExternalID externalID: String) -> Feed?
|
||||||
func existingFolder(with name: String) -> Folder?
|
func existingFolder(with name: String) -> Folder?
|
||||||
func existingFolder(withID: Int) -> Folder?
|
func existingFolder(withID: Int) -> Folder?
|
||||||
@ -49,7 +49,7 @@ public protocol Container: AnyObject, ContainerIdentifiable {
|
|||||||
public extension Container {
|
public extension Container {
|
||||||
|
|
||||||
func hasAtLeastOneWebFeed() -> Bool {
|
func hasAtLeastOneWebFeed() -> Bool {
|
||||||
return topLevelWebFeeds.count > 0
|
return topLevelFeeds.count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasChildFolder(with name: String) -> Bool {
|
func hasChildFolder(with name: String) -> Bool {
|
||||||
@ -70,7 +70,7 @@ public extension Container {
|
|||||||
|
|
||||||
func objectIsChild(_ object: AnyObject) -> Bool {
|
func objectIsChild(_ object: AnyObject) -> Bool {
|
||||||
if let feed = object as? Feed {
|
if let feed = object as? Feed {
|
||||||
return topLevelWebFeeds.contains(feed)
|
return topLevelFeeds.contains(feed)
|
||||||
}
|
}
|
||||||
if let folder = object as? Folder {
|
if let folder = object as? Folder {
|
||||||
return folders?.contains(folder) ?? false
|
return folders?.contains(folder) ?? false
|
||||||
@ -78,12 +78,12 @@ public extension Container {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func flattenedWebFeeds() -> Set<Feed> {
|
func flattenedFeeds() -> Set<Feed> {
|
||||||
var feeds = Set<Feed>()
|
var feeds = Set<Feed>()
|
||||||
feeds.formUnion(topLevelWebFeeds)
|
feeds.formUnion(topLevelFeeds)
|
||||||
if let folders = folders {
|
if let folders = folders {
|
||||||
for folder in folders {
|
for folder in folders {
|
||||||
feeds.formUnion(folder.flattenedWebFeeds())
|
feeds.formUnion(folder.flattenedFeeds())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return feeds
|
return feeds
|
||||||
@ -93,25 +93,25 @@ public extension Container {
|
|||||||
return existingWebFeed(withWebFeedID: webFeedID) != nil
|
return existingWebFeed(withWebFeedID: webFeedID) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasWebFeed(withURL url: String) -> Bool {
|
func hasFeed(withURL url: String) -> Bool {
|
||||||
return existingWebFeed(withURL: url) != nil
|
return existingFeed(withURL: url) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func has(_ webFeed: Feed) -> Bool {
|
func has(_ feed: Feed) -> Bool {
|
||||||
return flattenedWebFeeds().contains(webFeed)
|
return flattenedFeeds().contains(feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func existingWebFeed(withWebFeedID webFeedID: String) -> Feed? {
|
func existingWebFeed(withWebFeedID webFeedID: String) -> Feed? {
|
||||||
for feed in flattenedWebFeeds() {
|
for feed in flattenedFeeds() {
|
||||||
if feed.webFeedID == webFeedID {
|
if feed.feedID == webFeedID {
|
||||||
return feed
|
return feed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func existingWebFeed(withURL url: String) -> Feed? {
|
func existingFeed(withURL url: String) -> Feed? {
|
||||||
for feed in flattenedWebFeeds() {
|
for feed in flattenedFeeds() {
|
||||||
if feed.url == url {
|
if feed.url == url {
|
||||||
return feed
|
return feed
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ public extension Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func existingWebFeed(withExternalID externalID: String) -> Feed? {
|
func existingWebFeed(withExternalID externalID: String) -> Feed? {
|
||||||
for feed in flattenedWebFeeds() {
|
for feed in flattenedFeeds() {
|
||||||
if feed.externalID == externalID {
|
if feed.externalID == externalID {
|
||||||
return feed
|
return feed
|
||||||
}
|
}
|
||||||
|
@ -22,18 +22,18 @@ public final class Feed: SidebarItem, Renamable, Hashable {
|
|||||||
assertionFailure("Expected feed.account, but got nil.")
|
assertionFailure("Expected feed.account, but got nil.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return SidebarItemIdentifier.webFeed(accountID, webFeedID)
|
return SidebarItemIdentifier.webFeed(accountID, feedID)
|
||||||
}
|
}
|
||||||
|
|
||||||
public weak var account: Account?
|
public weak var account: Account?
|
||||||
public let url: String
|
public let url: String
|
||||||
|
|
||||||
public var webFeedID: String {
|
public var feedID: String {
|
||||||
get {
|
get {
|
||||||
return metadata.webFeedID
|
return metadata.feedID
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
metadata.webFeedID = newValue
|
metadata.feedID = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,13 +264,13 @@ public final class Feed: SidebarItem, Renamable, Hashable {
|
|||||||
// MARK: - Hashable
|
// MARK: - Hashable
|
||||||
|
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
hasher.combine(webFeedID)
|
hasher.combine(feedID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Equatable
|
// MARK: - Equatable
|
||||||
|
|
||||||
public class func ==(lhs: Feed, rhs: Feed) -> Bool {
|
public class func ==(lhs: Feed, rhs: Feed) -> Bool {
|
||||||
return lhs.webFeedID == rhs.webFeedID && lhs.accountID == rhs.accountID
|
return lhs.feedID == rhs.feedID && lhs.accountID == rhs.accountID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +307,7 @@ extension Feed: OPMLRepresentable {
|
|||||||
extension Set where Element == Feed {
|
extension Set where Element == Feed {
|
||||||
|
|
||||||
func webFeedIDs() -> Set<String> {
|
func webFeedIDs() -> Set<String> {
|
||||||
return Set<String>(map { $0.webFeedID })
|
return Set<String>(map { $0.feedID })
|
||||||
}
|
}
|
||||||
|
|
||||||
func sorted() -> Array<Feed> {
|
func sorted() -> Array<Feed> {
|
||||||
|
@ -173,7 +173,7 @@ final class FeedWranglerAPICaller: NSObject {
|
|||||||
let queryItems = [
|
let queryItems = [
|
||||||
URLQueryItem(name: "read", value: "false"),
|
URLQueryItem(name: "read", value: "false"),
|
||||||
URLQueryItem(name: "offset", value: String(page * FeedWranglerConfig.pageSize)),
|
URLQueryItem(name: "offset", value: String(page * FeedWranglerConfig.pageSize)),
|
||||||
feed.map { URLQueryItem(name: "feed_id", value: $0.webFeedID) }
|
feed.map { URLQueryItem(name: "feed_id", value: $0.feedID) }
|
||||||
].compactMap { $0 }
|
].compactMap { $0 }
|
||||||
let url = FeedWranglerConfig.clientURL
|
let url = FeedWranglerConfig.clientURL
|
||||||
.appendingPathComponent("feed_items/list")
|
.appendingPathComponent("feed_items/list")
|
||||||
|
@ -340,7 +340,7 @@ final class FeedWranglerAccountDelegate: AccountDelegate {
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let feed = account.createWebFeed(with: sub.title, url: sub.feedURL, webFeedID: String(sub.feedID), homePageURL: sub.siteURL)
|
let feed = account.createWebFeed(with: sub.title, url: sub.feedURL, webFeedID: String(sub.feedID), homePageURL: sub.siteURL)
|
||||||
|
|
||||||
account.addWebFeed(feed, to: container) { result in
|
account.addFeed(feed, to: container) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
if let name = name {
|
if let name = name {
|
||||||
@ -388,7 +388,7 @@ final class FeedWranglerAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
self.refreshCredentials(for: account) {
|
self.refreshCredentials(for: account) {
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
self.caller.renameSubscription(feedID: feed.webFeedID, newName: name) { result in
|
self.caller.renameSubscription(feedID: feed.feedID, newName: name) { result in
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
@ -421,7 +421,7 @@ final class FeedWranglerAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
self.refreshCredentials(for: account) {
|
self.refreshCredentials(for: account) {
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
self.caller.removeSubscription(feedID: feed.webFeedID) { result in
|
self.caller.removeSubscription(feedID: feed.feedID) { result in
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
@ -447,8 +447,8 @@ final class FeedWranglerAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
if let existingFeed = account.existingWebFeed(withURL: feed.url) {
|
if let existingFeed = account.existingFeed(withURL: feed.url) {
|
||||||
account.addWebFeed(existingFeed, to: container) { result in
|
account.addFeed(existingFeed, to: container) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
@ -533,7 +533,7 @@ private extension FeedWranglerAccountDelegate {
|
|||||||
assert(Thread.isMainThread)
|
assert(Thread.isMainThread)
|
||||||
let feedIds = subscriptions.map { String($0.feedID) }
|
let feedIds = subscriptions.map { String($0.feedID) }
|
||||||
|
|
||||||
let feedsToRemove = account.topLevelWebFeeds.filter { !feedIds.contains($0.webFeedID) }
|
let feedsToRemove = account.topLevelFeeds.filter { !feedIds.contains($0.feedID) }
|
||||||
account.removeFeeds(feedsToRemove)
|
account.removeFeeds(feedsToRemove)
|
||||||
|
|
||||||
var subscriptionsToAdd = Set<FeedWranglerSubscription>()
|
var subscriptionsToAdd = Set<FeedWranglerSubscription>()
|
||||||
|
@ -336,7 +336,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
|
|
||||||
if feed.folderRelationship?.count ?? 0 > 1 {
|
if feed.folderRelationship?.count ?? 0 > 1 {
|
||||||
|
|
||||||
@ -472,7 +472,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
func addWebFeed(for account: Account, with feed: Feed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
func addWebFeed(for account: Account, with feed: Feed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
|
|
||||||
if let folder = container as? Folder, let webFeedID = Int(feed.webFeedID) {
|
if let folder = container as? Folder, let webFeedID = Int(feed.feedID) {
|
||||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||||
caller.createTagging(webFeedID: webFeedID, name: folder.name ?? "") { result in
|
caller.createTagging(webFeedID: webFeedID, name: folder.name ?? "") { result in
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
@ -504,8 +504,8 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
|
|
||||||
if let existingFeed = account.existingWebFeed(withURL: feed.url) {
|
if let existingFeed = account.existingFeed(withURL: feed.url) {
|
||||||
account.addWebFeed(existingFeed, to: container) { result in
|
account.addFeed(existingFeed, to: container) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
@ -530,9 +530,9 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
|
|
||||||
folder.topLevelWebFeeds.remove(feed)
|
folder.topLevelFeeds.remove(feed)
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
restoreWebFeed(for: account, feed: feed, container: folder) { result in
|
restoreWebFeed(for: account, feed: feed, container: folder) { result in
|
||||||
@ -780,7 +780,7 @@ private extension FeedbinAccountDelegate {
|
|||||||
if let folders = account.folders {
|
if let folders = account.folders {
|
||||||
folders.forEach { folder in
|
folders.forEach { folder in
|
||||||
if !tagNames.contains(folder.name ?? "") {
|
if !tagNames.contains(folder.name ?? "") {
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
account.addWebFeed(feed)
|
account.addWebFeed(feed)
|
||||||
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
||||||
}
|
}
|
||||||
@ -818,16 +818,16 @@ private extension FeedbinAccountDelegate {
|
|||||||
// Remove any feeds that are no longer in the subscriptions
|
// Remove any feeds that are no longer in the subscriptions
|
||||||
if let folders = account.folders {
|
if let folders = account.folders {
|
||||||
for folder in folders {
|
for folder in folders {
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
if !subFeedIds.contains(feed.webFeedID) {
|
if !subFeedIds.contains(feed.feedID) {
|
||||||
folder.removeWebFeed(feed)
|
folder.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for feed in account.topLevelWebFeeds {
|
for feed in account.topLevelFeeds {
|
||||||
if !subFeedIds.contains(feed.webFeedID) {
|
if !subFeedIds.contains(feed.feedID) {
|
||||||
account.removeWebFeed(feed)
|
account.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -888,8 +888,8 @@ private extension FeedbinAccountDelegate {
|
|||||||
let taggingFeedIDs = groupedTaggings.map { String($0.feedID) }
|
let taggingFeedIDs = groupedTaggings.map { String($0.feedID) }
|
||||||
|
|
||||||
// Move any feeds not in the folder to the account
|
// Move any feeds not in the folder to the account
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
if !taggingFeedIDs.contains(feed.webFeedID) {
|
if !taggingFeedIDs.contains(feed.feedID) {
|
||||||
folder.removeWebFeed(feed)
|
folder.removeWebFeed(feed)
|
||||||
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
||||||
account.addWebFeed(feed)
|
account.addWebFeed(feed)
|
||||||
@ -897,7 +897,7 @@ private extension FeedbinAccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add any feeds not in the folder
|
// Add any feeds not in the folder
|
||||||
let folderFeedIds = folder.topLevelWebFeeds.map { $0.webFeedID }
|
let folderFeedIds = folder.topLevelFeeds.map { $0.feedID }
|
||||||
|
|
||||||
for tagging in groupedTaggings {
|
for tagging in groupedTaggings {
|
||||||
let taggingFeedID = String(tagging.feedID)
|
let taggingFeedID = String(tagging.feedID)
|
||||||
@ -915,8 +915,8 @@ private extension FeedbinAccountDelegate {
|
|||||||
let taggedFeedIDs = Set(taggings.map { String($0.feedID) })
|
let taggedFeedIDs = Set(taggings.map { String($0.feedID) })
|
||||||
|
|
||||||
// Remove all feeds from the account container that have a tag
|
// Remove all feeds from the account container that have a tag
|
||||||
for feed in account.topLevelWebFeeds {
|
for feed in account.topLevelFeeds {
|
||||||
if taggedFeedIDs.contains(feed.webFeedID) {
|
if taggedFeedIDs.contains(feed.feedID) {
|
||||||
account.removeWebFeed(feed)
|
account.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -980,7 +980,7 @@ private extension FeedbinAccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func renameFolderRelationship(for account: Account, fromName: String, toName: String) {
|
func renameFolderRelationship(for account: Account, fromName: String, toName: String) {
|
||||||
for feed in account.flattenedWebFeeds() {
|
for feed in account.flattenedFeeds() {
|
||||||
if var folderRelationship = feed.folderRelationship {
|
if var folderRelationship = feed.folderRelationship {
|
||||||
let relationship = folderRelationship[fromName]
|
let relationship = folderRelationship[fromName]
|
||||||
folderRelationship[fromName] = nil
|
folderRelationship[fromName] = nil
|
||||||
@ -1034,7 +1034,7 @@ private extension FeedbinAccountDelegate {
|
|||||||
feed.iconURL = sub.jsonFeed?.icon
|
feed.iconURL = sub.jsonFeed?.icon
|
||||||
feed.faviconURL = sub.jsonFeed?.favicon
|
feed.faviconURL = sub.jsonFeed?.favicon
|
||||||
|
|
||||||
account.addWebFeed(feed, to: container) { result in
|
account.addFeed(feed, to: container) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
if let name = name {
|
if let name = name {
|
||||||
@ -1064,7 +1064,7 @@ private extension FeedbinAccountDelegate {
|
|||||||
refreshProgress.addToNumberOfTasksAndRemaining(4)
|
refreshProgress.addToNumberOfTasksAndRemaining(4)
|
||||||
|
|
||||||
// Download the initial articles
|
// Download the initial articles
|
||||||
self.caller.retrieveEntries(feedID: feed.webFeedID) { result in
|
self.caller.retrieveEntries(feedID: feed.feedID) { result in
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
|
@ -354,7 +354,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let feedId = FeedlyFeedResourceId(id: feed.webFeedID)
|
let feedId = FeedlyFeedResourceId(id: feed.feedID)
|
||||||
let editedNameBefore = feed.editedName
|
let editedNameBefore = feed.editedName
|
||||||
|
|
||||||
// Adding an existing feed updates it.
|
// Adding an existing feed updates it.
|
||||||
@ -381,7 +381,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
throw FeedlyAccountDelegateError.notLoggedIn
|
throw FeedlyAccountDelegateError.notLoggedIn
|
||||||
}
|
}
|
||||||
|
|
||||||
let resource = FeedlyFeedResourceId(id: feed.webFeedID)
|
let resource = FeedlyFeedResourceId(id: feed.feedID)
|
||||||
let addExistingFeed = try FeedlyAddExistingFeedOperation(account: account,
|
let addExistingFeed = try FeedlyAddExistingFeedOperation(account: account,
|
||||||
credentials: credentials,
|
credentials: credentials,
|
||||||
resource: resource,
|
resource: resource,
|
||||||
@ -412,7 +412,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
caller.removeFeed(feed.webFeedID, fromCollectionWith: collectionId) { result in
|
caller.removeFeed(feed.feedID, fromCollectionWith: collectionId) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
@ -459,8 +459,8 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
if let existingFeed = account.existingWebFeed(withURL: feed.url) {
|
if let existingFeed = account.existingFeed(withURL: feed.url) {
|
||||||
account.addWebFeed(existingFeed, to: container) { result in
|
account.addFeed(existingFeed, to: container) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
@ -483,9 +483,9 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
|
func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
|
|
||||||
folder.topLevelWebFeeds.remove(feed)
|
folder.topLevelFeeds.remove(feed)
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
restoreWebFeed(for: account, feed: feed, container: folder) { result in
|
restoreWebFeed(for: account, feed: feed, container: folder) { result in
|
||||||
|
@ -31,13 +31,13 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
|
|||||||
|
|
||||||
let feedsBefore = Set(pairs
|
let feedsBefore = Set(pairs
|
||||||
.map { $0.1 }
|
.map { $0.1 }
|
||||||
.flatMap { $0.topLevelWebFeeds })
|
.flatMap { $0.topLevelFeeds })
|
||||||
|
|
||||||
// Remove feeds in a folder which are not in the corresponding collection.
|
// Remove feeds in a folder which are not in the corresponding collection.
|
||||||
for (collectionFeeds, folder) in pairs {
|
for (collectionFeeds, folder) in pairs {
|
||||||
let feedsInFolder = folder.topLevelWebFeeds
|
let feedsInFolder = folder.topLevelFeeds
|
||||||
let feedsInCollection = Set(collectionFeeds.map { $0.id })
|
let feedsInCollection = Set(collectionFeeds.map { $0.id })
|
||||||
let feedsToRemove = feedsInFolder.filter { !feedsInCollection.contains($0.webFeedID) }
|
let feedsToRemove = feedsInFolder.filter { !feedsInCollection.contains($0.feedID) }
|
||||||
if !feedsToRemove.isEmpty {
|
if !feedsToRemove.isEmpty {
|
||||||
folder.removeFeeds(feedsToRemove)
|
folder.removeFeeds(feedsToRemove)
|
||||||
// os_log(.debug, log: log, "\"%@\" - removed: %@", collection.label, feedsToRemove.map { $0.feedID }, feedsInCollection)
|
// os_log(.debug, log: log, "\"%@\" - removed: %@", collection.label, feedsToRemove.map { $0.feedID }, feedsInCollection)
|
||||||
@ -76,7 +76,7 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
|
|||||||
return (feed, folder)
|
return (feed, folder)
|
||||||
} else {
|
} else {
|
||||||
// find an existing feed we created below in an earlier value
|
// find an existing feed we created below in an earlier value
|
||||||
for feed in feedsAdded where feed.webFeedID == collectionFeed.id {
|
for feed in feedsAdded where feed.feedID == collectionFeed.id {
|
||||||
return (feed, folder)
|
return (feed, folder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ public final class Folder: SidebarItem, Renamable, Container, Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public weak var account: Account?
|
public weak var account: Account?
|
||||||
public var topLevelWebFeeds: Set<Feed> = Set<Feed>()
|
public var topLevelFeeds: Set<Feed> = Set<Feed>()
|
||||||
public var folders: Set<Folder>? = nil // subfolders are not supported, so this is always nil
|
public var folders: Set<Folder>? = nil // subfolders are not supported, so this is always nil
|
||||||
|
|
||||||
public var name: String? {
|
public var name: String? {
|
||||||
@ -100,9 +100,9 @@ public final class Folder: SidebarItem, Renamable, Container, Hashable {
|
|||||||
|
|
||||||
// MARK: Container
|
// MARK: Container
|
||||||
|
|
||||||
public func flattenedWebFeeds() -> Set<Feed> {
|
public func flattenedFeeds() -> Set<Feed> {
|
||||||
// Since sub-folders are not supported, it’s always the top-level feeds.
|
// Since sub-folders are not supported, it’s always the top-level feeds.
|
||||||
return topLevelWebFeeds
|
return topLevelFeeds
|
||||||
}
|
}
|
||||||
|
|
||||||
public func objectIsChild(_ object: AnyObject) -> Bool {
|
public func objectIsChild(_ object: AnyObject) -> Bool {
|
||||||
@ -110,11 +110,11 @@ public final class Folder: SidebarItem, Renamable, Container, Hashable {
|
|||||||
guard let feed = object as? Feed else {
|
guard let feed = object as? Feed else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return topLevelWebFeeds.contains(feed)
|
return topLevelFeeds.contains(feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func addWebFeed(_ feed: Feed) {
|
public func addWebFeed(_ feed: Feed) {
|
||||||
topLevelWebFeeds.insert(feed)
|
topLevelFeeds.insert(feed)
|
||||||
postChildrenDidChangeNotification()
|
postChildrenDidChangeNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,12 +122,12 @@ public final class Folder: SidebarItem, Renamable, Container, Hashable {
|
|||||||
guard !feeds.isEmpty else {
|
guard !feeds.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
topLevelWebFeeds.formUnion(feeds)
|
topLevelFeeds.formUnion(feeds)
|
||||||
postChildrenDidChangeNotification()
|
postChildrenDidChangeNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func removeWebFeed(_ feed: Feed) {
|
public func removeWebFeed(_ feed: Feed) {
|
||||||
topLevelWebFeeds.remove(feed)
|
topLevelFeeds.remove(feed)
|
||||||
postChildrenDidChangeNotification()
|
postChildrenDidChangeNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ public final class Folder: SidebarItem, Renamable, Container, Hashable {
|
|||||||
guard !feeds.isEmpty else {
|
guard !feeds.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
topLevelWebFeeds.subtract(feeds)
|
topLevelFeeds.subtract(feeds)
|
||||||
postChildrenDidChangeNotification()
|
postChildrenDidChangeNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,14 +158,14 @@ private extension Folder {
|
|||||||
|
|
||||||
func updateUnreadCount() {
|
func updateUnreadCount() {
|
||||||
var updatedUnreadCount = 0
|
var updatedUnreadCount = 0
|
||||||
for feed in topLevelWebFeeds {
|
for feed in topLevelFeeds {
|
||||||
updatedUnreadCount += feed.unreadCount
|
updatedUnreadCount += feed.unreadCount
|
||||||
}
|
}
|
||||||
unreadCount = updatedUnreadCount
|
unreadCount = updatedUnreadCount
|
||||||
}
|
}
|
||||||
|
|
||||||
func childrenContain(_ feed: Feed) -> Bool {
|
func childrenContain(_ feed: Feed) -> Bool {
|
||||||
return topLevelWebFeeds.contains(feed)
|
return topLevelFeeds.contains(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +189,7 @@ extension Folder: OPMLRepresentable {
|
|||||||
|
|
||||||
var hasAtLeastOneChild = false
|
var hasAtLeastOneChild = false
|
||||||
|
|
||||||
for feed in topLevelWebFeeds.sorted() {
|
for feed in topLevelFeeds.sorted() {
|
||||||
s += feed.OPMLString(indentLevel: indentLevel + 1, allowCustomAttributes: allowCustomAttributes)
|
s += feed.OPMLString(indentLevel: indentLevel + 1, allowCustomAttributes: allowCustomAttributes)
|
||||||
hasAtLeastOneChild = true
|
hasAtLeastOneChild = true
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ final class LocalAccountDelegate: AccountDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let webFeeds = account.flattenedWebFeeds()
|
let webFeeds = account.flattenedFeeds()
|
||||||
refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
|
refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
@ -250,7 +250,7 @@ private extension LocalAccountDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.hasWebFeed(withURL: bestFeedSpecifier.urlString) {
|
if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
BatchUpdate.shared.end()
|
BatchUpdate.shared.end()
|
||||||
completion(.failure(AccountError.createErrorAlreadySubscribed))
|
completion(.failure(AccountError.createErrorAlreadySubscribed))
|
||||||
|
@ -47,7 +47,7 @@ extension NewsBlurAccountDelegate {
|
|||||||
if let folders = account.folders {
|
if let folders = account.folders {
|
||||||
folders.forEach { folder in
|
folders.forEach { folder in
|
||||||
if !folderNames.contains(folder.name ?? "") {
|
if !folderNames.contains(folder.name ?? "") {
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
account.addWebFeed(feed)
|
account.addWebFeed(feed)
|
||||||
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
||||||
}
|
}
|
||||||
@ -84,16 +84,16 @@ extension NewsBlurAccountDelegate {
|
|||||||
// Remove any feeds that are no longer in the subscriptions
|
// Remove any feeds that are no longer in the subscriptions
|
||||||
if let folders = account.folders {
|
if let folders = account.folders {
|
||||||
for folder in folders {
|
for folder in folders {
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
if !newsBlurFeedIds.contains(feed.webFeedID) {
|
if !newsBlurFeedIds.contains(feed.feedID) {
|
||||||
folder.removeWebFeed(feed)
|
folder.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for feed in account.topLevelWebFeeds {
|
for feed in account.topLevelFeeds {
|
||||||
if !newsBlurFeedIds.contains(feed.webFeedID) {
|
if !newsBlurFeedIds.contains(feed.feedID) {
|
||||||
account.removeWebFeed(feed)
|
account.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,8 +155,8 @@ extension NewsBlurAccountDelegate {
|
|||||||
guard let folder = folderDict[folderName] else { return }
|
guard let folder = folderDict[folderName] else { return }
|
||||||
|
|
||||||
// Move any feeds not in the folder to the account
|
// Move any feeds not in the folder to the account
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
if !newsBlurFolderFeedIDs.contains(feed.webFeedID) {
|
if !newsBlurFolderFeedIDs.contains(feed.feedID) {
|
||||||
folder.removeWebFeed(feed)
|
folder.removeWebFeed(feed)
|
||||||
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
||||||
account.addWebFeed(feed)
|
account.addWebFeed(feed)
|
||||||
@ -164,7 +164,7 @@ extension NewsBlurAccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add any feeds not in the folder
|
// Add any feeds not in the folder
|
||||||
let folderFeedIds = folder.topLevelWebFeeds.map { $0.webFeedID }
|
let folderFeedIds = folder.topLevelFeeds.map { $0.feedID }
|
||||||
|
|
||||||
for relationship in folderRelationships {
|
for relationship in folderRelationships {
|
||||||
let folderFeedID = String(relationship.feedID)
|
let folderFeedID = String(relationship.feedID)
|
||||||
@ -182,13 +182,13 @@ extension NewsBlurAccountDelegate {
|
|||||||
// in folders and we need to remove them all from the account level.
|
// in folders and we need to remove them all from the account level.
|
||||||
if let folderRelationships = newsBlurFolderDict[" "] {
|
if let folderRelationships = newsBlurFolderDict[" "] {
|
||||||
let newsBlurFolderFeedIDs = folderRelationships.map { String($0.feedID) }
|
let newsBlurFolderFeedIDs = folderRelationships.map { String($0.feedID) }
|
||||||
for feed in account.topLevelWebFeeds {
|
for feed in account.topLevelFeeds {
|
||||||
if !newsBlurFolderFeedIDs.contains(feed.webFeedID) {
|
if !newsBlurFolderFeedIDs.contains(feed.feedID) {
|
||||||
account.removeWebFeed(feed)
|
account.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for feed in account.topLevelWebFeeds {
|
for feed in account.topLevelFeeds {
|
||||||
account.removeWebFeed(feed)
|
account.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -423,7 +423,7 @@ extension NewsBlurAccountDelegate {
|
|||||||
webFeed.externalID = String(feed.feedID)
|
webFeed.externalID = String(feed.feedID)
|
||||||
webFeed.faviconURL = feed.faviconURL
|
webFeed.faviconURL = feed.faviconURL
|
||||||
|
|
||||||
account.addWebFeed(webFeed, to: container) { result in
|
account.addFeed(webFeed, to: container) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
if let name = name {
|
if let name = name {
|
||||||
@ -448,7 +448,7 @@ extension NewsBlurAccountDelegate {
|
|||||||
func downloadFeed(account: Account, feed: Feed, page: Int, completion: @escaping (Result<Void, Error>) -> Void) {
|
func downloadFeed(account: Account, feed: Feed, page: Int, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||||
|
|
||||||
caller.retrieveStories(feedID: feed.webFeedID, page: page) { result in
|
caller.retrieveStories(feedID: feed.feedID, page: page) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success((let stories, _)):
|
case .success((let stories, _)):
|
||||||
// No more stories
|
// No more stories
|
||||||
@ -529,7 +529,7 @@ extension NewsBlurAccountDelegate {
|
|||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let feedID = feed.webFeedID
|
let feedID = feed.feedID
|
||||||
|
|
||||||
if folderName == nil {
|
if folderName == nil {
|
||||||
account.removeWebFeed(feed)
|
account.removeWebFeed(feed)
|
||||||
|
@ -400,7 +400,7 @@ final class NewsBlurAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var feedIDs: [String] = []
|
var feedIDs: [String] = []
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
if (feed.folderRelationship?.count ?? 0) > 1 {
|
if (feed.folderRelationship?.count ?? 0) > 1 {
|
||||||
clearFolderRelationship(for: feed, withFolderName: folderToRemove)
|
clearFolderRelationship(for: feed, withFolderName: folderToRemove)
|
||||||
} else if let feedID = feed.externalID {
|
} else if let feedID = feed.externalID {
|
||||||
@ -520,8 +520,8 @@ final class NewsBlurAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> ()) {
|
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> ()) {
|
||||||
if let existingFeed = account.existingWebFeed(withURL: feed.url) {
|
if let existingFeed = account.existingFeed(withURL: feed.url) {
|
||||||
account.addWebFeed(existingFeed, to: container) { result in
|
account.addFeed(existingFeed, to: container) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
@ -548,9 +548,9 @@ final class NewsBlurAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var feedsToRestore: [Feed] = []
|
var feedsToRestore: [Feed] = []
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
feedsToRestore.append(feed)
|
feedsToRestore.append(feed)
|
||||||
folder.topLevelWebFeeds.remove(feed)
|
folder.topLevelFeeds.remove(feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
@ -326,7 +326,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
|
|
||||||
if feed.folderRelationship?.count ?? 0 > 1 {
|
if feed.folderRelationship?.count ?? 0 > 1 {
|
||||||
|
|
||||||
@ -556,8 +556,8 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
func restoreWebFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
|
|
||||||
if let existingFeed = account.existingWebFeed(withURL: feed.url) {
|
if let existingFeed = account.existingFeed(withURL: feed.url) {
|
||||||
account.addWebFeed(existingFeed, to: container) { result in
|
account.addFeed(existingFeed, to: container) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
@ -582,9 +582,9 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
|
|
||||||
folder.topLevelWebFeeds.remove(feed)
|
folder.topLevelFeeds.remove(feed)
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
restoreWebFeed(for: account, feed: feed, container: folder) { result in
|
restoreWebFeed(for: account, feed: feed, container: folder) { result in
|
||||||
@ -719,7 +719,7 @@ private extension ReaderAPIAccountDelegate {
|
|||||||
if let folders = account.folders {
|
if let folders = account.folders {
|
||||||
folders.forEach { folder in
|
folders.forEach { folder in
|
||||||
if !readerFolderExternalIDs.contains(folder.externalID ?? "") {
|
if !readerFolderExternalIDs.contains(folder.externalID ?? "") {
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
account.addWebFeed(feed)
|
account.addWebFeed(feed)
|
||||||
clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
|
clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
|
||||||
}
|
}
|
||||||
@ -758,16 +758,16 @@ private extension ReaderAPIAccountDelegate {
|
|||||||
// Remove any feeds that are no longer in the subscriptions
|
// Remove any feeds that are no longer in the subscriptions
|
||||||
if let folders = account.folders {
|
if let folders = account.folders {
|
||||||
for folder in folders {
|
for folder in folders {
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
if !subFeedIds.contains(feed.webFeedID) {
|
if !subFeedIds.contains(feed.feedID) {
|
||||||
folder.removeWebFeed(feed)
|
folder.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for feed in account.topLevelWebFeeds {
|
for feed in account.topLevelFeeds {
|
||||||
if !subFeedIds.contains(feed.webFeedID) {
|
if !subFeedIds.contains(feed.feedID) {
|
||||||
account.clearWebFeedMetadata(feed)
|
account.clearWebFeedMetadata(feed)
|
||||||
account.removeWebFeed(feed)
|
account.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
@ -818,8 +818,8 @@ private extension ReaderAPIAccountDelegate {
|
|||||||
let taggingFeedIDs = groupedTaggings.map { $0.feedID }
|
let taggingFeedIDs = groupedTaggings.map { $0.feedID }
|
||||||
|
|
||||||
// Move any feeds not in the folder to the account
|
// Move any feeds not in the folder to the account
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
if !taggingFeedIDs.contains(feed.webFeedID) {
|
if !taggingFeedIDs.contains(feed.feedID) {
|
||||||
folder.removeWebFeed(feed)
|
folder.removeWebFeed(feed)
|
||||||
clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
|
clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
|
||||||
account.addWebFeed(feed)
|
account.addWebFeed(feed)
|
||||||
@ -827,7 +827,7 @@ private extension ReaderAPIAccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add any feeds not in the folder
|
// Add any feeds not in the folder
|
||||||
let folderFeedIds = folder.topLevelWebFeeds.map { $0.webFeedID }
|
let folderFeedIds = folder.topLevelFeeds.map { $0.feedID }
|
||||||
|
|
||||||
for subscription in groupedTaggings {
|
for subscription in groupedTaggings {
|
||||||
let taggingFeedID = subscription.feedID
|
let taggingFeedID = subscription.feedID
|
||||||
@ -845,8 +845,8 @@ private extension ReaderAPIAccountDelegate {
|
|||||||
let taggedFeedIDs = Set(subscriptions.filter({ !$0.categories.isEmpty }).map { String($0.feedID) })
|
let taggedFeedIDs = Set(subscriptions.filter({ !$0.categories.isEmpty }).map { String($0.feedID) })
|
||||||
|
|
||||||
// Remove all feeds from the account container that have a tag
|
// Remove all feeds from the account container that have a tag
|
||||||
for feed in account.topLevelWebFeeds {
|
for feed in account.topLevelFeeds {
|
||||||
if taggedFeedIDs.contains(feed.webFeedID) {
|
if taggedFeedIDs.contains(feed.feedID) {
|
||||||
account.removeWebFeed(feed)
|
account.removeWebFeed(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -924,7 +924,7 @@ private extension ReaderAPIAccountDelegate {
|
|||||||
let feed = account.createWebFeed(with: sub.name, url: sub.url, webFeedID: String(sub.feedID), homePageURL: sub.homePageURL)
|
let feed = account.createWebFeed(with: sub.name, url: sub.url, webFeedID: String(sub.feedID), homePageURL: sub.homePageURL)
|
||||||
feed.externalID = String(sub.feedID)
|
feed.externalID = String(sub.feedID)
|
||||||
|
|
||||||
account.addWebFeed(feed, to: container) { result in
|
account.addFeed(feed, to: container) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
if let name = name {
|
if let name = name {
|
||||||
@ -952,7 +952,7 @@ private extension ReaderAPIAccountDelegate {
|
|||||||
refreshProgress.addToNumberOfTasksAndRemaining(5)
|
refreshProgress.addToNumberOfTasksAndRemaining(5)
|
||||||
|
|
||||||
// Download the initial articles
|
// Download the initial articles
|
||||||
self.caller.retrieveItemIDs(type: .allForFeed, webFeedID: feed.webFeedID) { result in
|
self.caller.retrieveItemIDs(type: .allForFeed, webFeedID: feed.feedID) { result in
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let articleIDs):
|
case .success(let articleIDs):
|
||||||
|
@ -17,7 +17,7 @@ protocol WebFeedMetadataDelegate: AnyObject {
|
|||||||
final class WebFeedMetadata: Codable {
|
final class WebFeedMetadata: Codable {
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case webFeedID = "feedID"
|
case feedID
|
||||||
case homePageURL
|
case homePageURL
|
||||||
case iconURL
|
case iconURL
|
||||||
case faviconURL
|
case faviconURL
|
||||||
@ -32,10 +32,10 @@ final class WebFeedMetadata: Codable {
|
|||||||
case folderRelationship
|
case folderRelationship
|
||||||
}
|
}
|
||||||
|
|
||||||
var webFeedID: String {
|
var feedID: String {
|
||||||
didSet {
|
didSet {
|
||||||
if webFeedID != oldValue {
|
if feedID != oldValue {
|
||||||
valueDidChange(.webFeedID)
|
valueDidChange(.feedID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,8 +139,8 @@ final class WebFeedMetadata: Codable {
|
|||||||
|
|
||||||
weak var delegate: WebFeedMetadataDelegate?
|
weak var delegate: WebFeedMetadataDelegate?
|
||||||
|
|
||||||
init(webFeedID: String) {
|
init(feedID: String) {
|
||||||
self.webFeedID = webFeedID
|
self.feedID = feedID
|
||||||
}
|
}
|
||||||
|
|
||||||
func valueDidChange(_ key: CodingKeys) {
|
func valueDidChange(_ key: CodingKeys) {
|
||||||
|
@ -75,7 +75,7 @@ private extension WebFeedMetadataFile {
|
|||||||
private func metadataForOnlySubscribedToFeeds() -> Account.WebFeedMetadataDictionary {
|
private func metadataForOnlySubscribedToFeeds() -> Account.WebFeedMetadataDictionary {
|
||||||
let webFeedIDs = account.idToWebFeedDictionary.keys
|
let webFeedIDs = account.idToWebFeedDictionary.keys
|
||||||
return account.webFeedMetadata.filter { (feedID: String, metadata: WebFeedMetadata) -> Bool in
|
return account.webFeedMetadata.filter { (feedID: String, metadata: WebFeedMetadata) -> Bool in
|
||||||
return webFeedIDs.contains(metadata.webFeedID)
|
return webFeedIDs.contains(metadata.feedID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,12 +56,12 @@ class AddFeedController: AddFeedWindowControllerDelegate {
|
|||||||
}
|
}
|
||||||
let account = accountAndFolderSpecifier.account
|
let account = accountAndFolderSpecifier.account
|
||||||
|
|
||||||
if account.hasWebFeed(withURL: url.absoluteString) {
|
if account.hasFeed(withURL: url.absoluteString) {
|
||||||
showAlreadySubscribedError(url.absoluteString)
|
showAlreadySubscribedError(url.absoluteString)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
account.createWebFeed(url: url.absoluteString, name: title, container: container, validateFeed: true) { result in
|
account.createFeed(url: url.absoluteString, name: title, container: container, validateFeed: true) { result in
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.endShowingProgress()
|
self.endShowingProgress()
|
||||||
@ -69,7 +69,7 @@ class AddFeedController: AddFeedWindowControllerDelegate {
|
|||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let feed):
|
case .success(let feed):
|
||||||
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.webFeed: feed])
|
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
switch error {
|
switch error {
|
||||||
case AccountError.createErrorAlreadySubscribed:
|
case AccountError.createErrorAlreadySubscribed:
|
||||||
|
@ -70,11 +70,11 @@ struct PasteboardFeed: Hashable {
|
|||||||
|
|
||||||
init?(pasteboardItem: NSPasteboardItem) {
|
init?(pasteboardItem: NSPasteboardItem) {
|
||||||
var pasteboardType: NSPasteboard.PasteboardType?
|
var pasteboardType: NSPasteboard.PasteboardType?
|
||||||
if pasteboardItem.types.contains(WebFeedPasteboardWriter.webFeedUTIInternalType) {
|
if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIInternalType) {
|
||||||
pasteboardType = WebFeedPasteboardWriter.webFeedUTIInternalType
|
pasteboardType = FeedPasteboardWriter.feedUTIInternalType
|
||||||
}
|
}
|
||||||
else if pasteboardItem.types.contains(WebFeedPasteboardWriter.webFeedUTIType) {
|
else if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIType) {
|
||||||
pasteboardType = WebFeedPasteboardWriter.webFeedUTIType
|
pasteboardType = FeedPasteboardWriter.feedUTIType
|
||||||
}
|
}
|
||||||
if let foundType = pasteboardType {
|
if let foundType = pasteboardType {
|
||||||
if let feedDictionary = pasteboardItem.propertyList(forType: foundType) as? PasteboardFeedDictionary {
|
if let feedDictionary = pasteboardItem.propertyList(forType: foundType) as? PasteboardFeedDictionary {
|
||||||
@ -107,8 +107,8 @@ struct PasteboardFeed: Hashable {
|
|||||||
guard let items = pasteboard.pasteboardItems else {
|
guard let items = pasteboard.pasteboardItems else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let webFeeds = items.compactMap { PasteboardFeed(pasteboardItem: $0) }
|
let feeds = items.compactMap { PasteboardFeed(pasteboardItem: $0) }
|
||||||
return webFeeds.isEmpty ? nil : Set(webFeeds)
|
return feeds.isEmpty ? nil : Set(feeds)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Writing
|
// MARK: - Writing
|
||||||
@ -149,28 +149,28 @@ struct PasteboardFeed: Hashable {
|
|||||||
extension Feed: @retroactive PasteboardWriterOwner {
|
extension Feed: @retroactive PasteboardWriterOwner {
|
||||||
|
|
||||||
public var pasteboardWriter: NSPasteboardWriting {
|
public var pasteboardWriter: NSPasteboardWriting {
|
||||||
return WebFeedPasteboardWriter(webFeed: self)
|
return FeedPasteboardWriter(feed: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc final class WebFeedPasteboardWriter: NSObject, NSPasteboardWriting {
|
@objc final class FeedPasteboardWriter: NSObject, NSPasteboardWriting {
|
||||||
|
|
||||||
private let webFeed: Feed
|
private let feed: Feed
|
||||||
static let webFeedUTI = "com.ranchero.webFeed"
|
static let feedUTI = "com.ranchero.feed"
|
||||||
static let webFeedUTIType = NSPasteboard.PasteboardType(rawValue: webFeedUTI)
|
static let feedUTIType = NSPasteboard.PasteboardType(rawValue: feedUTI)
|
||||||
static let webFeedUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.webFeed"
|
static let feedUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.feed"
|
||||||
static let webFeedUTIInternalType = NSPasteboard.PasteboardType(rawValue: webFeedUTIInternal)
|
static let feedUTIInternalType = NSPasteboard.PasteboardType(rawValue: feedUTIInternal)
|
||||||
|
|
||||||
|
|
||||||
init(webFeed: Feed) {
|
init(feed: Feed) {
|
||||||
self.webFeed = webFeed
|
self.feed = feed
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - NSPasteboardWriting
|
// MARK: - NSPasteboardWriting
|
||||||
|
|
||||||
func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
|
func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
|
||||||
|
|
||||||
return [WebFeedPasteboardWriter.webFeedUTIType, .URL, .string, WebFeedPasteboardWriter.webFeedUTIInternalType]
|
return [FeedPasteboardWriter.feedUTIType, .URL, .string, FeedPasteboardWriter.feedUTIInternalType]
|
||||||
}
|
}
|
||||||
|
|
||||||
func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
|
func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
|
||||||
@ -179,12 +179,12 @@ extension Feed: @retroactive PasteboardWriterOwner {
|
|||||||
|
|
||||||
switch type {
|
switch type {
|
||||||
case .string:
|
case .string:
|
||||||
plist = webFeed.nameForDisplay
|
plist = feed.nameForDisplay
|
||||||
case .URL:
|
case .URL:
|
||||||
plist = webFeed.url
|
plist = feed.url
|
||||||
case WebFeedPasteboardWriter.webFeedUTIType:
|
case FeedPasteboardWriter.feedUTIType:
|
||||||
plist = exportDictionary
|
plist = exportDictionary
|
||||||
case WebFeedPasteboardWriter.webFeedUTIInternalType:
|
case FeedPasteboardWriter.feedUTIInternalType:
|
||||||
plist = internalDictionary
|
plist = internalDictionary
|
||||||
default:
|
default:
|
||||||
plist = nil
|
plist = nil
|
||||||
@ -194,10 +194,10 @@ extension Feed: @retroactive PasteboardWriterOwner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension WebFeedPasteboardWriter {
|
private extension FeedPasteboardWriter {
|
||||||
|
|
||||||
var pasteboardFeed: PasteboardFeed {
|
var pasteboardFeed: PasteboardFeed {
|
||||||
return PasteboardFeed(url: webFeed.url, feedID: webFeed.webFeedID, homePageURL: webFeed.homePageURL, name: webFeed.name, editedName: webFeed.editedName, accountID: webFeed.account?.accountID, accountType: webFeed.account?.type)
|
return PasteboardFeed(url: feed.url, feedID: feed.feedID, homePageURL: feed.homePageURL, name: feed.name, editedName: feed.editedName, accountID: feed.account?.accountID, accountType: feed.account?.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
var exportDictionary: PasteboardFeedDictionary {
|
var exportDictionary: PasteboardFeedDictionary {
|
||||||
|
@ -308,12 +308,12 @@ private extension SidebarOutlineDataSource {
|
|||||||
return localDragOperation(parentNode: parentNode)
|
return localDragOperation(parentNode: parentNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyWebFeedInAccount(node: Node, to parentNode: Node) {
|
func copyFeedInAccount(node: Node, to parentNode: Node) {
|
||||||
guard let feed = node.representedObject as? Feed, let destination = parentNode.representedObject as? Container else {
|
guard let feed = node.representedObject as? Feed, let destination = parentNode.representedObject as? Container else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
destination.account?.addWebFeed(feed, to: destination) { result in
|
destination.account?.addFeed(feed, to: destination) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
break
|
break
|
||||||
@ -323,7 +323,7 @@ private extension SidebarOutlineDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func moveWebFeedInAccount(node: Node, to parentNode: Node) {
|
func moveFeedInAccount(node: Node, to parentNode: Node) {
|
||||||
guard let feed = node.representedObject as? Feed,
|
guard let feed = node.representedObject as? Feed,
|
||||||
let source = node.parent?.representedObject as? Container,
|
let source = node.parent?.representedObject as? Container,
|
||||||
let destination = parentNode.representedObject as? Container else {
|
let destination = parentNode.representedObject as? Container else {
|
||||||
@ -331,7 +331,7 @@ private extension SidebarOutlineDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BatchUpdate.shared.start()
|
BatchUpdate.shared.start()
|
||||||
source.account?.moveWebFeed(feed, from: source, to: destination) { result in
|
source.account?.moveFeed(feed, from: source, to: destination) { result in
|
||||||
BatchUpdate.shared.end()
|
BatchUpdate.shared.end()
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
@ -342,15 +342,15 @@ private extension SidebarOutlineDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyWebFeedBetweenAccounts(node: Node, to parentNode: Node) {
|
func copyFeedBetweenAccounts(node: Node, to parentNode: Node) {
|
||||||
guard let feed = node.representedObject as? Feed,
|
guard let feed = node.representedObject as? Feed,
|
||||||
let destinationAccount = nodeAccount(parentNode),
|
let destinationAccount = nodeAccount(parentNode),
|
||||||
let destinationContainer = parentNode.representedObject as? Container else {
|
let destinationContainer = parentNode.representedObject as? Container else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let existingFeed = destinationAccount.existingWebFeed(withURL: feed.url) {
|
if let existingFeed = destinationAccount.existingFeed(withURL: feed.url) {
|
||||||
destinationAccount.addWebFeed(existingFeed, to: destinationContainer) { result in
|
destinationAccount.addFeed(existingFeed, to: destinationContainer) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
break
|
break
|
||||||
@ -359,7 +359,7 @@ private extension SidebarOutlineDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
destinationAccount.createWebFeed(url: feed.url, name: feed.nameForDisplay, container: destinationContainer, validateFeed: false) { result in
|
destinationAccount.createFeed(url: feed.url, name: feed.nameForDisplay, container: destinationContainer, validateFeed: false) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
break
|
break
|
||||||
@ -378,12 +378,12 @@ private extension SidebarOutlineDataSource {
|
|||||||
draggedNodes.forEach { node in
|
draggedNodes.forEach { node in
|
||||||
if sameAccount(node, parentNode) {
|
if sameAccount(node, parentNode) {
|
||||||
if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
|
if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
|
||||||
copyWebFeedInAccount(node: node, to: parentNode)
|
copyFeedInAccount(node: node, to: parentNode)
|
||||||
} else {
|
} else {
|
||||||
moveWebFeedInAccount(node: node, to: parentNode)
|
moveFeedInAccount(node: node, to: parentNode)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
copyWebFeedBetweenAccounts(node: node, to: parentNode)
|
copyFeedBetweenAccounts(node: node, to: parentNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,9 +431,9 @@ private extension SidebarOutlineDataSource {
|
|||||||
destinationAccount.addFolder(folder.name ?? "") { result in
|
destinationAccount.addFolder(folder.name ?? "") { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let destinationFolder):
|
case .success(let destinationFolder):
|
||||||
for feed in folder.topLevelWebFeeds {
|
for feed in folder.topLevelFeeds {
|
||||||
if let existingFeed = destinationAccount.existingWebFeed(withURL: feed.url) {
|
if let existingFeed = destinationAccount.existingFeed(withURL: feed.url) {
|
||||||
destinationAccount.addWebFeed(existingFeed, to: destinationFolder) { result in
|
destinationAccount.addFeed(existingFeed, to: destinationFolder) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
break
|
break
|
||||||
@ -442,7 +442,7 @@ private extension SidebarOutlineDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
destinationAccount.createWebFeed(url: feed.url, name: feed.nameForDisplay, container: destinationFolder, validateFeed: false) { result in
|
destinationAccount.createFeed(url: feed.url, name: feed.nameForDisplay, container: destinationFolder, validateFeed: false) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
break
|
break
|
||||||
@ -520,8 +520,8 @@ private extension SidebarOutlineDataSource {
|
|||||||
return account
|
return account
|
||||||
} else if let folder = node.representedObject as? Folder {
|
} else if let folder = node.representedObject as? Folder {
|
||||||
return folder.account
|
return folder.account
|
||||||
} else if let webFeed = node.representedObject as? Feed {
|
} else if let feed = node.representedObject as? Feed {
|
||||||
return webFeed.account
|
return feed.account
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -602,7 +602,7 @@ private extension SidebarOutlineDataSource {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if dropTargetAccount.hasWebFeed(withURL: draggedFeed.url) {
|
if dropTargetAccount.hasFeed(withURL: draggedFeed.url) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ protocol SidebarDelegate: AnyObject {
|
|||||||
weak var delegate: SidebarDelegate?
|
weak var delegate: SidebarDelegate?
|
||||||
|
|
||||||
private let rebuildTreeAndRestoreSelectionQueue = CoalescingQueue(name: "Rebuild Tree Queue", interval: 1.0)
|
private let rebuildTreeAndRestoreSelectionQueue = CoalescingQueue(name: "Rebuild Tree Queue", interval: 1.0)
|
||||||
let treeControllerDelegate = WebFeedTreeControllerDelegate()
|
let treeControllerDelegate = FeedTreeControllerDelegate()
|
||||||
lazy var treeController: TreeController = {
|
lazy var treeController: TreeController = {
|
||||||
return TreeController(delegate: treeControllerDelegate)
|
return TreeController(delegate: treeControllerDelegate)
|
||||||
}()
|
}()
|
||||||
@ -64,7 +64,7 @@ protocol SidebarDelegate: AnyObject {
|
|||||||
outlineView.dataSource = dataSource
|
outlineView.dataSource = dataSource
|
||||||
outlineView.doubleAction = #selector(doubleClickedSidebar(_:))
|
outlineView.doubleAction = #selector(doubleClickedSidebar(_:))
|
||||||
outlineView.setDraggingSourceOperationMask([.move, .copy], forLocal: true)
|
outlineView.setDraggingSourceOperationMask([.move, .copy], forLocal: true)
|
||||||
outlineView.registerForDraggedTypes([WebFeedPasteboardWriter.webFeedUTIInternalType, WebFeedPasteboardWriter.webFeedUTIType, .URL, .string])
|
outlineView.registerForDraggedTypes([FeedPasteboardWriter.feedUTIInternalType, FeedPasteboardWriter.feedUTIType, .URL, .string])
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidInitialize(_:)), name: .UnreadCountDidInitialize, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidInitialize(_:)), name: .UnreadCountDidInitialize, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||||
@ -75,8 +75,8 @@ protocol SidebarDelegate: AnyObject {
|
|||||||
NotificationCenter.default.addObserver(self, selector: #selector(userDidAddFeed(_:)), name: .UserDidAddFeed, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(userDidAddFeed(_:)), name: .UserDidAddFeed, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(batchUpdateDidPerform(_:)), name: .BatchUpdateDidPerform, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(batchUpdateDidPerform(_:)), name: .BatchUpdateDidPerform, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(webFeedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(feedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil)
|
||||||
DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleSideBarDefaultIconSizeChanged(_:)), name: .appleSideBarDefaultIconSizeChanged, object: nil)
|
DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleSideBarDefaultIconSizeChanged(_:)), name: .appleSideBarDefaultIconSizeChanged, object: nil)
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ protocol SidebarDelegate: AnyObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func userDidAddFeed(_ notification: Notification) {
|
@objc func userDidAddFeed(_ notification: Notification) {
|
||||||
guard let feed = notification.userInfo?[UserInfoKey.webFeed] else {
|
guard let feed = notification.userInfo?[UserInfoKey.feed] else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
revealAndSelectRepresentedObject(feed as AnyObject)
|
revealAndSelectRepresentedObject(feed as AnyObject)
|
||||||
@ -193,17 +193,17 @@ protocol SidebarDelegate: AnyObject {
|
|||||||
applyToAvailableCells(configureFavicon)
|
applyToAvailableCells(configureFavicon)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
@objc func feedIconDidBecomeAvailable(_ note: Notification) {
|
||||||
guard let webFeed = note.userInfo?[UserInfoKey.webFeed] as? Feed else { return }
|
guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed else { return }
|
||||||
configureCellsForRepresentedObject(webFeed)
|
configureCellsForRepresentedObject(feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func webFeedSettingDidChange(_ note: Notification) {
|
@objc func feedSettingDidChange(_ note: Notification) {
|
||||||
guard let webFeed = note.object as? Feed, let key = note.userInfo?[Feed.FeedSettingUserInfoKey] as? String else {
|
guard let feed = note.object as? Feed, let key = note.userInfo?[Feed.FeedSettingUserInfoKey] as? String else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if key == Feed.FeedSettingKey.homePageURL || key == Feed.FeedSettingKey.faviconURL {
|
if key == Feed.FeedSettingKey.homePageURL || key == Feed.FeedSettingKey.faviconURL {
|
||||||
configureCellsForRepresentedObject(webFeed)
|
configureCellsForRepresentedObject(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +255,7 @@ protocol SidebarDelegate: AnyObject {
|
|||||||
guard outlineView.clickedRow == outlineView.selectedRow else {
|
guard outlineView.clickedRow == outlineView.selectedRow else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if AppDefaults.shared.feedDoubleClickMarkAsRead, let articles = try? singleSelectedWebFeed?.fetchUnreadArticles() {
|
if AppDefaults.shared.feedDoubleClickMarkAsRead, let articles = try? singleSelectedFeed?.fetchUnreadArticles() {
|
||||||
if let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: Array(articles), markingRead: true, undoManager: undoManager) {
|
if let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: Array(articles), markingRead: true, undoManager: undoManager) {
|
||||||
runCommand(markReadCommand)
|
runCommand(markReadCommand)
|
||||||
}
|
}
|
||||||
@ -264,7 +264,7 @@ protocol SidebarDelegate: AnyObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func openInBrowser(_ sender: Any?) {
|
@IBAction func openInBrowser(_ sender: Any?) {
|
||||||
guard let feed = singleSelectedWebFeed, let homePageURL = feed.homePageURL else {
|
guard let feed = singleSelectedFeed, let homePageURL = feed.homePageURL else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Browser.open(homePageURL, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false)
|
Browser.open(homePageURL, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false)
|
||||||
@ -272,7 +272,7 @@ protocol SidebarDelegate: AnyObject {
|
|||||||
|
|
||||||
@objc func openInAppBrowser(_ sender: Any?) {
|
@objc func openInAppBrowser(_ sender: Any?) {
|
||||||
// There is no In-App Browser for mac - so we use safari
|
// There is no In-App Browser for mac - so we use safari
|
||||||
guard let feed = singleSelectedWebFeed, let homePageURL = feed.homePageURL else {
|
guard let feed = singleSelectedFeed, let homePageURL = feed.homePageURL else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Browser.open(homePageURL, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false)
|
Browser.open(homePageURL, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false)
|
||||||
@ -521,7 +521,7 @@ private extension SidebarViewController {
|
|||||||
return selectedNodes.first!
|
return selectedNodes.first!
|
||||||
}
|
}
|
||||||
|
|
||||||
var singleSelectedWebFeed: Feed? {
|
var singleSelectedFeed: Feed? {
|
||||||
guard let node = singleSelectedNode else {
|
guard let node = singleSelectedNode else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -541,7 +541,7 @@ private extension SidebarViewController {
|
|||||||
treeControllerDelegate.addFilterException(feedID)
|
treeControllerDelegate.addFilterException(feedID)
|
||||||
}
|
}
|
||||||
} else if let webFeed = feed as? Feed {
|
} else if let webFeed = feed as? Feed {
|
||||||
if webFeed.account?.existingWebFeed(withWebFeedID: webFeed.webFeedID) != nil {
|
if webFeed.account?.existingWebFeed(withWebFeedID: webFeed.feedID) != nil {
|
||||||
treeControllerDelegate.addFilterException(feedID)
|
treeControllerDelegate.addFilterException(feedID)
|
||||||
addParentFolderToFilterExceptions(webFeed)
|
addParentFolderToFilterExceptions(webFeed)
|
||||||
}
|
}
|
||||||
@ -741,7 +741,7 @@ private extension SidebarViewController {
|
|||||||
guard let webFeedID = userInfo?[ArticlePathKey.webFeedID] as? String else {
|
guard let webFeedID = userInfo?[ArticlePathKey.webFeedID] as? String else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if let node = startingNode.descendantNode(where: { ($0.representedObject as? Feed)?.webFeedID == webFeedID }) {
|
if let node = startingNode.descendantNode(where: { ($0.representedObject as? Feed)?.feedID == webFeedID }) {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -594,7 +594,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
||||||
guard showIcons, let feed = note.userInfo?[UserInfoKey.webFeed] as? Feed else {
|
guard showIcons, let feed = note.userInfo?[UserInfoKey.feed] as? Feed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let indexesToReload = tableView.indexesOfAvailableRowsPassingTest { (row) -> Bool in
|
let indexesToReload = tableView.indexesOfAvailableRowsPassingTest { (row) -> Bool in
|
||||||
@ -1235,14 +1235,14 @@ private extension TimelineViewController {
|
|||||||
for representedObject in representedObjects {
|
for representedObject in representedObjects {
|
||||||
if let feed = representedObject as? Feed {
|
if let feed = representedObject as? Feed {
|
||||||
for oneFeed in webFeeds {
|
for oneFeed in webFeeds {
|
||||||
if feed.webFeedID == oneFeed.webFeedID || feed.url == oneFeed.url {
|
if feed.feedID == oneFeed.feedID || feed.url == oneFeed.url {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if let folder = representedObject as? Folder {
|
else if let folder = representedObject as? Folder {
|
||||||
for oneFeed in webFeeds {
|
for oneFeed in webFeeds {
|
||||||
if folder.hasWebFeed(with: oneFeed.webFeedID) || folder.hasWebFeed(withURL: oneFeed.url) {
|
if folder.hasWebFeed(with: oneFeed.feedID) || folder.hasFeed(withURL: oneFeed.url) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
|
|||||||
|
|
||||||
@objc(webFeeds)
|
@objc(webFeeds)
|
||||||
var webFeeds:NSArray {
|
var webFeeds:NSArray {
|
||||||
return account.topLevelWebFeeds.map { ScriptableWebFeed($0, container:self) } as NSArray
|
return account.topLevelFeeds.map { ScriptableWebFeed($0, container:self) } as NSArray
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc(valueInWebFeedsWithUniqueID:)
|
@objc(valueInWebFeedsWithUniqueID:)
|
||||||
@ -107,7 +107,7 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
|
|||||||
|
|
||||||
@objc(valueInWebFeedsWithName:)
|
@objc(valueInWebFeedsWithName:)
|
||||||
func valueInWebFeeds(withName name:String) -> ScriptableWebFeed? {
|
func valueInWebFeeds(withName name:String) -> ScriptableWebFeed? {
|
||||||
let feeds = Array(account.flattenedWebFeeds())
|
let feeds = Array(account.flattenedFeeds())
|
||||||
guard let feed = feeds.first(where:{$0.name == name}) else { return nil }
|
guard let feed = feeds.first(where:{$0.name == name}) else { return nil }
|
||||||
return ScriptableWebFeed(feed, container:self)
|
return ScriptableWebFeed(feed, container:self)
|
||||||
}
|
}
|
||||||
@ -133,13 +133,13 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
|
|||||||
@objc(allWebFeeds)
|
@objc(allWebFeeds)
|
||||||
var allWebFeeds: NSArray {
|
var allWebFeeds: NSArray {
|
||||||
var webFeeds = [ScriptableWebFeed]()
|
var webFeeds = [ScriptableWebFeed]()
|
||||||
for webFeed in account.topLevelWebFeeds {
|
for webFeed in account.topLevelFeeds {
|
||||||
webFeeds.append(ScriptableWebFeed(webFeed, container: self))
|
webFeeds.append(ScriptableWebFeed(webFeed, container: self))
|
||||||
}
|
}
|
||||||
if let folders = account.folders {
|
if let folders = account.folders {
|
||||||
for folder in folders {
|
for folder in folders {
|
||||||
let scriptableFolder = ScriptableFolder(folder, container: self)
|
let scriptableFolder = ScriptableFolder(folder, container: self)
|
||||||
for webFeed in folder.topLevelWebFeeds {
|
for webFeed in folder.topLevelFeeds {
|
||||||
webFeeds.append(ScriptableWebFeed(webFeed, container: scriptableFolder))
|
webFeeds.append(ScriptableWebFeed(webFeed, container: scriptableFolder))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
|
|||||||
|
|
||||||
@objc(webFeeds)
|
@objc(webFeeds)
|
||||||
var webFeeds:NSArray {
|
var webFeeds:NSArray {
|
||||||
let feeds = Array(folder.topLevelWebFeeds)
|
let feeds = Array(folder.topLevelFeeds)
|
||||||
return feeds.map { ScriptableWebFeed($0, container:self) } as NSArray
|
return feeds.map { ScriptableWebFeed($0, container:self) } as NSArray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ extension NSApplication : ScriptingObjectContainer {
|
|||||||
let accounts = AccountManager.shared.activeAccounts
|
let accounts = AccountManager.shared.activeAccounts
|
||||||
let emptyFeeds:[Feed] = []
|
let emptyFeeds:[Feed] = []
|
||||||
return accounts.reduce(emptyFeeds) { (result, nthAccount) -> [Feed] in
|
return accounts.reduce(emptyFeeds) { (result, nthAccount) -> [Feed] in
|
||||||
let accountFeeds = Array(nthAccount.topLevelWebFeeds)
|
let accountFeeds = Array(nthAccount.topLevelFeeds)
|
||||||
return result + accountFeeds
|
return result + accountFeeds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ extension NSApplication : ScriptingObjectContainer {
|
|||||||
@objc(valueInWebFeedsWithUniqueID:)
|
@objc(valueInWebFeedsWithUniqueID:)
|
||||||
func valueInWebFeeds(withUniqueID id:String) -> ScriptableWebFeed? {
|
func valueInWebFeeds(withUniqueID id:String) -> ScriptableWebFeed? {
|
||||||
let webFeeds = self.allWebFeeds()
|
let webFeeds = self.allWebFeeds()
|
||||||
guard let webFeed = webFeeds.first(where:{$0.webFeedID == id}) else { return nil }
|
guard let webFeed = webFeeds.first(where:{$0.feedID == id}) else { return nil }
|
||||||
return ScriptableWebFeed(webFeed, container:self)
|
return ScriptableWebFeed(webFeed, container:self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ class ScriptableWebFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
|
|||||||
// but in either case it seems like the accountID would be used as the keydata, so I chose ID
|
// but in either case it seems like the accountID would be used as the keydata, so I chose ID
|
||||||
@objc(uniqueId)
|
@objc(uniqueId)
|
||||||
var scriptingUniqueId:Any {
|
var scriptingUniqueId:Any {
|
||||||
return webFeed.webFeedID
|
return webFeed.feedID
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: --- ScriptingObjectContainer protocol ---
|
// MARK: --- ScriptingObjectContainer protocol ---
|
||||||
@ -88,7 +88,7 @@ class ScriptableWebFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
|
|||||||
let (account, folder) = command.accountAndFolderForNewChild()
|
let (account, folder) = command.accountAndFolderForNewChild()
|
||||||
guard let url = self.urlForNewFeed(arguments:arguments) else {return nil}
|
guard let url = self.urlForNewFeed(arguments:arguments) else {return nil}
|
||||||
|
|
||||||
if let existingFeed = account.existingWebFeed(withURL:url) {
|
if let existingFeed = account.existingFeed(withURL:url) {
|
||||||
return scriptableFeed(existingFeed, account:account, folder:folder).objectSpecifier
|
return scriptableFeed(existingFeed, account:account, folder:folder).objectSpecifier
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,10 +102,10 @@ class ScriptableWebFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
|
|||||||
// suspendExecution(). When we get the callback, we supply the event result and call resumeExecution().
|
// suspendExecution(). When we get the callback, we supply the event result and call resumeExecution().
|
||||||
command.suspendExecution()
|
command.suspendExecution()
|
||||||
|
|
||||||
account.createWebFeed(url: url, name: titleFromArgs, container: container, validateFeed: true) { result in
|
account.createFeed(url: url, name: titleFromArgs, container: container, validateFeed: true) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let feed):
|
case .success(let feed):
|
||||||
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.webFeed: feed])
|
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
|
||||||
let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder)
|
let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder)
|
||||||
command.resumeExecution(withResult:scriptableFeed.objectSpecifier)
|
command.resumeExecution(withResult:scriptableFeed.objectSpecifier)
|
||||||
case .failure:
|
case .failure:
|
||||||
|
@ -111,7 +111,7 @@
|
|||||||
512D554423C804DE0023FFFA /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512D554323C804DE0023FFFA /* OpenInSafariActivity.swift */; };
|
512D554423C804DE0023FFFA /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512D554323C804DE0023FFFA /* OpenInSafariActivity.swift */; };
|
||||||
512DD4C92430086400C17B1F /* CloudKitAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512DD4C82430086400C17B1F /* CloudKitAccountViewController.swift */; };
|
512DD4C92430086400C17B1F /* CloudKitAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512DD4C82430086400C17B1F /* CloudKitAccountViewController.swift */; };
|
||||||
512E08E62268800D00BDCFDD /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; };
|
512E08E62268800D00BDCFDD /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; };
|
||||||
512E08E72268801200BDCFDD /* WebFeedTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97611ED9EB96007D329B /* WebFeedTreeControllerDelegate.swift */; };
|
512E08E72268801200BDCFDD /* FeedTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97611ED9EB96007D329B /* FeedTreeControllerDelegate.swift */; };
|
||||||
512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512E08F722688F7C00BDCFDD /* MasterFeedTableViewSectionHeader.swift */; };
|
512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512E08F722688F7C00BDCFDD /* MasterFeedTableViewSectionHeader.swift */; };
|
||||||
512E094D2268B8AB00BDCFDD /* DeleteCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B99C9C1FAE83C600ECDEDB /* DeleteCommand.swift */; };
|
512E094D2268B8AB00BDCFDD /* DeleteCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B99C9C1FAE83C600ECDEDB /* DeleteCommand.swift */; };
|
||||||
5131463E235A7BBE00387FDC /* NetNewsWire iOS Intents Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 51314637235A7BBE00387FDC /* NetNewsWire iOS Intents Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
5131463E235A7BBE00387FDC /* NetNewsWire iOS Intents Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 51314637235A7BBE00387FDC /* NetNewsWire iOS Intents Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
@ -447,7 +447,7 @@
|
|||||||
849A975C1ED9EB0D007D329B /* DefaultFeedsImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97591ED9EB0D007D329B /* DefaultFeedsImporter.swift */; };
|
849A975C1ED9EB0D007D329B /* DefaultFeedsImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97591ED9EB0D007D329B /* DefaultFeedsImporter.swift */; };
|
||||||
849A975E1ED9EB72007D329B /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A975D1ED9EB72007D329B /* MainWindowController.swift */; };
|
849A975E1ED9EB72007D329B /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A975D1ED9EB72007D329B /* MainWindowController.swift */; };
|
||||||
849A97641ED9EB96007D329B /* SidebarOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97601ED9EB96007D329B /* SidebarOutlineView.swift */; };
|
849A97641ED9EB96007D329B /* SidebarOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97601ED9EB96007D329B /* SidebarOutlineView.swift */; };
|
||||||
849A97651ED9EB96007D329B /* WebFeedTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97611ED9EB96007D329B /* WebFeedTreeControllerDelegate.swift */; };
|
849A97651ED9EB96007D329B /* FeedTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97611ED9EB96007D329B /* FeedTreeControllerDelegate.swift */; };
|
||||||
849A97661ED9EB96007D329B /* SidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97621ED9EB96007D329B /* SidebarViewController.swift */; };
|
849A97661ED9EB96007D329B /* SidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97621ED9EB96007D329B /* SidebarViewController.swift */; };
|
||||||
849A97671ED9EB96007D329B /* UnreadCountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97631ED9EB96007D329B /* UnreadCountView.swift */; };
|
849A97671ED9EB96007D329B /* UnreadCountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97631ED9EB96007D329B /* UnreadCountView.swift */; };
|
||||||
849A976C1ED9EBC8007D329B /* TimelineTableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97691ED9EBC8007D329B /* TimelineTableRowView.swift */; };
|
849A976C1ED9EBC8007D329B /* TimelineTableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97691ED9EBC8007D329B /* TimelineTableRowView.swift */; };
|
||||||
@ -1089,7 +1089,7 @@
|
|||||||
849A97591ED9EB0D007D329B /* DefaultFeedsImporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultFeedsImporter.swift; sourceTree = "<group>"; };
|
849A97591ED9EB0D007D329B /* DefaultFeedsImporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultFeedsImporter.swift; sourceTree = "<group>"; };
|
||||||
849A975D1ED9EB72007D329B /* MainWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowController.swift; sourceTree = "<group>"; };
|
849A975D1ED9EB72007D329B /* MainWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowController.swift; sourceTree = "<group>"; };
|
||||||
849A97601ED9EB96007D329B /* SidebarOutlineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SidebarOutlineView.swift; sourceTree = "<group>"; };
|
849A97601ED9EB96007D329B /* SidebarOutlineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SidebarOutlineView.swift; sourceTree = "<group>"; };
|
||||||
849A97611ED9EB96007D329B /* WebFeedTreeControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebFeedTreeControllerDelegate.swift; sourceTree = "<group>"; };
|
849A97611ED9EB96007D329B /* FeedTreeControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedTreeControllerDelegate.swift; sourceTree = "<group>"; };
|
||||||
849A97621ED9EB96007D329B /* SidebarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SidebarViewController.swift; sourceTree = "<group>"; };
|
849A97621ED9EB96007D329B /* SidebarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SidebarViewController.swift; sourceTree = "<group>"; };
|
||||||
849A97631ED9EB96007D329B /* UnreadCountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnreadCountView.swift; sourceTree = "<group>"; };
|
849A97631ED9EB96007D329B /* UnreadCountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnreadCountView.swift; sourceTree = "<group>"; };
|
||||||
849A97691ED9EBC8007D329B /* TimelineTableRowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineTableRowView.swift; sourceTree = "<group>"; };
|
849A97691ED9EBC8007D329B /* TimelineTableRowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineTableRowView.swift; sourceTree = "<group>"; };
|
||||||
@ -1446,7 +1446,7 @@
|
|||||||
512E08DD22687FA000BDCFDD /* Tree */ = {
|
512E08DD22687FA000BDCFDD /* Tree */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
849A97611ED9EB96007D329B /* WebFeedTreeControllerDelegate.swift */,
|
849A97611ED9EB96007D329B /* FeedTreeControllerDelegate.swift */,
|
||||||
849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */,
|
849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */,
|
||||||
);
|
);
|
||||||
path = Tree;
|
path = Tree;
|
||||||
@ -3111,7 +3111,7 @@
|
|||||||
512DD4C92430086400C17B1F /* CloudKitAccountViewController.swift in Sources */,
|
512DD4C92430086400C17B1F /* CloudKitAccountViewController.swift in Sources */,
|
||||||
840D617F2029031C009BC708 /* AppDelegate.swift in Sources */,
|
840D617F2029031C009BC708 /* AppDelegate.swift in Sources */,
|
||||||
51236339236915B100951F16 /* RoundedProgressView.swift in Sources */,
|
51236339236915B100951F16 /* RoundedProgressView.swift in Sources */,
|
||||||
512E08E72268801200BDCFDD /* WebFeedTreeControllerDelegate.swift in Sources */,
|
512E08E72268801200BDCFDD /* FeedTreeControllerDelegate.swift in Sources */,
|
||||||
51C452A422650A2D00C03939 /* ArticleUtilities.swift in Sources */,
|
51C452A422650A2D00C03939 /* ArticleUtilities.swift in Sources */,
|
||||||
51EF0F79227716380050506E /* ColorHash.swift in Sources */,
|
51EF0F79227716380050506E /* ColorHash.swift in Sources */,
|
||||||
51F9F3FB23DFB25700A314FD /* Animations.swift in Sources */,
|
51F9F3FB23DFB25700A314FD /* Animations.swift in Sources */,
|
||||||
@ -3319,7 +3319,7 @@
|
|||||||
8405DD9C22153BD7008CE1BF /* NSView-Extensions.swift in Sources */,
|
8405DD9C22153BD7008CE1BF /* NSView-Extensions.swift in Sources */,
|
||||||
849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */,
|
849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */,
|
||||||
51E595A5228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
|
51E595A5228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
|
||||||
849A97651ED9EB96007D329B /* WebFeedTreeControllerDelegate.swift in Sources */,
|
849A97651ED9EB96007D329B /* FeedTreeControllerDelegate.swift in Sources */,
|
||||||
849A97671ED9EB96007D329B /* UnreadCountView.swift in Sources */,
|
849A97671ED9EB96007D329B /* UnreadCountView.swift in Sources */,
|
||||||
510C418024E5D1AE008226FD /* ExtensionFeedAddRequestFile.swift in Sources */,
|
510C418024E5D1AE008226FD /* ExtensionFeedAddRequestFile.swift in Sources */,
|
||||||
51FE10092346739D0056195D /* ActivityType.swift in Sources */,
|
51FE10092346739D0056195D /* ActivityType.swift in Sources */,
|
||||||
|
@ -116,7 +116,7 @@ class ActivityManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for webFeed in account.flattenedWebFeeds() {
|
for webFeed in account.flattenedFeeds() {
|
||||||
ids.append(contentsOf: identifiers(for: webFeed))
|
ids.append(contentsOf: identifiers(for: webFeed))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ class ActivityManager {
|
|||||||
var ids = [String]()
|
var ids = [String]()
|
||||||
ids.append(identifier(for: folder))
|
ids.append(identifier(for: folder))
|
||||||
|
|
||||||
for webFeed in folder.flattenedWebFeeds() {
|
for webFeed in folder.flattenedFeeds() {
|
||||||
ids.append(contentsOf: identifiers(for: webFeed))
|
ids.append(contentsOf: identifiers(for: webFeed))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ class ActivityManager {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
||||||
guard let webFeed = note.userInfo?[UserInfoKey.webFeed] as? Feed, let activityFeedId = selectingActivity?.userInfo?[ArticlePathKey.webFeedID] as? String else {
|
guard let webFeed = note.userInfo?[UserInfoKey.feed] as? Feed, let activityFeedId = selectingActivity?.userInfo?[ArticlePathKey.webFeedID] as? String else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ class ActivityManager {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if activityFeedId == webFeed.webFeedID {
|
if activityFeedId == webFeed.feedID {
|
||||||
updateSelectingActivityFeedSearchAttributes(with: webFeed)
|
updateSelectingActivityFeedSearchAttributes(with: webFeed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -278,7 +278,7 @@ private extension ActivityManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func identifier(for feed: Feed) -> String {
|
static func identifier(for feed: Feed) -> String {
|
||||||
return "account_\(feed.account!.accountID)_feed_\(feed.webFeedID)"
|
return "account_\(feed.account!.accountID)_feed_\(feed.feedID)"
|
||||||
}
|
}
|
||||||
|
|
||||||
static func identifier(for article: Article) -> String {
|
static func identifier(for article: Article) -> String {
|
||||||
|
@ -125,7 +125,7 @@ extension Article {
|
|||||||
if let image = iconImage() {
|
if let image = iconImage() {
|
||||||
let fm = FileManager.default
|
let fm = FileManager.default
|
||||||
var path = fm.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
var path = fm.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
||||||
let feedID = webFeed.webFeedID.replacingOccurrences(of: "/", with: "_")
|
let feedID = webFeed.feedID.replacingOccurrences(of: "/", with: "_")
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
path.appendPathComponent(feedID + "_smallIcon.tiff")
|
path.appendPathComponent(feedID + "_smallIcon.tiff")
|
||||||
#else
|
#else
|
||||||
|
@ -180,7 +180,7 @@ private extension FeedIconDownloader {
|
|||||||
func postFeedIconDidBecomeAvailableNotification(_ feed: Feed) {
|
func postFeedIconDidBecomeAvailableNotification(_ feed: Feed) {
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let userInfo: [AnyHashable: Any] = [UserInfoKey.webFeed: feed]
|
let userInfo: [AnyHashable: Any] = [UserInfoKey.feed: feed]
|
||||||
NotificationCenter.default.post(name: .FeedIconDidBecomeAvailable, object: self, userInfo: userInfo)
|
NotificationCenter.default.post(name: .FeedIconDidBecomeAvailable, object: self, userInfo: userInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ private extension ExtensionFeedAddRequestFile {
|
|||||||
|
|
||||||
guard let container = destinationContainer else { return }
|
guard let container = destinationContainer else { return }
|
||||||
|
|
||||||
account.createWebFeed(url: request.feedURL.absoluteString, name: request.name, container: container, validateFeed: true) { _ in }
|
account.createFeed(url: request.feedURL.absoluteString, name: request.name, container: container, validateFeed: true) { _ in }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import RSTree
|
|||||||
import Articles
|
import Articles
|
||||||
import Account
|
import Account
|
||||||
|
|
||||||
final class WebFeedTreeControllerDelegate: TreeControllerDelegate {
|
final class FeedTreeControllerDelegate: TreeControllerDelegate {
|
||||||
|
|
||||||
private var filterExceptions = Set<SidebarItemIdentifier>()
|
private var filterExceptions = Set<SidebarItemIdentifier>()
|
||||||
var isReadFiltered = false
|
var isReadFiltered = false
|
||||||
@ -39,7 +39,7 @@ final class WebFeedTreeControllerDelegate: TreeControllerDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension WebFeedTreeControllerDelegate {
|
private extension FeedTreeControllerDelegate {
|
||||||
|
|
||||||
func childNodesForRootNode(_ rootNode: Node) -> [Node]? {
|
func childNodesForRootNode(_ rootNode: Node) -> [Node]? {
|
||||||
var topLevelNodes = [Node]()
|
var topLevelNodes = [Node]()
|
||||||
@ -66,7 +66,7 @@ private extension WebFeedTreeControllerDelegate {
|
|||||||
|
|
||||||
var children = [AnyObject]()
|
var children = [AnyObject]()
|
||||||
|
|
||||||
for webFeed in container.topLevelWebFeeds {
|
for webFeed in container.topLevelFeeds {
|
||||||
if let feedID = webFeed.sidebarItemID, !(!filterExceptions.contains(feedID) && isReadFiltered && webFeed.unreadCount == 0) {
|
if let feedID = webFeed.sidebarItemID, !(!filterExceptions.contains(feedID) && isReadFiltered && webFeed.unreadCount == 0) {
|
||||||
children.append(webFeed)
|
children.append(webFeed)
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ import Foundation
|
|||||||
|
|
||||||
struct UserInfoKey {
|
struct UserInfoKey {
|
||||||
|
|
||||||
static let webFeed = "webFeed"
|
static let feed = "feed"
|
||||||
static let url = "url"
|
static let url = "url"
|
||||||
static let articlePath = "articlePath"
|
static let articlePath = "articlePath"
|
||||||
static let feedIdentifier = "feedIdentifier"
|
static let feedIdentifier = "feedIdentifier"
|
||||||
|
@ -61,7 +61,7 @@ private extension UserNotificationManager {
|
|||||||
content.subtitle = ArticleStringFormatter.truncatedTitle(article)
|
content.subtitle = ArticleStringFormatter.truncatedTitle(article)
|
||||||
}
|
}
|
||||||
content.body = ArticleStringFormatter.truncatedSummary(article)
|
content.body = ArticleStringFormatter.truncatedSummary(article)
|
||||||
content.threadIdentifier = webFeed.webFeedID
|
content.threadIdentifier = webFeed.feedID
|
||||||
content.summaryArgument = "\(webFeed.nameForDisplay)"
|
content.summaryArgument = "\(webFeed.nameForDisplay)"
|
||||||
content.summaryArgumentCount = 1
|
content.summaryArgumentCount = 1
|
||||||
content.sound = UNNotificationSound.default
|
content.sound = UNNotificationSound.default
|
||||||
@ -83,7 +83,7 @@ private extension UserNotificationManager {
|
|||||||
/// - Warning: In certain scenarios, this will return the `faviconTemplateImage`.
|
/// - Warning: In certain scenarios, this will return the `faviconTemplateImage`.
|
||||||
func thumbnailAttachment(for article: Article, webFeed: Feed) -> UNNotificationAttachment? {
|
func thumbnailAttachment(for article: Article, webFeed: Feed) -> UNNotificationAttachment? {
|
||||||
if let imageURL = article.iconImageUrl(webFeed: webFeed) {
|
if let imageURL = article.iconImageUrl(webFeed: webFeed) {
|
||||||
let thumbnail = try? UNNotificationAttachment(identifier: webFeed.webFeedID, url: imageURL, options: nil)
|
let thumbnail = try? UNNotificationAttachment(identifier: webFeed.feedID, url: imageURL, options: nil)
|
||||||
return thumbnail
|
return thumbnail
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -99,7 +99,7 @@ class AddFeedViewController: UITableViewController {
|
|||||||
account = containerAccount
|
account = containerAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
if account!.hasWebFeed(withURL: url.absoluteString) {
|
if account!.hasFeed(withURL: url.absoluteString) {
|
||||||
presentError(AccountError.createErrorAlreadySubscribed)
|
presentError(AccountError.createErrorAlreadySubscribed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -112,14 +112,14 @@ class AddFeedViewController: UITableViewController {
|
|||||||
|
|
||||||
BatchUpdate.shared.start()
|
BatchUpdate.shared.start()
|
||||||
|
|
||||||
account!.createWebFeed(url: url.absoluteString, name: feedName, container: container, validateFeed: true) { result in
|
account!.createFeed(url: url.absoluteString, name: feedName, container: container, validateFeed: true) { result in
|
||||||
|
|
||||||
BatchUpdate.shared.end()
|
BatchUpdate.shared.end()
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let feed):
|
case .success(let feed):
|
||||||
self.dismiss(animated: true)
|
self.dismiss(animated: true)
|
||||||
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.webFeed: feed])
|
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
self.addButton.isEnabled = true
|
self.addButton.isEnabled = true
|
||||||
self.activityIndicator.isHidden = true
|
self.activityIndicator.isHidden = true
|
||||||
|
@ -32,7 +32,7 @@ extension MasterFeedViewController: UITableViewDropDelegate {
|
|||||||
if destAccount.behaviors.contains(.disallowFeedInMultipleFolders),
|
if destAccount.behaviors.contains(.disallowFeedInMultipleFolders),
|
||||||
let sourceNode = session.localDragSession?.items.first?.localObject as? Node,
|
let sourceNode = session.localDragSession?.items.first?.localObject as? Node,
|
||||||
let sourceWebFeed = sourceNode.representedObject as? Feed,
|
let sourceWebFeed = sourceNode.representedObject as? Feed,
|
||||||
sourceWebFeed.account?.accountID != destAccount.accountID && destAccount.hasWebFeed(withURL: sourceWebFeed.url) {
|
sourceWebFeed.account?.accountID != destAccount.accountID && destAccount.hasFeed(withURL: sourceWebFeed.url) {
|
||||||
return UITableViewDropProposal(operation: .forbidden)
|
return UITableViewDropProposal(operation: .forbidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ extension MasterFeedViewController: UITableViewDropDelegate {
|
|||||||
guard sourceContainer !== destinationContainer else { return }
|
guard sourceContainer !== destinationContainer else { return }
|
||||||
|
|
||||||
BatchUpdate.shared.start()
|
BatchUpdate.shared.start()
|
||||||
sourceContainer.account?.moveWebFeed(feed, from: sourceContainer, to: destinationContainer) { result in
|
sourceContainer.account?.moveFeed(feed, from: sourceContainer, to: destinationContainer) { result in
|
||||||
BatchUpdate.shared.end()
|
BatchUpdate.shared.end()
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
@ -117,10 +117,10 @@ extension MasterFeedViewController: UITableViewDropDelegate {
|
|||||||
|
|
||||||
func moveWebFeedBetweenAccounts(feed: Feed, sourceContainer: Container, destinationContainer: Container) {
|
func moveWebFeedBetweenAccounts(feed: Feed, sourceContainer: Container, destinationContainer: Container) {
|
||||||
|
|
||||||
if let existingFeed = destinationContainer.account?.existingWebFeed(withURL: feed.url) {
|
if let existingFeed = destinationContainer.account?.existingFeed(withURL: feed.url) {
|
||||||
|
|
||||||
BatchUpdate.shared.start()
|
BatchUpdate.shared.start()
|
||||||
destinationContainer.account?.addWebFeed(existingFeed, to: destinationContainer) { result in
|
destinationContainer.account?.addFeed(existingFeed, to: destinationContainer) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
sourceContainer.account?.removeWebFeed(feed, from: sourceContainer) { result in
|
sourceContainer.account?.removeWebFeed(feed, from: sourceContainer) { result in
|
||||||
@ -141,7 +141,7 @@ extension MasterFeedViewController: UITableViewDropDelegate {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
BatchUpdate.shared.start()
|
BatchUpdate.shared.start()
|
||||||
destinationContainer.account?.createWebFeed(url: feed.url, name: feed.editedName, container: destinationContainer, validateFeed: false) { result in
|
destinationContainer.account?.createFeed(url: feed.url, name: feed.editedName, container: destinationContainer, validateFeed: false) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
sourceContainer.account?.removeWebFeed(feed, from: sourceContainer) { result in
|
sourceContainer.account?.removeWebFeed(feed, from: sourceContainer) { result in
|
||||||
|
@ -130,7 +130,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
||||||
guard let webFeed = note.userInfo?[UserInfoKey.webFeed] as? Feed else {
|
guard let webFeed = note.userInfo?[UserInfoKey.feed] as? Feed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
applyToCellsForRepresentedObject(webFeed, configureIcon(_:_:))
|
applyToCellsForRepresentedObject(webFeed, configureIcon(_:_:))
|
||||||
|
@ -453,7 +453,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
|||||||
titleView.iconView.iconImage = coordinator.timelineIconImage
|
titleView.iconView.iconImage = coordinator.timelineIconImage
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let feed = note.userInfo?[UserInfoKey.webFeed] as? Feed else {
|
guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tableView.indexPathsForVisibleRows?.forEach { indexPath in
|
tableView.indexPathsForVisibleRows?.forEach { indexPath in
|
||||||
|
@ -127,7 +127,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
|
|
||||||
var prefersStatusBarHidden = false
|
var prefersStatusBarHidden = false
|
||||||
|
|
||||||
private let treeControllerDelegate = WebFeedTreeControllerDelegate()
|
private let treeControllerDelegate = FeedTreeControllerDelegate()
|
||||||
private let treeController: TreeController
|
private let treeController: TreeController
|
||||||
|
|
||||||
var stateRestorationActivity: NSUserActivity {
|
var stateRestorationActivity: NSUserActivity {
|
||||||
@ -545,7 +545,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func userDidAddFeed(_ notification: Notification) {
|
@objc func userDidAddFeed(_ notification: Notification) {
|
||||||
guard let webFeed = notification.userInfo?[UserInfoKey.webFeed] as? Feed else {
|
guard let webFeed = notification.userInfo?[UserInfoKey.feed] as? Feed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
discloseWebFeed(webFeed, animations: [.scroll, .navigation])
|
discloseWebFeed(webFeed, animations: [.scroll, .navigation])
|
||||||
@ -1477,7 +1477,7 @@ private extension SceneCoordinator {
|
|||||||
treeControllerDelegate.addFilterException(feedID)
|
treeControllerDelegate.addFilterException(feedID)
|
||||||
}
|
}
|
||||||
} else if let webFeed = feed as? Feed {
|
} else if let webFeed = feed as? Feed {
|
||||||
if webFeed.account?.existingWebFeed(withWebFeedID: webFeed.webFeedID) != nil {
|
if webFeed.account?.existingWebFeed(withWebFeedID: webFeed.feedID) != nil {
|
||||||
treeControllerDelegate.addFilterException(feedID)
|
treeControllerDelegate.addFilterException(feedID)
|
||||||
addParentFolderToFilterExceptions(webFeed)
|
addParentFolderToFilterExceptions(webFeed)
|
||||||
}
|
}
|
||||||
@ -2080,13 +2080,13 @@ private extension SceneCoordinator {
|
|||||||
|
|
||||||
if let feed = timelineFeed as? Feed {
|
if let feed = timelineFeed as? Feed {
|
||||||
for oneFeed in feeds {
|
for oneFeed in feeds {
|
||||||
if feed.webFeedID == oneFeed.webFeedID || feed.url == oneFeed.url {
|
if feed.feedID == oneFeed.feedID || feed.url == oneFeed.url {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let folder = timelineFeed as? Folder {
|
} else if let folder = timelineFeed as? Folder {
|
||||||
for oneFeed in feeds {
|
for oneFeed in feeds {
|
||||||
if folder.hasWebFeed(with: oneFeed.webFeedID) || folder.hasWebFeed(withURL: oneFeed.url) {
|
if folder.hasWebFeed(with: oneFeed.feedID) || folder.hasFeed(withURL: oneFeed.url) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2379,7 +2379,7 @@ private extension SceneCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findWebFeedNode(webFeedID: String, beginningAt startingNode: Node) -> Node? {
|
func findWebFeedNode(webFeedID: String, beginningAt startingNode: Node) -> Node? {
|
||||||
if let node = startingNode.descendantNode(where: { ($0.representedObject as? Feed)?.webFeedID == webFeedID }) {
|
if let node = startingNode.descendantNode(where: { ($0.representedObject as? Feed)?.feedID == webFeedID }) {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
x
Reference in New Issue
Block a user