Merge pull request #2293 from stuartbreckenridge/feature/sidebar-context-menus
Feature/sidebar context menus
This commit is contained in:
commit
48c721a468
|
@ -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)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import RSCore
|
||||
import Account
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue