diff --git a/packages/web/components/patterns/HighlightView.tsx b/packages/web/components/patterns/HighlightView.tsx
index 2534048ca..e69cda301 100644
--- a/packages/web/components/patterns/HighlightView.tsx
+++ b/packages/web/components/patterns/HighlightView.tsx
@@ -6,6 +6,7 @@ import { styled } from '../tokens/stitches.config'
type HighlightViewProps = {
highlight: Highlight
+ scrollToHighlight?: (arg: string) => void;
author?: string
title?: string
}
@@ -15,25 +16,23 @@ export function HighlightView(props: HighlightViewProps): JSX.Element {
() => props.highlight.quote.split('\n'),
[props.highlight.quote]
)
- const annotation = props.highlight.annotation ?? '';
const StyledQuote = styled(Blockquote, {
- margin: '0px 24px 16px 24px',
- fontSize: '18px',
- lineHeight: '27px',
- color: '$textDefault',
+ margin: '0px 0px 24px 0px',
+ fontSize: '14px',
+ lineHeight: '1.5',
+ fontFamily: 'Inter',
+ color: '$omnivoreGray',
+ cursor: 'pointer',
})
+ const scrollToHighlight = () => props.scrollToHighlight ? props.scrollToHighlight(props.highlight.id) : null
+
return (
-
- {annotation && (
-
- {annotation}
- )
- }
-
+
+
{props.highlight.prefix}
-
+
{lines.map((line: string, index: number) => (
{line}
@@ -48,10 +47,11 @@ export function HighlightView(props: HighlightViewProps): JSX.Element {
{props.highlight.suffix}
-
- {props.author && props.title &&(
- {props.title + props.author}
+
+ {props.author && (
+ {props.author}
)}
+ {props.title}
)
diff --git a/packages/web/components/templates/article/Article.tsx b/packages/web/components/templates/article/Article.tsx
index 9c6382857..d4f931565 100644
--- a/packages/web/components/templates/article/Article.tsx
+++ b/packages/web/components/templates/article/Article.tsx
@@ -21,8 +21,10 @@ import { ArticleMutations } from '../../../lib/articleActions'
export type ArticleProps = {
articleId: string
content: string
+ highlightReady: boolean
initialAnchorIndex: number
initialReadingProgress?: number
+ highlightHref: MutableRefObject
scrollElementRef: MutableRefObject
articleMutations: ArticleMutations
}
@@ -139,50 +141,53 @@ export function Article(props: ArticleProps): JSX.Element {
return
}
- if (!shouldScrollToInitialPosition) {
- return
- }
-
- setShouldScrollToInitialPosition(false)
-
- if (props.initialReadingProgress && props.initialReadingProgress >= 98) {
- return
- }
-
- const anchorElement = 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 (props.highlightReady) {
+ if (!shouldScrollToInitialPosition) {
+ return
}
- if (props.scrollElementRef.current) {
- props.scrollElementRef.current?.scroll(
- 0,
- calculateOffset(anchorElement)
- )
- } else {
- window.document.documentElement.scroll(
- 0,
- calculateOffset(anchorElement)
- )
+ setShouldScrollToInitialPosition(false)
+
+ 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()}']`
+ )
+
+ 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
+ }
+
+ const calculatedOffset = calculateOffset(anchorElement)
+
+ if (props.scrollElementRef.current) {
+ props.scrollElementRef.current?.scroll(0, calculatedOffset - 100)
+ } else {
+ window.document.documentElement.scroll(0, calculatedOffset - 100)
+ }
}
}
}, [
+ props.highlightReady,
+ props.scrollElementRef,
props.initialAnchorIndex,
props.initialReadingProgress,
- props.scrollElementRef,
shouldScrollToInitialPosition,
])
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index 1ef48a9f7..78c7371b4 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -6,7 +6,7 @@ import { ArticleSubtitle } from './../../patterns/ArticleSubtitle'
import { theme, ThemeId } from './../../tokens/stitches.config'
import { HighlightsLayer } from '../../templates/article/HighlightsLayer'
import { Button } from '../../elements/Button'
-import { MutableRefObject, useEffect, useState } from 'react'
+import { MutableRefObject, useEffect, useState, useRef } from 'react'
import { ReportIssuesModal } from './ReportIssuesModal'
import { reportIssueMutation } from '../../../lib/networking/mutations/reportIssueMutation'
import { ArticleHeaderToolbar } from './ArticleHeaderToolbar'
@@ -15,6 +15,7 @@ import { updateThemeLocally } from '../../../lib/themeUpdater'
import { ArticleMutations } from '../../../lib/articleActions'
import { LabelChip } from '../../elements/LabelChip'
import { Label } from '../../../lib/networking/fragments/labelFragment'
+import { HighlightLocation, makeHighlightStartEndOffset } from '../../../lib/highlights/highlightGenerator'
type ArticleContainerProps = {
article: ArticleAttributes
@@ -36,6 +37,11 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
const [showShareModal, setShowShareModal] = useState(false)
const [showReportIssuesModal, setShowReportIssuesModal] = useState(false)
const [fontSize, setFontSize] = useState(props.fontSize ?? 20)
+ const highlightHref = useRef(window.location.hash ? window.location.hash.split('#')[1] : null)
+ const [highlightReady, setHighlightReady] = useState(false)
+ const [highlightLocations, setHighlightLocations] = useState<
+ HighlightLocation[]
+ >([])
const updateFontSize = async (newFontSize: number) => {
if (fontSize !== newFontSize) {
@@ -48,6 +54,21 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
updateFontSize(props.fontSize ?? 20)
}, [props.fontSize])
+ // Load the highlights
+ useEffect(() => {
+ const res: HighlightLocation[] = []
+ props.article.highlights.forEach((highlight) => {
+ try {
+ const offset = makeHighlightStartEndOffset(highlight)
+ res.push(offset)
+ } catch (err) {
+ console.error(err)
+ }
+ })
+ setHighlightLocations(res)
+ setHighlightReady(true)
+ }, [props.article.highlights, setHighlightLocations])
+
// Listen for font size and color mode change events sent from host apps (ios, macos...)
useEffect(() => {
const increaseFontSize = async () => {
@@ -158,6 +179,8 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
)}
>
articleMutations: ArticleMutations
+ highlightLocations: HighlightLocation[]
}
type HighlightModalAction = 'none' | 'addComment' | 'share'
@@ -49,9 +50,6 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
const [highlightModalAction, setHighlightModalAction] =
useState({ highlightModalAction: 'none' })
- const [highlightLocations, setHighlightLocations] = useState<
- HighlightLocation[]
- >([])
const focusedHighlightMousePos = useRef({ pageX: 0, pageY: 0 })
const [focusedHighlight, setFocusedHighlight] = useState<
@@ -59,26 +57,12 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
>(undefined)
const [selectionData, setSelectionData] = useSelection(
- highlightLocations,
+ props.highlightLocations,
false //noteModal.open,
)
const canShareNative = useCanShareNative()
- // Load the highlights
- useEffect(() => {
- const res: HighlightLocation[] = []
- highlights.forEach((highlight) => {
- try {
- const offset = makeHighlightStartEndOffset(highlight)
- res.push(offset)
- } catch (err) {
- console.error(err)
- }
- })
- setHighlightLocations(res)
- }, [highlights, setHighlightLocations])
-
const removeHighlightCallback = useCallback(
async (id?: string) => {
const highlightId = id || focusedHighlight?.id
@@ -89,7 +73,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
if (didDeleteHighlight) {
removeHighlights(
highlights.map(($0) => $0.id),
- highlightLocations
+ props.highlightLocations
)
setHighlights(highlights.filter(($0) => $0.id !== highlightId))
setFocusedHighlight(undefined)
@@ -97,16 +81,16 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
console.error('Failed to delete highlight')
}
},
- [focusedHighlight, highlights, highlightLocations]
+ [focusedHighlight, highlights, props.highlightLocations]
)
const updateHighlightsCallback = useCallback(
(highlight: Highlight) => {
- removeHighlights([highlight.id], highlightLocations)
+ removeHighlights([highlight.id], props.highlightLocations)
const keptHighlights = highlights.filter(($0) => $0.id !== highlight.id)
setHighlights([...keptHighlights, highlight])
},
- [highlights, highlightLocations]
+ [highlights, props.highlightLocations]
)
const handleNativeShare = useCallback(
@@ -159,7 +143,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
selection: selection,
articleId: props.articleId,
existingHighlights: highlights,
- highlightStartEndOffsets: highlightLocations,
+ highlightStartEndOffsets: props.highlightLocations,
annotation: note,
}, props.articleMutations)
@@ -214,10 +198,22 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
selectionData,
setSelectionData,
canShareNative,
- highlightLocations,
+ props.highlightLocations,
]
)
+ 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)
+ }
+ }
+
// Detect mouseclick on a highlight -- call `setFocusedHighlight` when highlight detected
const handleClickHighlight = useCallback(
(event: MouseEvent) => {
@@ -261,7 +257,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
})
} else setFocusedHighlight(undefined)
},
- [highlights, highlightLocations]
+ [highlights, props.highlightLocations]
)
useEffect(() => {
@@ -469,9 +465,10 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
if (props.showHighlightsModal) {
return (
props.setShowHighlightsModal(false)}
- deleteHighlightAction={(highlightId: string) => {
+ highlights={highlights}
+ onOpenChange={() => props.setShowHighlightsModal(false)}
+ scrollToHighlight={scrollToHighlight}
+ deleteHighlightAction={(highlightId: string) => {
removeHighlightCallback(highlightId)
}}
/>
diff --git a/packages/web/components/templates/article/HighlightsModal.tsx b/packages/web/components/templates/article/HighlightsModal.tsx
index d31557c4b..9f3ffde6c 100644
--- a/packages/web/components/templates/article/HighlightsModal.tsx
+++ b/packages/web/components/templates/article/HighlightsModal.tsx
@@ -19,6 +19,7 @@ import { Pen, Trash } from 'phosphor-react'
type HighlightsModalProps = {
highlights: Highlight[]
+ scrollToHighlight?: (arg: string) => void;
deleteHighlightAction?: (highlightId: string) => void
onOpenChange: (open: boolean) => void
}
@@ -60,6 +61,7 @@ export function HighlightsModal(props: HighlightsModalProps): JSX.Element {
key={highlight.id}
highlight={highlight}
showDelete={!!props.deleteHighlightAction}
+ scrollToHighlight={props.scrollToHighlight}
deleteHighlightAction={() => {
if (props.deleteHighlightAction) {
props.deleteHighlightAction(highlight.id)
@@ -82,6 +84,7 @@ export function HighlightsModal(props: HighlightsModalProps): JSX.Element {
type ModalHighlightViewProps = {
highlight: Highlight
showDelete: boolean
+ scrollToHighlight?: (arg: string) => void;
deleteHighlightAction: () => void
}
@@ -156,7 +159,7 @@ function ModalHighlightView(props: ModalHighlightViewProps): JSX.Element {
return (
<>
-
+
{props.highlight.annotation && !isEditing ? (
{props.highlight.annotation}
) : null}