From b2c70e847c4ce0dd0adeb943d5514a8332cc13fd Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Fri, 24 Jul 2020 11:40:17 -0500 Subject: [PATCH] Change from using @Published as a PassthroughSubject to using real ones to avoid @Published quirks --- Multiplatform/Shared/SceneModel.swift | 4 +- .../Shared/Sidebar/SidebarContextMenu.swift | 12 +- .../Shared/Sidebar/SidebarModel.swift | 169 ++++++++++-------- .../Shared/Timeline/TimelineModel.swift | 5 +- 4 files changed, 105 insertions(+), 85 deletions(-) diff --git a/Multiplatform/Shared/SceneModel.swift b/Multiplatform/Shared/SceneModel.swift index e090e4576..6b181fa9e 100644 --- a/Multiplatform/Shared/SceneModel.swift +++ b/Multiplatform/Shared/SceneModel.swift @@ -115,8 +115,8 @@ extension SceneModel: SidebarModelDelegate { extension SceneModel: TimelineModelDelegate { - var selectedFeeds: Published<[Feed]>.Publisher { - return sidebarModel.$selectedFeeds + var selectedFeedsPublisher: AnyPublisher<[Feed], Never>? { + return sidebarModel.selectedFeedsPublisher } func timelineRequestedWebFeedSelection(_: TimelineModel, webFeed: WebFeed) { diff --git a/Multiplatform/Shared/Sidebar/SidebarContextMenu.swift b/Multiplatform/Shared/Sidebar/SidebarContextMenu.swift index d9a27f295..824bcc32f 100644 --- a/Multiplatform/Shared/Sidebar/SidebarContextMenu.swift +++ b/Multiplatform/Shared/Sidebar/SidebarContextMenu.swift @@ -31,7 +31,7 @@ struct SidebarContextMenu: View { #endif } Button { - sidebarModel.markAllAsRead(account: sidebarItem.represented as! Account) +// sidebarModel.markAllAsRead(account: sidebarItem.represented as! Account) } label: { Text("Mark All As Read") #if os(iOS) @@ -46,7 +46,7 @@ struct SidebarContextMenu: View { guard let feed = sidebarItem.feed else { return } - sidebarModel.markAllAsRead(feed: feed) + sidebarModel.markAllAsReadInFeed.send(feed) } label: { Text("Mark All As Read") #if os(iOS) @@ -69,7 +69,7 @@ struct SidebarContextMenu: View { guard let feed = sidebarItem.feed else { return } - sidebarModel.markAllAsRead(feed: feed) + sidebarModel.markAllAsReadInFeed.send(feed) } label: { Text("Mark All As Read") #if os(iOS) @@ -123,7 +123,7 @@ struct SidebarContextMenu: View { } Divider() Button { - sidebarModel.deleteItems(item: sidebarItem) +// sidebarModel.deleteItems(item: sidebarItem) } label: { Text("Delete") #if os(iOS) @@ -146,7 +146,7 @@ struct SidebarContextMenu: View { guard let feed = sidebarItem.feed else { return } - sidebarModel.markAllAsRead(feed: feed) + sidebarModel.markAllAsReadInFeed.send(feed) } label: { Text("Mark All As Read") #if os(iOS) @@ -155,7 +155,7 @@ struct SidebarContextMenu: View { } Divider() Button { - sidebarModel.deleteItems(item: sidebarItem) +// sidebarModel.deleteItems(item: sidebarItem) } label: { Text("Delete") #if os(iOS) diff --git a/Multiplatform/Shared/Sidebar/SidebarModel.swift b/Multiplatform/Shared/Sidebar/SidebarModel.swift index 87a2c57e6..049238064 100644 --- a/Multiplatform/Shared/Sidebar/SidebarModel.swift +++ b/Multiplatform/Shared/Sidebar/SidebarModel.swift @@ -18,16 +18,19 @@ protocol SidebarModelDelegate: class { class SidebarModel: ObservableObject, UndoableCommandRunner { + @Published var selectedFeedIdentifiers = Set() + @Published var selectedFeedIdentifier: FeedIdentifier? = .none + @Published var isReadFiltered = false + weak var delegate: SidebarModelDelegate? var sidebarItemsPublisher: AnyPublisher<[SidebarItem], Never>? + var selectedFeedsPublisher: AnyPublisher<[Feed], Never>? + var selectNextUnread = PassthroughSubject() - - @Published var selectedFeedIdentifiers = Set() - @Published var selectedFeedIdentifier: FeedIdentifier? = .none - @Published var selectedFeeds = [Feed]() - @Published var isReadFiltered = false - + var markAllAsReadInFeed = PassthroughSubject() + var markAllAsReadInAccount = PassthroughSubject() + private var cancellables = Set() var undoManager: UndoManager? @@ -37,29 +40,37 @@ class SidebarModel: ObservableObject, UndoableCommandRunner { subscribeToSelectedFeedChanges() subscribeToRebuildSidebarItemsEvents() subscribeToNextUnread() + subscribeToMarkAllAsReadInFeed() } } // MARK: Side Context Menu Actions -extension SidebarModel { +private extension SidebarModel { - func markAllAsRead(feed: Feed) { - var articles = Set
() - let fetchedArticles = try! feed.fetchArticles() - for article in fetchedArticles { - articles.insert(article) - } - - for selectedFeed in selectedFeeds { - let fetchedArticles = try! selectedFeed.fetchArticles() - for article in fetchedArticles { - articles.insert(article) + func subscribeToMarkAllAsReadInFeed() { + guard let selectedFeedsPublisher = selectedFeedsPublisher else { return } + + markAllAsReadInFeed + .withLatestFrom(selectedFeedsPublisher, resultSelector: { givenFeed, selectedFeeds -> [Feed] in + if selectedFeeds.contains(where: { $0.feedID == givenFeed.feedID }) { + return selectedFeeds + } else { + return [givenFeed] + } + }) + .map { feeds in + var articles = [Article]() + for feed in feeds { + articles.append(contentsOf: (try? feed.fetchArticles()) ?? Set
()) + } + return articles } - } - - markAllAsRead(Array(articles)) + .sink { [weak self] allArticles in + self?.markAllAsRead(allArticles) + } + .store(in: &cancellables) } func markAllAsRead(account: Account) { @@ -78,51 +89,50 @@ extension SidebarModel { private func markAllAsRead(_ articles: [Article]) { guard let undoManager = undoManager ?? UndoManager(), let markAsReadCommand = MarkStatusCommand(initialArticles: articles, markingRead: true, undoManager: undoManager) else { - return } runCommand(markAsReadCommand) } func deleteItems(item: SidebarItem) { - #if os(macOS) - if selectedFeeds.count > 0 { - for feed in selectedFeeds { - if feed is WebFeed { - print(feed.nameForDisplay) - let account = (feed as! WebFeed).account - account?.removeWebFeed(feed as! WebFeed) - } - if feed is Folder { - let account = (feed as! Folder).account - account?.removeFolder(feed as! Folder, completion: { (result) in - switch result { - case .success( _): - print("Deleted folder") - case .failure(let err): - print(err.localizedDescription) - } - }) - } - } - } - #else - if item.feed is WebFeed { - let account = (item.feed as! WebFeed).account - account?.removeWebFeed(item.feed as! WebFeed) - } - if item.feed is Folder { - let account = (item.feed as! Folder).account - account?.removeFolder(item.feed as! Folder, completion: { (result) in - switch result { - case .success( _): - print("Deleted folder") - case .failure(let err): - print(err.localizedDescription) - } - }) - } - #endif +// #if os(macOS) +// if selectedFeeds.count > 0 { +// for feed in selectedFeeds { +// if feed is WebFeed { +// print(feed.nameForDisplay) +// let account = (feed as! WebFeed).account +// account?.removeWebFeed(feed as! WebFeed) +// } +// if feed is Folder { +// let account = (feed as! Folder).account +// account?.removeFolder(feed as! Folder, completion: { (result) in +// switch result { +// case .success( _): +// print("Deleted folder") +// case .failure(let err): +// print(err.localizedDescription) +// } +// }) +// } +// } +// } +// #else +// if item.feed is WebFeed { +// let account = (item.feed as! WebFeed).account +// account?.removeWebFeed(item.feed as! WebFeed) +// } +// if item.feed is Folder { +// let account = (item.feed as! Folder).account +// account?.removeFolder(item.feed as! Folder, completion: { (result) in +// switch result { +// case .success( _): +// print("Deleted folder") +// case .failure(let err): +// print(err.localizedDescription) +// } +// }) +// } +// #endif } } @@ -135,22 +145,31 @@ private extension SidebarModel { // MARK: Subscriptions func subscribeToSelectedFeedChanges() { - $selectedFeedIdentifiers.map { [weak self] feedIDs in - feedIDs.compactMap { self?.findFeed($0) } - } - .assign(to: &$selectedFeeds) - - $selectedFeedIdentifier.compactMap { [weak self] feedID in - if let feedID = feedID, let feed = self?.findFeed(feedID) { - return [feed] - } else { - return nil + let selectedFeedIdentifersPublisher = $selectedFeedIdentifiers + .map { [weak self] feedIDs -> [Feed] in + return feedIDs.compactMap { self?.findFeed($0) } } - } - .assign(to: &$selectedFeeds) + + let selectedFeedIdentiferPublisher = $selectedFeedIdentifier + .compactMap { [weak self] feedID -> [Feed]? in + if let feedID = feedID, let feed = self?.findFeed(feedID) { + return [feed] + } else { + return nil + } + } + + selectedFeedsPublisher = selectedFeedIdentifersPublisher + .merge(with: selectedFeedIdentiferPublisher) + .removeDuplicates(by: { previousFeeds, currentFeeds in + return previousFeeds.elementsEqual(currentFeeds, by: { $0.feedID == $1.feedID }) + }) + .eraseToAnyPublisher() } func subscribeToRebuildSidebarItemsEvents() { + guard let selectedFeedsPublisher = selectedFeedsPublisher else { return } + let chidrenDidChangePublisher = NotificationCenter.default.publisher(for: .ChildrenDidChange) let batchUpdateDidPerformPublisher = NotificationCenter.default.publisher(for: .BatchUpdateDidPerform) let displayNameDidChangePublisher = NotificationCenter.default.publisher(for: .DisplayNameDidChange) @@ -173,7 +192,7 @@ private extension SidebarModel { sidebarItemsPublisher = sidebarRebuildPublishers .prepend(kickStarter) .debounce(for: .milliseconds(500), scheduler: RunLoop.main) - .combineLatest($isReadFiltered, $selectedFeeds) + .combineLatest($isReadFiltered.removeDuplicates(), selectedFeedsPublisher) .compactMap { [weak self] _, readFilter, selectedFeeds in self?.rebuildSidebarItems(isReadFiltered: readFilter, selectedFeeds: selectedFeeds) } @@ -181,10 +200,10 @@ private extension SidebarModel { } func subscribeToNextUnread() { - guard let sidebarItemsPublisher = sidebarItemsPublisher else { return } + guard let sidebarItemsPublisher = sidebarItemsPublisher, let selectedFeedsPublisher = selectedFeedsPublisher else { return } selectNextUnread - .withLatestFrom(sidebarItemsPublisher, $selectedFeeds) + .withLatestFrom(sidebarItemsPublisher, selectedFeedsPublisher) .compactMap { [weak self] (sidebarItems, selectedFeeds) in return self?.nextUnread(sidebarItems: sidebarItems, selectedFeeds: selectedFeeds) } diff --git a/Multiplatform/Shared/Timeline/TimelineModel.swift b/Multiplatform/Shared/Timeline/TimelineModel.swift index ff94ad44a..66085b1a1 100644 --- a/Multiplatform/Shared/Timeline/TimelineModel.swift +++ b/Multiplatform/Shared/Timeline/TimelineModel.swift @@ -17,7 +17,7 @@ import Account import Articles protocol TimelineModelDelegate: class { - var selectedFeeds: Published<[Feed]>.Publisher { get } + var selectedFeedsPublisher: AnyPublisher<[Feed], Never>? { get } func timelineRequestedWebFeedSelection(_: TimelineModel, webFeed: WebFeed) } @@ -136,7 +136,8 @@ class TimelineModel: ObservableObject, UndoableCommandRunner { } func subscribeToSelectedFeedChanges() { - delegate?.selectedFeeds.sink { [weak self] feeds in + guard let selectedFeedsPublisher = delegate?.selectedFeedsPublisher else { return } + selectedFeedsPublisher.sink { [weak self] feeds in guard let self = self else { return } self.feeds = feeds self.fetchArticles()