From 283a647539a36f6a288624c03f53982711d7e1db Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 7 Jan 2025 15:23:24 -0600 Subject: [PATCH] Clarify intent behind precacheProfile and its unstable query cache --- src/state/queries/profile.ts | 34 +++++++++++---- src/state/queries/resolve-uri.ts | 13 +++--- src/types/atproto/profile.ts | 41 ------------------- .../notifications/NotificationFeedItem.tsx | 9 +--- src/view/com/profile/ProfileCard.tsx | 2 +- src/view/com/util/UserAvatar.tsx | 2 +- 6 files changed, 35 insertions(+), 66 deletions(-) diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts index 42dddbb975..a5edd9b405 100644 --- a/src/state/queries/profile.ts +++ b/src/state/queries/profile.ts @@ -44,9 +44,13 @@ export const profilesQueryKey = (handles: string[]) => [ handles, ] -const profileBasicQueryKeyRoot = 'profileBasic' -export const profileBasicQueryKey = (didOrHandle: string) => [ - profileBasicQueryKeyRoot, +const unstableProfileViewCacheQueryKeyRoot = 'unstableProfileViewCache' +/** + * We cache multiple profile view types by this query key. If object shapes are + * important, you should validate the type when accessing the data. + */ +export const unstableProfileViewCacheQueryKey = (didOrHandle: string) => [ + unstableProfileViewCacheQueryKeyRoot, didOrHandle, ] @@ -74,9 +78,10 @@ export function useProfileQuery({ placeholderData: () => { if (!did) return - return queryClient.getQueryData( - profileBasicQueryKey(did), - ) + // This can return any profile view type + return queryClient.getQueryData( + unstableProfileViewCacheQueryKey(did), + ) as AppBskyActorDefs.ProfileViewDetailed }, enabled: !!did, }) @@ -507,12 +512,23 @@ function useProfileUnblockMutation() { }) } +/** + * This function is used to precache a profile view in the query client. Any + * profile view type is accepted, so you should validate the type when + * accessing the data. + */ export function precacheProfile( queryClient: QueryClient, - profile: AppBskyActorDefs.ProfileViewBasic, + profile: atp.profile.AnyProfileView, ) { - queryClient.setQueryData(profileBasicQueryKey(profile.handle), profile) - queryClient.setQueryData(profileBasicQueryKey(profile.did), profile) + queryClient.setQueryData( + unstableProfileViewCacheQueryKey(profile.handle), + profile, + ) + queryClient.setQueryData( + unstableProfileViewCacheQueryKey(profile.did), + profile, + ) } async function whenAppViewReady( diff --git a/src/state/queries/resolve-uri.ts b/src/state/queries/resolve-uri.ts index c1fd8e240a..8efa630599 100644 --- a/src/state/queries/resolve-uri.ts +++ b/src/state/queries/resolve-uri.ts @@ -1,4 +1,4 @@ -import {AppBskyActorDefs, AtUri} from '@atproto/api' +import {AtUri} from '@atproto/api' import { QueryClient, useQuery, @@ -8,7 +8,8 @@ import { import {STALE} from '#/state/queries' import {useAgent} from '#/state/session' -import {profileBasicQueryKey as RQKEY_PROFILE_BASIC} from './profile' +import * as atp from '#/types/atproto' +import {unstableProfileViewCacheQueryKey} from './profile' const RQKEY_ROOT = 'resolved-did' export const RQKEY = (didOrHandle: string) => [RQKEY_ROOT, didOrHandle] @@ -46,10 +47,10 @@ export function useResolveDidQuery(didOrHandle: string | undefined) { // Return undefined if no did or handle if (!didOrHandle) return - const profile = - queryClient.getQueryData( - RQKEY_PROFILE_BASIC(didOrHandle), - ) + // This can return any profile view type + const profile = queryClient.getQueryData( + unstableProfileViewCacheQueryKey(didOrHandle), + ) return profile?.did }, enabled: !!didOrHandle, diff --git a/src/types/atproto/profile.ts b/src/types/atproto/profile.ts index 55881fe126..7c0aae287e 100644 --- a/src/types/atproto/profile.ts +++ b/src/types/atproto/profile.ts @@ -26,44 +26,3 @@ export type AnyProfileView = | AppBskyActorDefs.ProfileView | AppBskyActorDefs.ProfileViewDetailed | ChatBskyActorDefs.ProfileViewBasic - -/** - * Maps any profile view type to `ProfileViewBasic`. - */ -export function anyToBasic( - view: AnyProfileView, -): AppBskyActorDefs.ProfileViewBasic { - return { - $type: 'app.bsky.actor.defs#profileViewBasic', - did: view.did, - handle: view.handle, - displayName: view.displayName, - avatar: view.avatar, - associated: view.associated, - viewer: view.viewer, - labels: view.labels, - // `createdAt` doesn't exist in ChatBskyActorDefs.ProfileViewBasic - createdAt: 'createdAt' in view ? view.createdAt : undefined, - } -} - -/** - * Maps `ProfileViewDetailed` to `ProfileView`. - */ -export function detailedToView( - view: AppBskyActorDefs.ProfileViewDetailed, -): AppBskyActorDefs.ProfileView { - return { - $type: 'app.bsky.actor.defs#profileView', - did: view.did, - handle: view.handle, - displayName: view.displayName, - avatar: view.avatar, - associated: view.associated, - viewer: view.viewer, - labels: view.labels, - createdAt: view.createdAt, - description: view.description, - indexedAt: view.indexedAt, - } -} diff --git a/src/view/com/notifications/NotificationFeedItem.tsx b/src/view/com/notifications/NotificationFeedItem.tsx index 658518bc89..186d054fca 100644 --- a/src/view/com/notifications/NotificationFeedItem.tsx +++ b/src/view/com/notifications/NotificationFeedItem.tsx @@ -121,14 +121,7 @@ let NotificationFeedItem = ({ } const onBeforePress = React.useCallback(() => { - /* - * Notification returns ProfileView, which has one additional field on top - * of `Basic`: `indexedAt`. Harmless for now, but should be fixed. - */ - precacheProfile( - queryClient, - atp.profile.anyToBasic(item.notification.author), - ) + precacheProfile(queryClient, item.notification.author) }, [queryClient, item.notification.author]) const authors: Author[] = useMemo(() => { diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx index 8a258498fb..f5376b4798 100644 --- a/src/view/com/profile/ProfileCard.tsx +++ b/src/view/com/profile/ProfileCard.tsx @@ -61,7 +61,7 @@ export function ProfileCard({ const onBeforePress = React.useCallback(() => { onPress?.() - precacheProfile(queryClient, atp.profile.anyToBasic(profile)) + precacheProfile(queryClient, profile) }, [onPress, profile, queryClient]) if (!moderationOpts) { diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index 1685ac9583..b583ae8acf 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -427,7 +427,7 @@ let PreviewableUserAvatar = ({ const onPress = React.useCallback(() => { onBeforePress?.() - precacheProfile(queryClient, atp.profile.anyToBasic(profile)) + precacheProfile(queryClient, profile) }, [profile, queryClient, onBeforePress]) return (