Preserve memoization of message list (#10434)
This commit is contained in:
@@ -479,11 +479,6 @@
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"src/state/messages/convo/agent.ts": {
|
||||
"@typescript-eslint/no-explicit-any": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"src/state/messages/events/agent.ts": {
|
||||
"@typescript-eslint/no-explicit-any": {
|
||||
"count": 2
|
||||
|
||||
@@ -20,6 +20,7 @@ import Animated, {
|
||||
} from 'react-native-reanimated'
|
||||
import {
|
||||
AppBskyEmbedRecord,
|
||||
type ChatBskyActorDefs,
|
||||
ChatBskyConvoDefs,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
@@ -87,9 +88,21 @@ function isWithinClusterBoundary({
|
||||
let MessageItem = ({
|
||||
item,
|
||||
isGroupChat = false,
|
||||
prevMessage,
|
||||
nextMessage,
|
||||
relatedProfiles,
|
||||
}: {
|
||||
item: ConvoItem & {type: 'message' | 'pending-message'}
|
||||
isGroupChat?: boolean
|
||||
prevMessage:
|
||||
| ChatBskyConvoDefs.MessageView
|
||||
| ChatBskyConvoDefs.DeletedMessageView
|
||||
| null
|
||||
nextMessage:
|
||||
| ChatBskyConvoDefs.MessageView
|
||||
| ChatBskyConvoDefs.DeletedMessageView
|
||||
| null
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
}): React.ReactNode => {
|
||||
const t = useTheme()
|
||||
const {currentAccount} = useSession()
|
||||
@@ -97,11 +110,11 @@ let MessageItem = ({
|
||||
const moderationOpts = useModerationOpts()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const profile = item.relatedProfiles.get(item.message.sender.did)
|
||||
const {message} = item
|
||||
const profile = relatedProfiles.get(message.sender.did)
|
||||
|
||||
const reactionsControl = useDialogControl()
|
||||
|
||||
const {message, nextMessage, prevMessage} = item
|
||||
const isPending = item.type === 'pending-message'
|
||||
|
||||
const displayName = profile ? createSanitizedDisplayName(profile) : null
|
||||
@@ -268,7 +281,7 @@ let MessageItem = ({
|
||||
return l`You reacted ${reaction.value}`
|
||||
} else {
|
||||
const senderDid = reaction.sender.did
|
||||
const memberSender = item.relatedProfiles.get(senderDid)
|
||||
const memberSender = relatedProfiles.get(senderDid)
|
||||
if (memberSender) {
|
||||
return l`${createSanitizedDisplayName(memberSender)} reacted ${reaction.value}`
|
||||
}
|
||||
@@ -279,13 +292,7 @@ let MessageItem = ({
|
||||
one: '# person',
|
||||
other: '# people',
|
||||
})} reacted – ${groupedReactions.map(g => g.value).join(' ')}`
|
||||
}, [
|
||||
reactions,
|
||||
groupedReactions,
|
||||
currentAccount?.did,
|
||||
item.relatedProfiles,
|
||||
l,
|
||||
])
|
||||
}, [reactions, groupedReactions, currentAccount?.did, relatedProfiles, l])
|
||||
|
||||
const appliedReactions = (
|
||||
<LayoutAnimationConfig skipEntering skipExiting>
|
||||
@@ -368,7 +375,7 @@ let MessageItem = ({
|
||||
) : null}
|
||||
<ReactionsDialog
|
||||
control={reactionsControl}
|
||||
relatedProfiles={item.relatedProfiles}
|
||||
relatedProfiles={relatedProfiles}
|
||||
message={message}
|
||||
reactions={message.reactions}
|
||||
groupedReactions={groupedReactions}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {View} from 'react-native'
|
||||
import {type ChatBskyActorDefs} from '@atproto/api'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {type ConvoItem} from '#/state/messages/convo/types'
|
||||
@@ -8,13 +9,15 @@ import {Text} from '#/components/Typography'
|
||||
|
||||
export function SystemMessageItem({
|
||||
item,
|
||||
relatedProfiles,
|
||||
}: {
|
||||
item: ConvoItem & {type: 'system-message'}
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {i18n} = useLingui()
|
||||
|
||||
const info = getSystemMessageInfo(item.message.data, item.relatedProfiles)
|
||||
const info = getSystemMessageInfo(item.message.data, relatedProfiles)
|
||||
if (!info) return null
|
||||
|
||||
const {Icon, message} = info
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
type $Typed,
|
||||
type AppBskyEmbedRecord,
|
||||
AppBskyRichtextFacet,
|
||||
ChatBskyConvoDefs,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {useScrollEdgeEffectRef} from '@bsky.app/expo-scroll-edge-effect'
|
||||
@@ -85,6 +86,27 @@ function keyExtractor(item: ConvoItem) {
|
||||
return item.key
|
||||
}
|
||||
|
||||
function getNeighborMessage(
|
||||
items: ConvoItem[],
|
||||
index: number,
|
||||
): ChatBskyConvoDefs.MessageView | ChatBskyConvoDefs.DeletedMessageView | null {
|
||||
const neighbor = items[index]
|
||||
if (!neighbor) return null
|
||||
if (
|
||||
neighbor.type === 'message' ||
|
||||
neighbor.type === 'pending-message' ||
|
||||
neighbor.type === 'deleted-message'
|
||||
) {
|
||||
if (
|
||||
ChatBskyConvoDefs.isMessageView(neighbor.message) ||
|
||||
ChatBskyConvoDefs.isDeletedMessageView(neighbor.message)
|
||||
) {
|
||||
return neighbor.message
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function onScrollToIndexFailed() {
|
||||
// Placeholder function. You have to give FlatList something or else it will error.
|
||||
}
|
||||
@@ -368,18 +390,26 @@ export function MessagesList({
|
||||
})
|
||||
}, [flatListRef])
|
||||
|
||||
const renderItem = ({item}: {item: ConvoItem}) => {
|
||||
const renderItem = ({item, index}: {item: ConvoItem; index: number}) => {
|
||||
if (item.type === 'message' || item.type === 'pending-message') {
|
||||
return (
|
||||
<MessageItem
|
||||
item={item}
|
||||
isGroupChat={convoState.convo.kind === 'group'}
|
||||
prevMessage={getNeighborMessage(convoState.items, index - 1)}
|
||||
nextMessage={getNeighborMessage(convoState.items, index + 1)}
|
||||
relatedProfiles={convoState.relatedProfiles}
|
||||
/>
|
||||
)
|
||||
} else if (item.type === 'deleted-message') {
|
||||
return <Text>Deleted message</Text>
|
||||
} else if (item.type === 'system-message') {
|
||||
return <SystemMessageItem item={item} />
|
||||
return (
|
||||
<SystemMessageItem
|
||||
item={item}
|
||||
relatedProfiles={convoState.relatedProfiles}
|
||||
/>
|
||||
)
|
||||
} else if (item.type === 'error') {
|
||||
return <MessageListError item={item} />
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ export class Convo {
|
||||
status: this.status,
|
||||
items: this.getItems(),
|
||||
convo: this.convo!,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
error: undefined,
|
||||
...shared,
|
||||
...methods,
|
||||
@@ -226,6 +227,7 @@ export class Convo {
|
||||
status: this.status,
|
||||
items: this.getItems(),
|
||||
convo: this.convo!,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
error: undefined,
|
||||
...shared,
|
||||
...methods,
|
||||
@@ -236,6 +238,7 @@ export class Convo {
|
||||
status: this.status,
|
||||
items: this.getItems(),
|
||||
convo: this.convo!,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
error: undefined,
|
||||
...shared,
|
||||
...methods,
|
||||
@@ -246,6 +249,7 @@ export class Convo {
|
||||
status: this.status,
|
||||
items: this.getItems(),
|
||||
convo: this.convo!,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
error: undefined,
|
||||
...shared,
|
||||
...methods,
|
||||
@@ -1078,7 +1082,8 @@ export class Convo {
|
||||
|
||||
// continue queue processing
|
||||
await this.processPendingMessages()
|
||||
} catch (e: any) {
|
||||
} catch (err) {
|
||||
const e = err as Error
|
||||
this.handleSendMessageFailure(e)
|
||||
this.isProcessingPendingMessages = false
|
||||
}
|
||||
@@ -1177,7 +1182,8 @@ export class Convo {
|
||||
this.commit()
|
||||
|
||||
logger.debug(`sent ${this.pendingMessages.size} pending messages`, {})
|
||||
} catch (e: any) {
|
||||
} catch (err) {
|
||||
const e = err as Error
|
||||
this.handleSendMessageFailure(e)
|
||||
}
|
||||
}
|
||||
@@ -1231,25 +1237,18 @@ export class Convo {
|
||||
type: 'message',
|
||||
key: m.id,
|
||||
message: m,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
nextMessage: null,
|
||||
prevMessage: null,
|
||||
})
|
||||
} else if (ChatBskyConvoDefs.isDeletedMessageView(m)) {
|
||||
items.unshift({
|
||||
type: 'deleted-message',
|
||||
key: m.id,
|
||||
message: m,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
nextMessage: null,
|
||||
prevMessage: null,
|
||||
})
|
||||
} else if (ChatBskyConvoDefs.isSystemMessageView(m)) {
|
||||
items.unshift({
|
||||
type: 'system-message',
|
||||
key: m.id,
|
||||
message: m,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1271,25 +1270,18 @@ export class Convo {
|
||||
type: 'message',
|
||||
key: m.id,
|
||||
message: m,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
nextMessage: null,
|
||||
prevMessage: null,
|
||||
})
|
||||
} else if (ChatBskyConvoDefs.isDeletedMessageView(m)) {
|
||||
items.push({
|
||||
type: 'deleted-message',
|
||||
key: m.id,
|
||||
message: m,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
nextMessage: null,
|
||||
prevMessage: null,
|
||||
})
|
||||
} else if (ChatBskyConvoDefs.isSystemMessageView(m)) {
|
||||
items.push({
|
||||
type: 'system-message',
|
||||
key: m.id,
|
||||
message: m,
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1310,9 +1302,6 @@ export class Convo {
|
||||
did: this.senderUserDid,
|
||||
},
|
||||
},
|
||||
relatedProfiles: this.relatedProfiles,
|
||||
nextMessage: null,
|
||||
prevMessage: null,
|
||||
failed: this.pendingMessageFailure !== null,
|
||||
retry:
|
||||
this.pendingMessageFailure === 'recoverable'
|
||||
@@ -1334,53 +1323,12 @@ export class Convo {
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
.filter(item => {
|
||||
if (isConvoItemMessage(item)) {
|
||||
return !this.deletedMessages.has(item.message.id)
|
||||
}
|
||||
return true
|
||||
})
|
||||
.map((item, i, arr) => {
|
||||
let nextMessage = null
|
||||
let prevMessage = null
|
||||
const isMessage = isConvoItemMessage(item)
|
||||
|
||||
if (isMessage) {
|
||||
if (
|
||||
ChatBskyConvoDefs.isMessageView(item.message) ||
|
||||
ChatBskyConvoDefs.isDeletedMessageView(item.message)
|
||||
) {
|
||||
const next = arr[i + 1]
|
||||
|
||||
if (
|
||||
isConvoItemMessage(next) &&
|
||||
(ChatBskyConvoDefs.isMessageView(next.message) ||
|
||||
ChatBskyConvoDefs.isDeletedMessageView(next.message))
|
||||
) {
|
||||
nextMessage = next.message
|
||||
}
|
||||
|
||||
const prev = arr[i - 1]
|
||||
|
||||
if (
|
||||
isConvoItemMessage(prev) &&
|
||||
(ChatBskyConvoDefs.isMessageView(prev.message) ||
|
||||
ChatBskyConvoDefs.isDeletedMessageView(prev.message))
|
||||
) {
|
||||
prevMessage = prev.message
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
nextMessage,
|
||||
prevMessage,
|
||||
}
|
||||
}
|
||||
|
||||
return item
|
||||
})
|
||||
return items.filter(item => {
|
||||
if (isConvoItemMessage(item)) {
|
||||
return !this.deletedMessages.has(item.message.id)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,29 +72,11 @@ export type ConvoItem =
|
||||
type: 'message'
|
||||
key: string
|
||||
message: ChatBskyConvoDefs.MessageView
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
nextMessage:
|
||||
| ChatBskyConvoDefs.MessageView
|
||||
| ChatBskyConvoDefs.DeletedMessageView
|
||||
| null
|
||||
prevMessage:
|
||||
| ChatBskyConvoDefs.MessageView
|
||||
| ChatBskyConvoDefs.DeletedMessageView
|
||||
| null
|
||||
}
|
||||
| {
|
||||
type: 'pending-message'
|
||||
key: string
|
||||
message: ChatBskyConvoDefs.MessageView
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
nextMessage:
|
||||
| ChatBskyConvoDefs.MessageView
|
||||
| ChatBskyConvoDefs.DeletedMessageView
|
||||
| null
|
||||
prevMessage:
|
||||
| ChatBskyConvoDefs.MessageView
|
||||
| ChatBskyConvoDefs.DeletedMessageView
|
||||
| null
|
||||
failed: boolean
|
||||
/**
|
||||
* Retry sending the message. If present, the message is in a failed state.
|
||||
@@ -105,21 +87,11 @@ export type ConvoItem =
|
||||
type: 'deleted-message'
|
||||
key: string
|
||||
message: ChatBskyConvoDefs.DeletedMessageView
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
nextMessage:
|
||||
| ChatBskyConvoDefs.MessageView
|
||||
| ChatBskyConvoDefs.DeletedMessageView
|
||||
| null
|
||||
prevMessage:
|
||||
| ChatBskyConvoDefs.MessageView
|
||||
| ChatBskyConvoDefs.DeletedMessageView
|
||||
| null
|
||||
}
|
||||
| {
|
||||
type: 'system-message'
|
||||
key: string
|
||||
message: ChatBskyConvoDefs.SystemMessageView
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
}
|
||||
| {
|
||||
type: 'error'
|
||||
@@ -172,6 +144,7 @@ export type ConvoStateReady = {
|
||||
status: ConvoStatus.Ready
|
||||
items: ConvoItem[]
|
||||
convo: ConvoWithDetails
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
error: undefined
|
||||
isFetchingHistory: boolean
|
||||
hasAllHistory: boolean
|
||||
@@ -186,6 +159,7 @@ export type ConvoStateBackgrounded = {
|
||||
status: ConvoStatus.Backgrounded
|
||||
items: ConvoItem[]
|
||||
convo: ConvoWithDetails
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
error: undefined
|
||||
isFetchingHistory: boolean
|
||||
hasAllHistory: boolean
|
||||
@@ -200,6 +174,7 @@ export type ConvoStateSuspended = {
|
||||
status: ConvoStatus.Suspended
|
||||
items: ConvoItem[]
|
||||
convo: ConvoWithDetails
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
error: undefined
|
||||
isFetchingHistory: boolean
|
||||
hasAllHistory: boolean
|
||||
@@ -228,6 +203,7 @@ export type ConvoStateDisabled = {
|
||||
status: ConvoStatus.Disabled
|
||||
items: ConvoItem[]
|
||||
convo: ConvoWithDetails
|
||||
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>
|
||||
error: undefined
|
||||
isFetchingHistory: boolean
|
||||
hasAllHistory: boolean
|
||||
|
||||
Reference in New Issue
Block a user