From 9be74570b670c2ffa0bfd70f3ae40f461c5a7712 Mon Sep 17 00:00:00 2001 From: Thomas Rogers Date: Mon, 7 Aug 2023 19:29:48 +0200 Subject: [PATCH] Fix up and refactor some elements of the code. Allow multiple loading items. --- .../web/components/elements/LoadingBar.tsx | 3 +- .../patterns/LibraryCards/LibraryListCard.tsx | 2 +- .../patterns/LibraryCards/LinkedItemCard.tsx | 2 - .../templates/homeFeed/HomeFeedContainer.tsx | 144 +++++++++--------- 4 files changed, 70 insertions(+), 81 deletions(-) diff --git a/packages/web/components/elements/LoadingBar.tsx b/packages/web/components/elements/LoadingBar.tsx index 8231a5b19..6b92e7bf8 100644 --- a/packages/web/components/elements/LoadingBar.tsx +++ b/packages/web/components/elements/LoadingBar.tsx @@ -12,9 +12,8 @@ type AnimationStatus = { position: number, transition: string } + export function LoadingBar(props: LoadingBarProps): JSX.Element { - // OK So, what we want to do is. - // We have two boxes. const [leftOne, setLeftOne] = useState({ position: 0, transition: 'left 0.5s linear' }) const [leftTwo, setLeftTwo] = useState({ position: -100, transition: 'left 0.5s linear' }) diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index 60b1d02b2..c4ff05907 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -217,7 +217,7 @@ const ListImage = (props: ListImageProps): JSX.Element => { /> ) } - {(props.readingProgress ?? 0) > 0 && ( + {(props.readingProgress ?? 0) > 0 && !props.isLoading && ( diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index db76086d1..c79201288 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -1,59 +1,40 @@ -import { Action, createAction, useKBar, useRegisterActions } from 'kbar' -import { - articleQuery, -} from "../../../lib/networking/queries/useGetArticleQuery" -import debounce from 'lodash/debounce' -import { useRouter } from 'next/router' -import { - useCallback, - useEffect, - useMemo, - useReducer, - useRef, - useState, -} from 'react' -import toast, { Toaster } from 'react-hot-toast' -import TopBarProgress from 'react-topbar-progress-indicator' -import { useFetchMore } from '../../../lib/hooks/useFetchMoreScroll' -import { usePersistedState } from '../../../lib/hooks/usePersistedState' -import { libraryListCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' -import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts' -import { - PageType, - State, -} from '../../../lib/networking/fragments/articleFragment' +import { Action, createAction, useKBar, useRegisterActions } from "kbar" +import { articleQuery } from "../../../lib/networking/queries/useGetArticleQuery" +import debounce from "lodash/debounce" +import { useRouter } from "next/router" +import { useCallback, useEffect, useMemo, useRef, useState } from "react" +import toast, { Toaster } from "react-hot-toast" +import TopBarProgress from "react-topbar-progress-indicator" +import { useFetchMore } from "../../../lib/hooks/useFetchMoreScroll" +import { usePersistedState } from "../../../lib/hooks/usePersistedState" +import { libraryListCommands } from "../../../lib/keyboardShortcuts/navigationShortcuts" +import { useKeyboardShortcuts } from "../../../lib/keyboardShortcuts/useKeyboardShortcuts" +import { PageType, State } from "../../../lib/networking/fragments/articleFragment" import { SearchItem, TypeaheadSearchItemsData, - typeaheadSearchQuery, -} from '../../../lib/networking/queries/typeaheadSearch' -import type { - LibraryItem, - LibraryItemsQueryInput, -} from '../../../lib/networking/queries/useGetLibraryItemsQuery' -import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery' -import { - useGetViewerQuery, - UserBasicData, -} from '../../../lib/networking/queries/useGetViewerQuery' -import { Button } from '../../elements/Button' -import { StyledText } from '../../elements/StyledText' -import { ConfirmationModal } from '../../patterns/ConfirmationModal' -import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes' -import { LinkedItemCard } from '../../patterns/LibraryCards/LinkedItemCard' -import { Box, HStack, VStack } from './../../elements/LayoutPrimitives' -import { AddLinkModal } from './AddLinkModal' -import { EditLibraryItemModal } from './EditItemModals' -import { EmptyLibrary } from './EmptyLibrary' -import { HighlightItemsLayout } from './HighlightsLayout' -import { LibraryFilterMenu } from './LibraryFilterMenu' -import { LibraryHeader, MultiSelectMode } from './LibraryHeader' -import { UploadModal } from '../UploadModal' -import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation' -import { bulkActionMutation } from '../../../lib/networking/mutations/bulkActionMutation' -import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' -import { SetPageLabelsModalPresenter } from '../article/SetLabelsModalPresenter' -import { NotebookPresenter } from '../article/NotebookPresenter' + typeaheadSearchQuery +} from "../../../lib/networking/queries/typeaheadSearch" +import type { LibraryItem, LibraryItemsQueryInput } from "../../../lib/networking/queries/useGetLibraryItemsQuery" +import { useGetLibraryItemsQuery } from "../../../lib/networking/queries/useGetLibraryItemsQuery" +import { useGetViewerQuery, UserBasicData } from "../../../lib/networking/queries/useGetViewerQuery" +import { Button } from "../../elements/Button" +import { StyledText } from "../../elements/StyledText" +import { ConfirmationModal } from "../../patterns/ConfirmationModal" +import { LinkedItemCardAction } from "../../patterns/LibraryCards/CardTypes" +import { LinkedItemCard } from "../../patterns/LibraryCards/LinkedItemCard" +import { Box, HStack, VStack } from "./../../elements/LayoutPrimitives" +import { AddLinkModal } from "./AddLinkModal" +import { EditLibraryItemModal } from "./EditItemModals" +import { EmptyLibrary } from "./EmptyLibrary" +import { HighlightItemsLayout } from "./HighlightsLayout" +import { LibraryFilterMenu } from "./LibraryFilterMenu" +import { LibraryHeader, MultiSelectMode } from "./LibraryHeader" +import { UploadModal } from "../UploadModal" +import { BulkAction, bulkActionMutation } from "../../../lib/networking/mutations/bulkActionMutation" +import { showErrorToast, showSuccessToast } from "../../../lib/toastHelpers" +import { SetPageLabelsModalPresenter } from "../article/SetLabelsModalPresenter" +import { NotebookPresenter } from "../article/NotebookPresenter" import { saveUrlMutation } from "../../../lib/networking/mutations/saveUrlMutation" export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT' @@ -72,8 +53,10 @@ const debouncedFetchSearchResults = debounce((query, cb) => { fetchSearchResults(query, cb) }, 300) -// We set a relatively high delay for the refresh. -const TIMEOUT_DELAYS = [1000, 3000, 4000, 5000, 10000]; +// We set a relatively high delay for the refresh at the end, as it's likely there's an issue +// in processing. We give it the best attempt to be able to resolve, but if it doesn't we set +// the state as Failed. On refresh it will try again if the backend sends "PROCESSING" +const TIMEOUT_DELAYS = [1000, 2000, 2500, 3500, 5000, 10000, 60000]; export function HomeFeedContainer(): JSX.Element { const { viewerData } = useGetViewerQuery() @@ -103,7 +86,7 @@ export function HomeFeedContainer(): JSX.Element { const [linkToRemove, setLinkToRemove] = useState() const [linkToEdit, setLinkToEdit] = useState() const [linkToUnsubscribe, setLinkToUnsubscribe] = useState() - const [savedLink, setSavedLink] = useState(); + const updatingLinks = useRef>(new Set()) const [queryInputs, setQueryInputs] = useState(defaultQuery) @@ -171,6 +154,7 @@ export function HomeFeedContainer(): JSX.Element { return itemsPages[itemsPages.length - 1].search.pageInfo.hasNextPage }, [itemsPages]) + const libraryItems = useMemo(() => { const items = itemsPages?.flatMap((ad) => { @@ -180,44 +164,53 @@ export function HomeFeedContainer(): JSX.Element { }, [itemsPages, performActionOnItem]) useEffect(() => { - let startIdx = 1; - let timeout : NodeJS.Timeout | undefined; - if (savedLink) { - const seeIfUpdated = async() => { - if (startIdx > 5) { + const timeout : NodeJS.Timeout[] = [] + const itemsToUpdate = libraryItems + .filter(it => it.isLoading && !updatingLinks.current.has(it.node.slug)); + + const items = + itemsPages?.flatMap((ad) => { + return ad.search.edges.map(it => ({ ...it, isLoading: it.node.state === 'PROCESSING'})); + }) || [] + + items.map(async (item) => { + let startIdx = 0; + updatingLinks.current.add(item.node.slug) + + const seeIfUpdated = async () => { + if (startIdx > TIMEOUT_DELAYS.length) { + item.node.state = State.FAILED; + performActionOnItem('update-item', item); + return } - const item = getItem(savedLink); const username = viewerData?.me?.profile.username + const itemsToUpdate = libraryItems.filter(it => it.isLoading); - if (item) { + if (itemsToUpdate.length > 0) { const link = await articleQuery({ username, slug: item.node.slug, includeFriendsHighlights: false }) if (link && link.state != "PROCESSING") { const updatedArticle = { ...item }; - updatedArticle.node = {...item.node, ...link } + updatedArticle.node = { ...item.node, ...link } updatedArticle.isLoading = false; - console.log('updating') + console.log(`Updating Metadata of ${item.node.slug}.`) + updatingLinks.current.delete(item.node.slug) performActionOnItem('update-item', updatedArticle); return; } - if (!item.isLoading) { - performActionOnItem('update-item', { ...item, isLoading: true }); - } console.log(`Trying to get the metadata of item ${item.node.slug}... Retry ${startIdx} of 5`); - timeout = setTimeout(seeIfUpdated, TIMEOUT_DELAYS[startIdx++]) + timeout.push(setTimeout(seeIfUpdated, TIMEOUT_DELAYS[startIdx++])) } - - // If the item was not found, this suggests that we are not in the right search view. So we can bail early. } - setSavedLink(undefined); - timeout = setTimeout(seeIfUpdated, TIMEOUT_DELAYS[0]); - } + + await seeIfUpdated(); + }); return () => { - clearTimeout(timeout); + timeout.forEach(clearTimeout); } }, [itemsPages]) @@ -782,7 +775,6 @@ export function HomeFeedContainer(): JSX.Element { ) const id = result.url?.match(/[^/]+$/)?.[0] ?? ""; performActionOnItem('refresh', undefined as unknown as any) - setSavedLink(id); } else { showErrorToast('Error saving link', { position: 'bottom-right' }) }