Enabled toolbar buttons for read and star
This commit is contained in:
parent
92ac91d9d5
commit
7998b5450b
@ -33,7 +33,7 @@ struct ArticleToolbarModifier: ViewModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ToolbarItem(placement: .bottomBar) {
|
ToolbarItem(placement: .bottomBar) {
|
||||||
Button(action: { }, label: {
|
Button(action: { sceneModel.toggleReadStatusForSelectedArticles() }, label: {
|
||||||
if sceneModel.readButtonState == .on {
|
if sceneModel.readButtonState == .on {
|
||||||
AppAssets.readClosedImage
|
AppAssets.readClosedImage
|
||||||
} else {
|
} else {
|
||||||
@ -49,7 +49,7 @@ struct ArticleToolbarModifier: ViewModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ToolbarItem(placement: .bottomBar) {
|
ToolbarItem(placement: .bottomBar) {
|
||||||
Button(action: { }, label: {
|
Button(action: { sceneModel.toggleStarredStatusForSelectedArticles() }, label: {
|
||||||
if sceneModel.starButtonState == .on {
|
if sceneModel.starButtonState == .on {
|
||||||
AppAssets.starClosedImage
|
AppAssets.starClosedImage
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Combine
|
||||||
import Account
|
import Account
|
||||||
import Articles
|
import Articles
|
||||||
import RSCore
|
import RSCore
|
||||||
@ -25,6 +26,8 @@ final class SceneModel: ObservableObject {
|
|||||||
private(set) var sidebarModel = SidebarModel()
|
private(set) var sidebarModel = SidebarModel()
|
||||||
private(set) var timelineModel = TimelineModel()
|
private(set) var timelineModel = TimelineModel()
|
||||||
|
|
||||||
|
private var selectedArticlesCancellable: AnyCancellable?
|
||||||
|
|
||||||
// MARK: Initialization API
|
// MARK: Initialization API
|
||||||
|
|
||||||
/// Prepares the SceneModel to be used in the views
|
/// Prepares the SceneModel to be used in the views
|
||||||
@ -39,10 +42,22 @@ final class SceneModel: ObservableObject {
|
|||||||
self.webViewProvider = WebViewProvider(articleIconSchemeHandler: self.articleIconSchemeHandler!)
|
self.webViewProvider = WebViewProvider(articleIconSchemeHandler: self.articleIconSchemeHandler!)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
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
|
// MARK: Article Management API
|
||||||
|
|
||||||
|
func toggleReadStatusForSelectedArticles() {
|
||||||
|
timelineModel.toggleReadStatusForSelectedArticles()
|
||||||
|
}
|
||||||
|
|
||||||
|
func toggleStarredStatusForSelectedArticles() {
|
||||||
|
timelineModel.toggleStarredStatusForSelectedArticles()
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves the article before the given article in the Timeline
|
/// Retrieves the article before the given article in the Timeline
|
||||||
func findPrevArticle(_ article: Article) -> Article? {
|
func findPrevArticle(_ article: Article) -> Article? {
|
||||||
return timelineModel.findPrevArticle(article)
|
return timelineModel.findPrevArticle(article)
|
||||||
@ -92,15 +107,13 @@ private extension SceneModel {
|
|||||||
}
|
}
|
||||||
let selectedArticleIDs = timelineModel.selectedArticles.map { $0.articleID }
|
let selectedArticleIDs = timelineModel.selectedArticles.map { $0.articleID }
|
||||||
if !articleIDs.intersection(selectedArticleIDs).isEmpty {
|
if !articleIDs.intersection(selectedArticleIDs).isEmpty {
|
||||||
updateArticleState()
|
updateArticleState(articles: timelineModel.selectedArticles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Button State Updates
|
// MARK: Button State Updates
|
||||||
|
|
||||||
func updateArticleState() {
|
func updateArticleState(articles: [Article]) {
|
||||||
let articles = timelineModel.selectedArticles
|
|
||||||
|
|
||||||
guard !articles.isEmpty else {
|
guard !articles.isEmpty else {
|
||||||
readButtonState = nil
|
readButtonState = nil
|
||||||
starButtonState = nil
|
starButtonState = nil
|
||||||
@ -116,9 +129,9 @@ private extension SceneModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if articles.anyArticleIsUnstarred() {
|
if articles.anyArticleIsUnstarred() {
|
||||||
starButtonState = .on
|
|
||||||
} else {
|
|
||||||
starButtonState = .off
|
starButtonState = .off
|
||||||
|
} else {
|
||||||
|
starButtonState = .on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ struct SceneNavigationView: View {
|
|||||||
}).help("Go to Next Unread").padding(.trailing, 40)
|
}).help("Go to Next Unread").padding(.trailing, 40)
|
||||||
}
|
}
|
||||||
ToolbarItem {
|
ToolbarItem {
|
||||||
Button(action: { }, label: {
|
Button(action: { sceneModel.toggleReadStatusForSelectedArticles() }, label: {
|
||||||
if sceneModel.readButtonState == .on {
|
if sceneModel.readButtonState == .on {
|
||||||
AppAssets.readClosedImage
|
AppAssets.readClosedImage
|
||||||
} else {
|
} else {
|
||||||
@ -109,7 +109,7 @@ struct SceneNavigationView: View {
|
|||||||
.help(sceneModel.readButtonState == .on ? "Mark as Unread" : "Mark as Read")
|
.help(sceneModel.readButtonState == .on ? "Mark as Unread" : "Mark as Read")
|
||||||
}
|
}
|
||||||
ToolbarItem {
|
ToolbarItem {
|
||||||
Button(action: { }, label: {
|
Button(action: { sceneModel.toggleStarredStatusForSelectedArticles() }, label: {
|
||||||
if sceneModel.starButtonState == .on {
|
if sceneModel.starButtonState == .on {
|
||||||
AppAssets.starClosedImage
|
AppAssets.starClosedImage
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,6 +31,7 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||||||
|
|
||||||
private var selectedArticleIDsCancellable: AnyCancellable?
|
private var selectedArticleIDsCancellable: AnyCancellable?
|
||||||
private var selectedArticleIDCancellable: AnyCancellable?
|
private var selectedArticleIDCancellable: AnyCancellable?
|
||||||
|
private var selectedArticlesCancellable: AnyCancellable?
|
||||||
|
|
||||||
private var fetchSerialNumber = 0
|
private var fetchSerialNumber = 0
|
||||||
private let fetchRequestQueue = FetchRequestQueue()
|
private let fetchRequestQueue = FetchRequestQueue()
|
||||||
@ -84,6 +85,16 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||||||
self.selectedArticles = [article]
|
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
|
// MARK: API
|
||||||
@ -97,6 +108,56 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||||||
fetchAndReplaceArticlesAsync(feeds: feeds)
|
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? {
|
func articleFor(_ articleID: String) -> Article? {
|
||||||
return idToArticleDictionary[articleID]
|
return idToArticleDictionary[articleID]
|
||||||
}
|
}
|
||||||
@ -138,7 +199,7 @@ private extension TimelineModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK:
|
// MARK: Timeline Management
|
||||||
|
|
||||||
func sortParametersDidChange() {
|
func sortParametersDidChange() {
|
||||||
performBlockAndRestoreSelection {
|
performBlockAndRestoreSelection {
|
||||||
@ -202,6 +263,4 @@ private extension TimelineModel {
|
|||||||
// TODO: Update unread counts and other item done in didSet on AppKit
|
// TODO: Update unread counts and other item done in didSet on AppKit
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Notifications
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user