mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-02-02 03:56:55 +01:00
Standardize notification handling on Combine instead of selectors
This commit is contained in:
parent
5845925b3a
commit
876f978347
@ -7,19 +7,40 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import Account
|
||||
import Articles
|
||||
|
||||
final class ArticleIconImageLoader: ObservableObject {
|
||||
|
||||
private var article: Article?
|
||||
|
||||
@Published var image: IconImage?
|
||||
|
||||
private var article: Article?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .WebFeedIconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.publisher(for: .FaviconDidBecomeAvailable).sink { [weak self] _ in
|
||||
guard let self = self, let article = self.article else { return }
|
||||
self.image = article.iconImage()
|
||||
}.store(in: &cancellables)
|
||||
|
||||
NotificationCenter.default.publisher(for: .WebFeedIconDidBecomeAvailable).sink { [weak self] note in
|
||||
guard let self = self, let article = self.article, let noteFeed = note.userInfo?[UserInfoKey.webFeed] as? WebFeed, noteFeed == article.webFeed else {
|
||||
return
|
||||
}
|
||||
self.image = article.iconImage()
|
||||
}.store(in: &cancellables)
|
||||
|
||||
NotificationCenter.default.publisher(for: .AvatarDidBecomeAvailable).sink { [weak self] note in
|
||||
guard let self = self, let article = self.article, let authors = article.authors, let avatarURL = note.userInfo?[UserInfoKey.url] as? String else {
|
||||
return
|
||||
}
|
||||
for author in authors {
|
||||
if author.avatarURL == avatarURL {
|
||||
self.image = article.iconImage()
|
||||
return
|
||||
}
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func loadImage(for article: Article) {
|
||||
@ -29,31 +50,3 @@ final class ArticleIconImageLoader: ObservableObject {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension ArticleIconImageLoader {
|
||||
|
||||
@objc func faviconDidBecomeAvailable(_ note: Notification) {
|
||||
guard let article = article else { return }
|
||||
image = article.iconImage()
|
||||
}
|
||||
|
||||
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
||||
guard let article = article, let noteFeed = note.userInfo?[UserInfoKey.webFeed] as? WebFeed, noteFeed == article.webFeed else {
|
||||
return
|
||||
}
|
||||
image = article.iconImage()
|
||||
}
|
||||
|
||||
@objc func avatarDidBecomeAvailable(_ note: Notification) {
|
||||
guard let article = article, let authors = article.authors, let avatarURL = note.userInfo?[UserInfoKey.url] as? String else {
|
||||
return
|
||||
}
|
||||
|
||||
for author in authors {
|
||||
if author.avatarURL == avatarURL {
|
||||
image = article.iconImage()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,17 +7,27 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Account
|
||||
|
||||
final class FeedIconImageLoader: ObservableObject {
|
||||
|
||||
private var feed: Feed?
|
||||
|
||||
@Published var image: IconImage?
|
||||
private var feed: Feed?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .WebFeedIconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.publisher(for: .FaviconDidBecomeAvailable).sink { [weak self] _ in
|
||||
self?.fetchImage()
|
||||
}.store(in: &cancellables)
|
||||
|
||||
|
||||
NotificationCenter.default.publisher(for: .WebFeedIconDidBecomeAvailable).sink { [weak self] note in
|
||||
guard let feed = self?.feed as? WebFeed, let noteFeed = note.userInfo?[UserInfoKey.webFeed] as? WebFeed, feed == noteFeed else {
|
||||
return
|
||||
}
|
||||
self?.fetchImage()
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func loadImage(for feed: Feed) {
|
||||
@ -30,17 +40,6 @@ final class FeedIconImageLoader: ObservableObject {
|
||||
|
||||
private extension FeedIconImageLoader {
|
||||
|
||||
@objc func faviconDidBecomeAvailable(_ note: Notification) {
|
||||
fetchImage()
|
||||
}
|
||||
|
||||
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
||||
guard let feed = feed as? WebFeed, let noteFeed = note.userInfo?[UserInfoKey.webFeed] as? WebFeed, feed == noteFeed else {
|
||||
return
|
||||
}
|
||||
fetchImage()
|
||||
}
|
||||
|
||||
func fetchImage() {
|
||||
if let webFeed = feed as? WebFeed {
|
||||
if let feedIconImage = appDelegate.webFeedIconDownloader.icon(for: webFeed) {
|
||||
|
@ -25,9 +25,7 @@ class SidebarModel: ObservableObject, UndoableCommandRunner {
|
||||
@Published var selectedFeeds = [Feed]()
|
||||
@Published var isReadFiltered = false
|
||||
|
||||
private var selectedFeedIdentifiersCancellable: AnyCancellable?
|
||||
private var selectedFeedIdentifierCancellable: AnyCancellable?
|
||||
private var selectedReadFilteredCancellable: AnyCancellable?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private let rebuildSidebarItemsQueue = CoalescingQueue(name: "Rebuild The Sidebar Items", interval: 0.5)
|
||||
|
||||
@ -35,33 +33,55 @@ class SidebarModel: ObservableObject, UndoableCommandRunner {
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
|
||||
init() {
|
||||
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(containerChildrenDidChange(_:)), name: .ChildrenDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(batchUpdateDidPerform(_:)), name: .BatchUpdateDidPerform, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountStateDidChange(_:)), name: .AccountStateDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(userDidAddAccount(_:)), name: .UserDidAddAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(userDidDeleteAccount(_:)), name: .UserDidDeleteAccount, object: nil)
|
||||
|
||||
NotificationCenter.default.publisher(for: .UnreadCountDidInitialize)
|
||||
.filter { $0.object is AccountManager }
|
||||
.sink { [weak self] note in
|
||||
guard let self = self else { return }
|
||||
self.rebuildSidebarItems(isReadFiltered: self.isReadFiltered)
|
||||
}.store(in: &cancellables)
|
||||
|
||||
NotificationCenter.default.publisher(for: .UnreadCountDidChange)
|
||||
.filter { _ in AccountManager.shared.isUnreadCountsInitialized }
|
||||
.sink { [weak self] _ in
|
||||
self?.queueRebuildSidebarItems()
|
||||
}.store(in: &cancellables)
|
||||
|
||||
let chidrenDidChangePublisher = NotificationCenter.default.publisher(for: .ChildrenDidChange)
|
||||
let batchUpdateDidPerformPublisher = NotificationCenter.default.publisher(for: .BatchUpdateDidPerform)
|
||||
let displayNameDidChangePublisher = NotificationCenter.default.publisher(for: .DisplayNameDidChange)
|
||||
let accountStateDidChangePublisher = NotificationCenter.default.publisher(for: .AccountStateDidChange)
|
||||
let userDidAddAccountPublisher = NotificationCenter.default.publisher(for: .UserDidAddAccount)
|
||||
let userDidDeleteAccountPublisher = NotificationCenter.default.publisher(for: .UserDidDeleteAccount)
|
||||
|
||||
let sidebarRebuildPublishers = chidrenDidChangePublisher.merge(with: batchUpdateDidPerformPublisher,
|
||||
displayNameDidChangePublisher,
|
||||
accountStateDidChangePublisher,
|
||||
userDidAddAccountPublisher,
|
||||
userDidDeleteAccountPublisher)
|
||||
|
||||
sidebarRebuildPublishers.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.rebuildSidebarItems(isReadFiltered: self.isReadFiltered)
|
||||
}.store(in: &cancellables)
|
||||
|
||||
|
||||
// TODO: This should be rewritten to use Combine correctly
|
||||
selectedFeedIdentifiersCancellable = $selectedFeedIdentifiers.sink { [weak self] feedIDs in
|
||||
$selectedFeedIdentifiers.sink { [weak self] feedIDs in
|
||||
guard let self = self else { return }
|
||||
self.selectedFeeds = feedIDs.compactMap { self.findFeed($0) }
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
|
||||
// TODO: This should be rewritten to use Combine correctly
|
||||
selectedFeedIdentifierCancellable = $selectedFeedIdentifier.sink { [weak self] feedID in
|
||||
$selectedFeedIdentifier.sink { [weak self] feedID in
|
||||
guard let self = self else { return }
|
||||
if let feedID = feedID, let feed = self.findFeed(feedID) {
|
||||
self.selectedFeeds = [feed]
|
||||
}
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
|
||||
selectedReadFilteredCancellable = $isReadFiltered.sink { [weak self] filter in
|
||||
guard let self = self else { return }
|
||||
self.rebuildSidebarItems(isReadFiltered: filter)
|
||||
}
|
||||
$isReadFiltered.sink { [weak self] filter in
|
||||
self?.rebuildSidebarItems(isReadFiltered: filter)
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
@ -143,45 +163,4 @@ private extension SidebarModel {
|
||||
sidebarItems = items
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc func unreadCountDidInitialize(_ notification: Notification) {
|
||||
guard notification.object is AccountManager else {
|
||||
return
|
||||
}
|
||||
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||
}
|
||||
|
||||
@objc func unreadCountDidChange(_ note: Notification) {
|
||||
// We will handle the filtering of unread feeds in unreadCountDidInitialize after they have all be calculated
|
||||
guard AccountManager.shared.isUnreadCountsInitialized else {
|
||||
return
|
||||
}
|
||||
queueRebuildSidebarItems()
|
||||
}
|
||||
|
||||
@objc func containerChildrenDidChange(_ notification: Notification) {
|
||||
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||
}
|
||||
|
||||
@objc func batchUpdateDidPerform(_ notification: Notification) {
|
||||
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||
}
|
||||
|
||||
@objc func displayNameDidChange(_ note: Notification) {
|
||||
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||
}
|
||||
|
||||
@objc func accountStateDidChange(_ note: Notification) {
|
||||
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||
}
|
||||
|
||||
@objc func userDidAddAccount(_ note: Notification) {
|
||||
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||
}
|
||||
|
||||
@objc func userDidDeleteAccount(_ note: Notification) {
|
||||
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,10 +42,7 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
||||
var undoManager: UndoManager?
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
|
||||
private var selectedFeedsCancellable: AnyCancellable?
|
||||
private var selectedArticleIDsCancellable: AnyCancellable?
|
||||
private var selectedArticleIDCancellable: AnyCancellable?
|
||||
private var selectedArticlesCancellable: AnyCancellable?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private var feeds = [Feed]()
|
||||
private var fetchSerialNumber = 0
|
||||
@ -78,39 +75,51 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
||||
}
|
||||
|
||||
func startup() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
|
||||
|
||||
// TODO: This should be rewritten to use Combine correctly
|
||||
selectedFeedsCancellable = delegate?.selectedFeeds.sink { [weak self] feeds in
|
||||
guard let self = self else { return }
|
||||
self.fetchArticles(feeds: feeds)
|
||||
}
|
||||
NotificationCenter.default.publisher(for: .StatusesDidChange).sink { [weak self] note in
|
||||
guard let self = self, let articleIDs = note.userInfo?[Account.UserInfoKey.articleIDs] as? Set<String> else {
|
||||
return
|
||||
}
|
||||
for i in 0..<self.timelineItems.count {
|
||||
if articleIDs.contains(self.timelineItems[i].article.articleID) {
|
||||
self.timelineItems[i].updateStatus()
|
||||
}
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
|
||||
NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification).sink { [weak self] _ in
|
||||
self?.sortDirection = AppDefaults.shared.timelineSortDirection
|
||||
self?.groupByFeed = AppDefaults.shared.timelineGroupByFeed
|
||||
}.store(in: &cancellables)
|
||||
|
||||
// TODO: This should be rewritten to use Combine correctly
|
||||
selectedArticleIDsCancellable = $selectedArticleIDs.sink { [weak self] articleIDs in
|
||||
delegate?.selectedFeeds.sink { [weak self] feeds in
|
||||
guard let self = self else { return }
|
||||
self.fetchArticles(feeds: feeds)
|
||||
}.store(in: &cancellables)
|
||||
|
||||
// TODO: This should be rewritten to use Combine correctly
|
||||
$selectedArticleIDs.sink { [weak self] articleIDs in
|
||||
guard let self = self else { return }
|
||||
self.selectedArticles = articleIDs.compactMap { self.idToArticleDictionary[$0] }
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
|
||||
// TODO: This should be rewritten to use Combine correctly
|
||||
selectedArticleIDCancellable = $selectedArticleID.sink { [weak self] articleID in
|
||||
$selectedArticleID.sink { [weak self] articleID in
|
||||
guard let self = self else { return }
|
||||
if let articleID = articleID, let article = self.idToArticleDictionary[articleID] {
|
||||
self.selectedArticles = [article]
|
||||
}
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
|
||||
// TODO: This should be rewritten to use Combine correctly
|
||||
selectedArticlesCancellable = $selectedArticles.sink { articles in
|
||||
$selectedArticles.sink { articles in
|
||||
if articles.count == 1 {
|
||||
let article = articles.first!
|
||||
if !article.status.read {
|
||||
markArticles(Set([article]), statusKey: .read, flag: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
@ -325,24 +334,6 @@ private extension TimelineModel {
|
||||
runCommand(markReadCommand)
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc func statusesDidChange(_ note: Notification) {
|
||||
guard let articleIDs = note.userInfo?[Account.UserInfoKey.articleIDs] as? Set<String> else {
|
||||
return
|
||||
}
|
||||
for i in 0..<timelineItems.count {
|
||||
if articleIDs.contains(timelineItems[i].article.articleID) {
|
||||
timelineItems[i].updateStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func userDefaultsDidChange(_ note: Notification) {
|
||||
sortDirection = AppDefaults.shared.timelineSortDirection
|
||||
groupByFeed = AppDefaults.shared.timelineGroupByFeed
|
||||
}
|
||||
|
||||
// MARK: Timeline Management
|
||||
|
||||
func resetReadFilter() {
|
||||
|
@ -63,22 +63,22 @@ class AccountsPreferencesModel: ObservableObject {
|
||||
@Published var showDeleteConfirmation: Bool = false
|
||||
|
||||
// Subscriptions
|
||||
var notificationSubscriptions = Set<AnyCancellable>()
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init() {
|
||||
sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
|
||||
NotificationCenter.default.publisher(for: .UserDidAddAccount).sink(receiveValue: { [weak self] _ in
|
||||
NotificationCenter.default.publisher(for: .UserDidAddAccount).sink { [weak self] _ in
|
||||
self?.sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
}).store(in: ¬ificationSubscriptions)
|
||||
}.store(in: &cancellables)
|
||||
|
||||
NotificationCenter.default.publisher(for: .UserDidDeleteAccount).sink(receiveValue: { [weak self] _ in
|
||||
NotificationCenter.default.publisher(for: .UserDidDeleteAccount).sink { [weak self] _ in
|
||||
self?.selectedConfiguredAccountID = nil
|
||||
self?.sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
self?.selectedConfiguredAccountID = AccountManager.shared.defaultAccount.accountID
|
||||
}).store(in: ¬ificationSubscriptions)
|
||||
}.store(in: &cancellables)
|
||||
|
||||
NotificationCenter.default.publisher(for: .AccountStateDidChange).sink(receiveValue: { [weak self] notification in
|
||||
NotificationCenter.default.publisher(for: .AccountStateDidChange).sink { [weak self] notification in
|
||||
guard let account = notification.object as? Account else {
|
||||
return
|
||||
}
|
||||
@ -87,7 +87,7 @@ class AccountsPreferencesModel: ObservableObject {
|
||||
self?.accountIsActive = account.isActive
|
||||
self?.accountName = account.name ?? ""
|
||||
}
|
||||
}).store(in: ¬ificationSubscriptions)
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user