UI for displaying article note modals
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
#if os(iOS)
|
||||
import CoreData
|
||||
import MarkdownUI
|
||||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
@ -10,6 +11,11 @@
|
||||
@Environment(\.presentationMode) private var presentationMode
|
||||
@StateObject var viewModel = NotebookViewModel()
|
||||
|
||||
@State var showAnnotationModal = false
|
||||
@State var errorAlertMessage: String?
|
||||
@State var showErrorAlertMessage = false
|
||||
@State var noteAnnotation = ""
|
||||
|
||||
let itemObjectID: NSManagedObjectID
|
||||
@Binding var hasHighlightMutations: Bool
|
||||
@State var setLabelsHighlight: Highlight?
|
||||
@ -17,12 +23,12 @@
|
||||
|
||||
var emptyView: some View {
|
||||
Text(LocalText.highlightCardNoHighlightsOnPage)
|
||||
.multilineTextAlignment(.center)
|
||||
.multilineTextAlignment(.leading)
|
||||
.padding(16)
|
||||
}
|
||||
|
||||
var innerBody: some View {
|
||||
(viewModel.highlightItems.count > 0 ? AnyView(listView) : AnyView(emptyView))
|
||||
listView
|
||||
.navigationTitle("Notebook")
|
||||
.listStyle(PlainListStyle())
|
||||
#if os(iOS)
|
||||
@ -46,35 +52,80 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
var noteSection: some View {
|
||||
HStack {
|
||||
// let isEmpty = viewModel.noteItem.annotation.isEmpty
|
||||
Spacer(minLength: 6)
|
||||
|
||||
if let note = viewModel.noteItem, let annotation = note.annotation, !annotation.isEmpty {
|
||||
Markdown(annotation)
|
||||
.lineSpacing(6)
|
||||
.accentColor(.appGraySolid)
|
||||
.foregroundColor(.appGrayTextContrast)
|
||||
.font(.appSubheadline)
|
||||
.padding(12)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(Color.appButtonBackground)
|
||||
.cornerRadius(8)
|
||||
|
||||
} else {
|
||||
Text("Add Notes...")
|
||||
.lineSpacing(6)
|
||||
.accentColor(.appGraySolid)
|
||||
.foregroundColor(.appGrayText)
|
||||
.font(.appSubheadline)
|
||||
.padding(12)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(Color.appButtonBackground)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
// annotation = highlightParams.annotation
|
||||
showAnnotationModal = true
|
||||
}
|
||||
}
|
||||
|
||||
var listView: some View {
|
||||
List {
|
||||
Section {
|
||||
ForEach(viewModel.highlightItems) { highlightParams in
|
||||
HighlightsListCard(
|
||||
viewModel: self.viewModel,
|
||||
highlightParams: highlightParams,
|
||||
hasHighlightMutations: $hasHighlightMutations,
|
||||
onSaveAnnotation: {
|
||||
viewModel.updateAnnotation(
|
||||
highlightID: highlightParams.highlightID,
|
||||
annotation: $0,
|
||||
dataService: dataService
|
||||
)
|
||||
},
|
||||
onDeleteHighlight: {
|
||||
hasHighlightMutations = true
|
||||
viewModel.deleteHighlight(
|
||||
highlightID: highlightParams.highlightID,
|
||||
dataService: dataService
|
||||
)
|
||||
},
|
||||
onSetLabels: { highlightID in
|
||||
setLabelsHighlight = Highlight.lookup(byID: highlightID, inContext: dataService.viewContext)
|
||||
}
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
Section("Article Notes") {
|
||||
noteSection
|
||||
.listRowSeparator(.hidden, edges: .bottom)
|
||||
}
|
||||
Section("Highlights") {
|
||||
if viewModel.highlightItems.count > 0 {
|
||||
ForEach(Array(viewModel.highlightItems.enumerated()), id: \.offset) { idx, highlightParams in
|
||||
HighlightsListCard(
|
||||
viewModel: self.viewModel,
|
||||
highlightParams: highlightParams,
|
||||
hasHighlightMutations: $hasHighlightMutations,
|
||||
onSaveAnnotation: {
|
||||
viewModel.updateAnnotation(
|
||||
highlightID: highlightParams.highlightID,
|
||||
annotation: $0,
|
||||
dataService: dataService
|
||||
)
|
||||
},
|
||||
onDeleteHighlight: {
|
||||
hasHighlightMutations = true
|
||||
viewModel.deleteHighlight(
|
||||
highlightID: highlightParams.highlightID,
|
||||
dataService: dataService
|
||||
)
|
||||
},
|
||||
onSetLabels: { highlightID in
|
||||
setLabelsHighlight = Highlight.lookup(byID: highlightID, inContext: dataService.viewContext)
|
||||
}
|
||||
)
|
||||
.listRowSeparator(.hidden, edges: idx == 0 ? .bottom : .all)
|
||||
}
|
||||
} else {
|
||||
emptyView
|
||||
.listRowSeparator(.hidden, edges: .bottom)
|
||||
}
|
||||
}
|
||||
Spacer(minLength: 120)
|
||||
.listRowSeparator(.hidden, edges: .all)
|
||||
}.sheet(item: $setLabelsHighlight) { highlight in
|
||||
ApplyLabelsView(mode: .highlight(highlight), isSearchFocused: false, onSave: { selectedLabels in
|
||||
hasHighlightMutations = true
|
||||
@ -83,6 +134,20 @@
|
||||
labels: selectedLabels,
|
||||
dataService: dataService)
|
||||
})
|
||||
}.sheet(isPresented: $showAnnotationModal) {
|
||||
HighlightAnnotationSheet(
|
||||
annotation: $noteAnnotation,
|
||||
onSave: {
|
||||
// onSaveAnnotation(annotation)
|
||||
showAnnotationModal = false
|
||||
hasHighlightMutations = true
|
||||
},
|
||||
onCancel: {
|
||||
showAnnotationModal = false
|
||||
},
|
||||
errorAlertMessage: $errorAlertMessage,
|
||||
showErrorAlertMessage: $showErrorAlertMessage
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ struct HighlightListItemParams: Identifiable {
|
||||
struct NoteItemParams: Identifiable {
|
||||
let id = UUID()
|
||||
let highlightID: String
|
||||
let annotation: String
|
||||
let annotation: String?
|
||||
}
|
||||
|
||||
@MainActor final class NotebookViewModel: ObservableObject {
|
||||
@ -79,7 +79,6 @@ struct NoteItemParams: Identifiable {
|
||||
}
|
||||
|
||||
private func loadHighlights(item: LinkedItem) {
|
||||
let notes = item.highlights.asArray(of: Highlight.self).filter { $0.type == "NOTE" }
|
||||
let unsortedHighlights = item.highlights.asArray(of: Highlight.self)
|
||||
.filter { $0.type == "HIGHLIGHT" && $0.serverSyncStatus != ServerSyncStatus.needsDeletion.rawValue }
|
||||
|
||||
@ -100,5 +99,10 @@ struct NoteItemParams: Identifiable {
|
||||
createdBy: $0.createdByMe ? nil : InternalUserProfile.makeSingle($0.createdBy)
|
||||
)
|
||||
}
|
||||
|
||||
noteItem = item.highlights.asArray(of: Highlight.self)
|
||||
.filter { $0.type == "NOTE" && $0.serverSyncStatus != ServerSyncStatus.needsDeletion.rawValue }
|
||||
.first
|
||||
.map { NoteItemParams(highlightID: $0.unwrappedID, annotation: $0.annotation) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
21
apple/OmnivoreKit/Sources/App/Views/WebReader/Media.xcassets/Hotpot.imageset/Contents.json
vendored
Normal file
21
apple/OmnivoreKit/Sources/App/Views/WebReader/Media.xcassets/Hotpot.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Hotpot.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
apple/OmnivoreKit/Sources/App/Views/WebReader/Media.xcassets/Hotpot.imageset/Hotpot.png
vendored
Normal file
BIN
apple/OmnivoreKit/Sources/App/Views/WebReader/Media.xcassets/Hotpot.imageset/Hotpot.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
24
apple/OmnivoreKit/Sources/App/Views/WebReader/Media.xcassets/notebook.imageset/Contents.json
vendored
Normal file
24
apple/OmnivoreKit/Sources/App/Views/WebReader/Media.xcassets/notebook.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "notebook.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
1
apple/OmnivoreKit/Sources/App/Views/WebReader/Media.xcassets/notebook.imageset/notebook.svg
vendored
Normal file
1
apple/OmnivoreKit/Sources/App/Views/WebReader/Media.xcassets/notebook.imageset/notebook.svg
vendored
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" viewBox="0 0 256 256"><path d="M184,112a8,8,0,0,1-8,8H112a8,8,0,0,1,0-16h64A8,8,0,0,1,184,112Zm-8,24H112a8,8,0,0,0,0,16h64a8,8,0,0,0,0-16Zm48-88V208a16,16,0,0,1-16,16H48a16,16,0,0,1-16-16V48A16,16,0,0,1,48,32H208A16,16,0,0,1,224,48ZM48,208H72V48H48Zm160,0V48H88V208H208Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 363 B |
@ -14,7 +14,7 @@ struct WebReaderContainerView: View {
|
||||
@State private var showLabelsModal = false
|
||||
@State private var showHighlightLabelsModal = false
|
||||
@State private var showTitleEdit = false
|
||||
@State private var showHighlightsView = false
|
||||
@State private var showNotebookView = false
|
||||
@State private var hasPerformedHighlightMutations = false
|
||||
@State var showHighlightAnnotationModal = false
|
||||
@State private var navBarVisibilityRatio = 1.0
|
||||
@ -61,7 +61,7 @@ struct WebReaderContainerView: View {
|
||||
lastScrollPercentage = percent
|
||||
}
|
||||
|
||||
func onHighlightListViewDismissal() {
|
||||
func onNotebookViewDismissal() {
|
||||
// Reload the web view if mutation happened in highlights list modal
|
||||
guard hasPerformedHighlightMutations else { return }
|
||||
|
||||
@ -205,7 +205,7 @@ struct WebReaderContainerView: View {
|
||||
let hasLabels = item.labels?.count != 0
|
||||
return Group {
|
||||
Button(
|
||||
action: { showHighlightsView = true },
|
||||
action: { showNotebookView = true },
|
||||
label: { Label("Notebook", systemImage: "highlighter") }
|
||||
)
|
||||
Button(
|
||||
@ -261,14 +261,13 @@ struct WebReaderContainerView: View {
|
||||
}
|
||||
|
||||
var navBar: some View {
|
||||
HStack(alignment: .center) {
|
||||
HStack(alignment: .center, spacing: 15) {
|
||||
#if os(iOS)
|
||||
Button(
|
||||
action: { self.presentationMode.wrappedValue.dismiss() },
|
||||
label: {
|
||||
Image(systemName: "chevron.backward")
|
||||
.font(.appNavbarIcon)
|
||||
// .foregroundColor(.appGrayTextContrast)
|
||||
.padding()
|
||||
}
|
||||
)
|
||||
@ -285,6 +284,14 @@ struct WebReaderContainerView: View {
|
||||
)
|
||||
.padding(.horizontal)
|
||||
.scaleEffect(navBarVisibilityRatio)
|
||||
Button(
|
||||
action: { showNotebookView.toggle() },
|
||||
label: {
|
||||
Image("notebook", bundle: Bundle.module)
|
||||
}
|
||||
)
|
||||
.padding(.horizontal)
|
||||
.scaleEffect(navBarVisibilityRatio)
|
||||
#if os(macOS)
|
||||
Spacer()
|
||||
#endif
|
||||
@ -297,7 +304,6 @@ struct WebReaderContainerView: View {
|
||||
Image(systemName: "ellipsis")
|
||||
.resizable(resizingMode: Image.ResizingMode.stretch)
|
||||
.aspectRatio(contentMode: .fit)
|
||||
// .foregroundColor(.appGrayTextContrast)
|
||||
.frame(width: 20, height: 20)
|
||||
.scaleEffect(navBarVisibilityRatio)
|
||||
.padding()
|
||||
@ -344,7 +350,7 @@ struct WebReaderContainerView: View {
|
||||
})
|
||||
}
|
||||
#if os(iOS)
|
||||
.sheet(isPresented: $showHighlightsView, onDismiss: onHighlightListViewDismissal) {
|
||||
.sheet(isPresented: $showNotebookView, onDismiss: onNotebookViewDismissal) {
|
||||
NotebookView(
|
||||
itemObjectID: item.objectID,
|
||||
hasHighlightMutations: $hasPerformedHighlightMutations
|
||||
|
||||
24
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/notebook.imageset/Contents.json
vendored
Normal file
24
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/notebook.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "notebook.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
1
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/notebook.imageset/notebook.svg
vendored
Normal file
1
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/notebook.imageset/notebook.svg
vendored
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" viewBox="0 0 256 256"><path d="M184,112a8,8,0,0,1-8,8H112a8,8,0,0,1,0-16h64A8,8,0,0,1,184,112Zm-8,24H112a8,8,0,0,0,0,16h64a8,8,0,0,0,0-16Zm48-88V208a16,16,0,0,1-16,16H48a16,16,0,0,1-16-16V48A16,16,0,0,1,48,32H208A16,16,0,0,1,224,48ZM48,208H72V48H48Zm160,0V48H88V208H208Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 363 B |
@ -13,7 +13,7 @@
|
||||
|
||||
// Highlights List Card
|
||||
"highlightCardHighlightByOther" = "Highlight by ";
|
||||
"highlightCardNoHighlightsOnPage" = "You have not added any highlights or notes to this page.";
|
||||
"highlightCardNoHighlightsOnPage" = "You have not added any highlights to this page.";
|
||||
|
||||
// Labels View
|
||||
"labelsViewAssignNameColor" = "Assign a name and color.";
|
||||
|
||||
Reference in New Issue
Block a user