From e16acb7f60902d4e0f17604d4d815e2d92f124b6 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 14 Aug 2024 11:41:55 +0800 Subject: [PATCH] Use react-query for most viewer queries to get better caching, remove the maxpages on library as that broke scrolling --- .../nav-containers/HighlightsContainer.tsx | 7 +-- .../nav-containers/HomeContainer.tsx | 11 +++-- .../web/components/templates/ErrorLayout.tsx | 9 ++-- .../components/templates/NavigationLayout.tsx | 9 ++-- .../components/templates/PrimaryDropdown.tsx | 15 ++++--- .../components/templates/PrimaryLayout.tsx | 9 ++-- .../templates/library/LibraryContainer.tsx | 20 ++++++--- .../library_items/useLibraryItems.tsx | 2 +- .../lib/networking/viewer/useGetViewer.tsx | 43 +++++++++++++++++++ .../web/pages/[username]/[slug]/index.tsx | 21 ++++----- packages/web/pages/index.tsx | 5 ++- packages/web/pages/settings/integrations.tsx | 3 +- 12 files changed, 106 insertions(+), 48 deletions(-) create mode 100644 packages/web/lib/networking/viewer/useGetViewer.tsx diff --git a/packages/web/components/nav-containers/HighlightsContainer.tsx b/packages/web/components/nav-containers/HighlightsContainer.tsx index a6a03716c..bc35a9d9f 100644 --- a/packages/web/components/nav-containers/HighlightsContainer.tsx +++ b/packages/web/components/nav-containers/HighlightsContainer.tsx @@ -33,12 +33,13 @@ import { HighlightViewNote } from '../patterns/HighlightNotes' import { theme } from '../tokens/stitches.config' import { useDeleteHighlight } from '../../lib/networking/highlights/useItemHighlights' import { EmptyLibrary } from '../templates/homeFeed/EmptyLibrary' +import { useGetViewer } from '../../lib/networking/viewer/useGetViewer' const PAGE_SIZE = 10 export function HighlightsContainer(): JSX.Element { const router = useRouter() - const viewer = useGetViewerQuery() + const { data: viewerData } = useGetViewer() const { isLoading, setSize, size, data, mutate } = useGetHighlights({ first: PAGE_SIZE, @@ -86,11 +87,11 @@ export function HighlightsContainer(): JSX.Element { )} {highlights.map((highlight) => { return ( - viewer.viewerData?.me && ( + viewerData && ( diff --git a/packages/web/components/nav-containers/HomeContainer.tsx b/packages/web/components/nav-containers/HomeContainer.tsx index 017d616e1..b6f80a65f 100644 --- a/packages/web/components/nav-containers/HomeContainer.tsx +++ b/packages/web/components/nav-containers/HomeContainer.tsx @@ -34,11 +34,11 @@ import { } from '../../lib/networking/queries/useGetSubscriptionsQuery' import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives' import { Toaster } from 'react-hot-toast' -import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery' import useLibraryItemActions from '../../lib/hooks/useLibraryItemActions' import { SyncLoader } from 'react-spinners' import { useGetLibraryItems } from '../../lib/networking/library_items/useLibraryItems' import { useRegisterActions } from 'kbar' +import { useGetViewer } from '../../lib/networking/viewer/useGetViewer' type HomeState = { items: HomeItem[] @@ -182,9 +182,8 @@ type NavigationContextType = { dispatch: React.Dispatch } -const NavigationContext = createContext( - undefined -) +const NavigationContext = + createContext(undefined) export const useNavigation = (): NavigationContextType => { const context = useContext(NavigationContext) @@ -199,7 +198,7 @@ export function HomeContainer(): JSX.Element { const homeData = useGetHomeItems() const router = useRouter() - const { viewerData } = useGetViewerQuery() + const { data: viewerData } = useGetViewer() const hasTopPicks = (homeData: HomeItemResponse) => { const topPicks = homeData.sections?.find( @@ -226,7 +225,7 @@ export function HomeContainer(): JSX.Element { useApplyLocalTheme() const viewerUsername = useMemo(() => { - return viewerData?.me?.profile.username + return viewerData?.profile.username }, [viewerData]) const searchItems = useMemo(() => { diff --git a/packages/web/components/templates/ErrorLayout.tsx b/packages/web/components/templates/ErrorLayout.tsx index be97b9c20..9848e6f61 100644 --- a/packages/web/components/templates/ErrorLayout.tsx +++ b/packages/web/components/templates/ErrorLayout.tsx @@ -3,6 +3,7 @@ import { StyledText } from '../elements/StyledText' import Link from 'next/link' import { Button } from '../elements/Button' import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery' +import { useGetViewer } from '../../lib/networking/viewer/useGetViewer' type ErrorPageStatusCode = 404 | 500 @@ -12,7 +13,7 @@ type ErrorLayoutProps = { } export function ErrorLayout(props: ErrorLayoutProps): JSX.Element { - const { viewerData } = useGetViewerQuery() + const { data: viewerData } = useGetViewer() return ( @@ -32,11 +33,11 @@ export function ErrorLayout(props: ErrorLayoutProps): JSX.Element { - + - ); + ) } diff --git a/packages/web/components/templates/NavigationLayout.tsx b/packages/web/components/templates/NavigationLayout.tsx index 15f1b921f..cf053db7a 100644 --- a/packages/web/components/templates/NavigationLayout.tsx +++ b/packages/web/components/templates/NavigationLayout.tsx @@ -26,6 +26,7 @@ import { import useWindowDimensions from '../../lib/hooks/useGetWindowDimensions' import { useAddItem } from '../../lib/networking/library_items/useLibraryItems' import { useHandleAddUrl } from '../../lib/hooks/useHandleAddUrl' +import { useGetViewer } from '../../lib/networking/viewer/useGetViewer' export type NavigationSection = | 'home' @@ -48,7 +49,7 @@ type NavigationLayoutProps = { export function NavigationLayout(props: NavigationLayoutProps): JSX.Element { useApplyLocalTheme() - const { viewerData } = useGetViewerQuery() + const { data: viewerData } = useGetViewer() const router = useRouter() const [showLogoutConfirmation, setShowLogoutConfirmation] = useState(false) const [showKeyboardCommandsModal, setShowKeyboardCommandsModal] = @@ -69,8 +70,10 @@ export function NavigationLayout(props: NavigationLayoutProps): JSX.Element { // Attempt to identify the user if they are logged in. useEffect(() => { - setupAnalytics(viewerData?.me) - }, [viewerData?.me]) + if (viewerData) { + setupAnalytics(viewerData) + } + }, [viewerData]) const showLogout = useCallback(() => { setShowLogoutConfirmation(true) diff --git a/packages/web/components/templates/PrimaryDropdown.tsx b/packages/web/components/templates/PrimaryDropdown.tsx index 659df484f..9c8ab53ea 100644 --- a/packages/web/components/templates/PrimaryDropdown.tsx +++ b/packages/web/components/templates/PrimaryDropdown.tsx @@ -17,6 +17,7 @@ import { styled, theme, ThemeId } from '../tokens/stitches.config' import { LayoutType } from './homeFeed/HomeFeedContainer' import { useCurrentTheme } from '../../lib/hooks/useCurrentTheme' import { ThemeSelector } from './article/ReaderSettingsControl' +import { useGetViewer } from '../../lib/networking/viewer/useGetViewer' type PrimaryDropdownProps = { children?: ReactNode @@ -82,7 +83,7 @@ const TriggerButton = (props: TriggerButtonProps): JSX.Element => { } export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element { - const { viewerData } = useGetViewerQuery() + const { data: viewerData } = useGetViewer() const router = useRouter() const headerDropdownActionHandler = useCallback( @@ -129,7 +130,7 @@ export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element { + props.children ?? } css={{ width: '240px', ml: '15px', bg: '$thNavMenuFooter' }} > @@ -150,16 +151,16 @@ export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element { }} > - {viewerData?.me && ( + {viewerData && ( <> - {viewerData.me.name} + {viewerData.name} - {`@${viewerData.me.profile.username}`} + {`@${viewerData.profile.username}`} )} diff --git a/packages/web/components/templates/PrimaryLayout.tsx b/packages/web/components/templates/PrimaryLayout.tsx index 6e5156f8a..3789b64d4 100644 --- a/packages/web/components/templates/PrimaryLayout.tsx +++ b/packages/web/components/templates/PrimaryLayout.tsx @@ -15,6 +15,7 @@ import { updateTheme } from '../../lib/themeUpdater' import { Priority, useRegisterActions } from 'kbar' import { ThemeId } from '../tokens/stitches.config' import { useVerifyAuth } from '../../lib/hooks/useVerifyAuth' +import { useGetViewer } from '../../lib/networking/viewer/useGetViewer' type PrimaryLayoutProps = { children: ReactNode @@ -28,7 +29,7 @@ type PrimaryLayoutProps = { export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element { useApplyLocalTheme() - const { viewerData } = useGetViewerQuery() + const { data: viewerData } = useGetViewer() const router = useRouter() const [showLogoutConfirmation, setShowLogoutConfirmation] = useState(false) const [showKeyboardCommandsModal, setShowKeyboardCommandsModal] = @@ -78,8 +79,10 @@ export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element { // Attempt to identify the user if they are logged in. useEffect(() => { - setupAnalytics(viewerData?.me) - }, [viewerData?.me]) + if (viewerData) { + setupAnalytics(viewerData) + } + }, [viewerData]) const showLogout = useCallback(() => { setShowLogoutConfirmation(true) diff --git a/packages/web/components/templates/library/LibraryContainer.tsx b/packages/web/components/templates/library/LibraryContainer.tsx index 64d1f8ce4..589dce6a3 100644 --- a/packages/web/components/templates/library/LibraryContainer.tsx +++ b/packages/web/components/templates/library/LibraryContainer.tsx @@ -55,6 +55,7 @@ import { emptyTrashMutation } from '../../../lib/networking/mutations/emptyTrash import { State } from '../../../lib/networking/fragments/articleFragment' import { useHandleAddUrl } from '../../../lib/hooks/useHandleAddUrl' import { QueryClient, useQueryClient } from '@tanstack/react-query' +import { useGetViewer } from '../../../lib/networking/viewer/useGetViewer' export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT' @@ -80,7 +81,7 @@ type LibraryContainerProps = { export function LibraryContainer(props: LibraryContainerProps): JSX.Element { const router = useRouter() - const { viewerData } = useGetViewerQuery() + const { data: viewerData } = useGetViewer() const { queryValue } = useKBar((state) => ({ queryValue: state.searchQuery })) const [searchResults, setSearchResults] = useState([]) @@ -116,6 +117,7 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element { const { data: itemsPages, isLoading, + isFetching, fetchNextPage, hasNextPage, error: fetchItemsError, @@ -309,7 +311,7 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element { switch (action) { case 'showDetail': - const username = viewerData?.me?.profile.username + const username = viewerData?.profile.username if (username) { setActiveCardId(item.node.id) if (item.node.state === State.PROCESSING) { @@ -652,7 +654,7 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element { name: link.title, keywords: '#' + link.title + ' #' + link.siteName, perform: () => { - const username = viewerData?.me?.profile.username + const username = viewerData?.profile.username if (username) { setActiveCardId(link.id) router.push(`/${username}/${link.slug}`) @@ -666,7 +668,11 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element { activeCardId ? [...ACTIVE_ACTIONS, ...UNACTIVE_ACTIONS] : UNACTIVE_ACTIONS, [activeCardId, activeItem] ) - useFetchMore(fetchNextPage) + useFetchMore(() => { + if (!isFetching && !isLoading && hasNextPage) { + fetchNextPage() + } + }) const setIsChecked = useCallback( (itemId: string, set: boolean) => { @@ -885,7 +891,7 @@ export type HomeFeedContentProps = { } function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { - const { viewerData } = useGetViewerQuery() + const { data: viewerData } = useGetViewer() const [layout, setLayout] = usePersistedState({ key: 'libraryLayout', initialValue: 'LIST_LAYOUT', @@ -928,7 +934,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { { return lastPage.pageInfo.hasNextPage diff --git a/packages/web/lib/networking/viewer/useGetViewer.tsx b/packages/web/lib/networking/viewer/useGetViewer.tsx new file mode 100644 index 000000000..fe9798933 --- /dev/null +++ b/packages/web/lib/networking/viewer/useGetViewer.tsx @@ -0,0 +1,43 @@ +import { useQuery } from '@tanstack/react-query' +import { gqlFetcher } from '../networkHelpers' +import { featureFragment } from '../fragments/featureFragment' +import { gql } from 'graphql-request' +import { UserBasicData } from '../queries/useGetViewerQuery' + +export function useGetViewer() { + return useQuery({ + queryKey: ['viewer'], + staleTime: Infinity, + queryFn: async () => { + const response = (await gqlFetcher(GQL_GET_VIEWER)) as ViewerData + return response.me + }, + }) +} + +type ViewerData = { + me?: UserBasicData +} + +const GQL_GET_VIEWER = gql` + query Viewer { + me { + id + name + isFullUser + profile { + id + username + pictureUrl + bio + } + email + source + intercomHash + featureList { + ...FeatureFields + } + } + } + ${featureFragment} +` diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx index 90107e997..7fcd54189 100644 --- a/packages/web/pages/[username]/[slug]/index.tsx +++ b/packages/web/pages/[username]/[slug]/index.tsx @@ -42,6 +42,7 @@ import { useMergeHighlight, useUpdateHighlight, } from '../../../lib/networking/highlights/useItemHighlights' +import { useGetViewer } from '../../../lib/networking/viewer/useGetViewer' const PdfArticleContainerNoSSR = dynamic( () => import('./../../../components/templates/article/PdfArticleContainer'), @@ -57,7 +58,7 @@ export default function Reader(): JSX.Element { const router = useRouter() const [showEditModal, setShowEditModal] = useState(false) const [showHighlightsModal, setShowHighlightsModal] = useState(false) - const { viewerData } = useGetViewerQuery() + const { data: viewerData } = useGetViewer() const readerSettings = useReaderSettings() const archiveItem = useArchiveItem() const deleteItem = useDeleteItem() @@ -275,7 +276,7 @@ export default function Reader(): JSX.Element { }, [actionHandler, goNextOrHome, goPreviousOrHome]) useEffect(() => { - if (libraryItem && viewerData?.me) { + if (libraryItem && viewerData) { posthog.capture('link_read', { link: libraryItem.id, slug: libraryItem.slug, @@ -547,15 +548,15 @@ export default function Reader(): JSX.Element { /> ) : null} - {libraryItem && viewerData?.me && libraryItem.contentReader == 'PDF' && ( + {libraryItem && viewerData && libraryItem.contentReader == 'PDF' && ( )} - {libraryItem && viewerData?.me && libraryItem.contentReader == 'WEB' && ( + {libraryItem && viewerData && libraryItem.contentReader == 'WEB' && ( - {libraryItem && viewerData?.me ? ( + {libraryItem && viewerData ? ( )} - {libraryItem && viewerData?.me && libraryItem.contentReader == 'EPUB' && ( + {libraryItem && viewerData && libraryItem.contentReader == 'EPUB' && ( - {libraryItem && viewerData?.me ? ( + {libraryItem && viewerData ? ( ) : (