From d82ffa44324d1cd2fadb8b45336a53f6caddab39 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 4 Oct 2022 08:45:48 -0700 Subject: [PATCH 01/11] add view highlights context menu to home feed items --- .../Sources/App/Views/Home/HomeFeedViewIOS.swift | 9 +++++++++ .../Sources/App/Views/Home/HomeFeedViewMac.swift | 2 ++ .../Sources/App/Views/Home/HomeFeedViewModel.swift | 1 + apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift | 1 + apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift | 5 +++++ 5 files changed, 18 insertions(+) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index a4a1e91b4..58800211b 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -62,6 +62,9 @@ import Views .sheet(item: $viewModel.itemUnderTitleEdit) { item in LinkedItemTitleEditView(item: item) } + .sheet(item: $viewModel.itemForHighlightsView) { item in + Text("Highlights view for: \(item.unwrappedTitle)") // TODO: implement view + } .toolbar { ToolbarItem(placement: .barTrailing) { Button("", action: {}) @@ -255,6 +258,10 @@ import Views viewModel: viewModel ) .contextMenu { + Button( + action: { viewModel.itemForHighlightsView = item }, + label: { Label("View Highlights", systemImage: "highlighter") } + ) Button( action: { viewModel.itemUnderTitleEdit = item }, label: { Label("Edit Title/Description", systemImage: "textbox") } @@ -372,6 +379,8 @@ import Views func contextMenuActionHandler(item: LinkedItem, action: GridCardAction) { switch action { + case .viewHighlights: + viewModel.itemForHighlightsView = item case .toggleArchiveStatus: viewModel.setLinkArchived(dataService: dataService, objectID: item.objectID, archived: !item.isArchived) case .delete: diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift index adced8706..49a12d1c9 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift @@ -36,6 +36,7 @@ import Views viewModel: viewModel ) .contextMenu { + // TODO: add highlights view button Button( action: { viewModel.itemUnderTitleEdit = item }, label: { Label("Edit Title/Description", systemImage: "textbox") } @@ -137,6 +138,7 @@ import Views .sheet(item: $viewModel.itemUnderTitleEdit) { item in LinkedItemTitleEditView(item: item) } + // TODO: add highlights view sheet .task { if viewModel.items.isEmpty { loadItems(isRefresh: true) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index 543d23727..65d4f4d75 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -17,6 +17,7 @@ import Views @Published var showPushNotificationPrimer = false @Published var itemUnderLabelEdit: LinkedItem? @Published var itemUnderTitleEdit: LinkedItem? + @Published var itemForHighlightsView: LinkedItem? @Published var searchTerm = "" @Published var selectedLabels = [LinkedItemLabel]() @Published var negatedLabels = [LinkedItemLabel]() diff --git a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift index 5ace227a6..5c8ac8803 100644 --- a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift +++ b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift @@ -15,4 +15,5 @@ public enum FeatureFlag { public static let enableSnooze = false public static let enableGridCardsOnPhone = false public static let enableTextToSpeechButton = true + public static let enableHighlightsView = true } diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift index d3f1707e8..99223de4d 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift @@ -8,6 +8,7 @@ public enum GridCardAction { case editLabels case editTitle case downloadAudio + case viewHighlights } public struct GridCard: View { @@ -45,6 +46,10 @@ public struct GridCard: View { var contextMenuView: some View { Group { + Button( + action: { menuActionHandler(.viewHighlights) }, + label: { Label("View Highlights", systemImage: "highlighter") } + ) Button( action: { menuActionHandler(.editTitle) }, label: { Label("Edit Title/Description", systemImage: "textbox") } From 397fb5d9f70783fd84909ada9fbde62d482b882b Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 4 Oct 2022 09:14:45 -0700 Subject: [PATCH 02/11] add a basic highlights list view --- .../Views/Highlights/HighlightsListView.swift | 60 +++++++++++++++++++ .../Highlights/HighlightsListViewModel.swift | 13 ++++ .../App/Views/Home/HomeFeedViewIOS.swift | 2 +- .../Views/WebReader/WebReaderContainer.swift | 8 +++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift create mode 100644 apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift new file mode 100644 index 000000000..75332e587 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift @@ -0,0 +1,60 @@ +import Models +import Services +import SwiftUI +import Views + +struct HighlightsListView: View { + @EnvironmentObject var dataService: DataService + @Environment(\.presentationMode) private var presentationMode + @StateObject var viewModel = HighlightsListViewModel() + + let item: LinkedItem + + var innerBody: some View { + List { + Section { + ForEach(viewModel.highlights, id: \.self) { highlight in + Text(highlight.quote ?? "no quote") + } + } + } + .navigationTitle("Highlights") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + dismissButton + } + } + #else + .toolbar { + ToolbarItemGroup { + dismissButton + } + } + #endif + } + + var dismissButton: some View { + Button( + action: { presentationMode.wrappedValue.dismiss() }, + label: { Text("Done").foregroundColor(.appGrayTextContrast) } + ) + } + + var body: some View { + Group { + #if os(iOS) + NavigationView { + innerBody + } + #elseif os(macOS) + innerBody + .frame(minWidth: 400, minHeight: 400) + #endif + } + .task { + viewModel.load(item: item) + } + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift new file mode 100644 index 000000000..611b9a2aa --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift @@ -0,0 +1,13 @@ +import CoreData +import Models +import Services +import SwiftUI +import Views + +@MainActor final class HighlightsListViewModel: ObservableObject { + @Published var highlights = [Highlight]() + + func load(item: LinkedItem) { + highlights = item.highlights.asArray(of: Highlight.self) + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 58800211b..f74763fcf 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -63,7 +63,7 @@ import Views LinkedItemTitleEditView(item: item) } .sheet(item: $viewModel.itemForHighlightsView) { item in - Text("Highlights view for: \(item.unwrappedTitle)") // TODO: implement view + HighlightsListView(item: item) } .toolbar { ToolbarItem(placement: .barTrailing) { diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index 3624aec4b..b13acc07a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -12,6 +12,7 @@ struct WebReaderContainerView: View { @State private var showPreferencesPopover = false @State private var showLabelsModal = false @State private var showTitleEdit = false + @State private var showHighlightsView = false @State var showHighlightAnnotationModal = false @State var safariWebLink: SafariWebLink? @State private var navBarVisibilityRatio = 1.0 @@ -131,6 +132,10 @@ struct WebReaderContainerView: View { Menu( content: { Group { + Button( + action: { showHighlightsView = true }, + label: { Label("View Highlights", systemImage: "highlighter") } + ) Button( action: { showTitleEdit = true }, label: { Label("Edit Title/Description", systemImage: "textbox") } @@ -198,6 +203,9 @@ struct WebReaderContainerView: View { .sheet(isPresented: $showTitleEdit) { LinkedItemTitleEditView(item: item) } + .sheet(isPresented: $showHighlightsView) { + HighlightsListView(item: item) + } #if os(macOS) .buttonStyle(PlainButtonStyle()) #endif From 29d14b3e8d9f513208dde045e5c218b468f6b645 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 4 Oct 2022 09:36:28 -0700 Subject: [PATCH 03/11] create highlight list card --- .../App/Views/Highlights/HighlightsListCard.swift | 10 ++++++++++ .../App/Views/Highlights/HighlightsListView.swift | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift new file mode 100644 index 000000000..200fd4c85 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift @@ -0,0 +1,10 @@ +import Models +import SwiftUI + +struct HighlightsListCard: View { + let highlight: Highlight + + var body: some View { + Text(highlight.quote ?? "no quote") + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift index 75332e587..157a998d4 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift @@ -14,11 +14,12 @@ struct HighlightsListView: View { List { Section { ForEach(viewModel.highlights, id: \.self) { highlight in - Text(highlight.quote ?? "no quote") + HighlightsListCard(highlight: highlight) } } } .navigationTitle("Highlights") + .listStyle(PlainListStyle()) #if os(iOS) .navigationBarTitleDisplayMode(.inline) .toolbar { From 18760d9043c18b820ad4b5c9d6f51b2c6ff9cb1b Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 4 Oct 2022 10:09:26 -0700 Subject: [PATCH 04/11] style highlights card --- .../Views/Highlights/HighlightsListCard.swift | 49 ++++++++++++++++++- .../Sources/Utils/FeatureFlags.swift | 2 +- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift index 200fd4c85..7767abbb5 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift @@ -2,9 +2,56 @@ import Models import SwiftUI struct HighlightsListCard: View { + @State var isContextMenuOpen = false + let highlight: Highlight + var contextMenuView: some View { + Group { + Button( + action: {}, + label: { Label("Stubby One", systemImage: "highlighter") } + ) + Button( + action: {}, + label: { Label("Stubby Two", systemImage: "textbox") } + ) + } + } + var body: some View { - Text(highlight.quote ?? "no quote") + VStack(alignment: .leading) { + HStack { + Image(systemName: "highlighter") + + Text(highlight.shortId ?? "no short Id") + .font(.appHeadline) + .foregroundColor(.appGrayTextContrast) + .lineLimit(1) + + Spacer() + + Menu( + content: { contextMenuView }, + label: { + Image(systemName: "ellipsis") + .foregroundColor(.appGrayTextContrast) + .padding() + } + ) + .frame(width: 16, height: 16, alignment: .center) + .onTapGesture { isContextMenuOpen = true } + } + .padding(.top, 8) + + HStack { + Divider() + .frame(width: 6) + .overlay(Color.appYellow48) + + Text(highlight.quote ?? "") + } + .padding(.bottom, 8) + } } } diff --git a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift index 5c8ac8803..18db882c2 100644 --- a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift +++ b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift @@ -13,7 +13,7 @@ public enum FeatureFlag { public static let enablePushNotifications = false public static let enableShareButton = false public static let enableSnooze = false - public static let enableGridCardsOnPhone = false + public static let enableGridCardsOnPhone = true public static let enableTextToSpeechButton = true public static let enableHighlightsView = true } From 79875706e9893a78b314483ba55642496075989e Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 4 Oct 2022 12:29:03 -0700 Subject: [PATCH 05/11] add note section to highlights view --- .../Views/Highlights/HighlightsListCard.swift | 35 +++++++++++++++++-- .../Highlights/HighlightsListViewModel.swift | 6 ++++ .../Sources/Utils/FeatureFlags.swift | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift index 7767abbb5..89cd3a8a2 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift @@ -19,12 +19,33 @@ struct HighlightsListCard: View { } } + var noteSection: some View { + Group { + HStack { + Image(systemName: "note.text") + + Text("Note") + .font(.appSubheadline) + .foregroundColor(.appGrayTextContrast) + .lineLimit(1) + + Spacer() + } + + Text(highlight.annotation ?? "") + } + } + + var addNoteSection: some View { + Text("Tap to add a note") + } + var body: some View { VStack(alignment: .leading) { HStack { Image(systemName: "highlighter") - Text(highlight.shortId ?? "no short Id") + Text(highlight.highlightCardTitle) .font(.appHeadline) .foregroundColor(.appGrayTextContrast) .lineLimit(1) @@ -49,7 +70,17 @@ struct HighlightsListCard: View { .frame(width: 6) .overlay(Color.appYellow48) - Text(highlight.quote ?? "") + VStack(alignment: .leading, spacing: 8) { + Text(highlight.quote ?? "") + + Divider() + + if highlight.annotation == nil || highlight.annotation?.isEmpty == true { + addNoteSection + } else { + noteSection + } + } } .padding(.bottom, 8) } diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift index 611b9a2aa..bf6905755 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift @@ -11,3 +11,9 @@ import Views highlights = item.highlights.asArray(of: Highlight.self) } } + +extension Highlight { + var highlightCardTitle: String { + "Highlight" + } +} diff --git a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift index 18db882c2..5c8ac8803 100644 --- a/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift +++ b/apple/OmnivoreKit/Sources/Utils/FeatureFlags.swift @@ -13,7 +13,7 @@ public enum FeatureFlag { public static let enablePushNotifications = false public static let enableShareButton = false public static let enableSnooze = false - public static let enableGridCardsOnPhone = true + public static let enableGridCardsOnPhone = false public static let enableTextToSpeechButton = true public static let enableHighlightsView = true } From cffdfa4342d3a9129a6029c77d738dc97fe78310 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 4 Oct 2022 22:09:44 -0700 Subject: [PATCH 06/11] sort highlights by createdAt --- .../App/Views/Highlights/HighlightsListCard.swift | 14 +++++++++++++- .../Views/Highlights/HighlightsListViewModel.swift | 6 +++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift index 89cd3a8a2..aa067a6fa 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift @@ -37,7 +37,19 @@ struct HighlightsListCard: View { } var addNoteSection: some View { - Text("Tap to add a note") + HStack { + Image(systemName: "note.text.badge.plus").foregroundColor(.appGrayTextContrast) + + Text("Add a Note") + .font(.appSubheadline) + .foregroundColor(.appGrayTextContrast) + .lineLimit(1) + + Spacer() + } + .onTapGesture { + print("add a note") + } } var body: some View { diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift index bf6905755..a407be8a2 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift @@ -8,7 +8,11 @@ import Views @Published var highlights = [Highlight]() func load(item: LinkedItem) { - highlights = item.highlights.asArray(of: Highlight.self) + let unsortedHighlights = item.highlights.asArray(of: Highlight.self) + + highlights = unsortedHighlights.sorted { + ($0.createdAt ?? Date()) < ($1.createdAt ?? Date()) + } } } From 49e911f023c87007b2309586aa3a6e58bc530ae7 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 5 Oct 2022 09:53:56 -0700 Subject: [PATCH 07/11] show highlight annotation sheet when tapping on note or 'add a note' --- .../Views/Highlights/HighlightsListCard.swift | 23 ++++++++++++++++++- .../Article/HighlightAnnotationSheet.swift | 6 ++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift index aa067a6fa..fd4e3fd12 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift @@ -1,8 +1,11 @@ import Models import SwiftUI +import Views struct HighlightsListCard: View { @State var isContextMenuOpen = false + @State var annotation = String() + @State var showAnnotationModal = false let highlight: Highlight @@ -34,6 +37,10 @@ struct HighlightsListCard: View { Text(highlight.annotation ?? "") } + .onTapGesture { + annotation = highlight.annotation ?? "" + showAnnotationModal = true + } } var addNoteSection: some View { @@ -48,7 +55,8 @@ struct HighlightsListCard: View { Spacer() } .onTapGesture { - print("add a note") + annotation = highlight.annotation ?? "" + showAnnotationModal = true } } @@ -96,5 +104,18 @@ struct HighlightsListCard: View { } .padding(.bottom, 8) } + .sheet(isPresented: $showAnnotationModal) { + HighlightAnnotationSheet( + annotation: $annotation, + onSave: { + print("new annotation = \(annotation)") + showAnnotationModal = false + }, + onCancel: { + print("cancel annotation") + showAnnotationModal = false + } + ) + } } } diff --git a/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift b/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift index 30dc1def4..bf0a872c6 100644 --- a/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift +++ b/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift @@ -22,15 +22,13 @@ public struct HighlightAnnotationSheet: View { HStack { Button("Cancel", action: onCancel) Spacer() - HStack { - Image(systemName: "note.text") - Text("Note") - } + Label("Note", systemImage: "note.text") Spacer() Button("Save") { onSave() } } + .foregroundColor(.appGrayTextContrast) ScrollView { TextEditor(text: $annotation) From c5ad8a238c7d33b9dbb7c1e56d83f57de8312a55 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 5 Oct 2022 10:57:21 -0700 Subject: [PATCH 08/11] persist annotation changes from highlights view --- .../Views/Highlights/HighlightsListCard.swift | 18 +++---- .../Views/Highlights/HighlightsListView.swift | 15 ++++-- .../Highlights/HighlightsListViewModel.swift | 50 +++++++++++++++---- .../App/Views/Home/HomeFeedViewIOS.swift | 2 +- .../Views/WebReader/WebReaderContainer.swift | 2 +- .../Sources/Models/DataModels/FeedItem.swift | 6 +++ 6 files changed, 68 insertions(+), 25 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift index fd4e3fd12..f488db303 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift @@ -7,7 +7,8 @@ struct HighlightsListCard: View { @State var annotation = String() @State var showAnnotationModal = false - let highlight: Highlight + let highlightParams: HighlightListItemParams + let onSaveAnnotation: (String) -> Void var contextMenuView: some View { Group { @@ -35,10 +36,10 @@ struct HighlightsListCard: View { Spacer() } - Text(highlight.annotation ?? "") + Text(highlightParams.annotation) } .onTapGesture { - annotation = highlight.annotation ?? "" + annotation = highlightParams.annotation showAnnotationModal = true } } @@ -55,7 +56,7 @@ struct HighlightsListCard: View { Spacer() } .onTapGesture { - annotation = highlight.annotation ?? "" + annotation = highlightParams.annotation showAnnotationModal = true } } @@ -65,7 +66,7 @@ struct HighlightsListCard: View { HStack { Image(systemName: "highlighter") - Text(highlight.highlightCardTitle) + Text(highlightParams.title) .font(.appHeadline) .foregroundColor(.appGrayTextContrast) .lineLimit(1) @@ -91,11 +92,11 @@ struct HighlightsListCard: View { .overlay(Color.appYellow48) VStack(alignment: .leading, spacing: 8) { - Text(highlight.quote ?? "") + Text(highlightParams.quote) Divider() - if highlight.annotation == nil || highlight.annotation?.isEmpty == true { + if highlightParams.annotation.isEmpty { addNoteSection } else { noteSection @@ -108,11 +109,10 @@ struct HighlightsListCard: View { HighlightAnnotationSheet( annotation: $annotation, onSave: { - print("new annotation = \(annotation)") + onSaveAnnotation(annotation) showAnnotationModal = false }, onCancel: { - print("cancel annotation") showAnnotationModal = false } ) diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift index 157a998d4..b3c0e848a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift @@ -1,3 +1,4 @@ +import CoreData import Models import Services import SwiftUI @@ -8,13 +9,19 @@ struct HighlightsListView: View { @Environment(\.presentationMode) private var presentationMode @StateObject var viewModel = HighlightsListViewModel() - let item: LinkedItem + let itemObjectID: NSManagedObjectID var innerBody: some View { List { Section { - ForEach(viewModel.highlights, id: \.self) { highlight in - HighlightsListCard(highlight: highlight) + ForEach(viewModel.highlightItems) { highlightParams in + HighlightsListCard(highlightParams: highlightParams) { newAnnotation in + viewModel.updateAnnotation( + highlightID: highlightParams.highlightID, + annotation: newAnnotation, + dataService: dataService + ) + } } } } @@ -55,7 +62,7 @@ struct HighlightsListView: View { #endif } .task { - viewModel.load(item: item) + viewModel.load(itemObjectID: itemObjectID, dataService: dataService) } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift index a407be8a2..791d85c29 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift @@ -4,20 +4,50 @@ import Services import SwiftUI import Views -@MainActor final class HighlightsListViewModel: ObservableObject { - @Published var highlights = [Highlight]() +struct HighlightListItemParams: Identifiable { + let id = UUID() + let highlightID: String + let title: String + let annotation: String + let quote: String +} - func load(item: LinkedItem) { +@MainActor final class HighlightsListViewModel: ObservableObject { + @Published var highlightItems = [HighlightListItemParams]() + + func load(itemObjectID: NSManagedObjectID, dataService: DataService) { + if let linkedItem = dataService.viewContext.object(with: itemObjectID) as? LinkedItem { + loadHighlights(item: linkedItem) + } + } + + func updateAnnotation(highlightID: String, annotation: String, dataService: DataService) { + dataService.updateHighlightAttributes(highlightID: highlightID, annotation: annotation) + + if let index = highlightItems.firstIndex(where: { $0.highlightID == highlightID }) { + highlightItems[index] = HighlightListItemParams( + highlightID: highlightID, + title: highlightItems[index].title, + annotation: annotation, + quote: highlightItems[index].quote + ) + } + } + + private func loadHighlights(item: LinkedItem) { let unsortedHighlights = item.highlights.asArray(of: Highlight.self) - highlights = unsortedHighlights.sorted { + let highlights = unsortedHighlights.sorted { ($0.createdAt ?? Date()) < ($1.createdAt ?? Date()) } + + highlightItems = highlights.map { + HighlightListItemParams( + highlightID: $0.unwrappedID, + title: "Highlight", + annotation: $0.annotation ?? "", + quote: $0.quote ?? "" + ) + } } } - -extension Highlight { - var highlightCardTitle: String { - "Highlight" - } -} diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index f74763fcf..85cb7c15b 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -63,7 +63,7 @@ import Views LinkedItemTitleEditView(item: item) } .sheet(item: $viewModel.itemForHighlightsView) { item in - HighlightsListView(item: item) + HighlightsListView(itemObjectID: item.objectID) } .toolbar { ToolbarItem(placement: .barTrailing) { diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index b13acc07a..a005c3808 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -204,7 +204,7 @@ struct WebReaderContainerView: View { LinkedItemTitleEditView(item: item) } .sheet(isPresented: $showHighlightsView) { - HighlightsListView(item: item) + HighlightsListView(itemObjectID: item.objectID) } #if os(macOS) .buttonStyle(PlainButtonStyle()) diff --git a/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift b/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift index e96ba857c..e8785777e 100644 --- a/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift +++ b/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift @@ -87,6 +87,12 @@ public extension LinkedItem { } } + var sortedHighlights: [Highlight] { + highlights.asArray(of: Highlight.self).sorted { + ($0.createdAt ?? Date()) < ($1.createdAt ?? Date()) + } + } + var labelsJSONString: String { let labels = self.labels.asArray(of: LinkedItemLabel.self).map { label in [ From db9246553fb121c394cef06928a82e48a1d48d07 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 5 Oct 2022 11:25:05 -0700 Subject: [PATCH 09/11] message web container view when highlight view is dismissed and mutations have occurred --- .../App/Views/Highlights/HighlightsListCard.swift | 2 ++ .../App/Views/Highlights/HighlightsListView.swift | 6 +++++- .../Sources/App/Views/Home/HomeFeedViewIOS.swift | 3 ++- .../App/Views/WebReader/WebReaderContainer.swift | 13 +++++++++++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift index f488db303..be1857d6f 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift @@ -8,6 +8,7 @@ struct HighlightsListCard: View { @State var showAnnotationModal = false let highlightParams: HighlightListItemParams + @Binding var hasHighlightMutations: Bool let onSaveAnnotation: (String) -> Void var contextMenuView: some View { @@ -111,6 +112,7 @@ struct HighlightsListCard: View { onSave: { onSaveAnnotation(annotation) showAnnotationModal = false + hasHighlightMutations = true }, onCancel: { showAnnotationModal = false diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift index b3c0e848a..ac23279f6 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift @@ -10,12 +10,16 @@ struct HighlightsListView: View { @StateObject var viewModel = HighlightsListViewModel() let itemObjectID: NSManagedObjectID + @Binding var hasHighlightMutations: Bool var innerBody: some View { List { Section { ForEach(viewModel.highlightItems) { highlightParams in - HighlightsListCard(highlightParams: highlightParams) { newAnnotation in + HighlightsListCard( + highlightParams: highlightParams, + hasHighlightMutations: $hasHighlightMutations + ) { newAnnotation in viewModel.updateAnnotation( highlightID: highlightParams.highlightID, annotation: newAnnotation, diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 85cb7c15b..b7d1cfa21 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -11,6 +11,7 @@ import Views private let enableGrid = UIDevice.isIPad || FeatureFlag.enableGridCardsOnPhone struct HomeFeedContainerView: View { + @State var hasHighlightMutations = false @EnvironmentObject var dataService: DataService @EnvironmentObject var audioController: AudioController @@ -63,7 +64,7 @@ import Views LinkedItemTitleEditView(item: item) } .sheet(item: $viewModel.itemForHighlightsView) { item in - HighlightsListView(itemObjectID: item.objectID) + HighlightsListView(itemObjectID: item.objectID, hasHighlightMutations: $hasHighlightMutations) } .toolbar { ToolbarItem(placement: .barTrailing) { diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index a005c3808..11b7e0717 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -13,6 +13,7 @@ struct WebReaderContainerView: View { @State private var showLabelsModal = false @State private var showTitleEdit = false @State private var showHighlightsView = false + @State private var hasPerformedHighlightMutations = false @State var showHighlightAnnotationModal = false @State var safariWebLink: SafariWebLink? @State private var navBarVisibilityRatio = 1.0 @@ -44,6 +45,11 @@ struct WebReaderContainerView: View { } } + func onHighlightListViewDismissal() { + print("has mutations: \(hasPerformedHighlightMutations)") + hasPerformedHighlightMutations = false + } + private func handleHighlightAction(message: WKScriptMessage) { guard let messageBody = message.body as? [String: String] else { return } guard let actionID = messageBody["actionID"] else { return } @@ -203,8 +209,11 @@ struct WebReaderContainerView: View { .sheet(isPresented: $showTitleEdit) { LinkedItemTitleEditView(item: item) } - .sheet(isPresented: $showHighlightsView) { - HighlightsListView(itemObjectID: item.objectID) + .sheet(isPresented: $showHighlightsView, onDismiss: onHighlightListViewDismissal) { + HighlightsListView( + itemObjectID: item.objectID, + hasHighlightMutations: $hasPerformedHighlightMutations + ) } #if os(macOS) .buttonStyle(PlainButtonStyle()) From e36920e96ea5dfcfe33e66f30f5b76dcdd109be3 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 5 Oct 2022 12:01:40 -0700 Subject: [PATCH 10/11] reload web view if article highlights had been mutated from the highlits list modal --- .../Sources/App/Views/WebReader/WebReader.swift | 5 ++++- .../App/Views/WebReader/WebReaderContainer.swift | 16 ++++++++++++++-- .../Views/WebReader/WebReaderCoordinator.swift | 1 + .../Models/DataModels/ArticleContent.swift | 1 + 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift index f30aab097..78a8f3dce 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift @@ -70,6 +70,7 @@ struct WebReader: PlatformViewRepresentable { context.coordinator.linkHandler = openLinkAction context.coordinator.webViewActionHandler = webViewActionHandler context.coordinator.updateNavBarVisibilityRatio = navBarVisibilityRatioUpdater + context.coordinator.articleContentID = articleContent.id loadContent(webView: webView) return webView @@ -101,8 +102,10 @@ struct WebReader: PlatformViewRepresentable { } // If the webview had been terminated `needsReload` will have been set to true - if context.coordinator.needsReload { + // Or if the articleContent value has changed then it's id will be different from the coordinator's + if context.coordinator.needsReload || context.coordinator.articleContentID != articleContent.id { loadContent(webView: webView) + context.coordinator.articleContentID = articleContent.id context.coordinator.needsReload = false return } diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index 11b7e0717..83c78fbf9 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -46,8 +46,20 @@ struct WebReaderContainerView: View { } func onHighlightListViewDismissal() { - print("has mutations: \(hasPerformedHighlightMutations)") - hasPerformedHighlightMutations = false + // Reload the web view if mutation happened in highlights list modal + guard hasPerformedHighlightMutations else { return } + + hasPerformedHighlightMutations.toggle() + + Task { + if let username = dataService.currentViewer?.username { + await viewModel.loadContent( + dataService: dataService, + username: username, + itemID: item.unwrappedID + ) + } + } } private func handleHighlightAction(message: WKScriptMessage) { diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderCoordinator.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderCoordinator.swift index acaaafd18..a08377a45 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderCoordinator.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderCoordinator.swift @@ -19,6 +19,7 @@ final class WebReaderCoordinator: NSObject { var previousShowNavBarActionID: UUID? var previousShareActionID: UUID? var updateNavBarVisibilityRatio: (Double) -> Void = { _ in } + var articleContentID = UUID() private var yOffsetAtStartOfDrag: Double? private var lastYOffset: Double = 0 private var hasDragged = false diff --git a/apple/OmnivoreKit/Sources/Models/DataModels/ArticleContent.swift b/apple/OmnivoreKit/Sources/Models/DataModels/ArticleContent.swift index 028dcbacc..86c5469d1 100644 --- a/apple/OmnivoreKit/Sources/Models/DataModels/ArticleContent.swift +++ b/apple/OmnivoreKit/Sources/Models/DataModels/ArticleContent.swift @@ -9,6 +9,7 @@ public enum ArticleContentStatus: String { } public struct ArticleContent { + public let id = UUID() public let title: String public let htmlContent: String public let highlightsJSONString: String From 36e5e768098cfffa313fb9e88c843217abc80969 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 5 Oct 2022 12:35:35 -0700 Subject: [PATCH 11/11] add copy and delete options to highlight list cards --- .../Views/Highlights/HighlightsListCard.swift | 21 +++++++++++++--- .../Views/Highlights/HighlightsListView.swift | 25 +++++++++++++------ .../Highlights/HighlightsListViewModel.swift | 5 ++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift index be1857d6f..efa1cd851 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift @@ -10,16 +10,29 @@ struct HighlightsListCard: View { let highlightParams: HighlightListItemParams @Binding var hasHighlightMutations: Bool let onSaveAnnotation: (String) -> Void + let onDeleteHighlight: () -> Void var contextMenuView: some View { Group { Button( - action: {}, - label: { Label("Stubby One", systemImage: "highlighter") } + action: { + #if os(iOS) + UIPasteboard.general.string = highlightParams.quote + #endif + + #if os(macOS) + let pasteBoard = NSPasteboard.general + pasteBoard.clearContents() + pasteBoard.writeObjects([highlightParams.quote as NSString]) + #endif + + Snackbar.show(message: "Highlight copied") + }, + label: { Label("Copy", systemImage: "doc.on.doc") } ) Button( - action: {}, - label: { Label("Stubby Two", systemImage: "textbox") } + action: onDeleteHighlight, + label: { Label("Delete", systemImage: "trash") } ) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift index ac23279f6..b16a0c3bf 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListView.swift @@ -18,14 +18,23 @@ struct HighlightsListView: View { ForEach(viewModel.highlightItems) { highlightParams in HighlightsListCard( highlightParams: highlightParams, - hasHighlightMutations: $hasHighlightMutations - ) { newAnnotation in - viewModel.updateAnnotation( - highlightID: highlightParams.highlightID, - annotation: newAnnotation, - dataService: dataService - ) - } + hasHighlightMutations: $hasHighlightMutations, + onSaveAnnotation: { + viewModel.updateAnnotation( + highlightID: highlightParams.highlightID, + annotation: $0, + dataService: dataService + ) + }, + onDeleteHighlight: { + hasHighlightMutations = true + + viewModel.deleteHighlight( + highlightID: highlightParams.highlightID, + dataService: dataService + ) + } + ) } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift index 791d85c29..6eca3b4e9 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListViewModel.swift @@ -34,6 +34,11 @@ struct HighlightListItemParams: Identifiable { } } + func deleteHighlight(highlightID: String, dataService: DataService) { + dataService.deleteHighlight(highlightID: highlightID) + highlightItems.removeAll { $0.highlightID == highlightID } + } + private func loadHighlights(item: LinkedItem) { let unsortedHighlights = item.highlights.asArray(of: Highlight.self)