From 191fcb2675e3b54824fd9e83df73521af03997d6 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 8 Mar 2022 13:54:20 -0800 Subject: [PATCH] send reading progres updates from embedded web views --- .../App/Views/LinkItemDetailView.swift | 2 + .../Views/Article/WebAppWrapperView.swift | 7 +++ .../Sources/Views/Article/WebView.swift | 1 + packages/web/additional.d.ts | 2 +- .../components/templates/article/Article.tsx | 49 ++++++++++++------- .../templates/article/HighlightsLayer.tsx | 20 ++++++-- 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift index 3f0d9cead..aed43d232 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift @@ -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) diff --git a/apple/OmnivoreKit/Sources/Views/Article/WebAppWrapperView.swift b/apple/OmnivoreKit/Sources/Views/Article/WebAppWrapperView.swift index 6f704e6b1..4fe383aca 100644 --- a/apple/OmnivoreKit/Sources/Views/Article/WebAppWrapperView.swift +++ b/apple/OmnivoreKit/Sources/Views/Article/WebAppWrapperView.swift @@ -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() @@ -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) { diff --git a/apple/OmnivoreKit/Sources/Views/Article/WebView.swift b/apple/OmnivoreKit/Sources/Views/Article/WebView.swift index dd802b68e..44931a57d 100644 --- a/apple/OmnivoreKit/Sources/Views/Article/WebView.swift +++ b/apple/OmnivoreKit/Sources/Views/Article/WebView.swift @@ -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 { diff --git a/packages/web/additional.d.ts b/packages/web/additional.d.ts index 552a77414..c121550ba 100644 --- a/packages/web/additional.d.ts +++ b/packages/web/additional.d.ts @@ -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 = { diff --git a/packages/web/components/templates/article/Article.tsx b/packages/web/components/templates/article/Article.tsx index e4b44f47d..f5d3cd00b 100644 --- a/packages/web/components/templates/article/Article.tsx +++ b/packages/web/components/templates/article/Article.tsx @@ -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(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 ( <> - + ) diff --git a/packages/web/components/templates/article/HighlightsLayer.tsx b/packages/web/components/templates/article/HighlightsLayer.tsx index 5f9938acc..714b5ae3e 100644 --- a/packages/web/components/templates/article/HighlightsLayer.tsx +++ b/packages/web/components/templates/article/HighlightsLayer.tsx @@ -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({ highlightModalAction: 'none' }) - const [highlightLocations, setHighlightLocations] = useState([]) + 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 => { + const createHighlightFromSelection = async ( + selection: SelectionAttributes, + note?: string + ): Promise => { 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' }) }