mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
send reading progres updates from embedded web views
This commit is contained in:
parent
83241477a8
commit
191fcb2675
6 changed files with 58 additions and 23 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
2
packages/web/additional.d.ts
vendored
2
packages/web/additional.d.ts
vendored
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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' })
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue