From d0e4908c0f6ebc824420057b32b960c4b2a39c23 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 29 Jul 2024 21:18:40 +0800 Subject: [PATCH] Better cache handling when setting labels --- packages/web/lib/hooks/useSetPageLabels.tsx | 9 +- .../web/lib/networking/library_items/gql.tsx | 16 ++ .../library_items/useLibraryItems.tsx | 159 ++++++++++-------- .../networking/mutations/setLabelsMutation.ts | 43 ----- 4 files changed, 114 insertions(+), 113 deletions(-) delete mode 100644 packages/web/lib/networking/mutations/setLabelsMutation.ts diff --git a/packages/web/lib/hooks/useSetPageLabels.tsx b/packages/web/lib/hooks/useSetPageLabels.tsx index 2f07de068..b1216f138 100644 --- a/packages/web/lib/hooks/useSetPageLabels.tsx +++ b/packages/web/lib/hooks/useSetPageLabels.tsx @@ -1,8 +1,8 @@ import { useCallback, useEffect, useReducer } from 'react' -import { setLabelsMutation } from '../networking/mutations/setLabelsMutation' import { Label } from '../networking/fragments/labelFragment' import { showErrorToast } from '../toastHelpers' import throttle from 'lodash/throttle' +import { useSetItemLabels } from '../networking/library_items/useLibraryItems' export type LabelAction = 'RESET' | 'TEMP' | 'SAVE' export type LabelsDispatcher = (action: { @@ -13,11 +13,14 @@ export type LabelsDispatcher = (action: { export const useSetPageLabels = ( articleId?: string ): [{ labels: Label[] }, LabelsDispatcher] => { + const setItemLabels = useSetItemLabels() const saveLabels = (labels: Label[], articleId: string) => { ;(async () => { - const labelIds = labels.map((l) => l.id) if (articleId) { - const result = await setLabelsMutation(articleId, labelIds) + const result = await setItemLabels.mutateAsync({ + itemId: articleId, + labels, + }) if (!result) { showErrorToast('Error saving labels', { position: 'bottom-right', diff --git a/packages/web/lib/networking/library_items/gql.tsx b/packages/web/lib/networking/library_items/gql.tsx index f9215c9b2..20df3bf4f 100644 --- a/packages/web/lib/networking/library_items/gql.tsx +++ b/packages/web/lib/networking/library_items/gql.tsx @@ -145,6 +145,22 @@ export const GQL_MOVE_ITEM_TO_FOLDER = gql` } ` +export const GQL_SET_LABELS = gql` + mutation SetLabels($input: SetLabelsInput!) { + setLabels(input: $input) { + ... on SetLabelsSuccess { + labels { + ...LabelFields + } + } + ... on SetLabelsError { + errorCodes + } + } + } + ${labelFragment} +` + export const GQL_UPDATE_LIBRARY_ITEM = gql` mutation UpdatePage($input: UpdatePageInput!) { updatePage(input: $input) { diff --git a/packages/web/lib/networking/library_items/useLibraryItems.tsx b/packages/web/lib/networking/library_items/useLibraryItems.tsx index fa0ba67ae..07be6ec19 100644 --- a/packages/web/lib/networking/library_items/useLibraryItems.tsx +++ b/packages/web/lib/networking/library_items/useLibraryItems.tsx @@ -16,6 +16,7 @@ import { GQL_GET_LIBRARY_ITEM_CONTENT, GQL_MOVE_ITEM_TO_FOLDER, GQL_SEARCH_QUERY, + GQL_SET_LABELS, GQL_SET_LINK_ARCHIVED, GQL_UPDATE_LIBRARY_ITEM, } from './gql' @@ -269,43 +270,6 @@ export const useRestoreItem = () => { }) } -export const useMoveItemToFolder = () => { - const queryClient = useQueryClient() - const restoreItem = async (variables: { itemId: string; folder: string }) => { - const result = (await gqlFetcher(GQL_MOVE_ITEM_TO_FOLDER, { - id: variables.itemId, - folder: variables.folder, - })) as MoveToFolderData - if (result.moveToFolder.errorCodes?.length) { - throw new Error(result.moveToFolder.errorCodes[0]) - } - return result.moveToFolder - } - return useMutation({ - mutationFn: restoreItem, - onMutate: async (variables: { itemId: string; folder: string }) => { - await queryClient.cancelQueries({ queryKey: ['libraryItems'] }) - updateItemPropertyInCache( - queryClient, - variables.itemId, - 'folder', - variables.folder - ) - return { previousItems: queryClient.getQueryData(['libraryItems']) } - }, - onError: (error, itemId, context) => { - if (context?.previousItems) { - queryClient.setQueryData(['libraryItems'], context.previousItems) - } - }, - onSettled: async () => { - await queryClient.invalidateQueries({ - queryKey: ['libraryItems'], - }) - }, - }) -} - export const useUpdateItemReadStatus = () => { const queryClient = useQueryClient() const updateItemReadStatus = async ( @@ -371,6 +335,97 @@ export const useGetLibraryItemContent = (username: string, slug: string) => { }) } +export const useMoveItemToFolder = () => { + const queryClient = useQueryClient() + const moveItem = async (variables: { itemId: string; folder: string }) => { + const result = (await gqlFetcher(GQL_MOVE_ITEM_TO_FOLDER, { + id: variables.itemId, + folder: variables.folder, + })) as MoveToFolderData + if (result.moveToFolder.errorCodes?.length) { + throw new Error(result.moveToFolder.errorCodes[0]) + } + return result.moveToFolder + } + return useMutation({ + mutationFn: moveItem, + onMutate: async (variables: { itemId: string; folder: string }) => { + await queryClient.cancelQueries({ queryKey: ['libraryItems'] }) + updateItemPropertyInCache( + queryClient, + variables.itemId, + 'folder', + variables.folder + ) + return { previousItems: queryClient.getQueryData(['libraryItems']) } + }, + onError: (error, itemId, context) => { + if (context?.previousItems) { + queryClient.setQueryData(['libraryItems'], context.previousItems) + } + }, + onSettled: async () => { + await queryClient.invalidateQueries({ + queryKey: ['libraryItems'], + }) + }, + }) +} + +export const useSetItemLabels = () => { + const queryClient = useQueryClient() + const setLabels = async (variables: { itemId: string; labels: Label[] }) => { + const labelIds = variables.labels.map((l) => l.id) + const result = (await gqlFetcher(GQL_SET_LABELS, { + input: { pageId: variables.itemId, labelIds }, + })) as SetLabelsData + if (result.setLabels.errorCodes?.length) { + throw new Error(result.setLabels.errorCodes[0]) + } + return result.setLabels.labels + } + return useMutation({ + mutationFn: setLabels, + onMutate: async (variables: { itemId: string; labels: Label[] }) => { + await queryClient.cancelQueries({ queryKey: ['libraryItems'] }) + updateItemPropertyInCache( + queryClient, + variables.itemId, + 'labels', + variables.labels + ) + return { previousItems: queryClient.getQueryData(['libraryItems']) } + }, + onError: (error, itemId, context) => { + if (context?.previousItems) { + queryClient.setQueryData(['libraryItems'], context.previousItems) + } + }, + onSuccess: (newLabels, variables) => { + updateItemPropertyInCache( + queryClient, + variables.itemId, + 'labels', + newLabels + ) + }, + onSettled: async () => { + await queryClient.invalidateQueries({ + queryKey: ['libraryItems'], + }) + }, + }) +} + +type SetLabelsData = { + setLabels: SetLabelsResult +} + +type SetLabelsResult = { + labels?: Label[] + errorCodes?: string[] +} + export type TextDirection = 'RTL' | 'LTR' export type ArticleAttributes = { @@ -461,36 +516,6 @@ const GQL_SAVE_ARTICLE_READING_PROGRESS = gql` } ` -// export async function articleReadingProgressMutation( -// input: ArticleReadingProgressMutationInput -// ): Promise { -// const mutation = gql` -// mutation SaveArticleReadingProgress( -// $input: SaveArticleReadingProgressInput! -// ) { -// saveArticleReadingProgress(input: $input) { -// ... on SaveArticleReadingProgressSuccess { -// updatedArticle { -// id -// readingProgressPercent -// readingProgressAnchorIndex -// } -// } -// ... on SaveArticleReadingProgressError { -// errorCodes -// } -// } -// } -// ` - -// try { -// await gqlFetcher(mutation, { input }) -// return true -// } catch { -// return false -// } -// } - export interface ReadableItem { id: string title: string diff --git a/packages/web/lib/networking/mutations/setLabelsMutation.ts b/packages/web/lib/networking/mutations/setLabelsMutation.ts deleted file mode 100644 index 9889c130a..000000000 --- a/packages/web/lib/networking/mutations/setLabelsMutation.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { gql } from 'graphql-request' -import { Label, labelFragment } from '../fragments/labelFragment' -import { gqlFetcher } from '../networkHelpers' - -type SetLabelsResult = { - setLabels: SetLabels -} - -type SetLabels = { - labels: Label[] - errorCodes?: unknown[] -} - -export async function setLabelsMutation( - pageId: string, - labelIds: string[] -): Promise { - const mutation = gql` - mutation SetLabels($input: SetLabelsInput!) { - setLabels(input: $input) { - ... on SetLabelsSuccess { - labels { - ...LabelFields - } - } - ... on SetLabelsError { - errorCodes - } - } - } - ${labelFragment} - ` - - try { - const data = (await gqlFetcher(mutation, { - input: { pageId, labelIds }, - })) as SetLabelsResult - return data.setLabels.errorCodes ? undefined : data.setLabels.labels - } catch (error) { - console.log(' -- SetLabelsOutput error', error) - return undefined - } -}