optimistic fetch convo data
This commit is contained in:
@@ -226,7 +226,6 @@ export function MessagesScreen({navigation, route}: Props) {
|
||||
onEndReachedThreshold={isNative ? 1.5 : 0}
|
||||
initialNumToRender={initialNumToRender}
|
||||
windowSize={11}
|
||||
// @ts-ignore our .web version only -sfn
|
||||
desktopFixedHeight
|
||||
sideBorders={false}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import React, {useCallback} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {AppBskyActorDefs, moderateProfile, ModerationOpts} from '@atproto/api'
|
||||
import {
|
||||
AppBskyActorDefs,
|
||||
moderateProfile,
|
||||
ModerationCause,
|
||||
ModerationDecision,
|
||||
} from '@atproto/api'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||
@@ -86,6 +91,23 @@ function Inner() {
|
||||
!convoState.isFetchingHistory &&
|
||||
convoState.items.length === 0)
|
||||
|
||||
const moderation = React.useMemo(() => {
|
||||
if (!recipient || !moderationOpts) return
|
||||
return moderateProfile(recipient, moderationOpts)
|
||||
}, [recipient, moderationOpts])
|
||||
|
||||
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])
|
||||
|
||||
// Any time that we re-render the `Initializing` state, we have to reset `hasScrolled` to false. After entering this
|
||||
// state, we know that we're resetting the list of messages and need to re-scroll to the bottom when they get added.
|
||||
React.useEffect(() => {
|
||||
@@ -110,11 +132,18 @@ function Inner() {
|
||||
|
||||
return (
|
||||
<Layout.Center style={[a.flex_1]}>
|
||||
{!readyToShow && <MessagesListHeader />}
|
||||
{!readyToShow && (
|
||||
<MessagesListHeader
|
||||
profile={recipient}
|
||||
moderation={moderation}
|
||||
blockInfo={blockInfo}
|
||||
/>
|
||||
)}
|
||||
<View style={[a.flex_1]}>
|
||||
{moderationOpts && recipient ? (
|
||||
{moderation && blockInfo && recipient ? (
|
||||
<InnerReady
|
||||
moderationOpts={moderationOpts}
|
||||
moderation={moderation}
|
||||
blockInfo={blockInfo}
|
||||
recipient={recipient}
|
||||
hasScrolled={hasScrolled}
|
||||
setHasScrolled={setHasScrolled}
|
||||
@@ -144,12 +173,17 @@ function Inner() {
|
||||
}
|
||||
|
||||
function InnerReady({
|
||||
moderationOpts,
|
||||
moderation,
|
||||
blockInfo,
|
||||
recipient: recipientUnshadowed,
|
||||
hasScrolled,
|
||||
setHasScrolled,
|
||||
}: {
|
||||
moderationOpts: ModerationOpts
|
||||
moderation: ModerationDecision
|
||||
blockInfo: {
|
||||
listBlocks: ModerationCause[]
|
||||
userBlock: ModerationCause | undefined
|
||||
}
|
||||
recipient: AppBskyActorDefs.ProfileViewBasic
|
||||
hasScrolled: boolean
|
||||
setHasScrolled: React.Dispatch<React.SetStateAction<boolean>>
|
||||
@@ -161,21 +195,6 @@ function InnerReady({
|
||||
const verifyEmailControl = useDialogControl()
|
||||
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(() => {
|
||||
if (needsEmailVerification) {
|
||||
verifyEmailControl.open()
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '@atproto/api'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {GestureActionView} from '#/lib/custom-animations/GestureActionView'
|
||||
import {useHaptics} from '#/lib/haptics'
|
||||
@@ -24,6 +25,7 @@ import {isNative} from '#/platform/detection'
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useMarkAsReadMutation} from '#/state/queries/messages/conversation'
|
||||
import {precacheProfile} from '#/state/queries/profile'
|
||||
import {useSession} from '#/state/session'
|
||||
import {TimeElapsed} from '#/view/com/util/TimeElapsed'
|
||||
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
||||
@@ -77,6 +79,7 @@ function ChatListItemReady({
|
||||
moderationOpts: ModerationOpts
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const queryClient = useQueryClient()
|
||||
const {_} = useLingui()
|
||||
const {currentAccount} = useSession()
|
||||
const menuControl = useMenuControl()
|
||||
@@ -189,6 +192,7 @@ function ChatListItemReady({
|
||||
const onPress = useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
decrementBadgeCount(convo.unreadCount)
|
||||
precacheProfile(queryClient, profile)
|
||||
if (isDeletedAccount) {
|
||||
e.preventDefault()
|
||||
menuControl.open()
|
||||
@@ -197,7 +201,7 @@ function ChatListItemReady({
|
||||
logEvent('chat:open', {logContext: 'ChatsList'})
|
||||
}
|
||||
},
|
||||
[convo.unreadCount, isDeletedAccount, menuControl],
|
||||
[convo.unreadCount, isDeletedAccount, menuControl, queryClient, profile],
|
||||
)
|
||||
|
||||
const onLongPress = useCallback(() => {
|
||||
|
||||
@@ -3,9 +3,11 @@ import {
|
||||
BskyAgent,
|
||||
ChatBskyConvoDefs,
|
||||
ChatBskyConvoGetLog,
|
||||
ChatBskyConvoListConvos,
|
||||
ChatBskyConvoSendMessage,
|
||||
} from '@atproto/api'
|
||||
import {XRPCError} from '@atproto/xrpc'
|
||||
import {InfiniteData, QueryClient} from '@tanstack/react-query'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
@@ -33,6 +35,7 @@ import {
|
||||
import {MessagesEventBus} from '#/state/messages/events/agent'
|
||||
import {MessagesEventBusError} from '#/state/messages/events/types'
|
||||
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
|
||||
import {RQKEY as RQKEY_LIST_CONVOS} from '#/state/queries/messages/list-conversations'
|
||||
|
||||
export function isConvoItemMessage(
|
||||
item: ConvoItem,
|
||||
@@ -83,6 +86,7 @@ export class Convo {
|
||||
sender: AppBskyActorDefs.ProfileViewBasic | undefined
|
||||
recipients: AppBskyActorDefs.ProfileViewBasic[] | undefined = undefined
|
||||
snapshot: ConvoState | undefined
|
||||
queryClient: QueryClient | undefined
|
||||
|
||||
constructor(params: ConvoParams) {
|
||||
this.id = nanoid(3)
|
||||
@@ -90,6 +94,7 @@ export class Convo {
|
||||
this.agent = params.agent
|
||||
this.events = params.events
|
||||
this.senderUserDid = params.agent.session?.did!
|
||||
this.queryClient = params.queryClient
|
||||
|
||||
this.subscribe = this.subscribe.bind(this)
|
||||
this.getSnapshot = this.getSnapshot.bind(this)
|
||||
@@ -99,6 +104,8 @@ export class Convo {
|
||||
this.ingestFirehose = this.ingestFirehose.bind(this)
|
||||
this.onFirehoseConnect = this.onFirehoseConnect.bind(this)
|
||||
this.onFirehoseError = this.onFirehoseError.bind(this)
|
||||
this.optimisticFetchConvoFromCache =
|
||||
this.optimisticFetchConvoFromCache.bind(this)
|
||||
}
|
||||
|
||||
private commit() {
|
||||
@@ -426,6 +433,8 @@ export class Convo {
|
||||
|
||||
private async setup() {
|
||||
try {
|
||||
this.optimisticFetchConvoFromCache()
|
||||
|
||||
const {convo, sender, recipients} = await this.fetchConvo()
|
||||
|
||||
this.convo = convo
|
||||
@@ -508,6 +517,25 @@ export class Convo {
|
||||
}
|
||||
}
|
||||
|
||||
private optimisticFetchConvoFromCache() {
|
||||
if (!this.queryClient) return
|
||||
if (this.convo) return // bail if already loaded
|
||||
|
||||
const convoList =
|
||||
this.queryClient.getQueryData<
|
||||
InfiniteData<ChatBskyConvoListConvos.OutputSchema, string | undefined>
|
||||
>(RQKEY_LIST_CONVOS)
|
||||
|
||||
const convo = convoList?.pages
|
||||
?.flatMap(c => c.convos)
|
||||
?.find(c => c.id === this.convoId)
|
||||
if (convo) {
|
||||
this.convo = convo
|
||||
this.sender = convo.members.find(m => m.did === this.senderUserDid)
|
||||
this.recipients = convo.members.filter(m => m.did !== this.senderUserDid)
|
||||
}
|
||||
}
|
||||
|
||||
private pendingFetchConvo:
|
||||
| Promise<{
|
||||
convo: ChatBskyConvoDefs.ConvoView
|
||||
@@ -525,7 +553,7 @@ export class Convo {
|
||||
}>(async (resolve, reject) => {
|
||||
try {
|
||||
const response = await networkRetry(2, () => {
|
||||
return this.agent.api.chat.bsky.convo.getConvo(
|
||||
return this.agent.chat.bsky.convo.getConvo(
|
||||
{
|
||||
convoId: this.convoId,
|
||||
},
|
||||
@@ -592,7 +620,7 @@ export class Convo {
|
||||
|
||||
const nextCursor = this.oldestRev // for TS
|
||||
const response = await networkRetry(2, () => {
|
||||
return this.agent.api.chat.bsky.convo.getMessages(
|
||||
return this.agent.chat.bsky.convo.getMessages(
|
||||
{
|
||||
cursor: nextCursor,
|
||||
convoId: this.convoId,
|
||||
@@ -793,7 +821,7 @@ export class Convo {
|
||||
|
||||
const {id, message} = pendingMessage
|
||||
|
||||
const response = await this.agent.api.chat.bsky.convo.sendMessage(
|
||||
const response = await this.agent.chat.bsky.convo.sendMessage(
|
||||
{
|
||||
convoId: this.convoId,
|
||||
message,
|
||||
@@ -888,7 +916,7 @@ export class Convo {
|
||||
)
|
||||
|
||||
try {
|
||||
const {data} = await this.agent.api.chat.bsky.convo.sendMessageBatch(
|
||||
const {data} = await this.agent.chat.bsky.convo.sendMessageBatch(
|
||||
{
|
||||
items: messageArray.map(({message}) => ({
|
||||
convoId: this.convoId,
|
||||
@@ -935,7 +963,7 @@ export class Convo {
|
||||
|
||||
try {
|
||||
await networkRetry(2, () => {
|
||||
return this.agent.api.chat.bsky.convo.deleteMessageForSelf(
|
||||
return this.agent.chat.bsky.convo.deleteMessageForSelf(
|
||||
{
|
||||
convoId: this.convoId,
|
||||
messageId,
|
||||
|
||||
@@ -66,6 +66,7 @@ export function ConvoProvider({
|
||||
convoId,
|
||||
agent,
|
||||
events,
|
||||
queryClient,
|
||||
}),
|
||||
)
|
||||
const service = useSyncExternalStore(convo.subscribe, convo.getSnapshot)
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
ChatBskyConvoDefs,
|
||||
ChatBskyConvoSendMessage,
|
||||
} from '@atproto/api'
|
||||
import {QueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {MessagesEventBus} from '#/state/messages/events/agent'
|
||||
|
||||
@@ -11,6 +12,7 @@ export type ConvoParams = {
|
||||
convoId: string
|
||||
agent: BskyAgent
|
||||
events: MessagesEventBus
|
||||
queryClient?: QueryClient
|
||||
}
|
||||
|
||||
export enum ConvoStatus {
|
||||
|
||||
Reference in New Issue
Block a user