Better handling of errors when dispatching annotation events to JS
This commit is contained in:
@ -25,12 +25,12 @@ import Views
|
||||
func handleArchiveAction(dataService: DataService) {
|
||||
guard let objectID = item?.objectID ?? pdfItem?.objectID else { return }
|
||||
dataService.archiveLink(objectID: objectID, archived: !isItemArchived)
|
||||
Snackbar.show(message: !isItemArchived ? "Link archived" : "Link moved to Inbox")
|
||||
showInSnackbar(!isItemArchived ? "Link archived" : "Link moved to Inbox")
|
||||
}
|
||||
|
||||
func handleDeleteAction(dataService: DataService) {
|
||||
guard let objectID = item?.objectID ?? pdfItem?.objectID else { return }
|
||||
Snackbar.show(message: "Link removed")
|
||||
showInSnackbar("Link removed")
|
||||
dataService.removeLink(objectID: objectID)
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ struct WebReader: PlatformViewRepresentable {
|
||||
@Binding var shareActionID: UUID?
|
||||
@Binding var annotation: String
|
||||
@Binding var showBottomBar: Bool
|
||||
@Binding var showHighlightAnnotationModal: Bool
|
||||
|
||||
func makeCoordinator() -> WebReaderCoordinator {
|
||||
WebReaderCoordinator()
|
||||
@ -84,7 +85,15 @@ struct WebReader: PlatformViewRepresentable {
|
||||
private func updatePlatformView(_ webView: WKWebView, context: Context) {
|
||||
if annotationSaveTransactionID != context.coordinator.lastSavedAnnotationID {
|
||||
context.coordinator.lastSavedAnnotationID = annotationSaveTransactionID
|
||||
(webView as? OmnivoreWebView)?.dispatchEvent(.saveAnnotation(annotation: annotation))
|
||||
do {
|
||||
try (webView as? OmnivoreWebView)?.dispatchEvent(.saveAnnotation(annotation: annotation))
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||
showHighlightAnnotationModal = false
|
||||
}
|
||||
} catch {
|
||||
showInSnackbar("Error saving note.")
|
||||
showHighlightAnnotationModal = true
|
||||
}
|
||||
}
|
||||
|
||||
if readerSettingsChangedTransactionID != context.coordinator.previousReaderSettingsChangedUUID {
|
||||
|
||||
@ -300,7 +300,8 @@ struct WebReaderContainerView: View {
|
||||
showNavBarActionID: $showNavBarActionID,
|
||||
shareActionID: $shareActionID,
|
||||
annotation: $annotation,
|
||||
showBottomBar: $showBottomBar
|
||||
showBottomBar: $showBottomBar,
|
||||
showHighlightAnnotationModal: $showHighlightAnnotationModal
|
||||
)
|
||||
.background(Theme.fromName(themeName: ThemeManager.currentThemeName)?.bgColor ?? .clear)
|
||||
.onTapGesture {
|
||||
@ -319,7 +320,6 @@ struct WebReaderContainerView: View {
|
||||
annotation: $annotation,
|
||||
onSave: {
|
||||
annotationSaveTransactionID = UUID()
|
||||
showHighlightAnnotationModal = false
|
||||
},
|
||||
onCancel: {
|
||||
showHighlightAnnotationModal = false
|
||||
|
||||
18
apple/OmnivoreKit/Sources/Utils/ShowInSnackbar.swift
Normal file
18
apple/OmnivoreKit/Sources/Utils/ShowInSnackbar.swift
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// ShowInSnackbar.swift
|
||||
//
|
||||
//
|
||||
// Created by Jackson Harper on 11/1/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public func showInSnackbar(_ message: String) {
|
||||
let nname = Notification.Name("OperationSuccess")
|
||||
NotificationCenter.default.post(name: nname, object: nil, userInfo: ["message": message])
|
||||
}
|
||||
|
||||
public func showErrorInSnackbar(_ message: String) {
|
||||
let nname = Notification.Name("OperationFailure")
|
||||
NotificationCenter.default.post(name: nname, object: nil, userInfo: ["message": message])
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import Utils
|
||||
import WebKit
|
||||
|
||||
|
||||
/// Describes actions that can be sent from the WebView back to native views.
|
||||
/// The names on the javascript side must match for an action to be handled.
|
||||
public enum WebViewAction: String, CaseIterable {
|
||||
@ -31,12 +32,6 @@ public final class OmnivoreWebView: WKWebView {
|
||||
self.isFindInteractionEnabled = true
|
||||
}
|
||||
#endif
|
||||
|
||||
NotificationCenter.default.addObserver(forName: NSNotification.Name("SpeakingReaderItem"), object: nil, queue: OperationQueue.main, using: { notification in
|
||||
if let pageID = notification.userInfo?["pageID"] as? String, let anchorIdx = notification.userInfo?["anchorIdx"] as? String {
|
||||
self.dispatchEvent(.speakingSection(anchorIdx: anchorIdx))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
@ -45,20 +40,32 @@ public final class OmnivoreWebView: WKWebView {
|
||||
}
|
||||
|
||||
public func updateTheme() {
|
||||
if let themeName = UserDefaults.standard.value(forKey: UserDefaultKey.themeName.rawValue) as? String {
|
||||
dispatchEvent(.updateTheme(themeName: "Gray" /* themeName */ ))
|
||||
do {
|
||||
if let themeName = UserDefaults.standard.value(forKey: UserDefaultKey.themeName.rawValue) as? String {
|
||||
try dispatchEvent(.updateTheme(themeName: "Gray" /* themeName */ ))
|
||||
}
|
||||
} catch {
|
||||
showErrorInSnackbar("Error updating theme")
|
||||
}
|
||||
}
|
||||
|
||||
public func updateFontFamily() {
|
||||
if let fontFamily = UserDefaults.standard.value(forKey: UserDefaultKey.preferredWebFont.rawValue) as? String {
|
||||
dispatchEvent(.updateFontFamily(family: fontFamily))
|
||||
do {
|
||||
if let fontFamily = UserDefaults.standard.value(forKey: UserDefaultKey.preferredWebFont.rawValue) as? String {
|
||||
try dispatchEvent(.updateFontFamily(family: fontFamily))
|
||||
}
|
||||
} catch {
|
||||
showErrorInSnackbar("Error updating font")
|
||||
}
|
||||
}
|
||||
|
||||
public func updateFontSize() {
|
||||
if let fontSize = UserDefaults.standard.value(forKey: UserDefaultKey.preferredWebFontSize.rawValue) as? Int {
|
||||
dispatchEvent(.updateFontSize(size: fontSize))
|
||||
do {
|
||||
if let fontSize = UserDefaults.standard.value(forKey: UserDefaultKey.preferredWebFontSize.rawValue) as? Int {
|
||||
try dispatchEvent(.updateFontSize(size: fontSize))
|
||||
}
|
||||
} catch {
|
||||
showErrorInSnackbar("Error updating font")
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,13 +73,21 @@ public final class OmnivoreWebView: WKWebView {
|
||||
if let maxWidthPercentage = UserDefaults.standard.value(
|
||||
forKey: UserDefaultKey.preferredWebMaxWidthPercentage.rawValue
|
||||
) as? Int {
|
||||
dispatchEvent(.updateMaxWidthPercentage(maxWidthPercentage: maxWidthPercentage))
|
||||
do {
|
||||
try dispatchEvent(.updateMaxWidthPercentage(maxWidthPercentage: maxWidthPercentage))
|
||||
} catch {
|
||||
showErrorInSnackbar("Error updating max width")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLineHeight() {
|
||||
if let height = UserDefaults.standard.value(forKey: UserDefaultKey.preferredWebLineSpacing.rawValue) as? Int {
|
||||
dispatchEvent(.updateLineHeight(height: height))
|
||||
do {
|
||||
try dispatchEvent(.updateLineHeight(height: height))
|
||||
} catch {
|
||||
showErrorInSnackbar("Error updating line height")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,17 +97,35 @@ public final class OmnivoreWebView: WKWebView {
|
||||
) as? Bool
|
||||
|
||||
if let isHighContrast = isHighContrast {
|
||||
dispatchEvent(.handleFontContrastChange(isHighContrast: isHighContrast))
|
||||
do {
|
||||
try dispatchEvent(.handleFontContrastChange(isHighContrast: isHighContrast))
|
||||
} catch {
|
||||
showErrorInSnackbar("Error updating text contrast")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func shareOriginalItem() {
|
||||
dispatchEvent(.share)
|
||||
do {
|
||||
try dispatchEvent(.share)
|
||||
} catch {
|
||||
showErrorInSnackbar("Error updating line height")
|
||||
}
|
||||
}
|
||||
|
||||
public func dispatchEvent(_ event: WebViewDispatchEvent) {
|
||||
evaluateJavaScript(event.script) { _, err in
|
||||
if let err = err { print("evaluateJavaScript error", err) }
|
||||
public func dispatchEvent(_ event: WebViewDispatchEvent) throws {
|
||||
let script = try event.script
|
||||
var errResult: Error?
|
||||
|
||||
evaluateJavaScript(script) { _, err in
|
||||
if let err = err {
|
||||
print("evaluateJavaScript error", err)
|
||||
errResult = err
|
||||
}
|
||||
}
|
||||
|
||||
if let errResult = errResult {
|
||||
throw errResult
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +133,11 @@ public final class OmnivoreWebView: WKWebView {
|
||||
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
guard previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle else { return }
|
||||
dispatchEvent(.updateColorMode(isDark: traitCollection.userInterfaceStyle == .dark))
|
||||
do {
|
||||
try dispatchEvent(.updateColorMode(isDark: traitCollection.userInterfaceStyle == .dark))
|
||||
} catch {
|
||||
showErrorInSnackbar("Error updating theme")
|
||||
}
|
||||
}
|
||||
|
||||
#elseif os(macOS)
|
||||
@ -210,28 +247,48 @@ public final class OmnivoreWebView: WKWebView {
|
||||
}
|
||||
|
||||
@objc private func annotateSelection() {
|
||||
dispatchEvent(.annotate)
|
||||
do {
|
||||
try dispatchEvent(.annotate)
|
||||
} catch {
|
||||
showErrorInSnackbar("Error creating highlight")
|
||||
}
|
||||
hideMenu()
|
||||
}
|
||||
|
||||
@objc private func highlightSelection() {
|
||||
dispatchEvent(.highlight)
|
||||
do {
|
||||
try dispatchEvent(.highlight)
|
||||
} catch {
|
||||
showErrorInSnackbar("Error creating highlight")
|
||||
}
|
||||
hideMenu()
|
||||
}
|
||||
|
||||
@objc private func shareSelection() {
|
||||
dispatchEvent(.share)
|
||||
do {
|
||||
try dispatchEvent(.share)
|
||||
} catch {
|
||||
showErrorInSnackbar("Error sharing highlight")
|
||||
}
|
||||
hideMenu()
|
||||
}
|
||||
|
||||
@objc private func removeSelection() {
|
||||
dispatchEvent(.remove)
|
||||
do {
|
||||
try dispatchEvent(.remove)
|
||||
} catch {
|
||||
showErrorInSnackbar("Error deleting highlight")
|
||||
}
|
||||
hideMenu()
|
||||
}
|
||||
|
||||
@objc override public func copy(_ sender: Any?) {
|
||||
super.copy(sender)
|
||||
dispatchEvent(.copyHighlight)
|
||||
do {
|
||||
try dispatchEvent(.copyHighlight)
|
||||
} catch {
|
||||
showErrorInSnackbar("Error copying highlight")
|
||||
}
|
||||
hideMenu()
|
||||
}
|
||||
|
||||
@ -265,7 +322,7 @@ public final class OmnivoreWebView: WKWebView {
|
||||
|
||||
private func hideMenuAndDismissHighlight() {
|
||||
hideMenu()
|
||||
dispatchEvent(.dismissHighlight)
|
||||
try? dispatchEvent(.dismissHighlight)
|
||||
}
|
||||
|
||||
private func showHighlightMenu(_ rect: CGRect) {
|
||||
@ -310,7 +367,10 @@ public enum WebViewDispatchEvent {
|
||||
case speakingSection(anchorIdx: String)
|
||||
|
||||
var script: String {
|
||||
"var event = new Event('\(eventName)');\(scriptPropertyLine)document.dispatchEvent(event);"
|
||||
get throws {
|
||||
let propertyLine = try scriptPropertyLine
|
||||
return "var event = new Event('\(eventName)');\(propertyLine)document.dispatchEvent(event);"
|
||||
}
|
||||
}
|
||||
|
||||
private var eventName: String {
|
||||
@ -349,27 +409,35 @@ public enum WebViewDispatchEvent {
|
||||
}
|
||||
|
||||
private var scriptPropertyLine: String {
|
||||
switch self {
|
||||
case let .handleFontContrastChange(isHighContrast: isHighContrast):
|
||||
return "event.fontContrast = '\(isHighContrast ? "high" : "normal")';"
|
||||
case let .updateLineHeight(height: height):
|
||||
return "event.lineHeight = '\(height)';"
|
||||
case let .updateMaxWidthPercentage(maxWidthPercentage: maxWidthPercentage):
|
||||
return "event.maxWidthPercentage = '\(maxWidthPercentage)';"
|
||||
case let .updateTheme(themeName: themeName):
|
||||
return "event.themeName = '\(themeName)';"
|
||||
case let .updateFontSize(size: size):
|
||||
return "event.fontSize = '\(size)';"
|
||||
case let .updateColorMode(isDark: isDark):
|
||||
return "event.isDark = '\(isDark)';"
|
||||
case let .updateFontFamily(family: family):
|
||||
return "event.fontFamily = '\(family)';"
|
||||
case let .saveAnnotation(annotation: annotation):
|
||||
return "event.annotation = '\(annotation)';"
|
||||
case let .speakingSection(anchorIdx: anchorIdx):
|
||||
return "event.anchorIdx = '\(anchorIdx)';"
|
||||
case .annotate, .highlight, .share, .remove, .copyHighlight, .dismissHighlight:
|
||||
return ""
|
||||
get throws {
|
||||
switch self {
|
||||
case let .handleFontContrastChange(isHighContrast: isHighContrast):
|
||||
return "event.fontContrast = '\(isHighContrast ? "high" : "normal")';"
|
||||
case let .updateLineHeight(height: height):
|
||||
return "event.lineHeight = '\(height)';"
|
||||
case let .updateMaxWidthPercentage(maxWidthPercentage: maxWidthPercentage):
|
||||
return "event.maxWidthPercentage = '\(maxWidthPercentage)';"
|
||||
case let .updateTheme(themeName: themeName):
|
||||
return "event.themeName = '\(themeName)';"
|
||||
case let .updateFontSize(size: size):
|
||||
return "event.fontSize = '\(size)';"
|
||||
case let .updateColorMode(isDark: isDark):
|
||||
return "event.isDark = '\(isDark)';"
|
||||
case let .updateFontFamily(family: family):
|
||||
return "event.fontFamily = '\(family)';"
|
||||
case let .saveAnnotation(annotation: annotation):
|
||||
let encoder = JSONEncoder()
|
||||
if let encoded = try? encoder.encode(annotation) {
|
||||
let str = String(decoding: encoded, as: UTF8.self)
|
||||
return "event.annotation = '\(str)';"
|
||||
} else {
|
||||
throw BasicError.message(messageText: "Unable to serialize highlight note.")
|
||||
}
|
||||
case let .speakingSection(anchorIdx: anchorIdx):
|
||||
return "event.anchorIdx = '\(anchorIdx)';"
|
||||
case .annotate, .highlight, .share, .remove, .copyHighlight, .dismissHighlight:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,8 +103,8 @@ public enum WebFont: String, CaseIterable {
|
||||
public var body: some View {
|
||||
NavigationView {
|
||||
VStack(alignment: .center) {
|
||||
themePicker
|
||||
.padding(.bottom, 16)
|
||||
// themePicker
|
||||
// .padding(.bottom, 16)
|
||||
|
||||
LabelledStepper(
|
||||
labelText: "Font Size",
|
||||
|
||||
@ -19,7 +19,8 @@ import WebKit
|
||||
webView.isOpaque = false
|
||||
webView.backgroundColor = UIColor.clear
|
||||
if let url = request.url {
|
||||
let themeID = Color.isDarkMode ? "Gray" /* "Sepia" */ : "Charcoal"
|
||||
// let themeID = Color.isDarkMode ? "Gray" /* "Sepia" */ : "Charcoal"
|
||||
let themeID = Color.isDarkMode ? "Gray" : "LightGray"
|
||||
webView.injectCookie(cookieString: "theme=\(themeID); Max-Age=31536000;", url: url)
|
||||
}
|
||||
return webView
|
||||
|
||||
Reference in New Issue
Block a user