diff options
Diffstat (limited to 'src/state')
-rw-r--r-- | src/state/cache/profile-shadow.ts | 38 | ||||
-rw-r--r-- | src/state/messages/convo/agent.ts | 32 | ||||
-rw-r--r-- | src/state/messages/convo/index.tsx | 25 | ||||
-rw-r--r-- | src/state/messages/convo/types.ts | 15 | ||||
-rw-r--r-- | src/state/queries/messages/conversation.ts | 16 |
5 files changed, 102 insertions, 24 deletions
diff --git a/src/state/cache/profile-shadow.ts b/src/state/cache/profile-shadow.ts index afd3f1935..4d823ec8e 100644 --- a/src/state/cache/profile-shadow.ts +++ b/src/state/cache/profile-shadow.ts @@ -63,6 +63,44 @@ export function useProfileShadow< }, [profile, shadow]) } +/** + * Same as useProfileShadow, but allows for the profile to be undefined. + * This is useful for when the profile is not guaranteed to be loaded yet. + */ +export function useMaybeProfileShadow< + TProfileView extends AppBskyActorDefs.ProfileView, +>(profile?: TProfileView): Shadow<TProfileView> | undefined { + const [shadow, setShadow] = useState(() => + profile ? shadows.get(profile) : undefined, + ) + const [prevPost, setPrevPost] = useState(profile) + if (profile !== prevPost) { + setPrevPost(profile) + setShadow(profile ? shadows.get(profile) : undefined) + } + + useEffect(() => { + if (!profile) return + function onUpdate() { + if (!profile) return + setShadow(shadows.get(profile)) + } + emitter.addListener(profile.did, onUpdate) + return () => { + emitter.removeListener(profile.did, onUpdate) + } + }, [profile]) + + return useMemo(() => { + if (!profile) return undefined + if (shadow) { + return mergeShadow(profile, shadow) + } else { + return castAsShadow(profile) + } + }, [profile, shadow]) +} + export function updateProfileShadow( queryClient: QueryClient, did: string, diff --git a/src/state/messages/convo/agent.ts b/src/state/messages/convo/agent.ts index 53d77046a..91dd59813 100644 --- a/src/state/messages/convo/agent.ts +++ b/src/state/messages/convo/agent.ts @@ -81,7 +81,7 @@ export class Convo { convoId: string convo: ChatBskyConvoDefs.ConvoView | undefined sender: AppBskyActorDefs.ProfileViewBasic | undefined - recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined = undefined + recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined snapshot: ConvoState | undefined constructor(params: ConvoParams) { @@ -91,6 +91,10 @@ export class Convo { this.events = params.events this.senderUserDid = params.agent.session?.did! + if (params.placeholderData) { + this.setupPlaceholderData(params.placeholderData) + } + this.subscribe = this.subscribe.bind(this) this.getSnapshot = this.getSnapshot.bind(this) this.sendMessage = this.sendMessage.bind(this) @@ -131,10 +135,10 @@ export class Convo { return { status: ConvoStatus.Initializing, items: [], - convo: undefined, + convo: this.convo, error: undefined, - sender: undefined, - recipients: undefined, + sender: this.sender, + recipients: this.recipients, isFetchingHistory: this.isFetchingHistory, deleteMessage: undefined, sendMessage: undefined, @@ -176,10 +180,10 @@ export class Convo { return { status: ConvoStatus.Uninitialized, items: [], - convo: undefined, + convo: this.convo, error: undefined, - sender: undefined, - recipients: undefined, + sender: this.sender, + recipients: this.recipients, isFetchingHistory: false, deleteMessage: undefined, sendMessage: undefined, @@ -424,6 +428,20 @@ export class Convo { } } + /** + * Initialises the convo with placeholder data, if provided. We still refetch it before rendering the convo, + * but this allows us to render the convo header immediately. + */ + private setupPlaceholderData( + data: NonNullable<ConvoParams['placeholderData']>, + ) { + this.convo = data.convo + this.sender = data.convo.members.find(m => m.did === this.senderUserDid) + this.recipients = data.convo.members.filter( + m => m.did !== this.senderUserDid, + ) + } + private async setup() { try { const {convo, sender, recipients} = await this.fetchConvo() diff --git a/src/state/messages/convo/index.tsx b/src/state/messages/convo/index.tsx index 10ec2a348..a1750bdf0 100644 --- a/src/state/messages/convo/index.tsx +++ b/src/state/messages/convo/index.tsx @@ -1,4 +1,5 @@ import React, {useContext, useState, useSyncExternalStore} from 'react' +import {ChatBskyConvoDefs} from '@atproto/api' import {useFocusEffect} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' @@ -14,7 +15,10 @@ import { } from '#/state/messages/convo/types' import {isConvoActive} from '#/state/messages/convo/util' import {useMessagesEventBus} from '#/state/messages/events' -import {useMarkAsReadMutation} from '#/state/queries/messages/conversation' +import { + RQKEY as getConvoKey, + useMarkAsReadMutation, +} from '#/state/queries/messages/conversation' import {RQKEY as ListConvosQueryKey} from '#/state/queries/messages/list-conversations' import {RQKEY as createProfileQueryKey} from '#/state/queries/profile' import {useAgent} from '#/state/session' @@ -60,14 +64,17 @@ export function ConvoProvider({ const queryClient = useQueryClient() const agent = useAgent() const events = useMessagesEventBus() - const [convo] = useState( - () => - new Convo({ - convoId, - agent, - events, - }), - ) + const [convo] = useState(() => { + const placeholder = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>( + getConvoKey(convoId), + ) + return new Convo({ + convoId, + agent, + events, + placeholderData: placeholder ? {convo: placeholder} : undefined, + }) + }) const service = useSyncExternalStore(convo.subscribe, convo.getSnapshot) const {mutate: markAsRead} = useMarkAsReadMutation() diff --git a/src/state/messages/convo/types.ts b/src/state/messages/convo/types.ts index 21772262e..9f1707c71 100644 --- a/src/state/messages/convo/types.ts +++ b/src/state/messages/convo/types.ts @@ -11,6 +11,9 @@ export type ConvoParams = { convoId: string agent: BskyAgent events: MessagesEventBus + placeholderData?: { + convo: ChatBskyConvoDefs.ConvoView + } } export enum ConvoStatus { @@ -142,10 +145,10 @@ type FetchMessageHistory = () => Promise<void> export type ConvoStateUninitialized = { status: ConvoStatus.Uninitialized items: [] - convo: undefined + convo: ChatBskyConvoDefs.ConvoView | undefined error: undefined - sender: undefined - recipients: undefined + sender: AppBskyActorDefs.ProfileViewBasic | undefined + recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined isFetchingHistory: false deleteMessage: undefined sendMessage: undefined @@ -154,10 +157,10 @@ export type ConvoStateUninitialized = { export type ConvoStateInitializing = { status: ConvoStatus.Initializing items: [] - convo: undefined + convo: ChatBskyConvoDefs.ConvoView | undefined error: undefined - sender: undefined - recipients: undefined + sender: AppBskyActorDefs.ProfileViewBasic | undefined + recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined isFetchingHistory: boolean deleteMessage: undefined sendMessage: undefined diff --git a/src/state/queries/messages/conversation.ts b/src/state/queries/messages/conversation.ts index 9edde4aaf..260524524 100644 --- a/src/state/queries/messages/conversation.ts +++ b/src/state/queries/messages/conversation.ts @@ -1,5 +1,10 @@ import {ChatBskyConvoDefs} from '@atproto/api' -import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' +import { + QueryClient, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query' import {STALE} from '#/state/queries' import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const' @@ -20,7 +25,7 @@ export function useConvoQuery(convo: ChatBskyConvoDefs.ConvoView) { return useQuery({ queryKey: RQKEY(convo.id), queryFn: async () => { - const {data} = await agent.api.chat.bsky.convo.getConvo( + const {data} = await agent.chat.bsky.convo.getConvo( {convoId: convo.id}, {headers: DM_SERVICE_HEADERS}, ) @@ -31,6 +36,13 @@ export function useConvoQuery(convo: ChatBskyConvoDefs.ConvoView) { }) } +export function precacheConvoQuery( + queryClient: QueryClient, + convo: ChatBskyConvoDefs.ConvoView, +) { + queryClient.setQueryData(RQKEY(convo.id), convo) +} + export function useMarkAsReadMutation() { const optimisticUpdate = useOnMarkAsRead() const queryClient = useQueryClient() |