Merge pull request #2293 from stuartbreckenridge/feature/sidebar-context-menus

Feature/sidebar context menus
This commit is contained in:
Maurice Parker 2020-07-22 16:50:53 -05:00 committed by GitHub
commit 48c721a468
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 151 additions and 4 deletions

View File

@ -7,15 +7,20 @@
//
import SwiftUI
import RSCore
import Account
struct SidebarContextMenu: View {
@Environment(\.undoManager) var undoManager
@Environment(\.openURL) var openURL
@EnvironmentObject private var sidebarModel: SidebarModel
@Binding var showInspector: Bool
var sidebarItem: SidebarItem
@ViewBuilder var body: some View {
// MARK: Account Context Menu
if sidebarItem.representedType == .account {
Button {
showInspector = true
@ -26,6 +31,7 @@ struct SidebarContextMenu: View {
#endif
}
Button {
sidebarModel.markAllAsRead(account: sidebarItem.represented as! Account)
} label: {
Text("Mark All As Read")
#if os(iOS)
@ -34,8 +40,13 @@ struct SidebarContextMenu: View {
}
}
// MARK: Pseudofeed Context Menu
if sidebarItem.representedType == .pseudoFeed {
Button {
guard let feed = sidebarItem.feed else {
return
}
sidebarModel.markAllAsRead(feed: feed)
} label: {
Text("Mark All As Read")
#if os(iOS)
@ -44,6 +55,7 @@ struct SidebarContextMenu: View {
}
}
// MARK: Webfeed Context Menu
if sidebarItem.representedType == .webFeed {
Button {
showInspector = true
@ -54,6 +66,10 @@ struct SidebarContextMenu: View {
#endif
}
Button {
guard let feed = sidebarItem.feed else {
return
}
sidebarModel.markAllAsRead(feed: feed)
} label: {
Text("Mark All As Read")
#if os(iOS)
@ -62,6 +78,11 @@ struct SidebarContextMenu: View {
}
Divider()
Button {
guard let homepage = (sidebarItem.feed as? WebFeed)?.homePageURL,
let url = URL(string: homepage) else {
return
}
openURL(url)
} label: {
Text("Open Home Page")
#if os(iOS)
@ -70,6 +91,15 @@ struct SidebarContextMenu: View {
}
Divider()
Button {
guard let feedUrl = (sidebarItem.feed as? WebFeed)?.url else {
return
}
#if os(macOS)
URLPasteboardWriter.write(urlString: feedUrl, to: NSPasteboard.general)
#else
UIPasteboard.general.string = feedUrl
#endif
} label: {
Text("Copy Feed URL")
#if os(iOS)
@ -77,6 +107,14 @@ struct SidebarContextMenu: View {
#endif
}
Button {
guard let homepage = (sidebarItem.feed as? WebFeed)?.homePageURL else {
return
}
#if os(macOS)
URLPasteboardWriter.write(urlString: homepage, to: NSPasteboard.general)
#else
UIPasteboard.general.string = homepage
#endif
} label: {
Text("Copy Home Page URL")
#if os(iOS)
@ -85,6 +123,7 @@ struct SidebarContextMenu: View {
}
Divider()
Button {
sidebarModel.deleteItems(item: sidebarItem)
} label: {
Text("Delete")
#if os(iOS)
@ -93,6 +132,7 @@ struct SidebarContextMenu: View {
}
}
// MARK: Folder Context Menu
if sidebarItem.representedType == .folder {
Button {
showInspector = true
@ -103,6 +143,10 @@ struct SidebarContextMenu: View {
#endif
}
Button {
guard let feed = sidebarItem.feed else {
return
}
sidebarModel.markAllAsRead(feed: feed)
} label: {
Text("Mark All As Read")
#if os(iOS)
@ -111,6 +155,7 @@ struct SidebarContextMenu: View {
}
Divider()
Button {
sidebarModel.deleteItems(item: sidebarItem)
} label: {
Text("Delete")
#if os(iOS)

View File

@ -6,7 +6,7 @@
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
import SwiftUI
import RSCore
import Account

View File

@ -12,6 +12,7 @@ import Account
struct SidebarItemView: View {
@StateObject var feedIconImageLoader = FeedIconImageLoader()
@EnvironmentObject private var sidebarModel: SidebarModel
@State private var showInspector: Bool = false
var sidebarItem: SidebarItem
@ -53,6 +54,7 @@ struct SidebarItemView: View {
}
}.contextMenu {
SidebarContextMenu(showInspector: $showInspector, sidebarItem: sidebarItem)
.environmentObject(sidebarModel)
}
.sheet(isPresented: $showInspector, onDismiss: { showInspector = false}) {
InspectorView(sidebarItem: sidebarItem)

View File

@ -10,6 +10,7 @@ import Foundation
import Combine
import RSCore
import Account
import Articles
protocol SidebarModelDelegate: class {
func unreadCount(for: Feed) -> Int
@ -49,6 +50,93 @@ class SidebarModel: ObservableObject, UndoableCommandRunner {
}
// MARK: Side Context Menu Actions
extension SidebarModel {
func markAllAsRead(feed: Feed) {
var articles = Set<Article>()
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)
}
}
markAllAsRead(Array(articles))
}
func markAllAsRead(account: Account) {
var articles = Set<Article>()
for feed in account.flattenedWebFeeds() {
let unreadArticles = try! feed.fetchUnreadArticles()
articles.formUnion(unreadArticles)
}
markAllAsRead(Array(articles))
}
/// Marks provided artices as read.
/// - Parameter articles: An array of `Article`s.
/// - Warning: An `UndoManager` is created here as the `Environment`'s undo manager appears to be `nil`.
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
}
}
// MARK: Private
private extension SidebarModel {

View File

@ -13,11 +13,12 @@ struct SidebarView: View {
// I had to comment out SceneStorage because it blows up if used on macOS
// @SceneStorage("expandedContainers") private var expandedContainerData = Data()
@Environment(\.undoManager) var undoManager
@StateObject private var expandedContainers = SidebarExpandedContainers()
@EnvironmentObject private var refreshProgress: RefreshProgressModel
@EnvironmentObject private var sceneModel: SceneModel
@EnvironmentObject private var sidebarModel: SidebarModel
private let threshold: CGFloat = 80
@State private var previousScrollOffset: CGFloat = 0
@State private var scrollOffset: CGFloat = 0
@ -60,6 +61,9 @@ struct SidebarView: View {
.transition(.move(edge: .bottom))
}
}
.onAppear {
sidebarModel.undoManager = undoManager
}
#else
ZStack(alignment: .top) {
List {
@ -74,7 +78,11 @@ struct SidebarView: View {
ProgressView().offset(y: -40)
}
}
.onAppear {
sidebarModel.undoManager = undoManager
}
#endif
// .onAppear {
// expandedContainers.data = expandedContainerData
// }
@ -158,15 +166,19 @@ struct SidebarView: View {
}
} label: {
#if os(macOS)
SidebarItemView(sidebarItem: sidebarItem).padding(.leading, 4)
SidebarItemView(sidebarItem: sidebarItem)
.padding(.leading, 4)
.environmentObject(sidebarModel)
#else
if sidebarItem.representedType == .smartFeedController {
GeometryReader { proxy in
SidebarItemView(sidebarItem: sidebarItem)
.preference(key: RefreshKeyTypes.PrefKey.self, value: [RefreshKeyTypes.PrefData(vType: .movingView, bounds: proxy.frame(in: .global))])
.environmentObject(sidebarModel)
}
} else {
SidebarItemView(sidebarItem: sidebarItem)
.environmentObject(sidebarModel)
}
#endif
}