diff --git a/packages/api/src/elastic/index.ts b/packages/api/src/elastic/index.ts index 65a645bc5..3ec074933 100644 --- a/packages/api/src/elastic/index.ts +++ b/packages/api/src/elastic/index.ts @@ -201,6 +201,7 @@ export const updatePage = async ( }, }, refresh: ctx.refresh, + retry_on_conflict: 3, }) if (body.result !== 'updated') return false @@ -237,6 +238,7 @@ export const addLabelInPage = async ( }, }, refresh: ctx.refresh, + retry_on_conflict: 3, }) return body.result === 'updated' diff --git a/packages/api/src/resolvers/article/index.ts b/packages/api/src/resolvers/article/index.ts index 71b372a0f..e1337f7e8 100644 --- a/packages/api/src/resolvers/article/index.ts +++ b/packages/api/src/resolvers/article/index.ts @@ -707,9 +707,9 @@ export const saveArticleReadingProgressResolver = authorized< { input: { id, readingProgressPercent, readingProgressAnchorIndex } }, { claims: { uid }, pubsub } ) => { - const userArticleRecord = await getPageByParam({ userId: uid, _id: id }) + const page = await getPageByParam({ userId: uid, _id: id }) - if (!userArticleRecord) { + if (!page) { return { errorCodes: [SaveArticleReadingProgressErrorCode.NotFound] } } @@ -725,26 +725,25 @@ export const saveArticleReadingProgressResolver = authorized< // be greater than the current reading progress. const shouldUpdate = readingProgressPercent === 0 || - (userArticleRecord.readingProgressPercent || 0) < - readingProgressPercent || - (userArticleRecord.readingProgressAnchorIndex || 0) < - readingProgressAnchorIndex + page.readingProgressPercent < readingProgressPercent || + page.readingProgressAnchorIndex < readingProgressAnchorIndex - const updatedArticle = Object.assign(userArticleRecord, { + const updatedPart = { readingProgressPercent: shouldUpdate ? readingProgressPercent - : userArticleRecord.readingProgressPercent, + : page.readingProgressPercent, readingProgressAnchorIndex: shouldUpdate ? readingProgressAnchorIndex - : userArticleRecord.readingProgressAnchorIndex, - }) + : page.readingProgressAnchorIndex, + } - shouldUpdate && (await updatePage(id, updatedArticle, { pubsub, uid })) + shouldUpdate && (await updatePage(id, updatedPart, { pubsub, uid })) return { updatedArticle: { - ...updatedArticle, - isArchived: !!updatedArticle.archivedAt, + ...page, + ...updatedPart, + isArchived: !!page.archivedAt, }, } } diff --git a/packages/web/components/templates/article/Article.tsx b/packages/web/components/templates/article/Article.tsx index 60bbe1ff1..f02be67c2 100644 --- a/packages/web/components/templates/article/Article.tsx +++ b/packages/web/components/templates/article/Article.tsx @@ -1,21 +1,22 @@ -import { Box } from './../../../components/elements/LayoutPrimitives' +import { Box } from '../../elements/LayoutPrimitives' import { useReadingProgressAnchor } from '../../../lib/hooks/useReadingProgressAnchor' import { - useScrollWatcher, ScrollOffsetChangeset, + useScrollWatcher, } from '../../../lib/hooks/useScrollWatcher' import { - useRef, - useState, - useEffect, MutableRefObject, useCallback, + useEffect, + useMemo, + useRef, + useState, } from 'react' import { articleReadingProgressMutation } from '../../../lib/networking/mutations/articleReadingProgressMutation' import { Tweet } from 'react-twitter-widgets' import { render } from 'react-dom' import { isDarkTheme } from '../../../lib/themeUpdater' -import useDebounce from '../../../lib/hooks/useDebounce' +import { debounce } from 'lodash' export type ArticleProps = { articleId: string @@ -28,7 +29,9 @@ export type ArticleProps = { export function Article(props: ArticleProps): JSX.Element { const highlightTheme = isDarkTheme() ? 'dark' : 'default' - const [readingProgress, setReadingProgress] = useState(props.initialReadingProgress) + const [readingProgress, setReadingProgress] = useState( + props.initialReadingProgress + ) const [readingAnchorIndex, setReadingAnchorIndex] = useState( props.initialAnchorIndex @@ -41,14 +44,30 @@ export function Article(props: ArticleProps): JSX.Element { useReadingProgressAnchor(articleContentRef, setReadingAnchorIndex) - const debouncedReadingProgress = useDebounce(readingProgress, 1000); + const debouncedSetReadingProgress = useMemo( + () => + debounce((readingProgress: number) => { + console.log('setReadingProgress', readingProgress) + setReadingProgress(readingProgress) + }, 2000), + [] + ) + + // Stop the invocation of the debounced function + // after unmounting + useEffect(() => { + return () => { + debouncedSetReadingProgress.cancel() + } + }, []) useEffect(() => { ;(async () => { - if (!debouncedReadingProgress) return + if (!readingProgress) return await articleReadingProgressMutation({ id: props.articleId, - readingProgressPercent: debouncedReadingProgress, + // round reading progress to 100% if more than that + readingProgressPercent: readingProgress > 100 ? 100 : readingProgress, readingProgressAnchorIndex: readingAnchorIndex, }) })() @@ -56,20 +75,16 @@ export function Article(props: ArticleProps): JSX.Element { // We don't react to changes to readingAnchorIndex we // only care about the progress (scroll position) changed. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - props.articleId, - debouncedReadingProgress, - readingAnchorIndex, - ]) + }, [props.articleId, readingProgress]) // Post message to webkit so apple app embeds get progress updates useEffect(() => { if (typeof window?.webkit != 'undefined') { window.webkit.messageHandlers.readingProgressUpdate?.postMessage({ - progress: debouncedReadingProgress, + progress: readingProgress, }) } - }, [readingProgress, debouncedReadingProgress]) + }, [readingProgress]) const setScrollWatchedElement = useScrollWatcher( (changeset: ScrollOffsetChangeset) => { @@ -79,13 +94,13 @@ export function Article(props: ArticleProps): JSX.Element { (changeset.current.y + scrollContainer.clientHeight) / scrollContainer.scrollHeight - setReadingProgress(newReadingProgress * 100) + debouncedSetReadingProgress(newReadingProgress * 100) } else if (window && window.document.scrollingElement) { const newReadingProgress = window.scrollY / window.document.scrollingElement.scrollHeight const adjustedReadingProgress = newReadingProgress > 0.92 ? 1 : newReadingProgress - setReadingProgress(adjustedReadingProgress * 100) + debouncedSetReadingProgress(adjustedReadingProgress * 100) } }, 1000 @@ -152,9 +167,15 @@ export function Article(props: ArticleProps): JSX.Element { } if (props.scrollElementRef.current) { - props.scrollElementRef.current?.scroll(0, calculateOffset(anchorElement)) + props.scrollElementRef.current?.scroll( + 0, + calculateOffset(anchorElement) + ) } else { - window.document.documentElement.scroll(0, calculateOffset(anchorElement)) + window.document.documentElement.scroll( + 0, + calculateOffset(anchorElement) + ) } } }, [ diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 7b04fabab..36c2371c4 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -206,9 +206,12 @@ export function HomeFeedContainer(props: HomeFeedContainerProps): JSX.Element { if (activeCardId && !alreadyScrolled.current) { scrollToActiveCard(activeCardId) alreadyScrolled.current = true - } - if (activeItem) { - performActionOnItem('refresh', activeItem) + + if (activeItem) { + console.log('refreshing') + // refresh items on home feed + performActionOnItem('refresh', activeItem) + } } }, [activeCardId, scrollToActiveCard]) diff --git a/packages/web/package.json b/packages/web/package.json index 34236e76d..49a6a9737 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -50,6 +50,7 @@ "@types/cookie": "^0.4.1", "@types/diff-match-patch": "^1.0.32", "@types/jest": "^27.0.2", + "@types/lodash.debounce": "^4.0.6", "@types/react": "17.0.2", "@types/react-dom": "^17.0.2", "@types/segment-analytics": "^0.0.34", diff --git a/yarn.lock b/yarn.lock index e99e3552b..bd1b1dd0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5443,6 +5443,18 @@ "@types/interpret" "*" "@types/node" "*" +"@types/lodash.debounce@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz#c5a2326cd3efc46566c47e4c0aa248dc0ee57d60" + integrity sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.180" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" + integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== + "@types/long@^4.0.0", "@types/long@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"