From 9eca54d0a1c1d4f90a8ada5d3d0c2acabdaf1e5b Mon Sep 17 00:00:00 2001 From: Amar Trebinjac Date: Mon, 28 Apr 2025 17:30:01 +0900 Subject: [PATCH 1/3] fix: update list when approving or rejecting moderation item --- packages/shared/src/hooks/squads/useSourceModerationList.ts | 2 +- packages/shared/src/hooks/squads/useSquadPendingPosts.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/shared/src/hooks/squads/useSourceModerationList.ts b/packages/shared/src/hooks/squads/useSourceModerationList.ts index 4ac0c55fa0..83af205486 100644 --- a/packages/shared/src/hooks/squads/useSourceModerationList.ts +++ b/packages/shared/src/hooks/squads/useSourceModerationList.ts @@ -104,7 +104,7 @@ export const useSourceModerationList = ({ user, squad?.id, ); - const squadQueryKey = generateQueryKey(RequestKey.Squad, user, squad?.handle); + const squadQueryKey = generateQueryKey(RequestKey.Squad, user, squad?.id); const handleOptimistic = useCallback( (data: SquadPostModerationProps) => { diff --git a/packages/shared/src/hooks/squads/useSquadPendingPosts.ts b/packages/shared/src/hooks/squads/useSquadPendingPosts.ts index 8cc6e33071..073a12c263 100644 --- a/packages/shared/src/hooks/squads/useSquadPendingPosts.ts +++ b/packages/shared/src/hooks/squads/useSquadPendingPosts.ts @@ -47,8 +47,7 @@ export const useSquadPendingPosts = ({ queryKey: generateQueryKey( RequestKey.SquadPostRequests, user, - squadId ?? 'all', - status, + squadId, ), queryFn: async ({ pageParam }) => { return gqlClient From 9632b9b3e836136fa5e4282eaa439d5086eecb7b Mon Sep 17 00:00:00 2001 From: Amar Trebinjac Date: Tue, 29 Apr 2025 13:23:45 +0900 Subject: [PATCH 2/3] refactor: create a different key for all squads --- .../src/components/squads/SquadTabs.tsx | 8 +++- .../hooks/squads/useSourceModerationList.ts | 43 ++++++++++++++++--- .../src/hooks/squads/useSquadPendingPosts.ts | 6 +-- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/packages/shared/src/components/squads/SquadTabs.tsx b/packages/shared/src/components/squads/SquadTabs.tsx index c377f3bc84..1cc1b7f243 100644 --- a/packages/shared/src/components/squads/SquadTabs.tsx +++ b/packages/shared/src/components/squads/SquadTabs.tsx @@ -5,6 +5,7 @@ import { webappUrl } from '../../lib/constants'; import { SourcePermissions } from '../../graphql/sources'; import { verifyPermission } from '../../graphql/squads'; import { useSquad } from '../../hooks'; +import { useSquadPendingPosts } from '../../hooks/squads/useSquadPendingPosts'; export enum SquadTab { Settings = 'Settings', @@ -20,10 +21,13 @@ export function SquadTabs({ active, handle }: SquadTabsProps): ReactElement { const { squad } = useSquad({ handle, }); + const { count } = useSquadPendingPosts({ + squadId: squad?.id, + }); const isModerator = verifyPermission(squad, SourcePermissions.ModeratePost); const squadLink = `${webappUrl}squads/${handle}`; - const pendingTabLabel = squad?.moderationPostCount - ? `${SquadTab.PendingPosts} (${squad?.moderationPostCount})` + const pendingTabLabel = count + ? `${SquadTab.PendingPosts} (${count})` : SquadTab.PendingPosts; const links = [ diff --git a/packages/shared/src/hooks/squads/useSourceModerationList.ts b/packages/shared/src/hooks/squads/useSourceModerationList.ts index 83af205486..958d882683 100644 --- a/packages/shared/src/hooks/squads/useSourceModerationList.ts +++ b/packages/shared/src/hooks/squads/useSourceModerationList.ts @@ -99,11 +99,16 @@ export const useSourceModerationList = ({ const { logEvent } = useLogContext(); const { user } = useAuthContext(); const queryClient = useQueryClient(); - const listQueryKey = generateQueryKey( + const currentListKey = generateQueryKey( RequestKey.SquadPostRequests, user, squad?.id, ); + const multiSquadKey = generateQueryKey( + RequestKey.SquadPostRequests, + user, + undefined, + ); const squadQueryKey = generateQueryKey(RequestKey.Squad, user, squad?.id); const handleOptimistic = useCallback( @@ -111,7 +116,12 @@ export const useSourceModerationList = ({ const currentData = queryClient.getQueryData< InfiniteData> - >(listQueryKey); + >(currentListKey); + + const multiSquadData = + queryClient.getQueryData< + InfiniteData> + >(multiSquadKey); const currentSquad = queryClient.getQueryData( squadQueryKey, @@ -127,7 +137,25 @@ export const useSourceModerationList = ({ } queryClient.setQueryData>>( - listQueryKey, + multiSquadKey, + (oldData) => { + if (!oldData) { + return oldData; + } + return { + ...oldData, + pages: oldData.pages.map((page) => ({ + ...page, + edges: page.edges.filter( + (edge) => !data.postIds.includes(edge.node.id), + ), + })), + }; + }, + ); + + queryClient.setQueryData>>( + currentListKey, (oldData) => { if (!oldData) { return oldData; @@ -144,9 +172,9 @@ export const useSourceModerationList = ({ }, ); - return { currentData, currentSquad }; + return { currentData, currentSquad, multiSquadData }; }, - [queryClient, listQueryKey, squadQueryKey], + [queryClient, currentListKey, squadQueryKey, multiSquadKey], ); const { @@ -170,8 +198,9 @@ export const useSourceModerationList = ({ ); return; } - queryClient.setQueryData(listQueryKey, context?.currentData); + queryClient.setQueryData(currentListKey, context?.currentData); queryClient.setQueryData(squadQueryKey, context?.currentSquad); + queryClient.setQueryData(multiSquadKey, context?.multiSquadData); displayToast('Failed to approve post(s)'); }, }); @@ -212,7 +241,7 @@ export const useSourceModerationList = ({ }); }, onError: (_, __, context) => { - queryClient.setQueryData(listQueryKey, context.currentData); + queryClient.setQueryData(currentListKey, context.currentData); queryClient.setQueryData(squadQueryKey, context.currentSquad); }, }); diff --git a/packages/shared/src/hooks/squads/useSquadPendingPosts.ts b/packages/shared/src/hooks/squads/useSquadPendingPosts.ts index 073a12c263..a370b60a2d 100644 --- a/packages/shared/src/hooks/squads/useSquadPendingPosts.ts +++ b/packages/shared/src/hooks/squads/useSquadPendingPosts.ts @@ -44,11 +44,7 @@ export const useSquadPendingPosts = ({ }, [squads]); const query = useInfiniteQuery>({ - queryKey: generateQueryKey( - RequestKey.SquadPostRequests, - user, - squadId, - ), + queryKey: generateQueryKey(RequestKey.SquadPostRequests, user, squadId), queryFn: async ({ pageParam }) => { return gqlClient .request<{ From a4ae44f3a41eaebf69c299c2f712fd1fe1d883c8 Mon Sep 17 00:00:00 2001 From: Amar Trebinjac Date: Thu, 8 May 2025 18:57:31 +0900 Subject: [PATCH 3/3] refactor: move to key invalidation instead --- .../modals/squads/PostModerationModal.tsx | 2 +- .../squads/moderation/SquadModerationList.tsx | 4 +- .../moderation/useSourceModerationItem.ts | 2 +- .../hooks/squads/useSourceModerationList.ts | 113 +++--------------- 4 files changed, 21 insertions(+), 100 deletions(-) diff --git a/packages/shared/src/components/modals/squads/PostModerationModal.tsx b/packages/shared/src/components/modals/squads/PostModerationModal.tsx index ceaa950341..02376c6237 100644 --- a/packages/shared/src/components/modals/squads/PostModerationModal.tsx +++ b/packages/shared/src/components/modals/squads/PostModerationModal.tsx @@ -63,7 +63,7 @@ function PostModerationModal({ post: sharedPost, }); const isModerator = verifyPermission(squad, SourcePermissions.ModeratePost); - const { onDelete } = useSourceModerationList({ squad }); + const { onDelete } = useSourceModerationList(); const onDeleteClick = (postId: string) => onDelete(postId).then(() => modalProps.onRequestClose(null)); diff --git a/packages/shared/src/components/squads/moderation/SquadModerationList.tsx b/packages/shared/src/components/squads/moderation/SquadModerationList.tsx index 7ef466a528..f1eec17022 100644 --- a/packages/shared/src/components/squads/moderation/SquadModerationList.tsx +++ b/packages/shared/src/components/squads/moderation/SquadModerationList.tsx @@ -20,9 +20,7 @@ export function SquadModerationList({ squad, isModerator, }: SquadModerationListProps): ReactElement { - const moderate = useSourceModerationList({ - squad, - }); + const moderate = useSourceModerationList(); const { data, isFetched, fetchNextPage, hasNextPage, isPending } = useSquadPendingPosts({ diff --git a/packages/shared/src/components/squads/moderation/useSourceModerationItem.ts b/packages/shared/src/components/squads/moderation/useSourceModerationItem.ts index 8888499654..654712510b 100644 --- a/packages/shared/src/components/squads/moderation/useSourceModerationItem.ts +++ b/packages/shared/src/components/squads/moderation/useSourceModerationItem.ts @@ -50,7 +50,7 @@ export const useSourceModerationItem = ({ verifyPermission(squad, SourcePermissions.ModeratePost) || !searchParams?.get('handle'); - const { onDelete } = useSourceModerationList({ squad }); + const { onDelete } = useSourceModerationList(); return { context: { diff --git a/packages/shared/src/hooks/squads/useSourceModerationList.ts b/packages/shared/src/hooks/squads/useSourceModerationList.ts index 958d882683..beb816b4fc 100644 --- a/packages/shared/src/hooks/squads/useSourceModerationList.ts +++ b/packages/shared/src/hooks/squads/useSourceModerationList.ts @@ -1,4 +1,3 @@ -import type { InfiniteData } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import type { MouseEventHandler } from 'react'; import { useCallback } from 'react'; @@ -18,12 +17,10 @@ import { LazyModal } from '../../components/modals/common/types'; import { usePrompt } from '../usePrompt'; import { useToastNotification } from '../useToastNotification'; import { generateQueryKey, RequestKey } from '../../lib/query'; -import type { Squad } from '../../graphql/sources'; import { LogEvent } from '../../lib/log'; import { useLogContext } from '../../contexts/LogContext'; import { postLogEvent } from '../../lib/feed'; import type { Post } from '../../graphql/posts'; -import type { Connection } from '../../graphql/common'; import { useAuthContext } from '../../contexts/AuthContext'; export const rejectReasons: { value: PostModerationReason; label: string }[] = [ @@ -33,7 +30,7 @@ export const rejectReasons: { value: PostModerationReason; label: string }[] = [ }, { value: PostModerationReason.Violation, - label: 'Violates the Squad’s code of conduct', + label: "Violates the Squad's code of conduct", }, { value: PostModerationReason.Promotional, @@ -88,94 +85,14 @@ const getLogPostsFromModerationArray = (data: SourcePostModeration[]) => { })); }; -export const useSourceModerationList = ({ - squad, -}: { - squad?: Squad; -}): UseSourceModerationList => { +export const useSourceModerationList = (): UseSourceModerationList => { const { openModal, closeModal } = useLazyModal(); const { displayToast } = useToastNotification(); const { showPrompt } = usePrompt(); const { logEvent } = useLogContext(); const { user } = useAuthContext(); const queryClient = useQueryClient(); - const currentListKey = generateQueryKey( - RequestKey.SquadPostRequests, - user, - squad?.id, - ); - const multiSquadKey = generateQueryKey( - RequestKey.SquadPostRequests, - user, - undefined, - ); - const squadQueryKey = generateQueryKey(RequestKey.Squad, user, squad?.id); - - const handleOptimistic = useCallback( - (data: SquadPostModerationProps) => { - const currentData = - queryClient.getQueryData< - InfiniteData> - >(currentListKey); - - const multiSquadData = - queryClient.getQueryData< - InfiniteData> - >(multiSquadKey); - - const currentSquad = queryClient.getQueryData( - squadQueryKey, - ); - if (currentSquad) { - queryClient.setQueryData(squadQueryKey, (sqd) => { - return { - ...sqd, - moderationPostCount: - currentSquad.moderationPostCount - data.postIds.length, - }; - }); - } - - queryClient.setQueryData>>( - multiSquadKey, - (oldData) => { - if (!oldData) { - return oldData; - } - return { - ...oldData, - pages: oldData.pages.map((page) => ({ - ...page, - edges: page.edges.filter( - (edge) => !data.postIds.includes(edge.node.id), - ), - })), - }; - }, - ); - - queryClient.setQueryData>>( - currentListKey, - (oldData) => { - if (!oldData) { - return oldData; - } - return { - ...oldData, - pages: oldData.pages.map((page) => ({ - ...page, - edges: page.edges.filter( - (edge) => !data.postIds.includes(edge.node.id), - ), - })), - }; - }, - ); - - return { currentData, currentSquad, multiSquadData }; - }, - [queryClient, currentListKey, squadQueryKey, multiSquadKey], - ); + const queryKey = generateQueryKey(RequestKey.SquadPostRequests, user); const { mutateAsync: onApprove, @@ -184,23 +101,22 @@ export const useSourceModerationList = ({ } = useMutation({ mutationFn: ({ postIds }: SquadPostModerationProps) => squadApproveMutation(postIds), - onMutate: (data) => handleOptimistic(data), onSuccess: (data) => { displayToast('Post(s) approved successfully'); + queryClient.invalidateQueries({ + queryKey, + }); getLogPostsFromModerationArray(data).forEach((post) => { logEvent(postLogEvent(LogEvent.ApprovePost, post)); }); }, - onError: (_, variables, context) => { + onError: (_, variables) => { if (variables.postIds.length > 50) { displayToast( 'Failed to approve post(s). Please approve maximum 50 posts at a time', ); return; } - queryClient.setQueryData(currentListKey, context?.currentData); - queryClient.setQueryData(squadQueryKey, context?.currentSquad); - queryClient.setQueryData(multiSquadKey, context?.multiSquadData); displayToast('Failed to approve post(s)'); }, }); @@ -233,16 +149,17 @@ export const useSourceModerationList = ({ isSuccess: isSuccessReject, } = useMutation({ mutationFn: (props: SquadPostRejectionProps) => squadRejectMutation(props), - onMutate: (data) => handleOptimistic(data), onSuccess: (data) => { displayToast('Post(s) declined successfully'); + queryClient.invalidateQueries({ + queryKey, + }); getLogPostsFromModerationArray(data).forEach((post) => { logEvent(postLogEvent(LogEvent.RejectPost, post)); }); }, - onError: (_, __, context) => { - queryClient.setQueryData(currentListKey, context.currentData); - queryClient.setQueryData(squadQueryKey, context.currentSquad); + onError: () => { + displayToast('Failed to decline post(s)'); }, }); @@ -270,6 +187,12 @@ export const useSourceModerationList = ({ mutationFn: (postId: string) => deletePendingPostMutation(postId), onSuccess: () => { displayToast('Post deleted successfully'); + queryClient.invalidateQueries({ + queryKey, + }); + }, + onError: () => { + displayToast('Failed to delete post'); }, });