Move useListConvoMembersQuery inside AddMembersFlow (#10386)
This commit is contained in:
@@ -11,16 +11,18 @@ import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
|
||||
import {useListConvoMembersQuery} from '#/state/queries/messages/list-convo-members'
|
||||
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
||||
import {useSession} from '#/state/session'
|
||||
import {type ListMethods} from '#/view/com/util/List'
|
||||
import {android, atoms as a, native, useTheme, web} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {canBeMessaged} from '#/components/dms/util'
|
||||
import {canBeMessaged, type ConvoWithDetails} from '#/components/dms/util'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {IS_NATIVE, IS_WEB} from '#/env'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
@@ -54,12 +56,12 @@ type PlaceholderItem = {
|
||||
key: string
|
||||
}
|
||||
|
||||
type ErrorItem = {
|
||||
type: 'error'
|
||||
type LoadingItem = {
|
||||
type: 'loading'
|
||||
key: string
|
||||
}
|
||||
|
||||
type Item = LabelItem | ProfileItem | EmptyItem | PlaceholderItem | ErrorItem
|
||||
type Item = LabelItem | ProfileItem | EmptyItem | PlaceholderItem | LoadingItem
|
||||
|
||||
export type State = {
|
||||
groupChatDids: string[]
|
||||
@@ -98,11 +100,11 @@ function reducer(state: State, action: Action): State {
|
||||
}
|
||||
|
||||
export function AddMembersFlow({
|
||||
members,
|
||||
convo,
|
||||
title,
|
||||
onAddMembers,
|
||||
}: {
|
||||
members: string[]
|
||||
convo: Extract<ConvoWithDetails, {kind: 'group'}>
|
||||
title: string
|
||||
onAddMembers: (
|
||||
dids: string[],
|
||||
@@ -112,21 +114,32 @@ export function AddMembersFlow({
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const control = Dialog.useDialogContext()
|
||||
|
||||
const [headerHeight, setHeaderHeight] = useState(0)
|
||||
const [footerHeight, setFooterHeight] = useState(0)
|
||||
const listRef = useRef<ListMethods>(null)
|
||||
const {currentAccount} = useSession()
|
||||
const inputRef = useRef<TextInput>(null)
|
||||
|
||||
const [searchText, setSearchText] = useState('')
|
||||
|
||||
const listRef = useRef<ListMethods>(null)
|
||||
const inputRef = useRef<TextInput>(null)
|
||||
|
||||
const {
|
||||
data: results,
|
||||
data: autocompleteResults,
|
||||
isError,
|
||||
isFetching,
|
||||
isFetching: isAutocompleteFetching,
|
||||
} = useActorAutocompleteQuery(searchText, true, 12)
|
||||
const {data: follows} = useProfileFollowsQuery(currentAccount?.did)
|
||||
const {data: memberListData = [], isPending: isMemberListPending} =
|
||||
useListConvoMembersQuery({
|
||||
convoId: convo.view.id,
|
||||
placeholderData: convo.members,
|
||||
})
|
||||
const memberDidSet = useMemo(
|
||||
() => new Set(memberListData.map(profile => profile.did)),
|
||||
[memberListData],
|
||||
)
|
||||
|
||||
const [{groupChatDids, groupChatProfiles}, dispatch] = useReducer(reducer, {
|
||||
groupChatDids: [],
|
||||
@@ -147,8 +160,13 @@ export function AddMembersFlow({
|
||||
[groupChatDids, groupChatProfiles],
|
||||
)
|
||||
|
||||
const items = useMemo(() => {
|
||||
let _items: Item[] = []
|
||||
const items = useMemo<Item[]>(() => {
|
||||
if (isMemberListPending) {
|
||||
// Still fetching chat member DIDs for filtering, so force the loading state.
|
||||
return []
|
||||
}
|
||||
|
||||
const _items: Item[] = []
|
||||
|
||||
if (isError) {
|
||||
_items.push({
|
||||
@@ -157,11 +175,11 @@ export function AddMembersFlow({
|
||||
message: l`We’re having network issues, try again`,
|
||||
})
|
||||
} else if (searchText.length) {
|
||||
if (results?.length) {
|
||||
for (const profile of results) {
|
||||
if (autocompleteResults?.length) {
|
||||
for (const profile of autocompleteResults) {
|
||||
if (
|
||||
profile.did === currentAccount?.did ||
|
||||
members.includes(profile.did)
|
||||
memberDidSet.has(profile.did)
|
||||
)
|
||||
continue
|
||||
_items.push({
|
||||
@@ -171,18 +189,11 @@ export function AddMembersFlow({
|
||||
})
|
||||
}
|
||||
|
||||
_items = _items.sort(item => {
|
||||
_items.sort(item => {
|
||||
return item.type === 'profile' && canBeMessaged(item.profile) ? -1 : 1
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const placeholders: Item[] = Array(10)
|
||||
.fill(0)
|
||||
.map((__, i) => ({
|
||||
type: 'placeholder',
|
||||
key: i + '',
|
||||
}))
|
||||
|
||||
if (follows) {
|
||||
for (const page of follows.pages) {
|
||||
for (const profile of page.follows) {
|
||||
@@ -194,11 +205,13 @@ export function AddMembersFlow({
|
||||
}
|
||||
}
|
||||
|
||||
_items = _items.sort(item => {
|
||||
_items.sort(item => {
|
||||
return item.type === 'profile' && canBeMessaged(item.profile) ? -1 : 1
|
||||
})
|
||||
} else {
|
||||
_items.push(...placeholders)
|
||||
for (let i = 0; i < 10; i++) {
|
||||
_items.push({type: 'placeholder', key: i + ''})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,12 +223,31 @@ export function AddMembersFlow({
|
||||
})
|
||||
}
|
||||
|
||||
return _items
|
||||
}, [isError, searchText, l, results, currentAccount?.did, members, follows])
|
||||
if (searchText && isAutocompleteFetching && _items.length > 0) {
|
||||
// Stale results are still showing while autocomplete refetches -
|
||||
// append an inline indicator so the user sees that work is happening.
|
||||
_items.push({type: 'loading', key: 'loading'})
|
||||
} else if (
|
||||
searchText &&
|
||||
!isAutocompleteFetching &&
|
||||
!_items.length &&
|
||||
!isError
|
||||
) {
|
||||
_items.push({type: 'empty', key: 'empty', message: l`No results`})
|
||||
}
|
||||
|
||||
if (searchText && !isFetching && !items.length && !isError) {
|
||||
items.push({type: 'empty', key: 'empty', message: l`No results`})
|
||||
}
|
||||
return _items
|
||||
}, [
|
||||
autocompleteResults,
|
||||
currentAccount?.did,
|
||||
follows,
|
||||
isAutocompleteFetching,
|
||||
isError,
|
||||
isMemberListPending,
|
||||
l,
|
||||
memberDidSet,
|
||||
searchText,
|
||||
])
|
||||
|
||||
const handlePressBack = useCallback(() => {
|
||||
control.close()
|
||||
@@ -243,6 +275,13 @@ export function AddMembersFlow({
|
||||
case 'placeholder': {
|
||||
return <ProfileCardSkeleton key={item.key} />
|
||||
}
|
||||
case 'loading': {
|
||||
return (
|
||||
<View style={[a.px_lg, a.py_xl, a.align_center]}>
|
||||
<Loader size="lg" />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
case 'empty': {
|
||||
return <EmptyMemberList key={item.key} message={item.message} />
|
||||
}
|
||||
@@ -436,12 +475,24 @@ export function AddMembersFlow({
|
||||
renderItem={renderItems}
|
||||
ListHeaderComponent={listHeader}
|
||||
stickyHeaderIndices={[0]}
|
||||
ListEmptyComponent={
|
||||
isMemberListPending || isAutocompleteFetching ? (
|
||||
<View style={[a.flex_1, a.align_center, a.justify_center]}>
|
||||
<Loader size="xl" />
|
||||
</View>
|
||||
) : null
|
||||
}
|
||||
keyExtractor={(item: Item) => item.key}
|
||||
style={[
|
||||
web([a.py_0, {height: '100vh', maxHeight: 600}, a.px_0]),
|
||||
native({height: '100%'}),
|
||||
]}
|
||||
webInnerContentContainerStyle={[a.py_0, {paddingBottom: footerHeight}]}
|
||||
contentContainerStyle={items.length === 0 ? {flexGrow: 1} : undefined}
|
||||
webInnerContentContainerStyle={[
|
||||
a.py_0,
|
||||
{paddingBottom: footerHeight},
|
||||
items.length === 0 && {flexGrow: 1},
|
||||
]}
|
||||
webInnerStyle={[a.py_0, {maxWidth: 500, minWidth: 200}]}
|
||||
scrollIndicatorInsets={{top: headerHeight, bottom: footerHeight}}
|
||||
keyboardDismissMode="on-drag"
|
||||
@@ -457,7 +508,6 @@ export function AddMembersFlow({
|
||||
onPress={handlePressBack}>
|
||||
<ButtonIcon icon={ArrowLeftIcon} size="md" />
|
||||
<ButtonText>
|
||||
{' '}
|
||||
<Trans>Back</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user