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