From fc1e30afd67e594e254d90e9337a585c19c8fee7 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 3 Apr 2024 23:33:46 +0100 Subject: [PATCH] Thread queryClient explicitly through (#3328) * Pass queryClient explicitly to resetProfilePostsQueries * Pass queryClient explicitly to updatePostShadow * Pass queryClient explicitly to updateProfileShadow --- src/state/cache/post-shadow.ts | 18 ++++++--- src/state/cache/profile-shadow.ts | 15 ++++--- src/state/queries/post-feed.ts | 44 +++++++++++--------- src/state/queries/post.ts | 36 +++++++++-------- src/state/queries/profile.ts | 67 +++++++++++++++++-------------- src/view/screens/Profile.tsx | 62 ++++++++++++++-------------- 6 files changed, 133 insertions(+), 109 deletions(-) diff --git a/src/state/cache/post-shadow.ts b/src/state/cache/post-shadow.ts index 7cf72fae..6225cbdb 100644 --- a/src/state/cache/post-shadow.ts +++ b/src/state/cache/post-shadow.ts @@ -1,13 +1,14 @@ -import {useEffect, useState, useMemo} from 'react' -import EventEmitter from 'eventemitter3' +import {useEffect, useMemo, useState} from 'react' import {AppBskyFeedDefs} from '@atproto/api' +import {QueryClient} from '@tanstack/react-query' +import EventEmitter from 'eventemitter3' + import {batchedUpdates} from '#/lib/batchedUpdates' -import {Shadow, castAsShadow} from './types' import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '../queries/notifications/feed' import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from '../queries/post-feed' import {findAllPostsInQueryData as findAllPostsInThreadQueryData} from '../queries/post-thread' import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '../queries/search-posts' -import {queryClient} from 'lib/react-query' +import {castAsShadow, Shadow} from './types' export type {Shadow} from './types' export interface PostShadow { @@ -93,8 +94,12 @@ function mergeShadow( }) } -export function updatePostShadow(uri: string, value: Partial) { - const cachedPosts = findPostsInCache(uri) +export function updatePostShadow( + queryClient: QueryClient, + uri: string, + value: Partial, +) { + const cachedPosts = findPostsInCache(queryClient, uri) for (let post of cachedPosts) { shadows.set(post, {...shadows.get(post), ...value}) } @@ -104,6 +109,7 @@ export function updatePostShadow(uri: string, value: Partial) { } function* findPostsInCache( + queryClient: QueryClient, uri: string, ): Generator { for (let post of findAllPostsInFeedQueryData(queryClient, uri)) { diff --git a/src/state/cache/profile-shadow.ts b/src/state/cache/profile-shadow.ts index 34fe5995..ca791bc9 100644 --- a/src/state/cache/profile-shadow.ts +++ b/src/state/cache/profile-shadow.ts @@ -1,7 +1,10 @@ -import {useEffect, useState, useMemo} from 'react' -import EventEmitter from 'eventemitter3' +import {useEffect, useMemo, useState} from 'react' import {AppBskyActorDefs} from '@atproto/api' +import {QueryClient} from '@tanstack/react-query' +import EventEmitter from 'eventemitter3' + import {batchedUpdates} from '#/lib/batchedUpdates' +import {findAllProfilesInQueryData as findAllProfilesInActorSearchQueryData} from '../queries/actor-search' import {findAllProfilesInQueryData as findAllProfilesInListMembersQueryData} from '../queries/list-members' import {findAllProfilesInQueryData as findAllProfilesInMyBlockedAccountsQueryData} from '../queries/my-blocked-accounts' import {findAllProfilesInQueryData as findAllProfilesInMyMutedAccountsQueryData} from '../queries/my-muted-accounts' @@ -11,9 +14,7 @@ import {findAllProfilesInQueryData as findAllProfilesInProfileQueryData} from '. import {findAllProfilesInQueryData as findAllProfilesInProfileFollowersQueryData} from '../queries/profile-followers' import {findAllProfilesInQueryData as findAllProfilesInProfileFollowsQueryData} from '../queries/profile-follows' import {findAllProfilesInQueryData as findAllProfilesInSuggestedFollowsQueryData} from '../queries/suggested-follows' -import {findAllProfilesInQueryData as findAllProfilesInActorSearchQueryData} from '../queries/actor-search' -import {Shadow, castAsShadow} from './types' -import {queryClient} from 'lib/react-query' +import {castAsShadow, Shadow} from './types' export type {Shadow} from './types' export interface ProfileShadow { @@ -58,10 +59,11 @@ export function useProfileShadow< } export function updateProfileShadow( + queryClient: QueryClient, did: string, value: Partial, ) { - const cachedProfiles = findProfilesInCache(did) + const cachedProfiles = findProfilesInCache(queryClient, did) for (let post of cachedProfiles) { shadows.set(post, {...shadows.get(post), ...value}) } @@ -90,6 +92,7 @@ function mergeShadow( } function* findProfilesInCache( + queryClient: QueryClient, did: string, ): Generator { yield* findAllProfilesInListMembersQueryData(queryClient, did) diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index 0e6eef52..b8988819 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -3,37 +3,37 @@ import {AppState} from 'react-native' import { AppBskyFeedDefs, AppBskyFeedPost, - ModerationDecision, AtUri, + ModerationDecision, } from '@atproto/api' import { - useInfiniteQuery, InfiniteData, - QueryKey, QueryClient, + QueryKey, + useInfiniteQuery, useQueryClient, } from '@tanstack/react-query' -import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' -import {useFeedTuners} from '../preferences/feed-tuners' -import {FeedTuner, FeedTunerFn, NoopFeedTuner} from 'lib/api/feed-manip' -import {FeedAPI, ReasonFeedSource} from 'lib/api/feed/types' -import {FollowingFeedAPI} from 'lib/api/feed/following' -import {AuthorFeedAPI} from 'lib/api/feed/author' -import {LikesFeedAPI} from 'lib/api/feed/likes' -import {CustomFeedAPI} from 'lib/api/feed/custom' -import {ListFeedAPI} from 'lib/api/feed/list' -import {MergeFeedAPI} from 'lib/api/feed/merge' + import {HomeFeedAPI} from '#/lib/api/feed/home' +import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' import {logger} from '#/logger' import {STALE} from '#/state/queries' -import {precacheFeedPostProfiles} from './profile' -import {getAgent} from '#/state/session' import {DEFAULT_LOGGED_OUT_PREFERENCES} from '#/state/queries/preferences/const' -import {KnownError} from '#/view/com/posts/FeedErrorMessage' -import {embedViewRecordToPostView, getEmbeddedPost} from './util' -import {useModerationOpts} from './preferences' -import {queryClient} from 'lib/react-query' +import {getAgent} from '#/state/session' +import {AuthorFeedAPI} from 'lib/api/feed/author' +import {CustomFeedAPI} from 'lib/api/feed/custom' +import {FollowingFeedAPI} from 'lib/api/feed/following' +import {LikesFeedAPI} from 'lib/api/feed/likes' +import {ListFeedAPI} from 'lib/api/feed/list' +import {MergeFeedAPI} from 'lib/api/feed/merge' +import {FeedAPI, ReasonFeedSource} from 'lib/api/feed/types' +import {FeedTuner, FeedTunerFn, NoopFeedTuner} from 'lib/api/feed-manip' import {BSKY_FEED_OWNER_DIDS} from 'lib/constants' +import {KnownError} from '#/view/com/posts/FeedErrorMessage' +import {useFeedTuners} from '../preferences/feed-tuners' +import {useModerationOpts} from './preferences' +import {precacheFeedPostProfiles} from './profile' +import {embedViewRecordToPostView, getEmbeddedPost} from './util' type ActorDid = string type AuthorFilter = @@ -458,7 +458,11 @@ function assertSomePostsPassModeration(feed: AppBskyFeedDefs.FeedViewPost[]) { } } -export function resetProfilePostsQueries(did: string, timeout = 0) { +export function resetProfilePostsQueries( + queryClient: QueryClient, + did: string, + timeout = 0, +) { setTimeout(() => { queryClient.resetQueries({ predicate: query => diff --git a/src/state/queries/post.ts b/src/state/queries/post.ts index e3682e30..b868a1da 100644 --- a/src/state/queries/post.ts +++ b/src/state/queries/post.ts @@ -1,12 +1,13 @@ import {useCallback} from 'react' import {AppBskyFeedDefs, AtUri} from '@atproto/api' -import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query' +import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' + +import {track} from '#/lib/analytics/analytics' +import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue' +import {logEvent, LogEvents} from '#/lib/statsig/statsig' +import {updatePostShadow} from '#/state/cache/post-shadow' import {Shadow} from '#/state/cache/types' import {getAgent} from '#/state/session' -import {updatePostShadow} from '#/state/cache/post-shadow' -import {track} from '#/lib/analytics/analytics' -import {logEvent, LogEvents} from '#/lib/statsig/statsig' -import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue' export const RQKEY = (postUri: string) => ['post', postUri] @@ -62,6 +63,7 @@ export function usePostLikeMutationQueue( logContext: LogEvents['post:like']['logContext'] & LogEvents['post:unlike']['logContext'], ) { + const queryClient = useQueryClient() const postUri = post.uri const postCid = post.cid const initialLikeUri = post.viewer?.like @@ -89,7 +91,7 @@ export function usePostLikeMutationQueue( }, onSuccess(finalLikeUri) { // finalize - updatePostShadow(postUri, { + updatePostShadow(queryClient, postUri, { likeUri: finalLikeUri, }) }, @@ -97,19 +99,19 @@ export function usePostLikeMutationQueue( const queueLike = useCallback(() => { // optimistically update - updatePostShadow(postUri, { + updatePostShadow(queryClient, postUri, { likeUri: 'pending', }) return queueToggle(true) - }, [postUri, queueToggle]) + }, [queryClient, postUri, queueToggle]) const queueUnlike = useCallback(() => { // optimistically update - updatePostShadow(postUri, { + updatePostShadow(queryClient, postUri, { likeUri: undefined, }) return queueToggle(false) - }, [postUri, queueToggle]) + }, [queryClient, postUri, queueToggle]) return [queueLike, queueUnlike] } @@ -149,6 +151,7 @@ export function usePostRepostMutationQueue( logContext: LogEvents['post:repost']['logContext'] & LogEvents['post:unrepost']['logContext'], ) { + const queryClient = useQueryClient() const postUri = post.uri const postCid = post.cid const initialRepostUri = post.viewer?.repost @@ -176,7 +179,7 @@ export function usePostRepostMutationQueue( }, onSuccess(finalRepostUri) { // finalize - updatePostShadow(postUri, { + updatePostShadow(queryClient, postUri, { repostUri: finalRepostUri, }) }, @@ -184,19 +187,19 @@ export function usePostRepostMutationQueue( const queueRepost = useCallback(() => { // optimistically update - updatePostShadow(postUri, { + updatePostShadow(queryClient, postUri, { repostUri: 'pending', }) return queueToggle(true) - }, [postUri, queueToggle]) + }, [queryClient, postUri, queueToggle]) const queueUnrepost = useCallback(() => { // optimistically update - updatePostShadow(postUri, { + updatePostShadow(queryClient, postUri, { repostUri: undefined, }) return queueToggle(false) - }, [postUri, queueToggle]) + }, [queryClient, postUri, queueToggle]) return [queueRepost, queueUnrepost] } @@ -234,12 +237,13 @@ function usePostUnrepostMutation( } export function usePostDeleteMutation() { + const queryClient = useQueryClient() return useMutation({ mutationFn: async ({uri}) => { await getAgent().deletePost(uri) }, onSuccess(data, variables) { - updatePostShadow(variables.uri, {isDeleted: true}) + updatePostShadow(queryClient, variables.uri, {isDeleted: true}) track('Post:Delete') }, }) diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts index 3c9e3e41..19492cf6 100644 --- a/src/state/queries/profile.ts +++ b/src/state/queries/profile.ts @@ -1,32 +1,33 @@ import {useCallback} from 'react' +import {Image as RNImage} from 'react-native-image-crop-picker' import { - AtUri, AppBskyActorDefs, - AppBskyActorProfile, AppBskyActorGetProfile, - AppBskyFeedDefs, + AppBskyActorProfile, AppBskyEmbedRecord, AppBskyEmbedRecordWithMedia, + AppBskyFeedDefs, + AtUri, } from '@atproto/api' import { + QueryClient, + useMutation, useQuery, useQueryClient, - useMutation, - QueryClient, } from '@tanstack/react-query' -import {Image as RNImage} from 'react-native-image-crop-picker' -import {useSession, getAgent} from '../session' -import {updateProfileShadow} from '../cache/profile-shadow' + +import {track} from '#/lib/analytics/analytics' import {uploadBlob} from '#/lib/api' import {until} from '#/lib/async/until' -import {Shadow} from '#/state/cache/types' -import {resetProfilePostsQueries} from '#/state/queries/post-feed' import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue' -import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts' -import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts' -import {STALE} from '#/state/queries' -import {track} from '#/lib/analytics/analytics' import {logEvent, LogEvents} from '#/lib/statsig/statsig' +import {Shadow} from '#/state/cache/types' +import {STALE} from '#/state/queries' +import {resetProfilePostsQueries} from '#/state/queries/post-feed' +import {updateProfileShadow} from '../cache/profile-shadow' +import {getAgent, useSession} from '../session' +import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts' +import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts' import {ThreadNode} from './post-thread' export const RQKEY = (did: string) => ['profile', did] @@ -190,6 +191,7 @@ export function useProfileFollowMutationQueue( logContext: LogEvents['profile:follow']['logContext'] & LogEvents['profile:unfollow']['logContext'], ) { + const queryClient = useQueryClient() const did = profile.did const initialFollowingUri = profile.viewer?.following const followMutation = useProfileFollowMutation(logContext) @@ -215,7 +217,7 @@ export function useProfileFollowMutationQueue( }, onSuccess(finalFollowingUri) { // finalize - updateProfileShadow(did, { + updateProfileShadow(queryClient, did, { followingUri: finalFollowingUri, }) }, @@ -223,19 +225,19 @@ export function useProfileFollowMutationQueue( const queueFollow = useCallback(() => { // optimistically update - updateProfileShadow(did, { + updateProfileShadow(queryClient, did, { followingUri: 'pending', }) return queueToggle(true) - }, [did, queueToggle]) + }, [queryClient, did, queueToggle]) const queueUnfollow = useCallback(() => { // optimistically update - updateProfileShadow(did, { + updateProfileShadow(queryClient, did, { followingUri: undefined, }) return queueToggle(false) - }, [did, queueToggle]) + }, [queryClient, did, queueToggle]) return [queueFollow, queueUnfollow] } @@ -269,6 +271,7 @@ function useProfileUnfollowMutation( export function useProfileMuteMutationQueue( profile: Shadow, ) { + const queryClient = useQueryClient() const did = profile.did const initialMuted = profile.viewer?.muted const muteMutation = useProfileMuteMutation() @@ -291,25 +294,25 @@ export function useProfileMuteMutationQueue( }, onSuccess(finalMuted) { // finalize - updateProfileShadow(did, {muted: finalMuted}) + updateProfileShadow(queryClient, did, {muted: finalMuted}) }, }) const queueMute = useCallback(() => { // optimistically update - updateProfileShadow(did, { + updateProfileShadow(queryClient, did, { muted: true, }) return queueToggle(true) - }, [did, queueToggle]) + }, [queryClient, did, queueToggle]) const queueUnmute = useCallback(() => { // optimistically update - updateProfileShadow(did, { + updateProfileShadow(queryClient, did, { muted: false, }) return queueToggle(false) - }, [did, queueToggle]) + }, [queryClient, did, queueToggle]) return [queueMute, queueUnmute] } @@ -341,6 +344,7 @@ function useProfileUnmuteMutation() { export function useProfileBlockMutationQueue( profile: Shadow, ) { + const queryClient = useQueryClient() const did = profile.did const initialBlockingUri = profile.viewer?.blocking const blockMutation = useProfileBlockMutation() @@ -366,7 +370,7 @@ export function useProfileBlockMutationQueue( }, onSuccess(finalBlockingUri) { // finalize - updateProfileShadow(did, { + updateProfileShadow(queryClient, did, { blockingUri: finalBlockingUri, }) }, @@ -374,19 +378,19 @@ export function useProfileBlockMutationQueue( const queueBlock = useCallback(() => { // optimistically update - updateProfileShadow(did, { + updateProfileShadow(queryClient, did, { blockingUri: 'pending', }) return queueToggle(true) - }, [did, queueToggle]) + }, [queryClient, did, queueToggle]) const queueUnblock = useCallback(() => { // optimistically update - updateProfileShadow(did, { + updateProfileShadow(queryClient, did, { blockingUri: undefined, }) return queueToggle(false) - }, [did, queueToggle]) + }, [queryClient, did, queueToggle]) return [queueBlock, queueUnblock] } @@ -406,13 +410,14 @@ function useProfileBlockMutation() { }, onSuccess(_, {did}) { queryClient.invalidateQueries({queryKey: RQKEY_MY_BLOCKED()}) - resetProfilePostsQueries(did, 1000) + resetProfilePostsQueries(queryClient, did, 1000) }, }) } function useProfileUnblockMutation() { const {currentAccount} = useSession() + const queryClient = useQueryClient() return useMutation({ mutationFn: async ({blockUri}) => { if (!currentAccount) { @@ -425,7 +430,7 @@ function useProfileUnblockMutation() { }) }, onSuccess(_, {did}) { - resetProfilePostsQueries(did, 1000) + resetProfilePostsQueries(queryClient, did, 1000) }, }) } diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index d5a46c5c..6073b957 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -1,6 +1,5 @@ import React, {useMemo} from 'react' import {StyleSheet} from 'react-native' -import {useFocusEffect} from '@react-navigation/native' import { AppBskyActorDefs, moderateProfile, @@ -9,36 +8,38 @@ import { } from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' -import {CenteredView} from '../com/util/Views' -import {ListRef} from '../com/util/List' -import {ScreenHider} from '#/components/moderation/ScreenHider' -import {ProfileLists} from '../com/lists/ProfileLists' -import {ProfileFeedgens} from '../com/feeds/ProfileFeedgens' -import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' -import {ErrorScreen} from '../com/util/error/ErrorScreen' -import {FAB} from '../com/util/fab/FAB' -import {s, colors} from 'lib/styles' -import {useAnalytics} from 'lib/analytics/analytics' -import {ComposeIcon2} from 'lib/icons' -import {useSetTitle} from 'lib/hooks/useSetTitle' -import {combinedDisplayName} from 'lib/strings/display-names' -import {resetProfilePostsQueries} from '#/state/queries/post-feed' -import {useResolveDidQuery} from '#/state/queries/resolve-uri' -import {useProfileQuery} from '#/state/queries/profile' -import {useProfileShadow} from '#/state/cache/profile-shadow' -import {useSession, getAgent} from '#/state/session' -import {useModerationOpts} from '#/state/queries/preferences' -import {useLabelerInfoQuery} from '#/state/queries/labeler' -import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' -import {cleanError} from '#/lib/strings/errors' -import {useComposerControls} from '#/state/shell/composer' -import {listenSoftReset} from '#/state/events' -import {isInvalidHandle} from '#/lib/strings/handles' +import {useFocusEffect} from '@react-navigation/native' +import {useQueryClient} from '@tanstack/react-query' +import {cleanError} from '#/lib/strings/errors' +import {isInvalidHandle} from '#/lib/strings/handles' +import {useProfileShadow} from '#/state/cache/profile-shadow' +import {listenSoftReset} from '#/state/events' +import {useLabelerInfoQuery} from '#/state/queries/labeler' +import {resetProfilePostsQueries} from '#/state/queries/post-feed' +import {useModerationOpts} from '#/state/queries/preferences' +import {useProfileQuery} from '#/state/queries/profile' +import {useResolveDidQuery} from '#/state/queries/resolve-uri' +import {getAgent, useSession} from '#/state/session' +import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' +import {useComposerControls} from '#/state/shell/composer' +import {useAnalytics} from 'lib/analytics/analytics' +import {useSetTitle} from 'lib/hooks/useSetTitle' +import {ComposeIcon2} from 'lib/icons' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {combinedDisplayName} from 'lib/strings/display-names' +import {colors, s} from 'lib/styles' +import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' +import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header' import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed' import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels' -import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header' +import {ScreenHider} from '#/components/moderation/ScreenHider' +import {ProfileFeedgens} from '../com/feeds/ProfileFeedgens' +import {ProfileLists} from '../com/lists/ProfileLists' +import {ErrorScreen} from '../com/util/error/ErrorScreen' +import {FAB} from '../com/util/fab/FAB' +import {ListRef} from '../com/util/List' +import {CenteredView} from '../com/util/Views' interface SectionRef { scrollToTop: () => void @@ -48,6 +49,7 @@ type Props = NativeStackScreenProps export function ProfileScreen({route}: Props) { const {_} = useLingui() const {currentAccount} = useSession() + const queryClient = useQueryClient() const name = route.params.name === 'me' ? currentAccount?.did : route.params.name const moderationOpts = useModerationOpts() @@ -78,9 +80,9 @@ export function ProfileScreen({route}: Props) { // When we open the profile, we want to reset the posts query if we are blocked. React.useEffect(() => { if (resolvedDid && profile?.viewer?.blockedBy) { - resetProfilePostsQueries(resolvedDid) + resetProfilePostsQueries(queryClient, resolvedDid) } - }, [profile?.viewer?.blockedBy, resolvedDid]) + }, [queryClient, profile?.viewer?.blockedBy, resolvedDid]) // Most pushes will happen here, since we will have only placeholder data if (isLoadingDid || isLoadingProfile) {