add android message handlers in same places webKit handlers are used
This commit is contained in:
@ -66,7 +66,7 @@ fun WebReader(params: WebReaderParams) {
|
||||
webViewClient = object : WebViewClient() {
|
||||
}
|
||||
|
||||
addJavascriptInterface(WebMessageHandler(), "WebMessageHandler")
|
||||
addJavascriptInterface(AndroidWebKitMessageHandler(), "AndroidWebKitMessageHandler")
|
||||
loadDataWithBaseURL("file:///android_asset/", styledContent, "text/html; charset=utf-8", "utf-8", null);
|
||||
|
||||
}
|
||||
@ -139,7 +139,7 @@ class OmnivoreWebView(context: Context) : WebView(context) {
|
||||
}
|
||||
|
||||
|
||||
class WebMessageHandler {
|
||||
class AndroidWebKitMessageHandler {
|
||||
@JavascriptInterface
|
||||
fun handleMessage(jsonString: String) {
|
||||
val message = JSONObject(jsonString)
|
||||
|
||||
@ -18,7 +18,7 @@ const mutation = async (name, input) => {
|
||||
} else {
|
||||
// Send android a message
|
||||
console.log('sending android a message', message)
|
||||
WebMessageHandler.handleMessage(JSON.stringify(message))
|
||||
AndroidWebKitMessageHandler.handleMessage(JSON.stringify(message))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
packages/web/additional.d.ts
vendored
5
packages/web/additional.d.ts
vendored
@ -1,5 +1,9 @@
|
||||
export {}
|
||||
|
||||
declare type AndroidWebKitMessageHandler = {
|
||||
handleMessage: (string) => void
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
webkit?: Webkit
|
||||
@ -9,6 +13,7 @@ declare global {
|
||||
Intercom: Function
|
||||
intercomSettings: IntercomSettings
|
||||
analytics?: Analytics
|
||||
AndroidWebKitMessageHandler?: AndroidWebKitMessageHandler
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -84,21 +84,22 @@ export function Article(props: ArticleProps): JSX.Element {
|
||||
window.webkit.messageHandlers.readingProgressUpdate?.postMessage({
|
||||
progress: readingProgress,
|
||||
})
|
||||
} else if (typeof window?.AndroidWebKitMessageHandler != 'undefined') {
|
||||
window.AndroidWebKitMessageHandler.handleMessage(
|
||||
JSON.stringify({ progress: readingProgress })
|
||||
)
|
||||
}
|
||||
}, [readingProgress])
|
||||
|
||||
useScrollWatcher(
|
||||
(changeset: ScrollOffsetChangeset) => {
|
||||
if (window && window.document.scrollingElement) {
|
||||
const newReadingProgress =
|
||||
window.scrollY / window.document.scrollingElement.scrollHeight
|
||||
const adjustedReadingProgress =
|
||||
newReadingProgress > 0.92 ? 1 : newReadingProgress
|
||||
debouncedSetReadingProgress(adjustedReadingProgress * 100)
|
||||
}
|
||||
},
|
||||
1000
|
||||
)
|
||||
useScrollWatcher((changeset: ScrollOffsetChangeset) => {
|
||||
if (window && window.document.scrollingElement) {
|
||||
const newReadingProgress =
|
||||
window.scrollY / window.document.scrollingElement.scrollHeight
|
||||
const adjustedReadingProgress =
|
||||
newReadingProgress > 0.92 ? 1 : newReadingProgress
|
||||
debouncedSetReadingProgress(adjustedReadingProgress * 100)
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
const layoutImages = useCallback(
|
||||
(image: HTMLImageElement, container: HTMLDivElement | null) => {
|
||||
@ -127,46 +128,46 @@ export function Article(props: ArticleProps): JSX.Element {
|
||||
if (typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
if (!shouldScrollToInitialPosition) {
|
||||
return
|
||||
}
|
||||
if (!shouldScrollToInitialPosition) {
|
||||
return
|
||||
}
|
||||
|
||||
setShouldScrollToInitialPosition(false)
|
||||
setShouldScrollToInitialPosition(false)
|
||||
|
||||
// If we are scrolling to a highlight, dont scroll to read position
|
||||
if (props.highlightHref.current) {
|
||||
return
|
||||
}
|
||||
// If we are scrolling to a highlight, dont scroll to read position
|
||||
if (props.highlightHref.current) {
|
||||
return
|
||||
}
|
||||
|
||||
if (props.initialReadingProgress && props.initialReadingProgress >= 98) {
|
||||
return
|
||||
}
|
||||
if (props.initialReadingProgress && props.initialReadingProgress >= 98) {
|
||||
return
|
||||
}
|
||||
|
||||
const anchorElement = props.highlightHref.current
|
||||
? document.querySelector(
|
||||
`[omnivore-highlight-id="${props.highlightHref.current}"]`
|
||||
)
|
||||
: document.querySelector(
|
||||
`[data-omnivore-anchor-idx='${props.initialAnchorIndex.toString()}']`
|
||||
)
|
||||
const anchorElement = props.highlightHref.current
|
||||
? document.querySelector(
|
||||
`[omnivore-highlight-id="${props.highlightHref.current}"]`
|
||||
)
|
||||
: document.querySelector(
|
||||
`[data-omnivore-anchor-idx='${props.initialAnchorIndex.toString()}']`
|
||||
)
|
||||
|
||||
if (anchorElement) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const calculateOffset = (obj: any): number => {
|
||||
let offset = 0
|
||||
if (obj.offsetParent) {
|
||||
do {
|
||||
offset += obj.offsetTop
|
||||
} while ((obj = obj.offsetParent))
|
||||
return offset
|
||||
}
|
||||
|
||||
return 0
|
||||
if (anchorElement) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const calculateOffset = (obj: any): number => {
|
||||
let offset = 0
|
||||
if (obj.offsetParent) {
|
||||
do {
|
||||
offset += obj.offsetTop
|
||||
} while ((obj = obj.offsetParent))
|
||||
return offset
|
||||
}
|
||||
|
||||
const calculatedOffset = calculateOffset(anchorElement)
|
||||
window.document.documentElement.scroll(0, calculatedOffset - 100)
|
||||
return 0
|
||||
}
|
||||
|
||||
const calculatedOffset = calculateOffset(anchorElement)
|
||||
window.document.documentElement.scroll(0, calculatedOffset - 100)
|
||||
}
|
||||
}, [
|
||||
props.initialAnchorIndex,
|
||||
props.initialReadingProgress,
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { useEffect, useRef, useCallback, useState, MutableRefObject } from 'react'
|
||||
import {
|
||||
useEffect,
|
||||
useRef,
|
||||
useCallback,
|
||||
useState,
|
||||
MutableRefObject,
|
||||
} from 'react'
|
||||
import { makeHighlightStartEndOffset } from '../../../lib/highlights/highlightGenerator'
|
||||
import type { HighlightLocation } from '../../../lib/highlights/highlightGenerator'
|
||||
import { useSelection } from '../../../lib/highlights/useSelection'
|
||||
@ -30,7 +36,6 @@ type HighlightsLayerProps = {
|
||||
scrollToHighlight: MutableRefObject<string | null>
|
||||
setShowHighlightsModal: React.Dispatch<React.SetStateAction<boolean>>
|
||||
articleMutations: ArticleMutations
|
||||
|
||||
}
|
||||
|
||||
type HighlightModalAction = 'none' | 'addComment' | 'share'
|
||||
@ -88,7 +93,9 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
// that now that all the content has been injected into the
|
||||
// page.
|
||||
if (props.scrollToHighlight.current) {
|
||||
const anchorElement = document.querySelector(`[omnivore-highlight-id="${props.scrollToHighlight.current}"]`)
|
||||
const anchorElement = document.querySelector(
|
||||
`[omnivore-highlight-id="${props.scrollToHighlight.current}"]`
|
||||
)
|
||||
if (anchorElement) {
|
||||
anchorElement.scrollIntoView({ behavior: 'auto' })
|
||||
}
|
||||
@ -100,7 +107,8 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
const highlightId = id || focusedHighlight?.id
|
||||
if (!highlightId) return
|
||||
|
||||
const didDeleteHighlight = await props.articleMutations.deleteHighlightMutation(highlightId)
|
||||
const didDeleteHighlight =
|
||||
await props.articleMutations.deleteHighlightMutation(highlightId)
|
||||
|
||||
if (didDeleteHighlight) {
|
||||
removeHighlights(
|
||||
@ -154,6 +162,13 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
actionID: 'annotate',
|
||||
annotation: inputs.highlight?.annotation ?? '',
|
||||
})
|
||||
} else if (typeof window?.AndroidWebKitMessageHandler != 'undefined') {
|
||||
window.AndroidWebKitMessageHandler.handleMessage(
|
||||
JSON.stringify({
|
||||
actionID: 'annotate',
|
||||
annotation: inputs.highlight?.annotation ?? '',
|
||||
})
|
||||
)
|
||||
} else {
|
||||
inputs.createHighlightForNote = async (note?: string) => {
|
||||
if (!inputs.selectionData) {
|
||||
@ -171,13 +186,16 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
selection: SelectionAttributes,
|
||||
note?: string
|
||||
): Promise<Highlight | undefined> => {
|
||||
const result = await createHighlight({
|
||||
selection: selection,
|
||||
articleId: props.articleId,
|
||||
existingHighlights: highlights,
|
||||
highlightStartEndOffsets: highlightLocations,
|
||||
annotation: note,
|
||||
}, props.articleMutations)
|
||||
const result = await createHighlight(
|
||||
{
|
||||
selection: selection,
|
||||
articleId: props.articleId,
|
||||
existingHighlights: highlights,
|
||||
highlightStartEndOffsets: highlightLocations,
|
||||
annotation: note,
|
||||
},
|
||||
props.articleMutations
|
||||
)
|
||||
|
||||
if (!result.highlights || result.highlights.length == 0) {
|
||||
// TODO: show an error message
|
||||
@ -235,16 +253,18 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
)
|
||||
|
||||
const scrollToHighlight = (id: string) => {
|
||||
const foundElement = document.querySelector(`[omnivore-highlight-id="${id}"]`)
|
||||
if(foundElement){
|
||||
foundElement.scrollIntoView({
|
||||
block: 'center',
|
||||
behavior: 'smooth'
|
||||
})
|
||||
window.location.hash = `#${id}`
|
||||
props.setShowHighlightsModal(false)
|
||||
}
|
||||
}
|
||||
const foundElement = document.querySelector(
|
||||
`[omnivore-highlight-id="${id}"]`
|
||||
)
|
||||
if (foundElement) {
|
||||
foundElement.scrollIntoView({
|
||||
block: 'center',
|
||||
behavior: 'smooth',
|
||||
})
|
||||
window.location.hash = `#${id}`
|
||||
props.setShowHighlightsModal(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Detect mouseclick on a highlight -- call `setFocusedHighlight` when highlight detected
|
||||
const handleClickHighlight = useCallback(
|
||||
@ -269,13 +289,17 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
// In the native app we post a message with the rect of the
|
||||
// highlight, so the app can display a native menu
|
||||
const rect = (target as Element).getBoundingClientRect()
|
||||
window?.webkit?.messageHandlers.viewerAction?.postMessage({
|
||||
const message = {
|
||||
actionID: 'showMenu',
|
||||
rectX: rect.x,
|
||||
rectY: rect.y,
|
||||
rectWidth: rect.width,
|
||||
rectHeight: rect.height,
|
||||
})
|
||||
}
|
||||
window?.webkit?.messageHandlers.viewerAction?.postMessage(message)
|
||||
window?.AndroidWebKitMessageHandler?.handleMessage(
|
||||
JSON.stringify(message)
|
||||
)
|
||||
setFocusedHighlight(highlight)
|
||||
}
|
||||
} else if ((target as Element).hasAttribute(highlightNoteIdAttribute)) {
|
||||
@ -326,13 +350,19 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
break
|
||||
case 'share':
|
||||
if (props.isAppleAppEmbed) {
|
||||
// send action to native app (naive app doesn't handle this yet so it's a no-op)
|
||||
window?.webkit?.messageHandlers.highlightAction?.postMessage({
|
||||
const message = {
|
||||
actionID: 'share',
|
||||
highlightID: focusedHighlight?.id,
|
||||
})
|
||||
}
|
||||
window?.webkit?.messageHandlers.highlightAction?.postMessage(
|
||||
message
|
||||
)
|
||||
}
|
||||
|
||||
window?.AndroidWebKitMessageHandler?.handleMessage(
|
||||
JSON.stringify(message)
|
||||
)
|
||||
|
||||
if (focusedHighlight) {
|
||||
if (canShareNative) {
|
||||
handleNativeShare(focusedHighlight.shortId)
|
||||
@ -392,7 +422,9 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
}
|
||||
|
||||
const speakingSection = async (event: SpeakingSectionEvent) => {
|
||||
const item = document.querySelector(`[data-omnivore-anchor-idx="${event.anchorIdx}"]`)
|
||||
const item = document.querySelector(
|
||||
`[data-omnivore-anchor-idx="${event.anchorIdx}"]`
|
||||
)
|
||||
const otherItems = document.querySelectorAll('.speakingSection')
|
||||
otherItems.forEach((other) => {
|
||||
if (other != item) {
|
||||
@ -435,7 +467,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
document.addEventListener('saveAnnotation', saveAnnotation)
|
||||
document.addEventListener('speakingSection', speakingSection)
|
||||
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('annotate', annotate)
|
||||
document.removeEventListener('highlight', highlight)
|
||||
@ -445,7 +476,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
document.removeEventListener('dismissHighlight', dismissHighlight)
|
||||
document.removeEventListener('saveAnnotation', saveAnnotation)
|
||||
document.removeEventListener('speakingSection', speakingSection)
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
@ -523,4 +553,4 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
}
|
||||
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user