send reading progres updates from embedded web views

This commit is contained in:
Satindar Dhillon 2022-03-08 13:54:20 -08:00
parent 83241477a8
commit 191fcb2675
6 changed files with 58 additions and 23 deletions

View file

@ -91,6 +91,8 @@ final class LinkItemDetailViewModel: ObservableObject {
switch action {
case let .shareHighlight(highlightID):
print("show share modal for highlight with id: \(highlightID)")
case let .updateReadingProgess(progress: progress):
print("new reading progress: \(progress)")
}
}
.store(in: &newWebAppWrapperViewModel.subscriptions)

View file

@ -6,6 +6,7 @@ import WebKit
public final class WebAppWrapperViewModel: ObservableObject {
public enum Action {
case shareHighlight(highlightID: String)
case updateReadingProgess(progress: Double)
}
public var subscriptions = Set<AnyCancellable>()
@ -86,6 +87,12 @@ public struct WebAppWrapperView: View {
if message.name == WebViewAction.highlightAction.rawValue {
handleHighlightAction(message: message)
}
if message.name == WebViewAction.readingProgressUpdate.rawValue {
guard let messageBody = message.body as? [String: String] else { return }
guard let progress = messageBody["progress"] else { return }
print(progress)
}
}
private func handleHighlightAction(message: WKScriptMessage) {

View file

@ -4,6 +4,7 @@ import WebKit
/// The names on the javascript side must match for an action to be handled.
enum WebViewAction: String, CaseIterable {
case highlightAction
case readingProgressUpdate
}
final class WebView: WKWebView {

View file

@ -1,7 +1,6 @@
export {}
declare global {
// eslint-disable-next-line functional/prefer-type-literal
interface Window {
webkit?: Webkit
MathJax?: MathJax
@ -24,6 +23,7 @@ declare type Webkit = {
declare type MessageHandlers = {
viewerAction?: WebKitMessageHandler
highlightAction?: WebKitMessageHandler
readingProgressUpdate?: WebKitMessageHandler
}
declare type WebKitMessageHandler = {

View file

@ -36,27 +36,37 @@ export function Article(props: ArticleProps): JSX.Element {
props.initialAnchorIndex
)
const [
shouldScrollToInitialPosition,
setShouldScrollToInitialPosition,
] = useState(true)
const [shouldScrollToInitialPosition, setShouldScrollToInitialPosition] =
useState(true)
const articleContentRef = useRef<HTMLDivElement | null>(null)
useReadingProgressAnchor(articleContentRef, setReadingAnchorIndex)
const debouncedReadingProgress = useDebounce(readingProgress, 1000);
const debouncedReadingAnchorIndex = useDebounce(readingAnchorIndex, 1000);
const debouncedReadingProgress = useDebounce(readingProgress, 1000)
const debouncedReadingAnchorIndex = useDebounce(readingAnchorIndex, 1000)
useEffect(() => {
(async () => {
;(async () => {
await articleReadingProgressMutation({
id: props.articleId,
readingProgressPercent: readingProgress,
readingProgressPercent: debouncedReadingProgress,
readingProgressAnchorIndex: readingAnchorIndex,
})
})()
}, [props.articleId, debouncedReadingProgress, debouncedReadingAnchorIndex])
}, [
props.articleId,
debouncedReadingProgress,
debouncedReadingAnchorIndex,
readingAnchorIndex,
])
// Post message to webkit so apple app embeds get progress updates
useEffect(() => {
window?.webkit?.messageHandlers.readingProgressUpdate?.postMessage({
progress: debouncedReadingProgress,
})
}, [debouncedReadingProgress])
const setScrollWatchedElement = useScrollWatcher(
(changeset: ScrollOffsetChangeset) => {
@ -182,16 +192,19 @@ export function Article(props: ArticleProps): JSX.Element {
return (
<>
<link rel="stylesheet" href={`https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/${highlightTheme}.min.css`} />
<link
rel="stylesheet"
href={`https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/${highlightTheme}.min.css`}
/>
<Box
ref={articleContentRef}
css={{
maxWidth: '100%',
}}
className="article-inner-css"
dangerouslySetInnerHTML={{
__html: props.content,
}}
ref={articleContentRef}
css={{
maxWidth: '100%',
}}
className="article-inner-css"
dangerouslySetInnerHTML={{
__html: props.content,
}}
/>
</>
)

View file

@ -6,7 +6,11 @@ import type { Highlight } from '../../../lib/networking/fragments/highlightFragm
import { deleteHighlightMutation } from '../../../lib/networking/mutations/deleteHighlightMutation'
import { shareHighlightToFeedMutation } from '../../../lib/networking/mutations/shareHighlightToFeedMutation'
import { shareHighlightCommentMutation } from '../../../lib/networking/mutations/updateShareHighlightCommentMutation'
import { highlightIdAttribute, highlightNoteIdAttribute, SelectionAttributes } from '../../../lib/highlights/highlightHelpers'
import {
highlightIdAttribute,
highlightNoteIdAttribute,
SelectionAttributes,
} from '../../../lib/highlights/highlightHelpers'
import { HighlightBar, HighlightAction } from '../../patterns/HighlightBar'
import { removeHighlights } from '../../../lib/highlights/deleteHighlight'
import { createHighlight } from '../../../lib/highlights/createHighlight'
@ -49,7 +53,9 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
const [highlightModalAction, setHighlightModalAction] =
useState<HighlightActionProps>({ highlightModalAction: 'none' })
const [highlightLocations, setHighlightLocations] = useState<HighlightLocation[]>([])
const [highlightLocations, setHighlightLocations] = useState<
HighlightLocation[]
>([])
const focusedHighlightMousePos = useRef({ pageX: 0, pageY: 0 })
const [focusedHighlight, setFocusedHighlight] = useState<
@ -175,7 +181,10 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
[props.highlightBarDisabled]
)
const createHighlightFromSelection = async (selection: SelectionAttributes, note?: string): Promise<Highlight | undefined> => {
const createHighlightFromSelection = async (
selection: SelectionAttributes,
note?: string
): Promise<Highlight | undefined> => {
const result = await createHighlight({
selection: selection,
articleId: props.articleId,
@ -206,7 +215,10 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
if (!selectionData) {
return
}
const result = await createHighlightFromSelection(selectionData, annotation)
const result = await createHighlightFromSelection(
selectionData,
annotation
)
if (!result) {
toast.error('Error saving highlight', { position: 'bottom-right' })
}