Add header to group clip clops (#10254)
Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
@@ -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()}>
|
||||
<ButtonText>
|
||||
<Trans>Retry</Trans>
|
||||
</ButtonText>
|
||||
@@ -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={
|
||||
<ListFooter
|
||||
isFetchingNextPage={isFetchingNextPage}
|
||||
|
||||
@@ -142,7 +142,7 @@ function Inner() {
|
||||
return (
|
||||
<>
|
||||
<Layout.Center
|
||||
style={[a.flex_1, IS_LIQUID_GLASS && {paddingTop: topInset}]}>
|
||||
style={[a.w_full, IS_LIQUID_GLASS && {paddingTop: topInset}]}>
|
||||
{moderation ? (
|
||||
<MessagesListHeader profile={recipient} moderation={moderation} />
|
||||
) : (
|
||||
|
||||
@@ -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 (
|
||||
<DateDividerToggleProvider>
|
||||
<KeyboardGestureArea
|
||||
@@ -449,7 +461,12 @@ export function MessagesList({
|
||||
showsVerticalScrollIndicator={!IS_ANDROID}
|
||||
scrollEventThrottle={100}
|
||||
ListHeaderComponent={
|
||||
<MaybeLoader isLoading={convoState.isFetchingHistory} />
|
||||
<>
|
||||
<MaybeLoader isLoading={convoState.isFetchingHistory} />
|
||||
{convoState.isGroup() && convoState.hasAllHistory ? (
|
||||
<MessagesListInfoPanel convoState={convoState} />
|
||||
) : null}
|
||||
</>
|
||||
}
|
||||
// native only (prop is not supported on web)
|
||||
renderScrollComponent={renderScrollComponent}
|
||||
|
||||
@@ -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 = <Trans>New chat with {members[0].displayName}</Trans>
|
||||
}
|
||||
if (members.length === 2) {
|
||||
names = (
|
||||
<Trans>
|
||||
New chat with {members[0].displayName} and {members[1].displayName}
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
if (members.length > 2) {
|
||||
names = (
|
||||
<Trans>
|
||||
New chat with {members[0].displayName}, {members[1].displayName}, and{' '}
|
||||
<Plural
|
||||
value={members.length - 2}
|
||||
one={`${members.length - 2} more`}
|
||||
other={`${members.length - 2} more`}
|
||||
/>
|
||||
.
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[a.align_center, a.justify_center]}>
|
||||
<AvatarBubbles animate={true} profiles={members} />
|
||||
{groupName ? (
|
||||
<Text style={[a.text_2xl, a.font_bold, a.mt_lg, t.atoms.text]}>
|
||||
{groupName}
|
||||
</Text>
|
||||
) : null}
|
||||
{names ? (
|
||||
<Text style={[a.text_sm, a.mt_xs, t.atoms.text_contrast_high]}>
|
||||
{names}
|
||||
</Text>
|
||||
) : null}
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
a.gap_sm,
|
||||
a.mt_lg,
|
||||
a.mb_4xl,
|
||||
]}>
|
||||
<Button
|
||||
color="secondary"
|
||||
size="small"
|
||||
label={l`Click here to add people to this group chat`}
|
||||
onPress={() => {}}>
|
||||
<ButtonIcon icon={PersonPlusIcon} />
|
||||
<ButtonText>
|
||||
<Trans>Add people</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
size="small"
|
||||
label={l`Click here to view or create an invite link for this group chat`}
|
||||
onPress={() => {}}>
|
||||
<ButtonIcon icon={ChainLinkIcon} />
|
||||
<ButtonText>
|
||||
<Trans>Invite link</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user