Fix convo header loading state (#7603)

* get initial convo state from cache

* undo useConvoQuery changes

* fix shadowing situation with new hook
This commit is contained in:
Samuel Newman
2025-02-03 14:37:24 -08:00
committed by GitHub
parent fa8607b861
commit 32b28d6662
11 changed files with 174 additions and 84 deletions
+5 -8
View File
@@ -73,13 +73,14 @@ let ConvoMenu = ({
const isBlocking = userBlock || !!listBlocks.length const isBlocking = userBlock || !!listBlocks.length
const isDeletedAccount = profile.handle === 'missing.invalid' const isDeletedAccount = profile.handle === 'missing.invalid'
const convoId = initialConvo.id
const {data: convo} = useConvoQuery(initialConvo) const {data: convo} = useConvoQuery(initialConvo)
const onNavigateToProfile = useCallback(() => { const onNavigateToProfile = useCallback(() => {
navigation.navigate('Profile', {name: profile.did}) navigation.navigate('Profile', {name: profile.did})
}, [navigation, profile.did]) }, [navigation, profile.did])
const {mutate: muteConvo} = useMuteConvo(convo?.id, { const {mutate: muteConvo} = useMuteConvo(convoId, {
onSuccess: data => { onSuccess: data => {
if (data.convo.muted) { if (data.convo.muted) {
Toast.show(_(msg`Chat muted`)) Toast.show(_(msg`Chat muted`))
@@ -152,11 +153,7 @@ let ConvoMenu = ({
{showMarkAsRead && ( {showMarkAsRead && (
<Menu.Item <Menu.Item
label={_(msg`Mark as read`)} label={_(msg`Mark as read`)}
onPress={() => onPress={() => markAsRead({convoId})}>
markAsRead({
convoId: convo?.id,
})
}>
<Menu.ItemText> <Menu.ItemText>
<Trans>Mark as read</Trans> <Trans>Mark as read</Trans>
</Menu.ItemText> </Menu.ItemText>
@@ -222,7 +219,7 @@ let ConvoMenu = ({
<LeaveConvoPrompt <LeaveConvoPrompt
control={leaveConvoControl} control={leaveConvoControl}
convoId={convo.id} convoId={convoId}
currentScreen={currentScreen} currentScreen={currentScreen}
/> />
{latestReportableMessage ? ( {latestReportableMessage ? (
@@ -230,7 +227,7 @@ let ConvoMenu = ({
currentScreen={currentScreen} currentScreen={currentScreen}
params={{ params={{
type: 'convoMessage', type: 'convoMessage',
convoId: convo.id, convoId: convoId,
message: latestReportableMessage, message: latestReportableMessage,
}} }}
control={reportControl} control={reportControl}
@@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {AppBskyActorDefs, ModerationCause} from '@atproto/api' import {AppBskyActorDefs, ModerationDecision} from '@atproto/api'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@@ -19,15 +19,12 @@ export function MessagesListBlockedFooter({
recipient: initialRecipient, recipient: initialRecipient,
convoId, convoId,
hasMessages, hasMessages,
blockInfo, moderation,
}: { }: {
recipient: AppBskyActorDefs.ProfileViewBasic recipient: AppBskyActorDefs.ProfileViewBasic
convoId: string convoId: string
hasMessages: boolean hasMessages: boolean
blockInfo: { moderation: ModerationDecision
listBlocks: ModerationCause[]
userBlock: ModerationCause | undefined
}
}) { }) {
const t = useTheme() const t = useTheme()
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
@@ -39,7 +36,17 @@ export function MessagesListBlockedFooter({
const reportControl = useDialogControl() const reportControl = useDialogControl()
const blockedByListControl = useDialogControl() const blockedByListControl = useDialogControl()
const {listBlocks, userBlock} = blockInfo const {listBlocks, userBlock} = React.useMemo(() => {
const modui = moderation.ui('profileView')
const blocks = modui.alerts.filter(alert => alert.type === 'blocking')
const listBlocks = blocks.filter(alert => alert.source.type === 'list')
const userBlock = blocks.find(alert => alert.source.type === 'user')
return {
listBlocks,
userBlock,
}
}, [moderation])
const isBlocking = !!userBlock || !!listBlocks.length const isBlocking = !!userBlock || !!listBlocks.length
const onUnblockPress = React.useCallback(() => { const onUnblockPress = React.useCallback(() => {
+16 -10
View File
@@ -15,7 +15,7 @@ import {makeProfileLink} from '#/lib/routes/links'
import {NavigationProp} from '#/lib/routes/types' import {NavigationProp} from '#/lib/routes/types'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {isWeb} from '#/platform/detection' import {isWeb} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {Shadow} from '#/state/cache/profile-shadow'
import {isConvoActive, useConvo} from '#/state/messages/convo' import {isConvoActive, useConvo} from '#/state/messages/convo'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
@@ -30,20 +30,27 @@ const PFP_SIZE = isWeb ? 40 : 34
export let MessagesListHeader = ({ export let MessagesListHeader = ({
profile, profile,
moderation, moderation,
blockInfo,
}: { }: {
profile?: AppBskyActorDefs.ProfileViewBasic profile?: Shadow<AppBskyActorDefs.ProfileViewBasic>
moderation?: ModerationDecision moderation?: ModerationDecision
blockInfo?: {
listBlocks: ModerationCause[]
userBlock?: ModerationCause
}
}): React.ReactNode => { }): React.ReactNode => {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
const {gtTablet} = useBreakpoints() const {gtTablet} = useBreakpoints()
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const blockInfo = React.useMemo(() => {
if (!moderation) return
const modui = moderation.ui('profileView')
const blocks = modui.alerts.filter(alert => alert.type === 'blocking')
const listBlocks = blocks.filter(alert => alert.source.type === 'list')
const userBlock = blocks.find(alert => alert.source.type === 'user')
return {
listBlocks,
userBlock,
}
}, [moderation])
const onPressBack = useCallback(() => { const onPressBack = useCallback(() => {
if (isWeb) { if (isWeb) {
navigation.replace('Messages', {}) navigation.replace('Messages', {})
@@ -127,11 +134,11 @@ export let MessagesListHeader = ({
MessagesListHeader = React.memo(MessagesListHeader) MessagesListHeader = React.memo(MessagesListHeader)
function HeaderReady({ function HeaderReady({
profile: profileUnshadowed, profile,
moderation, moderation,
blockInfo, blockInfo,
}: { }: {
profile: AppBskyActorDefs.ProfileViewBasic profile: Shadow<AppBskyActorDefs.ProfileViewBasic>
moderation: ModerationDecision moderation: ModerationDecision
blockInfo: { blockInfo: {
listBlocks: ModerationCause[] listBlocks: ModerationCause[]
@@ -141,7 +148,6 @@ function HeaderReady({
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
const convoState = useConvo() const convoState = useConvo()
const profile = useProfileShadow(profileUnshadowed)
const isDeletedAccount = profile?.handle === 'missing.invalid' const isDeletedAccount = profile?.handle === 'missing.invalid'
const displayName = isDeletedAccount const displayName = isDeletedAccount
-1
View File
@@ -236,7 +236,6 @@ export function MessagesScreen({navigation, route}: Props) {
onEndReachedThreshold={isNative ? 1.5 : 0} onEndReachedThreshold={isNative ? 1.5 : 0}
initialNumToRender={initialNumToRender} initialNumToRender={initialNumToRender}
windowSize={11} windowSize={11}
// @ts-ignore our .web version only -sfn
desktopFixedHeight desktopFixedHeight
sideBorders={false} sideBorders={false}
/> />
+27 -32
View File
@@ -1,6 +1,10 @@
import React, {useCallback} from 'react' import React, {useCallback} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api' import {
AppBskyActorDefs,
moderateProfile,
ModerationDecision,
} from '@atproto/api'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useFocusEffect, useNavigation} from '@react-navigation/native' import {useFocusEffect, useNavigation} from '@react-navigation/native'
@@ -10,7 +14,7 @@ import {useEmail} from '#/lib/hooks/useEmail'
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController' import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types' import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types'
import {isWeb} from '#/platform/detection' import {isWeb} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {Shadow, useMaybeProfileShadow} from '#/state/cache/profile-shadow'
import {ConvoProvider, isConvoActive, useConvo} from '#/state/messages/convo' import {ConvoProvider, isConvoActive, useConvo} from '#/state/messages/convo'
import {ConvoStatus} from '#/state/messages/convo/types' import {ConvoStatus} from '#/state/messages/convo/types'
import {useCurrentConvoId} from '#/state/messages/current-convo-id' import {useCurrentConvoId} from '#/state/messages/current-convo-id'
@@ -72,9 +76,15 @@ function Inner() {
const {_} = useLingui() const {_} = useLingui()
const moderationOpts = useModerationOpts() const moderationOpts = useModerationOpts()
const {data: recipient} = useProfileQuery({ const {data: recipientUnshadowed} = useProfileQuery({
did: convoState.recipients?.[0].did, did: convoState.recipients?.[0].did,
}) })
const recipient = useMaybeProfileShadow(recipientUnshadowed)
const moderation = React.useMemo(() => {
if (!recipient || !moderationOpts) return null
return moderateProfile(recipient, moderationOpts)
}, [recipient, moderationOpts])
// Because we want to give the list a chance to asynchronously scroll to the end before it is visible to the user, // Because we want to give the list a chance to asynchronously scroll to the end before it is visible to the user,
// we use `hasScrolled` to determine when to render. With that said however, there is a chance that the chat will be // we use `hasScrolled` to determine when to render. With that said however, there is a chance that the chat will be
@@ -110,11 +120,16 @@ function Inner() {
return ( return (
<Layout.Center style={[a.flex_1]}> <Layout.Center style={[a.flex_1]}>
{!readyToShow && <MessagesListHeader />} {!readyToShow &&
(moderation ? (
<MessagesListHeader moderation={moderation} profile={recipient} />
) : (
<MessagesListHeader />
))}
<View style={[a.flex_1]}> <View style={[a.flex_1]}>
{moderationOpts && recipient ? ( {moderation && recipient ? (
<InnerReady <InnerReady
moderationOpts={moderationOpts} moderation={moderation}
recipient={recipient} recipient={recipient}
hasScrolled={hasScrolled} hasScrolled={hasScrolled}
setHasScrolled={setHasScrolled} setHasScrolled={setHasScrolled}
@@ -144,38 +159,22 @@ function Inner() {
} }
function InnerReady({ function InnerReady({
moderationOpts, moderation,
recipient: recipientUnshadowed, recipient,
hasScrolled, hasScrolled,
setHasScrolled, setHasScrolled,
}: { }: {
moderationOpts: ModerationOpts moderation: ModerationDecision
recipient: AppBskyActorDefs.ProfileViewBasic recipient: Shadow<AppBskyActorDefs.ProfileViewBasic>
hasScrolled: boolean hasScrolled: boolean
setHasScrolled: React.Dispatch<React.SetStateAction<boolean>> setHasScrolled: React.Dispatch<React.SetStateAction<boolean>>
}) { }) {
const {_} = useLingui() const {_} = useLingui()
const convoState = useConvo() const convoState = useConvo()
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const recipient = useProfileShadow(recipientUnshadowed)
const verifyEmailControl = useDialogControl() const verifyEmailControl = useDialogControl()
const {needsEmailVerification} = useEmail() const {needsEmailVerification} = useEmail()
const moderation = React.useMemo(() => {
return moderateProfile(recipient, moderationOpts)
}, [recipient, moderationOpts])
const blockInfo = React.useMemo(() => {
const modui = moderation.ui('profileView')
const blocks = modui.alerts.filter(alert => alert.type === 'blocking')
const listBlocks = blocks.filter(alert => alert.source.type === 'list')
const userBlock = blocks.find(alert => alert.source.type === 'user')
return {
listBlocks,
userBlock,
}
}, [moderation])
React.useEffect(() => { React.useEffect(() => {
if (needsEmailVerification) { if (needsEmailVerification) {
verifyEmailControl.open() verifyEmailControl.open()
@@ -184,11 +183,7 @@ function InnerReady({
return ( return (
<> <>
<MessagesListHeader <MessagesListHeader profile={recipient} moderation={moderation} />
profile={recipient}
moderation={moderation}
blockInfo={blockInfo}
/>
{isConvoActive(convoState) && ( {isConvoActive(convoState) && (
<MessagesList <MessagesList
hasScrolled={hasScrolled} hasScrolled={hasScrolled}
@@ -199,7 +194,7 @@ function InnerReady({
recipient={recipient} recipient={recipient}
convoId={convoState.convo.id} convoId={convoState.convo.id}
hasMessages={convoState.items.length > 0} hasMessages={convoState.items.length > 0}
blockInfo={blockInfo} moderation={moderation}
/> />
} }
/> />
@@ -9,6 +9,7 @@ import {
} from '@atproto/api' } from '@atproto/api'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query'
import {GestureActionView} from '#/lib/custom-animations/GestureActionView' import {GestureActionView} from '#/lib/custom-animations/GestureActionView'
import {useHaptics} from '#/lib/haptics' import {useHaptics} from '#/lib/haptics'
@@ -23,7 +24,11 @@ import {
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useMarkAsReadMutation} from '#/state/queries/messages/conversation' import {
precacheConvoQuery,
useMarkAsReadMutation,
} from '#/state/queries/messages/conversation'
import {precacheProfile} from '#/state/queries/profile'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {TimeElapsed} from '#/view/com/util/TimeElapsed' import {TimeElapsed} from '#/view/com/util/TimeElapsed'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
@@ -89,6 +94,7 @@ function ChatListItemReady({
[profile, moderationOpts], [profile, moderationOpts],
) )
const playHaptic = useHaptics() const playHaptic = useHaptics()
const queryClient = useQueryClient()
const isUnread = convo.unreadCount > 0 const isUnread = convo.unreadCount > 0
const blockInfo = useMemo(() => { const blockInfo = useMemo(() => {
@@ -198,6 +204,8 @@ function ChatListItemReady({
const onPress = useCallback( const onPress = useCallback(
(e: GestureResponderEvent) => { (e: GestureResponderEvent) => {
precacheProfile(queryClient, profile)
precacheConvoQuery(queryClient, convo)
decrementBadgeCount(convo.unreadCount) decrementBadgeCount(convo.unreadCount)
if (isDeletedAccount) { if (isDeletedAccount) {
e.preventDefault() e.preventDefault()
@@ -207,7 +215,7 @@ function ChatListItemReady({
logEvent('chat:open', {logContext: 'ChatsList'}) logEvent('chat:open', {logContext: 'ChatsList'})
} }
}, },
[convo.unreadCount, isDeletedAccount, menuControl], [isDeletedAccount, menuControl, queryClient, profile, convo],
) )
const onLongPress = useCallback(() => { const onLongPress = useCallback(() => {
+38
View File
@@ -63,6 +63,44 @@ export function useProfileShadow<
}, [profile, shadow]) }, [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( export function updateProfileShadow(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
+25 -7
View File
@@ -81,7 +81,7 @@ export class Convo {
convoId: string convoId: string
convo: ChatBskyConvoDefs.ConvoView | undefined convo: ChatBskyConvoDefs.ConvoView | undefined
sender: AppBskyActorDefs.ProfileViewBasic | undefined sender: AppBskyActorDefs.ProfileViewBasic | undefined
recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined = undefined recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined
snapshot: ConvoState | undefined snapshot: ConvoState | undefined
constructor(params: ConvoParams) { constructor(params: ConvoParams) {
@@ -91,6 +91,10 @@ export class Convo {
this.events = params.events this.events = params.events
this.senderUserDid = params.agent.session?.did! this.senderUserDid = params.agent.session?.did!
if (params.placeholderData) {
this.setupPlaceholderData(params.placeholderData)
}
this.subscribe = this.subscribe.bind(this) this.subscribe = this.subscribe.bind(this)
this.getSnapshot = this.getSnapshot.bind(this) this.getSnapshot = this.getSnapshot.bind(this)
this.sendMessage = this.sendMessage.bind(this) this.sendMessage = this.sendMessage.bind(this)
@@ -131,10 +135,10 @@ export class Convo {
return { return {
status: ConvoStatus.Initializing, status: ConvoStatus.Initializing,
items: [], items: [],
convo: undefined, convo: this.convo,
error: undefined, error: undefined,
sender: undefined, sender: this.sender,
recipients: undefined, recipients: this.recipients,
isFetchingHistory: this.isFetchingHistory, isFetchingHistory: this.isFetchingHistory,
deleteMessage: undefined, deleteMessage: undefined,
sendMessage: undefined, sendMessage: undefined,
@@ -176,10 +180,10 @@ export class Convo {
return { return {
status: ConvoStatus.Uninitialized, status: ConvoStatus.Uninitialized,
items: [], items: [],
convo: undefined, convo: this.convo,
error: undefined, error: undefined,
sender: undefined, sender: this.sender,
recipients: undefined, recipients: this.recipients,
isFetchingHistory: false, isFetchingHistory: false,
deleteMessage: undefined, deleteMessage: undefined,
sendMessage: 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() { private async setup() {
try { try {
const {convo, sender, recipients} = await this.fetchConvo() const {convo, sender, recipients} = await this.fetchConvo()
+13 -6
View File
@@ -1,4 +1,5 @@
import React, {useContext, useState, useSyncExternalStore} from 'react' import React, {useContext, useState, useSyncExternalStore} from 'react'
import {ChatBskyConvoDefs} from '@atproto/api'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
@@ -14,7 +15,10 @@ import {
} from '#/state/messages/convo/types' } from '#/state/messages/convo/types'
import {isConvoActive} from '#/state/messages/convo/util' import {isConvoActive} from '#/state/messages/convo/util'
import {useMessagesEventBus} from '#/state/messages/events' 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 ListConvosQueryKey} from '#/state/queries/messages/list-conversations'
import {RQKEY as createProfileQueryKey} from '#/state/queries/profile' import {RQKEY as createProfileQueryKey} from '#/state/queries/profile'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
@@ -60,14 +64,17 @@ export function ConvoProvider({
const queryClient = useQueryClient() const queryClient = useQueryClient()
const agent = useAgent() const agent = useAgent()
const events = useMessagesEventBus() const events = useMessagesEventBus()
const [convo] = useState( const [convo] = useState(() => {
() => const placeholder = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>(
new Convo({ getConvoKey(convoId),
)
return new Convo({
convoId, convoId,
agent, agent,
events, events,
}), placeholderData: placeholder ? {convo: placeholder} : undefined,
) })
})
const service = useSyncExternalStore(convo.subscribe, convo.getSnapshot) const service = useSyncExternalStore(convo.subscribe, convo.getSnapshot)
const {mutate: markAsRead} = useMarkAsReadMutation() const {mutate: markAsRead} = useMarkAsReadMutation()
+9 -6
View File
@@ -11,6 +11,9 @@ export type ConvoParams = {
convoId: string convoId: string
agent: BskyAgent agent: BskyAgent
events: MessagesEventBus events: MessagesEventBus
placeholderData?: {
convo: ChatBskyConvoDefs.ConvoView
}
} }
export enum ConvoStatus { export enum ConvoStatus {
@@ -142,10 +145,10 @@ type FetchMessageHistory = () => Promise<void>
export type ConvoStateUninitialized = { export type ConvoStateUninitialized = {
status: ConvoStatus.Uninitialized status: ConvoStatus.Uninitialized
items: [] items: []
convo: undefined convo: ChatBskyConvoDefs.ConvoView | undefined
error: undefined error: undefined
sender: undefined sender: AppBskyActorDefs.ProfileViewBasic | undefined
recipients: undefined recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined
isFetchingHistory: false isFetchingHistory: false
deleteMessage: undefined deleteMessage: undefined
sendMessage: undefined sendMessage: undefined
@@ -154,10 +157,10 @@ export type ConvoStateUninitialized = {
export type ConvoStateInitializing = { export type ConvoStateInitializing = {
status: ConvoStatus.Initializing status: ConvoStatus.Initializing
items: [] items: []
convo: undefined convo: ChatBskyConvoDefs.ConvoView | undefined
error: undefined error: undefined
sender: undefined sender: AppBskyActorDefs.ProfileViewBasic | undefined
recipients: undefined recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined
isFetchingHistory: boolean isFetchingHistory: boolean
deleteMessage: undefined deleteMessage: undefined
sendMessage: undefined sendMessage: undefined
+14 -2
View File
@@ -1,5 +1,10 @@
import {ChatBskyConvoDefs} from '@atproto/api' 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 {STALE} from '#/state/queries'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const' import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
@@ -20,7 +25,7 @@ export function useConvoQuery(convo: ChatBskyConvoDefs.ConvoView) {
return useQuery({ return useQuery({
queryKey: RQKEY(convo.id), queryKey: RQKEY(convo.id),
queryFn: async () => { queryFn: async () => {
const {data} = await agent.api.chat.bsky.convo.getConvo( const {data} = await agent.chat.bsky.convo.getConvo(
{convoId: convo.id}, {convoId: convo.id},
{headers: DM_SERVICE_HEADERS}, {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() { export function useMarkAsReadMutation() {
const optimisticUpdate = useOnMarkAsRead() const optimisticUpdate = useOnMarkAsRead()
const queryClient = useQueryClient() const queryClient = useQueryClient()