Clarify intent behind precacheProfile and its unstable query cache

This commit is contained in:
Eric Bailey
2025-01-07 15:23:24 -06:00
parent 23672e0afc
commit 283a647539
6 changed files with 35 additions and 66 deletions
+25 -9
View File
@@ -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<AppBskyActorDefs.ProfileViewDetailed>(
profileBasicQueryKey(did),
)
// This can return any profile view type
return queryClient.getQueryData<atp.profile.AnyProfileView>(
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(
+7 -6
View File
@@ -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<AppBskyActorDefs.ProfileViewBasic>(
RQKEY_PROFILE_BASIC(didOrHandle),
)
// This can return any profile view type
const profile = queryClient.getQueryData<atp.profile.AnyProfileView>(
unstableProfileViewCacheQueryKey(didOrHandle),
)
return profile?.did
},
enabled: !!didOrHandle,
-41
View File
@@ -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,
}
}
@@ -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(() => {
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -427,7 +427,7 @@ let PreviewableUserAvatar = ({
const onPress = React.useCallback(() => {
onBeforePress?.()
precacheProfile(queryClient, atp.profile.anyToBasic(profile))
precacheProfile(queryClient, profile)
}, [profile, queryClient, onBeforePress])
return (