fix context menus so all actions are included across devices

This commit is contained in:
Satindar Dhillon 2022-03-02 10:24:35 -08:00
parent 2868e95716
commit b19ac104f4
7 changed files with 160 additions and 136 deletions

View file

@ -1,44 +0,0 @@
import Models
import Services
import SwiftUI
import Utils
import Views
struct FeedItemContextMenuView: View {
@EnvironmentObject var dataService: DataService
let item: FeedItem
@Binding var selectedLinkItem: FeedItem?
@Binding var snoozePresented: Bool
@Binding var itemToSnooze: FeedItem?
@ObservedObject var viewModel: HomeFeedViewModel
var body: some View {
if !item.isArchived {
Button(action: {
withAnimation(.linear(duration: 0.4)) {
viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: true)
if item == selectedLinkItem {
selectedLinkItem = nil
}
}
}, label: { Label("Archive", systemImage: "archivebox") })
} else {
Button(action: {
withAnimation(.linear(duration: 0.4)) {
viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: false)
}
}, label: { Label("Unarchive", systemImage: "tray.and.arrow.down.fill") })
}
if FeatureFlag.enableSnooze {
Button {
itemToSnooze = item
snoozePresented = true
} label: {
Label { Text("Snooze") } icon: { Image.moon }
}
}
}
}

View file

@ -152,13 +152,31 @@ import Views
viewModel: viewModel
)
.contextMenu {
FeedItemContextMenuView(
item: item,
selectedLinkItem: $selectedLinkItem,
snoozePresented: $snoozePresented,
itemToSnooze: $itemToSnooze,
viewModel: viewModel
Button(action: {
withAnimation(.linear(duration: 0.4)) {
viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: !item.isArchived)
}
}, label: {
Label(
item.isArchived ? "Unarchive" : "Archive",
systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox"
)
})
Button(
action: {
itemToRemove = item
confirmationShown = true
},
label: { Label("Delete Link", systemImage: "trash") }
)
if FeatureFlag.enableSnooze {
Button {
itemToSnooze = item
snoozePresented = true
} label: {
Label { Text("Snooze") } icon: { Image.moon }
}
}
}
if #available(iOS 15.0, *) {
link

View file

@ -22,18 +22,54 @@ import Views
List {
Section {
ForEach(viewModel.items) { item in
FeedCardNavigationLink(
item: item,
searchQuery: searchQuery,
selectedLinkItem: $selectedLinkItem,
viewModel: viewModel
)
.contextMenu {
FeedItemContextMenuView(
if #available(macOS 12.0, *) {
FeedCardNavigationLink(
item: item,
searchQuery: searchQuery,
selectedLinkItem: $selectedLinkItem,
viewModel: viewModel
)
.contextMenu {
Button(action: {
viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: !item.isArchived)
}, label: {
Label(
item.isArchived ? "Unarchive" : "Archive",
systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox"
)
})
Button(
action: {
itemToRemove = item
confirmationShown = true
},
label: { Label("Delete Link", systemImage: "trash") }
)
if FeatureFlag.enableSnooze {
Button {
itemToSnooze = item
snoozePresented = true
} label: {
Label { Text("Snooze") } icon: { Image.moon }
}
}
}
.alert("Are you sure?", isPresented: $confirmationShown) {
Button("Remove Link", role: .destructive) {
if let itemToRemove = itemToRemove {
withAnimation {
viewModel.removeLink(dataService: dataService, linkId: itemToRemove.id)
self.itemToRemove = nil
}
}
}
Button("Cancel", role: .cancel) { self.itemToRemove = nil }
}
} else {
FeedCardNavigationLink(
item: item,
searchQuery: searchQuery,
selectedLinkItem: $selectedLinkItem,
snoozePresented: $snoozePresented,
itemToSnooze: $itemToSnooze,
viewModel: viewModel
)
}

View file

@ -21,8 +21,12 @@ public extension Color {
#if os(iOS)
static var systemBackground: Color { Color(.systemBackground) }
static var systemPlaceholder: Color { Color(.placeholderText) }
static var secondarySystemGroupedBackground: Color { Color(.secondarySystemGroupedBackground) }
#elseif os(macOS)
static var systemBackground: Color { Color(.windowBackgroundColor) }
static var systemPlaceholder: Color { Color(.placeholderTextColor) }
// Just for compilation. secondarySystemGroupedBackground shouldn't be used on macOS
static var secondarySystemGroupedBackground: Color { Color(.windowBackgroundColor) }
#endif
}

View file

@ -2,54 +2,6 @@ import Models
import SwiftUI
import Utils
public struct FeedCard: View {
let item: FeedItem
public init(item: FeedItem) {
self.item = item
}
public var body: some View {
HStack(alignment: .top, spacing: 6) {
VStack(alignment: .leading, spacing: 6) {
Text(item.title)
.font(.appSubheadline)
.foregroundColor(.appGrayTextContrast)
.lineLimit(2)
.frame(maxWidth: .infinity, alignment: .leading)
if let author = item.author {
Text("By \(author)")
.font(.appCaption)
.foregroundColor(.appGrayText)
.lineLimit(1)
}
if let publisherURL = item.publisherHostname {
Text(publisherURL)
.font(.appCaption)
.foregroundColor(.appGrayText)
.underline()
.lineLimit(1)
}
}
.frame(maxWidth: .infinity)
.multilineTextAlignment(.leading)
.padding(0)
Group {
if let imageURL = item.imageURL {
AsyncImage(url: imageURL, isResizable: true)
.aspectRatio(1, contentMode: .fill)
.frame(width: 80, height: 80)
.cornerRadius(6)
}
}
}
.frame(maxWidth: .infinity, minHeight: 100, idealHeight: 100, maxHeight: 100)
}
}
public enum GridCardAction {
case toggleArchiveStatus
case delete
@ -110,14 +62,20 @@ public struct GridCard: View {
VStack(alignment: .leading, spacing: 16) {
// Progress Bar
Group {
if #available(iOS 15.0, *) {
ProgressView(value: min(abs(item.readingProgress) / 100, 1))
.tint(.appYellow48)
.frame(maxWidth: .infinity, alignment: .leading)
} else {
// Remove os check when dropping macOS 11
#if os(iOS)
if #available(iOS 15.0, *) {
ProgressView(value: min(abs(item.readingProgress) / 100, 1))
.tint(.appYellow48)
.frame(maxWidth: .infinity, alignment: .leading)
} else {
ProgressView(value: max(abs(item.readingProgress) / 100, 1))
.frame(maxWidth: .infinity, alignment: .leading)
}
#else
ProgressView(value: max(abs(item.readingProgress) / 100, 1))
.frame(maxWidth: .infinity, alignment: .leading)
}
#endif
}
.onTapGesture { tapHandler() }
@ -199,31 +157,10 @@ public struct GridCard: View {
}
}
.background(
Color(.secondarySystemGroupedBackground)
Color.secondarySystemGroupedBackground
.onTapGesture { tapHandler() }
)
.cornerRadius(6)
.contextMenu { contextMenuView }
}
}
struct TextChip: View {
let text: String
let color: Color
let cornerRadius = 20.0
var body: some View {
Text(text)
.padding(.horizontal, 10)
.padding(.vertical, 5)
.font(.appFootnote)
.foregroundColor(color)
.lineLimit(1)
.background(color.opacity(0.1))
.cornerRadius(cornerRadius)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(color.opacity(0.3), lineWidth: 1)
)
}
}

View file

@ -0,0 +1,51 @@
import Models
import SwiftUI
import Utils
public struct FeedCard: View {
let item: FeedItem
public init(item: FeedItem) {
self.item = item
}
public var body: some View {
HStack(alignment: .top, spacing: 6) {
VStack(alignment: .leading, spacing: 6) {
Text(item.title)
.font(.appSubheadline)
.foregroundColor(.appGrayTextContrast)
.lineLimit(2)
.frame(maxWidth: .infinity, alignment: .leading)
if let author = item.author {
Text("By \(author)")
.font(.appCaption)
.foregroundColor(.appGrayText)
.lineLimit(1)
}
if let publisherURL = item.publisherHostname {
Text(publisherURL)
.font(.appCaption)
.foregroundColor(.appGrayText)
.underline()
.lineLimit(1)
}
}
.frame(maxWidth: .infinity)
.multilineTextAlignment(.leading)
.padding(0)
Group {
if let imageURL = item.imageURL {
AsyncImage(url: imageURL, isResizable: true)
.aspectRatio(1, contentMode: .fill)
.frame(width: 80, height: 80)
.cornerRadius(6)
}
}
}
.frame(maxWidth: .infinity, minHeight: 100, idealHeight: 100, maxHeight: 100)
}
}

View file

@ -0,0 +1,22 @@
import SwiftUI
struct TextChip: View {
let text: String
let color: Color
let cornerRadius = 20.0
var body: some View {
Text(text)
.padding(.horizontal, 10)
.padding(.vertical, 5)
.font(.appFootnote)
.foregroundColor(color)
.lineLimit(1)
.background(color.opacity(0.1))
.cornerRadius(cornerRadius)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(color.opacity(0.3), lineWidth: 1)
)
}
}