From b19ac104f495e878435b30ec533505badce58c3e Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 2 Mar 2022 10:24:35 -0800 Subject: [PATCH] fix context menus so all actions are included across devices --- .../Components/FeedItemContextMenuView.swift | 44 --------- .../App/Views/Home/HomeFeedViewIOS.swift | 30 +++++-- .../App/Views/Home/HomeFeedViewMac.swift | 56 +++++++++--- .../Sources/Views/Colors/Colors.swift | 4 + .../GridCard.swift} | 89 +++---------------- .../Views/FeedItem/HomeFeedCardView.swift | 51 +++++++++++ .../OmnivoreKit/Sources/Views/TextChip.swift | 22 +++++ 7 files changed, 160 insertions(+), 136 deletions(-) delete mode 100644 apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedItemContextMenuView.swift rename apple/OmnivoreKit/Sources/Views/{HomeFeedCardView.swift => FeedItem/GridCard.swift} (66%) create mode 100644 apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift create mode 100644 apple/OmnivoreKit/Sources/Views/TextChip.swift diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedItemContextMenuView.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedItemContextMenuView.swift deleted file mode 100644 index 6441e5454..000000000 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedItemContextMenuView.swift +++ /dev/null @@ -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 } - } - } - } -} diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index e6cf98415..95832a2d0 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -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 diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift index 6f767a15d..2018fe402 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift @@ -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 ) } diff --git a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift index 6111d40d6..e3270b61c 100644 --- a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift +++ b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift @@ -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 } diff --git a/apple/OmnivoreKit/Sources/Views/HomeFeedCardView.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift similarity index 66% rename from apple/OmnivoreKit/Sources/Views/HomeFeedCardView.swift rename to apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift index f4830896d..9049024b1 100644 --- a/apple/OmnivoreKit/Sources/Views/HomeFeedCardView.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift @@ -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) - ) - } -} diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift new file mode 100644 index 000000000..5f917ae30 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift @@ -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) + } +} diff --git a/apple/OmnivoreKit/Sources/Views/TextChip.swift b/apple/OmnivoreKit/Sources/Views/TextChip.swift new file mode 100644 index 000000000..775ad6854 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/TextChip.swift @@ -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) + ) + } +}