about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
Diffstat (limited to 'src/state')
-rw-r--r--src/state/cache/profile-shadow.ts38
-rw-r--r--src/state/messages/convo/agent.ts32
-rw-r--r--src/state/messages/convo/index.tsx25
-rw-r--r--src/state/messages/convo/types.ts15
-rw-r--r--src/state/queries/messages/conversation.ts16
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()