diff --git a/src/screens/Messages/ChatList.tsx b/src/screens/Messages/ChatList.tsx
index 9a59352953..3463b04ed0 100644
--- a/src/screens/Messages/ChatList.tsx
+++ b/src/screens/Messages/ChatList.tsx
@@ -242,7 +242,7 @@ export function MessagesScreenInner({navigation, route}: Props) {
if (!isScreenFocused) {
return
}
- return listenSoftReset(onSoftReset)
+ return listenSoftReset(() => void onSoftReset())
}, [onSoftReset, isScreenFocused])
// NOTE(APiligrim)
@@ -292,7 +292,7 @@ export function MessagesScreenInner({navigation, route}: Props) {
size="small"
color="secondary_inverted"
variant="solid"
- onPress={() => refetch()}>
+ onPress={() => void refetch()}>
Retry
@@ -342,8 +342,8 @@ export function MessagesScreenInner({navigation, route}: Props) {
renderItem={renderItem}
keyExtractor={keyExtractor}
refreshing={isPTRing}
- onRefresh={onRefresh}
- onEndReached={onEndReached}
+ onRefresh={() => void onRefresh()}
+ onEndReached={() => void onEndReached()}
ListFooterComponent={
+ style={[a.w_full, IS_LIQUID_GLASS && {paddingTop: topInset}]}>
{moderation ? (
) : (
diff --git a/src/screens/Messages/components/MessagesList.tsx b/src/screens/Messages/components/MessagesList.tsx
index ca6280ec22..8ffb382704 100644
--- a/src/screens/Messages/components/MessagesList.tsx
+++ b/src/screens/Messages/components/MessagesList.tsx
@@ -1,4 +1,11 @@
-import {useCallback, useEffect, useId, useRef, useState} from 'react'
+import {
+ useCallback,
+ useEffect,
+ useId,
+ useLayoutEffect,
+ useRef,
+ useState,
+} from 'react'
import {type LayoutChangeEvent, type ScrollViewProps, View} from 'react-native'
import {
KeyboardChatScrollView,
@@ -62,6 +69,7 @@ import {useAnalytics} from '#/analytics'
import {IS_ANDROID, IS_NATIVE, IS_WEB} from '#/env'
import {ChatStatusInfo} from './ChatStatusInfo'
import {MessageInputEmbed, useMessageEmbed} from './MessageInputEmbed'
+import {MessagesListInfoPanel} from './MessagesListInfoPanel'
import {KeyboardStickyView} from './vendor/KeyboardStickyView'
function MaybeLoader({isLoading}: {isLoading: boolean}) {
@@ -151,10 +159,12 @@ export function MessagesList({
// Reset when hasScrolled goes back to false (e.g. convo re-initialization after backgrounding).
const hasInitiallyScrolled = useRef(false)
const prevHasScrolled = useRef(hasScrolled)
- if (prevHasScrolled.current && !hasScrolled) {
- hasInitiallyScrolled.current = false
- }
- prevHasScrolled.current = hasScrolled
+ useLayoutEffect(() => {
+ if (prevHasScrolled.current && !hasScrolled) {
+ hasInitiallyScrolled.current = false
+ }
+ prevHasScrolled.current = hasScrolled
+ }, [hasScrolled])
// -- Keep track of background state and positioning for new pill
const layoutHeight = useSharedValue(0)
@@ -384,7 +394,7 @@ export function MessagesList({
profile={convoState.convo.members.find(
member => member.did === item.message.sender.did,
)}
- isGroupChat={convoState.getGroupInfo?.() != null}
+ isGroupChat={convoState.isGroup()}
/>
)
} else if (item.type === 'deleted-message') {
@@ -417,6 +427,8 @@ export function MessagesList({
[inputHeightUI],
)
+ console.log('DEBUG >>>', 'convoState.hasAllHistory', convoState.hasAllHistory)
+
return (
+ <>
+
+ {convoState.isGroup() && convoState.hasAllHistory ? (
+
+ ) : null}
+ >
}
// native only (prop is not supported on web)
renderScrollComponent={renderScrollComponent}
diff --git a/src/screens/Messages/components/MessagesListInfoPanel.tsx b/src/screens/Messages/components/MessagesListInfoPanel.tsx
new file mode 100644
index 0000000000..9f904e1eb3
--- /dev/null
+++ b/src/screens/Messages/components/MessagesListInfoPanel.tsx
@@ -0,0 +1,95 @@
+import {View} from 'react-native'
+import {Plural, Trans, useLingui} from '@lingui/react/macro'
+
+import {type ConvoState} from '#/state/messages/convo/types'
+import {useSession} from '#/state/session'
+import {atoms as a, useTheme} from '#/alf'
+import {AvatarBubbles} from '#/components/AvatarBubbles'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
+import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink'
+import {PersonPlus_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person'
+import {Text} from '#/components/Typography'
+
+export function MessagesListInfoPanel({convoState}: {convoState: ConvoState}) {
+ const t = useTheme()
+ const {t: l} = useLingui()
+
+ const {currentAccount} = useSession()
+
+ const groupName = convoState.getGroupInfo?.()?.name
+
+ const members = (convoState?.convo?.members ?? []).filter(
+ profile => profile.did !== currentAccount?.did,
+ )
+
+ let names: React.ReactNode | null = null
+ if (members.length === 1) {
+ names = New chat with {members[0].displayName}
+ }
+ if (members.length === 2) {
+ names = (
+
+ New chat with {members[0].displayName} and {members[1].displayName}
+
+ )
+ }
+ if (members.length > 2) {
+ names = (
+
+ New chat with {members[0].displayName}, {members[1].displayName}, and{' '}
+
+ .
+
+ )
+ }
+
+ return (
+
+
+ {groupName ? (
+
+ {groupName}
+
+ ) : null}
+ {names ? (
+
+ {names}
+
+ ) : null}
+
+
+
+
+
+ )
+}
diff --git a/src/state/messages/convo/agent.ts b/src/state/messages/convo/agent.ts
index ef2b251cce..9f0693c4a3 100644
--- a/src/state/messages/convo/agent.ts
+++ b/src/state/messages/convo/agent.ts
@@ -153,6 +153,8 @@ export class Convo {
sender: this.sender,
recipients: this.recipients,
isFetchingHistory: this.isFetchingHistory,
+ // Explicit null check since the value is initially undefined.
+ hasAllHistory: this.oldestRev === null,
deleteMessage: undefined,
sendMessage: undefined,
fetchMessageHistory: undefined,
@@ -176,6 +178,8 @@ export class Convo {
sender: this.sender!,
recipients: this.recipients!,
isFetchingHistory: this.isFetchingHistory,
+ // Explicit null check since the value is initially undefined.
+ hasAllHistory: this.oldestRev === null,
deleteMessage: this.deleteMessage,
sendMessage: this.sendMessage,
fetchMessageHistory: this.fetchMessageHistory,
@@ -196,6 +200,7 @@ export class Convo {
sender: undefined,
recipients: undefined,
isFetchingHistory: false,
+ hasAllHistory: false,
deleteMessage: undefined,
sendMessage: undefined,
fetchMessageHistory: undefined,
@@ -216,6 +221,8 @@ export class Convo {
sender: this.sender,
recipients: this.recipients,
isFetchingHistory: false,
+ // Explicit null check since the value is initially undefined.
+ hasAllHistory: this.oldestRev === null,
deleteMessage: undefined,
sendMessage: undefined,
fetchMessageHistory: undefined,
@@ -627,6 +634,7 @@ export class Convo {
/*
* If oldestRev is null, we've fetched all history.
+ * Needs to explicitly check for `null` since this is initially `undefined`.
*/
if (this.oldestRev === null) return
@@ -660,6 +668,14 @@ export class Convo {
this.oldestRev = cursor ?? null
+ /*
+ * If the response contained fewer messages than the limit, we know
+ * there are no more pages, regardless of whether a cursor was returned.
+ */
+ if (messages.length < (IS_NATIVE ? 30 : 60)) {
+ this.oldestRev = null
+ }
+
for (const message of messages) {
if (
ChatBskyConvoDefs.isMessageView(message) ||
diff --git a/src/state/messages/convo/types.ts b/src/state/messages/convo/types.ts
index d7adb51c6d..f27610053a 100644
--- a/src/state/messages/convo/types.ts
+++ b/src/state/messages/convo/types.ts
@@ -156,6 +156,7 @@ export type ConvoStateUninitialized = {
sender: ChatBskyActorDefs.ProfileViewBasic | undefined
recipients: ChatBskyActorDefs.ProfileViewBasic[] | undefined
isFetchingHistory: false
+ hasAllHistory: boolean
deleteMessage: undefined
sendMessage: undefined
fetchMessageHistory: undefined
@@ -174,6 +175,7 @@ export type ConvoStateInitializing = {
sender: ChatBskyActorDefs.ProfileViewBasic | undefined
recipients: ChatBskyActorDefs.ProfileViewBasic[] | undefined
isFetchingHistory: boolean
+ hasAllHistory: boolean
deleteMessage: undefined
sendMessage: undefined
fetchMessageHistory: undefined
@@ -192,6 +194,7 @@ export type ConvoStateReady = {
sender: ChatBskyActorDefs.ProfileViewBasic
recipients: ChatBskyActorDefs.ProfileViewBasic[]
isFetchingHistory: boolean
+ hasAllHistory: boolean
deleteMessage: DeleteMessage
sendMessage: SendMessage
fetchMessageHistory: FetchMessageHistory
@@ -210,6 +213,7 @@ export type ConvoStateBackgrounded = {
sender: ChatBskyActorDefs.ProfileViewBasic
recipients: ChatBskyActorDefs.ProfileViewBasic[]
isFetchingHistory: boolean
+ hasAllHistory: boolean
deleteMessage: DeleteMessage
sendMessage: SendMessage
fetchMessageHistory: FetchMessageHistory
@@ -228,6 +232,7 @@ export type ConvoStateSuspended = {
sender: ChatBskyActorDefs.ProfileViewBasic
recipients: ChatBskyActorDefs.ProfileViewBasic[]
isFetchingHistory: boolean
+ hasAllHistory: boolean
deleteMessage: DeleteMessage
sendMessage: SendMessage
fetchMessageHistory: FetchMessageHistory
@@ -246,6 +251,7 @@ export type ConvoStateError = {
sender: undefined
recipients: undefined
isFetchingHistory: false
+ hasAllHistory: false
deleteMessage: undefined
sendMessage: undefined
fetchMessageHistory: undefined
@@ -264,6 +270,7 @@ export type ConvoStateDisabled = {
sender: ChatBskyActorDefs.ProfileViewBasic
recipients: ChatBskyActorDefs.ProfileViewBasic[]
isFetchingHistory: boolean
+ hasAllHistory: boolean
deleteMessage: DeleteMessage
sendMessage: SendMessage
fetchMessageHistory: FetchMessageHistory