use new react-query for addurl

This commit is contained in:
Jackson Harper
2024-08-12 15:03:05 +08:00
parent 2ccb98ce35
commit 74320d5917
5 changed files with 170 additions and 59 deletions

View File

@ -11,20 +11,21 @@ import { setupAnalytics } from '../../lib/analytics'
import { primaryCommands } from '../../lib/keyboardShortcuts/navigationShortcuts'
import { logout } from '../../lib/logout'
import { useApplyLocalTheme } from '../../lib/hooks/useApplyLocalTheme'
import { updateTheme } from '../../lib/themeUpdater'
import { Priority, useRegisterActions } from 'kbar'
import { ThemeId, theme } from '../tokens/stitches.config'
import { useRegisterActions } from 'kbar'
import { theme } from '../tokens/stitches.config'
import { NavigationMenu } from './navMenu/NavigationMenu'
import { Button } from '../elements/Button'
import { List } from '@phosphor-icons/react'
import { LIBRARY_LEFT_MENU_WIDTH } from './navMenu/LibraryLegacyMenu'
import { AddLinkModal } from './AddLinkModal'
import { saveUrlMutation } from '../../lib/networking/mutations/saveUrlMutation'
import { v4 as uuidv4 } from 'uuid'
import {
showErrorToast,
showSuccessToastWithAction,
} from '../../lib/toastHelpers'
import useWindowDimensions from '../../lib/hooks/useGetWindowDimensions'
import { useAddItem } from '../../lib/networking/library_items/useLibraryItems'
import { useHandleAddUrl } from '../../lib/hooks/useHandleAddUrl'
export type NavigationSection =
| 'home'
@ -52,6 +53,7 @@ export function NavigationLayout(props: NavigationLayoutProps): JSX.Element {
const [showLogoutConfirmation, setShowLogoutConfirmation] = useState(false)
const [showKeyboardCommandsModal, setShowKeyboardCommandsModal] =
useState(false)
const addItem = useAddItem()
useRegisterActions(navigationCommands(router))
@ -84,22 +86,7 @@ export function NavigationLayout(props: NavigationLayoutProps): JSX.Element {
const [showAddLinkModal, setShowAddLinkModal] = useState(false)
const handleLinkAdded = useCallback(
async (link: string, timezone: string, locale: string) => {
const result = await saveUrlMutation(link, timezone, locale)
if (result) {
showSuccessToastWithAction('Link saved', 'Read now', async () => {
window.location.href = `/article?url=${encodeURIComponent(link)}`
return Promise.resolve()
})
// const id = result.url?.match(/[^/]+$/)?.[0] ?? ''
// performActionOnItem('refresh', undefined as unknown as any)
} else {
showErrorToast('Error saving link', { position: 'bottom-right' })
}
},
[]
)
const handleLinkAdded = useHandleAddUrl()
useEffect(() => {
document.addEventListener('logout', showLogout)

View File

@ -58,6 +58,7 @@ import { TrashIcon } from '../../elements/icons/TrashIcon'
import { theme } from '../../tokens/stitches.config'
import { emptyTrashMutation } from '../../../lib/networking/mutations/emptyTrashMutation'
import { State } from '../../../lib/networking/fragments/articleFragment'
import { useHandleAddUrl } from '../../../lib/hooks/useHandleAddUrl'
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
@ -851,24 +852,6 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element {
[itemsPages, multiSelectMode, checkedItems]
)
const handleLinkSubmission = async (
link: string,
timezone: string,
locale: string
) => {
const result = await saveUrlMutation(link, timezone, locale)
if (result) {
showSuccessToastWithAction('Link saved', 'Read now', async () => {
window.location.href = `/article?url=${encodeURIComponent(link)}`
return Promise.resolve()
})
const id = result.url?.match(/[^/]+$/)?.[0] ?? ''
// performActionOnItem('refresh', undefined as unknown as any)
} else {
showErrorToast('Error saving link', { position: 'bottom-right' })
}
}
return (
<HomeFeedGrid
folder={props.folder}
@ -882,7 +865,6 @@ export function LibraryContainer(props: LibraryContainerProps): JSX.Element {
performMultiSelectAction={performMultiSelectAction}
searchTerm={queryInputs.searchQuery}
gridContainerRef={gridContainerRef}
handleLinkSubmission={handleLinkSubmission}
applySearchQuery={(searchQuery: string) => {
setQueryInputs({
...queryInputs,
@ -959,12 +941,6 @@ export type HomeFeedContentProps = {
item: LibraryItem | undefined
) => Promise<void>
handleLinkSubmission: (
link: string,
timezone: string,
locale: string
) => Promise<void>
showNavigationMenu: boolean
setIsChecked: (itemId: string, set: boolean) => void
@ -1003,6 +979,8 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
return true
}, [props])
const addUrl = useHandleAddUrl()
return (
<VStack
css={{
@ -1055,7 +1033,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
{props.showAddLinkModal && (
<AddLinkModal
handleLinkSubmission={props.handleLinkSubmission}
handleLinkSubmission={addUrl}
onOpenChange={() => props.setShowAddLinkModal(false)}
/>
)}

View File

@ -0,0 +1,26 @@
import { useCallback } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { useAddItem } from '../networking/library_items/useLibraryItems'
import { showErrorToast, showSuccessToastWithAction } from '../toastHelpers'
export const useHandleAddUrl = () => {
const addItem = useAddItem()
return useCallback(async (url: string, timezone: string, locale: string) => {
const itemId = uuidv4()
const result = await addItem.mutateAsync({
itemId,
url,
timezone,
locale,
})
console.log('result: ', result)
if (result) {
showSuccessToastWithAction('Item saving', 'Read now', async () => {
window.location.href = `/article?url=${encodeURIComponent(url)}`
return Promise.resolve()
})
} else {
showErrorToast('Error saving url', { position: 'bottom-right' })
}
}, [])
}

View File

@ -258,3 +258,18 @@ export const GQL_BULK_ACTION = gql`
}
}
`
export const GQL_SAVE_URL = gql`
mutation SaveUrl($input: SaveUrlInput!) {
saveUrl(input: $input) {
... on SaveSuccess {
url
clientRequestId
}
... on SaveError {
errorCodes
message
}
}
}
`

View File

@ -1,6 +1,5 @@
import { gql, GraphQLClient } from 'graphql-request'
import { GraphQLClient } from 'graphql-request'
import {
InfiniteData,
QueryClient,
useInfiniteQuery,
useMutation,
@ -8,8 +7,8 @@ import {
useQueryClient,
} from '@tanstack/react-query'
import { ContentReader, PageType, State } from '../fragments/articleFragment'
import { Highlight, highlightFragment } from '../fragments/highlightFragment'
import { makeGqlFetcher, requestHeaders } from '../networkHelpers'
import { Highlight } from '../fragments/highlightFragment'
import { requestHeaders } from '../networkHelpers'
import { Label } from '../fragments/labelFragment'
import {
GQL_BULK_ACTION,
@ -17,6 +16,7 @@ import {
GQL_GET_LIBRARY_ITEM_CONTENT,
GQL_MOVE_ITEM_TO_FOLDER,
GQL_SAVE_ARTICLE_READING_PROGRESS,
GQL_SAVE_URL,
GQL_SEARCH_QUERY,
GQL_SET_LABELS,
GQL_SET_LINK_ARCHIVED,
@ -165,12 +165,55 @@ const overwriteItemPropertiesInCache = (
}
}
export const insertItemInCache = (
queryClient: QueryClient,
itemId: string,
url: string
) => {
const keys = queryClient
.getQueryCache()
.findAll({ queryKey: ['libraryItems'] })
console.log('keys: ', keys)
keys.forEach((query) => {
queryClient.setQueryData(query.queryKey, (data: any) => {
console.log('data, data.pages', data)
if (!data) return data
if (data.pages.length > 0) {
const firstPage = data.pages[0] as LibraryItems
firstPage.edges = [
...firstPage.edges,
{
cursor: firstPage.pageInfo.endCursor,
node: {
id: itemId,
title: url,
url: url,
originalArticleUrl: url,
readingProgressPercent: 0,
readingProgressAnchorIndex: 0,
slug: url,
folder: 'inbox',
ownedByViewer: true,
state: State.PROCESSING,
pageType: PageType.UNKNOWN,
createdAt: new Date().toISOString(),
},
},
]
data.pages[0] = firstPage
console.log('data: ', data)
return data
}
})
})
}
export function useGetLibraryItems(
folder: string | undefined,
{ limit, searchQuery }: LibraryItemsQueryInput,
enabled = true
) {
console.log('folder: ', folder)
const fullQuery = folder
? (`in:${folder} use:folders ` + (searchQuery ?? '')).trim()
: searchQuery ?? ''
@ -623,6 +666,56 @@ export const useSetItemLabels = () => {
})
}
export const useAddItem = () => {
const queryClient = useQueryClient()
const addItem = async (variables: {
itemId: string
url: string
timezone: string | undefined
locale: string | undefined
}) => {
const result = (await gqlFetcher(GQL_SAVE_URL, {
input: {
clientRequestId: variables.itemId,
url: variables.url,
source: 'add-link',
timezone: variables.timezone,
locale: variables.locale,
},
})) as SaveUrlData
if (result.saveUrl?.errorCodes?.length) {
throw new Error(result.saveUrl.errorCodes[0])
}
return result.saveUrl?.clientRequestId
}
return useMutation({
mutationFn: addItem,
onMutate: async (variables: {
itemId: string
url: string
timezone: string | undefined
locale: string | undefined
}) => {
await queryClient.cancelQueries({ queryKey: ['libraryItems'] })
const previousState = {
previousItems: queryClient.getQueryData(['libraryItems']),
}
insertItemInCache(queryClient, variables.itemId, variables.url)
return previousState
},
onError: (error, variables, context) => {
if (context?.previousItems) {
queryClient.setQueryData(['libraryItems'], context.previousItems)
}
},
onSettled: async () => {
await queryClient.invalidateQueries({
queryKey: ['libraryItems'],
})
},
})
}
export const useBulkActions = () => {
const queryClient = useQueryClient()
const bulkAction = async (variables: {
@ -675,6 +768,18 @@ type BulkActionData = {
bulkAction: BulkActionResult
}
export type SaveUrlResult = {
id?: string
url?: string
slug?: string
clientRequestId?: string
errorCodes?: string[]
}
export type SaveUrlData = {
saveUrl?: SaveUrlResult
}
type UpdateLibraryItemInput = {
pageId: string
title?: string
@ -811,16 +916,16 @@ export type LibraryItemNode = {
readingProgressAnchorIndex: number
slug: string
folder?: string
description: string
ownedByViewer: boolean
uploadFileId: string
labels?: Label[]
pageId: string
shortId: string
quote: string
annotation: string
state: State
pageType: PageType
description?: string
ownedByViewer: boolean
uploadFileId?: string
labels?: Label[]
pageId?: string
shortId?: string
quote?: string
annotation?: string
siteName?: string
siteIcon?: string
subscription?: string