From 7998b5450b15aa872c2e690bf0682ea0da6fd6f2 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 11 Jul 2020 19:52:28 -0500 Subject: [PATCH] Enabled toolbar buttons for read and star --- .../Article/ArticleToolbarModifier.swift | 4 +- Multiplatform/Shared/SceneModel.swift | 25 +++++-- .../Shared/SceneNavigationView.swift | 4 +- .../Shared/Timeline/TimelineModel.swift | 65 ++++++++++++++++++- 4 files changed, 85 insertions(+), 13 deletions(-) diff --git a/Multiplatform/Shared/Article/ArticleToolbarModifier.swift b/Multiplatform/Shared/Article/ArticleToolbarModifier.swift index e1ff6eb6f..7e3700527 100644 --- a/Multiplatform/Shared/Article/ArticleToolbarModifier.swift +++ b/Multiplatform/Shared/Article/ArticleToolbarModifier.swift @@ -33,7 +33,7 @@ struct ArticleToolbarModifier: ViewModifier { } ToolbarItem(placement: .bottomBar) { - Button(action: { }, label: { + Button(action: { sceneModel.toggleReadStatusForSelectedArticles() }, label: { if sceneModel.readButtonState == .on { AppAssets.readClosedImage } else { @@ -49,7 +49,7 @@ struct ArticleToolbarModifier: ViewModifier { } ToolbarItem(placement: .bottomBar) { - Button(action: { }, label: { + Button(action: { sceneModel.toggleStarredStatusForSelectedArticles() }, label: { if sceneModel.starButtonState == .on { AppAssets.starClosedImage } else { diff --git a/Multiplatform/Shared/SceneModel.swift b/Multiplatform/Shared/SceneModel.swift index 0c92e80a5..8a89e0812 100644 --- a/Multiplatform/Shared/SceneModel.swift +++ b/Multiplatform/Shared/SceneModel.swift @@ -7,6 +7,7 @@ // import Foundation +import Combine import Account import Articles import RSCore @@ -25,6 +26,8 @@ final class SceneModel: ObservableObject { private(set) var sidebarModel = SidebarModel() private(set) var timelineModel = TimelineModel() + private var selectedArticlesCancellable: AnyCancellable? + // MARK: Initialization API /// Prepares the SceneModel to be used in the views @@ -39,10 +42,22 @@ final class SceneModel: ObservableObject { self.webViewProvider = WebViewProvider(articleIconSchemeHandler: self.articleIconSchemeHandler!) NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil) + + selectedArticlesCancellable = timelineModel.$selectedArticles.sink { [weak self] articles in + self?.updateArticleState(articles: articles) + } } // MARK: Article Management API + func toggleReadStatusForSelectedArticles() { + timelineModel.toggleReadStatusForSelectedArticles() + } + + func toggleStarredStatusForSelectedArticles() { + timelineModel.toggleStarredStatusForSelectedArticles() + } + /// Retrieves the article before the given article in the Timeline func findPrevArticle(_ article: Article) -> Article? { return timelineModel.findPrevArticle(article) @@ -92,15 +107,13 @@ private extension SceneModel { } let selectedArticleIDs = timelineModel.selectedArticles.map { $0.articleID } if !articleIDs.intersection(selectedArticleIDs).isEmpty { - updateArticleState() + updateArticleState(articles: timelineModel.selectedArticles) } } // MARK: Button State Updates - func updateArticleState() { - let articles = timelineModel.selectedArticles - + func updateArticleState(articles: [Article]) { guard !articles.isEmpty else { readButtonState = nil starButtonState = nil @@ -116,9 +129,9 @@ private extension SceneModel { } if articles.anyArticleIsUnstarred() { - starButtonState = .on - } else { starButtonState = .off + } else { + starButtonState = .on } } diff --git a/Multiplatform/Shared/SceneNavigationView.swift b/Multiplatform/Shared/SceneNavigationView.swift index 346ad2564..e142486ab 100644 --- a/Multiplatform/Shared/SceneNavigationView.swift +++ b/Multiplatform/Shared/SceneNavigationView.swift @@ -98,7 +98,7 @@ struct SceneNavigationView: View { }).help("Go to Next Unread").padding(.trailing, 40) } ToolbarItem { - Button(action: { }, label: { + Button(action: { sceneModel.toggleReadStatusForSelectedArticles() }, label: { if sceneModel.readButtonState == .on { AppAssets.readClosedImage } else { @@ -109,7 +109,7 @@ struct SceneNavigationView: View { .help(sceneModel.readButtonState == .on ? "Mark as Unread" : "Mark as Read") } ToolbarItem { - Button(action: { }, label: { + Button(action: { sceneModel.toggleStarredStatusForSelectedArticles() }, label: { if sceneModel.starButtonState == .on { AppAssets.starClosedImage } else { diff --git a/Multiplatform/Shared/Timeline/TimelineModel.swift b/Multiplatform/Shared/Timeline/TimelineModel.swift index 319e050d9..901a0286e 100644 --- a/Multiplatform/Shared/Timeline/TimelineModel.swift +++ b/Multiplatform/Shared/Timeline/TimelineModel.swift @@ -31,6 +31,7 @@ class TimelineModel: ObservableObject, UndoableCommandRunner { private var selectedArticleIDsCancellable: AnyCancellable? private var selectedArticleIDCancellable: AnyCancellable? + private var selectedArticlesCancellable: AnyCancellable? private var fetchSerialNumber = 0 private let fetchRequestQueue = FetchRequestQueue() @@ -84,6 +85,16 @@ class TimelineModel: ObservableObject, UndoableCommandRunner { self.selectedArticles = [article] } } + + // TODO: This should be rewritten to use Combine correctly + selectedArticlesCancellable = $selectedArticles.sink { articles in + if articles.count == 1 { + let article = articles.first! + if !article.status.read { + markArticles(Set([article]), statusKey: .read, flag: true) + } + } + } } // MARK: API @@ -97,6 +108,56 @@ class TimelineModel: ObservableObject, UndoableCommandRunner { fetchAndReplaceArticlesAsync(feeds: feeds) } + func toggleReadStatusForSelectedArticles() { + guard !selectedArticles.isEmpty else { + return + } + if selectedArticles.anyArticleIsUnread() { + markSelectedArticlesAsRead() + } else { + markSelectedArticlesAsUnread() + } + } + + func markSelectedArticlesAsRead() { + guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: selectedArticles, markingRead: true, undoManager: undoManager) else { + return + } + runCommand(markReadCommand) + } + + func markSelectedArticlesAsUnread() { + guard let undoManager = undoManager, let markUnreadCommand = MarkStatusCommand(initialArticles: selectedArticles, markingRead: false, undoManager: undoManager) else { + return + } + runCommand(markUnreadCommand) + } + + func toggleStarredStatusForSelectedArticles() { + guard !selectedArticles.isEmpty else { + return + } + if selectedArticles.anyArticleIsUnstarred() { + markSelectedArticlesAsStarred() + } else { + markSelectedArticlesAsUnstarred() + } + } + + func markSelectedArticlesAsStarred() { + guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: selectedArticles, markingStarred: true, undoManager: undoManager) else { + return + } + runCommand(markReadCommand) + } + + func markSelectedArticlesAsUnstarred() { + guard let undoManager = undoManager, let markUnreadCommand = MarkStatusCommand(initialArticles: selectedArticles, markingStarred: false, undoManager: undoManager) else { + return + } + runCommand(markUnreadCommand) + } + func articleFor(_ articleID: String) -> Article? { return idToArticleDictionary[articleID] } @@ -138,7 +199,7 @@ private extension TimelineModel { } } - // MARK: + // MARK: Timeline Management func sortParametersDidChange() { performBlockAndRestoreSelection { @@ -202,6 +263,4 @@ private extension TimelineModel { // TODO: Update unread counts and other item done in didSet on AppKit } - // MARK: - Notifications - }