mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Better handling of errors in javascript while waiting in swift
Two main changes here: - Wait for JS highlight methods to complete, if an error occurs post that error back to Swift. - When creating a highlight with a note, post a message back to Swift to signify success. We wait for that message instead of closing immediately, so that a user doesn't lose their note if creating the highlight failed.
This commit is contained in:
parent
6f0f0ec8b5
commit
1b57d879ed
5 changed files with 101 additions and 38 deletions
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
onSave: @escaping () -> Void,
|
||||
onCancel: @escaping () -> Void
|
||||
onCancel: @escaping () -> Void,
|
||||
errorAlertMessage: Binding<String?>,
|
||||
showErrorAlertMessage: Binding<Bool>
|
||||
) {
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue