Improve initial loading state for chat settings (#10424)
This commit is contained in:
@@ -16,8 +16,10 @@ import {Text} from '#/components/Typography'
|
||||
|
||||
export function AddMembersLink({
|
||||
convo,
|
||||
disabled,
|
||||
}: {
|
||||
convo: Extract<ConvoWithDetails, {kind: 'group'}>
|
||||
disabled?: boolean
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
@@ -41,7 +43,7 @@ export function AddMembersLink({
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
disabled={isAddPending}
|
||||
disabled={disabled || isAddPending}
|
||||
label={l`Add members`}
|
||||
onPress={addMembersControl.open}>
|
||||
{({interacting}) => (
|
||||
|
||||
@@ -23,21 +23,19 @@ export function MembersAndRequests({
|
||||
|
||||
return (
|
||||
<View style={[a.flex_row, a.justify_between, a.px_xl, a.pt_xl, a.pb_sm]}>
|
||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||
<View style={[a.flex_row, a.align_center, a.gap_xs]}>
|
||||
<Text style={[a.text_lg, a.font_semi_bold, t.atoms.text]}>
|
||||
<Trans>Members</Trans>
|
||||
<Trans comment="The heading above the list of chat members.">
|
||||
Members
|
||||
</Trans>
|
||||
</Text>
|
||||
<Text style={[a.text_xs, a.font_medium, t.atoms.text_contrast_medium]}>
|
||||
{l({
|
||||
message: `${memberCount}/${MEMBER_LIMIT}`,
|
||||
comment:
|
||||
'The number of group chat members out of the total number of permitted users.',
|
||||
})}
|
||||
</Text>
|
||||
<View
|
||||
style={[a.px_xs, a.py_2xs, t.atoms.bg_contrast_50, a.rounded_full]}>
|
||||
<Text
|
||||
style={[a.text_xs, a.font_medium, {color: t.palette.contrast_500}]}>
|
||||
{l({
|
||||
message: `${memberCount}/${MEMBER_LIMIT}`,
|
||||
comment:
|
||||
'The number of group chat members out of the total number of permitted users.',
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
{isOwner && requestCount > 0 ? (
|
||||
<InlineLinkText
|
||||
|
||||
@@ -3,7 +3,7 @@ import {View} from 'react-native'
|
||||
import {
|
||||
ChatBskyActorDefs,
|
||||
ChatBskyConvoDefs,
|
||||
ModerationOpts,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
@@ -117,7 +117,7 @@ function SettingsInner() {
|
||||
)
|
||||
}
|
||||
|
||||
if (!isConvoActive(convoState) || !moderationOpts) {
|
||||
if (!convoState.convo || !moderationOpts) {
|
||||
return (
|
||||
<View style={[a.flex_1, a.align_center, a.justify_center]}>
|
||||
<Loader size="xl" />
|
||||
@@ -125,7 +125,7 @@ function SettingsInner() {
|
||||
)
|
||||
}
|
||||
|
||||
if (convoState.convo?.kind !== 'group') {
|
||||
if (convoState.convo.kind !== 'group') {
|
||||
return (
|
||||
<Error
|
||||
title={l`Wrong kind of conversation`}
|
||||
@@ -142,7 +142,11 @@ function SettingsInner() {
|
||||
}
|
||||
|
||||
return (
|
||||
<GroupSettings convo={convoState.convo} moderationOpts={moderationOpts} />
|
||||
<GroupSettings
|
||||
convo={convoState.convo}
|
||||
moderationOpts={moderationOpts}
|
||||
isReady={isConvoActive(convoState)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -166,9 +170,11 @@ function isGroupMember(
|
||||
function GroupSettings({
|
||||
convo,
|
||||
moderationOpts,
|
||||
isReady,
|
||||
}: {
|
||||
convo: Extract<ConvoWithDetails, {kind: 'group'}>
|
||||
moderationOpts: ModerationOpts
|
||||
isReady: boolean
|
||||
}) {
|
||||
const initialNumToRender = useInitialNumToRender({minItemHeight: 68})
|
||||
const bottomBarOffset = useBottomBarOffset()
|
||||
@@ -178,7 +184,7 @@ function GroupSettings({
|
||||
const primaryMember = convo.primaryMember
|
||||
const isOwner = !!primaryMember && primaryMember.did === currentAccount?.did
|
||||
|
||||
const {data: memberListData = [], isPending} = useListConvoMembersQuery({
|
||||
const {data: memberListData = []} = useListConvoMembersQuery({
|
||||
convoId: convo.view.id,
|
||||
placeholderData: convo.members,
|
||||
})
|
||||
@@ -197,6 +203,16 @@ function GroupSettings({
|
||||
0,
|
||||
) ?? 0
|
||||
|
||||
const groupMembers = memberListData.filter(isGroupMember).sort((a, b) => {
|
||||
const aIsOwner = a.did === primaryMember?.did
|
||||
const bIsOwner = b.did === primaryMember?.did
|
||||
const aIsSelf = a.did === currentAccount?.did
|
||||
const bIsSelf = b.did === currentAccount?.did
|
||||
if (aIsOwner !== bIsOwner) return aIsOwner ? -1 : 1
|
||||
if (aIsSelf !== bIsSelf) return aIsSelf ? -1 : 1
|
||||
return 0
|
||||
})
|
||||
|
||||
const items: Item[] = [
|
||||
{
|
||||
type: 'MEMBERS_AND_REQUESTS',
|
||||
@@ -206,41 +222,30 @@ function GroupSettings({
|
||||
? [{type: 'ADD_MEMBERS_LINK', key: 'add-members-link'} as const]
|
||||
: []),
|
||||
]
|
||||
if (isPending) {
|
||||
// should never be pending if we correctly set the query cache data
|
||||
items.push(
|
||||
...Array.from({length: 5}, (_, i) => ({
|
||||
type: 'CHAT_MEMBER_PLACEHOLDER' as const,
|
||||
key: `chat-member-placeholder-${i}`,
|
||||
})),
|
||||
)
|
||||
} else {
|
||||
items.push(
|
||||
...memberListData
|
||||
.filter(isGroupMember)
|
||||
.sort((a, b) => {
|
||||
const aIsOwner = a.did === primaryMember?.did
|
||||
const bIsOwner = b.did === primaryMember?.did
|
||||
const aIsSelf = a.did === currentAccount?.did
|
||||
const bIsSelf = b.did === currentAccount?.did
|
||||
if (aIsOwner !== bIsOwner) return aIsOwner ? -1 : 1
|
||||
if (aIsSelf !== bIsSelf) return aIsSelf ? -1 : 1
|
||||
return 0
|
||||
})
|
||||
.map(
|
||||
(profile): Item => ({
|
||||
type: 'CHAT_MEMBER',
|
||||
key: profile.did,
|
||||
profile,
|
||||
status:
|
||||
primaryMember?.did === profile.did
|
||||
? 'owner'
|
||||
: invites.includes(profile.did)
|
||||
? 'invited'
|
||||
: 'standard',
|
||||
}),
|
||||
),
|
||||
)
|
||||
items.push(
|
||||
...groupMembers.map(
|
||||
(profile): Item => ({
|
||||
type: 'CHAT_MEMBER',
|
||||
key: profile.did,
|
||||
profile,
|
||||
status:
|
||||
primaryMember?.did === profile.did
|
||||
? 'owner'
|
||||
: invites.includes(profile.did)
|
||||
? 'invited'
|
||||
: 'standard',
|
||||
}),
|
||||
),
|
||||
)
|
||||
const placeholderCount = Math.max(
|
||||
0,
|
||||
convo.details.memberCount - groupMembers.length,
|
||||
)
|
||||
for (let i = 0; i < placeholderCount; i++) {
|
||||
items.push({
|
||||
type: 'CHAT_MEMBER_PLACEHOLDER',
|
||||
key: `chat-member-placeholder-${i}`,
|
||||
})
|
||||
}
|
||||
|
||||
function renderItem({item}: {item: Item}) {
|
||||
@@ -255,7 +260,7 @@ function GroupSettings({
|
||||
/>
|
||||
)
|
||||
case 'ADD_MEMBERS_LINK':
|
||||
return <AddMembersLink convo={convo} />
|
||||
return <AddMembersLink convo={convo} disabled={!isReady} />
|
||||
case 'CHAT_MEMBER':
|
||||
return (
|
||||
<Member
|
||||
@@ -286,6 +291,7 @@ function GroupSettings({
|
||||
convo={convo}
|
||||
isOwner={isOwner}
|
||||
moderationOpts={moderationOpts}
|
||||
isReady={isReady}
|
||||
/>
|
||||
}
|
||||
renderItem={renderItem}
|
||||
@@ -299,10 +305,12 @@ function SettingsHeader({
|
||||
convo,
|
||||
isOwner,
|
||||
moderationOpts,
|
||||
isReady,
|
||||
}: {
|
||||
convo: Extract<ConvoWithDetails, {kind: 'group'}>
|
||||
isOwner: boolean
|
||||
moderationOpts: ModerationOpts
|
||||
isReady: boolean
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {i18n, t: l} = useLingui()
|
||||
@@ -462,7 +470,7 @@ function SettingsHeader({
|
||||
]}>
|
||||
<SettingsButton
|
||||
color={convo.view.muted ? 'negative_subtle' : 'secondary'}
|
||||
disabled={isMuting}
|
||||
disabled={!isReady || isMuting}
|
||||
icon={convo.view.muted ? BellOffIcon : BellIcon}
|
||||
label={
|
||||
convo.view.muted
|
||||
@@ -474,7 +482,7 @@ function SettingsHeader({
|
||||
/>
|
||||
{isOwner ? (
|
||||
<SettingsButton
|
||||
disabled={isEditingName}
|
||||
disabled={!isReady || isEditingName}
|
||||
icon={EditIcon}
|
||||
label={l`Edit this group chat’s name`}
|
||||
text={l`Edit name`}
|
||||
@@ -483,7 +491,7 @@ function SettingsHeader({
|
||||
) : null}
|
||||
{isJoinLinkEnabled ? (
|
||||
<SettingsButton
|
||||
disabled={lockStatus !== 'unlocked'}
|
||||
disabled={!isReady || lockStatus !== 'unlocked'}
|
||||
icon={ChainLinkIcon}
|
||||
label={
|
||||
isOwner
|
||||
@@ -497,7 +505,7 @@ function SettingsHeader({
|
||||
{canLockGroupChat ? (
|
||||
<SettingsButton
|
||||
color={lockStatus === 'locked' ? 'negative_subtle' : 'secondary'}
|
||||
disabled={isLocking}
|
||||
disabled={!isReady || isLocking}
|
||||
icon={LockIcon}
|
||||
label={
|
||||
lockStatus === 'locked'
|
||||
@@ -512,6 +520,7 @@ function SettingsHeader({
|
||||
) : null}
|
||||
{!isOwner && isReportLinkEnabled && (
|
||||
<SettingsButton
|
||||
disabled={!isReady}
|
||||
icon={FlagIcon}
|
||||
label={l`Report this group chat`}
|
||||
text={l`Report`}
|
||||
@@ -520,7 +529,7 @@ function SettingsHeader({
|
||||
)}
|
||||
{!isOwner && (
|
||||
<SettingsButton
|
||||
disabled={isLeaving}
|
||||
disabled={!isReady || isLeaving}
|
||||
icon={ArrowBoxLeftIcon}
|
||||
label={l`Leave this group chat`}
|
||||
text={l`Leave`}
|
||||
|
||||
Reference in New Issue
Block a user