diff --git a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift index a7f218b42..4564a116b 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Highlights/HighlightsListCard.swift @@ -13,6 +13,9 @@ struct HighlightsListCard: View { let onDeleteHighlight: () -> Void let onSetLabels: (String) -> Void + @State var errorAlertMessage: String? + @State var showErrorAlertMessage = false + var contextMenuView: some View { Group { Button( @@ -131,7 +134,9 @@ struct HighlightsListCard: View { }, onCancel: { showAnnotationModal = false - } + }, + errorAlertMessage: $errorAlertMessage, + showErrorAlertMessage: $showErrorAlertMessage ) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift index 6c1551fd7..b13432631 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReader.swift @@ -87,9 +87,6 @@ struct WebReader: PlatformViewRepresentable { context.coordinator.lastSavedAnnotationID = annotationSaveTransactionID do { try (webView as? OmnivoreWebView)?.dispatchEvent(.saveAnnotation(annotation: annotation)) - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) { - showHighlightAnnotationModal = false - } } catch { showInSnackbar("Error saving note.") } diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift index 7ed6bffca..cc3e0823f 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift @@ -27,6 +27,8 @@ struct WebReaderContainerView: View { @State var annotation = String() @State var showBottomBar = false @State private var bottomBarOpacity = 0.0 + @State private var errorAlertMessage: String? + @State private var showErrorAlertMessage = false @EnvironmentObject var dataService: DataService @EnvironmentObject var audioController: AudioController @@ -73,6 +75,11 @@ struct WebReaderContainerView: View { case "annotate": annotation = messageBody["annotation"] ?? "" showHighlightAnnotationModal = true + case "noteCreated": + showHighlightAnnotationModal = false + case "highlightError": + errorAlertMessage = messageBody["error"] ?? "An error occurred." + showErrorAlertMessage = true case "setHighlightLabels": annotation = messageBody["highlightID"] ?? "" showHighlightLabelsModal = true @@ -334,6 +341,12 @@ struct WebReaderContainerView: View { SafariView(url: $0.url) } #endif + .alert(errorAlertMessage ?? "An error occurred", isPresented: $showErrorAlertMessage) { + Button("Ok", role: .cancel, action: { + errorAlertMessage = nil + showErrorAlertMessage = false + }) + } .sheet(isPresented: $showHighlightAnnotationModal) { HighlightAnnotationSheet( annotation: $annotation, @@ -342,7 +355,9 @@ struct WebReaderContainerView: View { }, onCancel: { showHighlightAnnotationModal = false - } + }, + errorAlertMessage: $errorAlertMessage, + showErrorAlertMessage: $showErrorAlertMessage ) } .sheet(isPresented: $showHighlightLabelsModal) { diff --git a/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift b/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift index bf0a872c6..c461327be 100644 --- a/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift +++ b/apple/OmnivoreKit/Sources/Views/Article/HighlightAnnotationSheet.swift @@ -3,6 +3,8 @@ import SwiftUI public struct HighlightAnnotationSheet: View { @Binding var annotation: String + @Binding var errorAlertMessage: String? + @Binding var showErrorAlertMessage: Bool let onSave: () -> Void let onCancel: () -> Void @@ -10,11 +12,15 @@ public struct HighlightAnnotationSheet: View { public init( annotation: Binding, onSave: @escaping () -> Void, - onCancel: @escaping () -> Void + onCancel: @escaping () -> Void, + errorAlertMessage: Binding, + showErrorAlertMessage: Binding ) { self._annotation = annotation self.onSave = onSave self.onCancel = onCancel + self._errorAlertMessage = errorAlertMessage + self._showErrorAlertMessage = showErrorAlertMessage } public var body: some View { @@ -43,5 +49,11 @@ public struct HighlightAnnotationSheet: View { Spacer() } .padding() + .alert(errorAlertMessage ?? "An error occurred", isPresented: $showErrorAlertMessage) { + Button("Ok", role: .cancel, action: { + errorAlertMessage = nil + showErrorAlertMessage = false + }) + } } } diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index 2b72cba81..0df03a686 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -196,6 +196,10 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { props.articleMutations ) + if (result.errorMessage) { + throw 'Failed to create highlight: ' + result.errorMessage + } + if (!result.highlights || result.highlights.length == 0) { // TODO: show an error message console.error('Failed to create highlight') @@ -218,26 +222,18 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { if (!selectionData) { return } - const result = await createHighlightFromSelection( - selectionData, - annotation - ) - if (!result) { - showErrorToast('Error saving highlight', { position: 'bottom-right' }) + try { + const result = await createHighlightFromSelection( + selectionData, + annotation + ) + if (!result) { + showErrorToast('Error saving highlight', { position: 'bottom-right' }) + throw 'Error creating highlight' + } + } catch (error) { + throw error } - // if (successAction === 'share' && canShareNative) { - // handleNativeShare(highlight.shortId) - // return - // } else { - // setFocusedHighlight(undefined) - // } - - // if (successAction === 'addComment') { - // openNoteModal({ - // highlightModalAction: 'addComment', - // highlight, - // }) - // } }, [ handleNativeShare, @@ -328,13 +324,13 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { }, [handleClickHighlight]) const handleAction = useCallback( - (action: HighlightAction) => { + async (action: HighlightAction) => { switch (action) { case 'delete': - removeHighlightCallback() + await removeHighlightCallback() break case 'create': - createHighlightCallback('none') + await createHighlightCallback('none') break case 'comment': if (props.highlightBarDisabled || focusedHighlight) { @@ -375,7 +371,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { }) } } else { - createHighlightCallback('share') + await createHighlightCallback('share') } break case 'unshare': @@ -403,21 +399,49 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { ] ) + const dispatchHighlightError = (action: string, error: unknown) => { + if (props.isAppleAppEmbed) { + window?.webkit?.messageHandlers.highlightAction?.postMessage({ + actionID: 'highlightError', + highlightAction: action, + highlightID: focusedHighlight?.id, + error: typeof error === 'string' ? error : JSON.stringify(error), + }) + } + } + + const dispatchHighlightMessage = (actionID: string) => { + if (props.isAppleAppEmbed) { + window?.webkit?.messageHandlers.highlightAction?.postMessage({ + actionID: actionID, + highlightID: focusedHighlight?.id, + }) + } + } + useEffect(() => { - const annotate = () => { - handleAction('comment') + const safeHandleAction = async (action: HighlightAction) => { + try { + await handleAction(action) + } catch (error) { + dispatchHighlightError(action, error) + } } - const highlight = () => { - handleAction('create') + const annotate = async () => { + await safeHandleAction('comment') } - const share = () => { - handleAction('share') + const highlight = async () => { + await safeHandleAction('create') } - const remove = () => { - handleAction('delete') + const share = async () => { + await safeHandleAction('share') + } + + const remove = async () => { + await safeHandleAction('delete') } const dismissHighlight = () => { @@ -465,10 +489,20 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element { 'failed to change annotation for highlight with id', focusedHighlight.id ) + dispatchHighlightError( + 'saveAnnotation', + 'Failed to create highlight.' + ) } setFocusedHighlight(undefined) + dispatchHighlightMessage('noteCreated') } else { - createHighlightCallback('none', event.annotation) + try { + await createHighlightCallback('none', event.annotation) + dispatchHighlightMessage('noteCreated') + } catch (error) { + dispatchHighlightError('saveAnnotation', error) + } } }