From 067041195da46b5d856fdfc72571e995e3b4f600 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Fri, 17 Jul 2020 17:59:35 -0500 Subject: [PATCH] Implement Mark Above and Mark Below context menu items. --- Multiplatform/Shared/AppAssets.swift | 8 ++++ .../Shared/Timeline/TimelineContextMenu.swift | 41 +++++++++++++++++++ .../Shared/Timeline/TimelineItemView.swift | 3 ++ .../Shared/Timeline/TimelineModel.swift | 26 ++++++++++++ NetNewsWire.xcodeproj/project.pbxproj | 6 +++ 5 files changed, 84 insertions(+) create mode 100644 Multiplatform/Shared/Timeline/TimelineContextMenu.swift diff --git a/Multiplatform/Shared/AppAssets.swift b/Multiplatform/Shared/AppAssets.swift index 3243d65f9..da20e0d01 100644 --- a/Multiplatform/Shared/AppAssets.swift +++ b/Multiplatform/Shared/AppAssets.swift @@ -126,6 +126,14 @@ struct AppAssets { }() #endif + static var markBelowAsReadImage: Image = { + return Image(systemName: "arrowtriangle.down.circle") + }() + + static var markAboveAsReadImage: Image = { + return Image(systemName: "arrowtriangle.up.circle") + }() + static var nextArticleImage: Image = { return Image(systemName: "chevron.down") }() diff --git a/Multiplatform/Shared/Timeline/TimelineContextMenu.swift b/Multiplatform/Shared/Timeline/TimelineContextMenu.swift new file mode 100644 index 000000000..6b98e519f --- /dev/null +++ b/Multiplatform/Shared/Timeline/TimelineContextMenu.swift @@ -0,0 +1,41 @@ +// +// TimelineContextMenu.swift +// NetNewsWire +// +// Created by Maurice Parker on 7/17/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import SwiftUI + +struct TimelineContextMenu: View { + + @EnvironmentObject private var timelineModel: TimelineModel + var timelineItem: TimelineItem + + @ViewBuilder var body: some View { + + if timelineModel.canMarkAboveAsRead(timelineItem.article) { + Button { + timelineModel.markAboveAsRead(timelineItem.article) + } label: { + Text("Mark Above as Read") + #if os(iOS) + AppAssets.markAboveAsReadImage + #endif + } + } + + if timelineModel.canMarkBelowAsRead(timelineItem.article) { + Button { + timelineModel.markBelowAsRead(timelineItem.article) + } label: { + Text("Mark Below As Read") + #if os(iOS) + AppAssets.markBelowAsReadImage + #endif + } + } + + } +} diff --git a/Multiplatform/Shared/Timeline/TimelineItemView.swift b/Multiplatform/Shared/Timeline/TimelineItemView.swift index 4dca058a9..f481846b3 100644 --- a/Multiplatform/Shared/Timeline/TimelineItemView.swift +++ b/Multiplatform/Shared/Timeline/TimelineItemView.swift @@ -67,5 +67,8 @@ struct TimelineItemView: View { .onAppear { articleIconImageLoader.loadImage(for: timelineItem.article) } + .contextMenu { + TimelineContextMenu(timelineItem: timelineItem) + } } } diff --git a/Multiplatform/Shared/Timeline/TimelineModel.swift b/Multiplatform/Shared/Timeline/TimelineModel.swift index f0310d7c0..73d0497c8 100644 --- a/Multiplatform/Shared/Timeline/TimelineModel.swift +++ b/Multiplatform/Shared/Timeline/TimelineModel.swift @@ -171,6 +171,32 @@ class TimelineModel: ObservableObject, UndoableCommandRunner { } runCommand(markUnreadCommand) } + + func canMarkAboveAsRead(_ article: Article) -> Bool { + return articles.articlesAbove(article: article).canMarkAllAsRead() + } + + func canMarkBelowAsRead(_ article: Article) -> Bool { + return articles.articlesBelow(article: article).canMarkAllAsRead() + } + + func markAboveAsRead(_ article: Article) { + let articlesToMark = articles.articlesAbove(article: article) + guard !articlesToMark.isEmpty else { return } + guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: articlesToMark, markingRead: true, undoManager: undoManager) else { + return + } + runCommand(markReadCommand) + } + + func markBelowAsRead(_ article: Article) { + let articlesToMark = articles.articlesBelow(article: article) + guard !articlesToMark.isEmpty else { return } + guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: articlesToMark, markingRead: true, undoManager: undoManager) else { + return + } + runCommand(markReadCommand) + } func articleFor(_ articleID: String) -> Article? { return idToArticleDictionary[articleID] diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 42deff748..6219e64ed 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -374,6 +374,8 @@ 51B8104624C0E6D200C6C32D /* TimelineTextSizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B8104424C0E6D200C6C32D /* TimelineTextSizer.swift */; }; 51B8BCC224C25C3E00360B00 /* SidebarContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B8BCC124C25C3E00360B00 /* SidebarContextMenu.swift */; }; 51B8BCC324C25C3E00360B00 /* SidebarContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B8BCC124C25C3E00360B00 /* SidebarContextMenu.swift */; }; + 51B8BCE624C25F7C00360B00 /* TimelineContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B8BCE524C25F7C00360B00 /* TimelineContextMenu.swift */; }; + 51B8BCE724C25F7C00360B00 /* TimelineContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B8BCE524C25F7C00360B00 /* TimelineContextMenu.swift */; }; 51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */; }; 51BB7C312335ACDE008E8144 /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = 51BB7C302335ACDE008E8144 /* page.html */; }; 51BC4AFF247277E0000A6ED8 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */; }; @@ -2038,6 +2040,7 @@ 51B80F4524BF76E700C6C32D /* Browser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Browser.swift; sourceTree = ""; }; 51B8104424C0E6D200C6C32D /* TimelineTextSizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTextSizer.swift; sourceTree = ""; }; 51B8BCC124C25C3E00360B00 /* SidebarContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarContextMenu.swift; sourceTree = ""; }; + 51B8BCE524C25F7C00360B00 /* TimelineContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineContextMenu.swift; sourceTree = ""; }; 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleActivityItemSource.swift; sourceTree = ""; }; 51BB7C302335ACDE008E8144 /* page.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = page.html; sourceTree = ""; }; 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL-Extensions.swift"; sourceTree = ""; }; @@ -2952,6 +2955,7 @@ isa = PBXGroup; children = ( 51919FED24AB85E400541E64 /* TimelineContainerView.swift */, + 51B8BCE524C25F7C00360B00 /* TimelineContextMenu.swift */, 51919FF324AB869C00541E64 /* TimelineItem.swift */, 514E6C0124AD29A300AC6F6E /* TimelineItemStatusView.swift */, 514E6BD924ACEA0400AC6F6E /* TimelineItemView.swift */, @@ -5237,6 +5241,7 @@ 51919FEE24AB85E400541E64 /* TimelineContainerView.swift in Sources */, 653A4E7924BCA5BB00EF2D7F /* SettingsCloudKitAccountView.swift in Sources */, 51E4995724A8734D00B667CB /* ExtensionPoint.swift in Sources */, + 51B8BCE624C25F7C00360B00 /* TimelineContextMenu.swift in Sources */, 1776E88E24AC5F8A00E78166 /* AppDefaults.swift in Sources */, 51E4991124A808DE00B667CB /* SmallIconProvider.swift in Sources */, ); @@ -5339,6 +5344,7 @@ 1729529B24AA1FD200D65E66 /* MacSearchField.swift in Sources */, 51408B7F24A9EC6F0073CF4E /* SidebarItem.swift in Sources */, 514E6BDB24ACEA0400AC6F6E /* TimelineItemView.swift in Sources */, + 51B8BCE724C25F7C00360B00 /* TimelineContextMenu.swift in Sources */, 51E4996E24A8764C00B667CB /* ActivityManager.swift in Sources */, 1769E33024BD6271000E1E8E /* EditAccountCredentialsView.swift in Sources */, 51E4995A24A873F900B667CB /* ErrorHandler.swift in Sources */,