import * as React from 'react'; import { ListRenderItemInfo, Pressable, View } from 'react-native'; import { useChatContext } from '../../chat'; import { useColors } from '../../hook'; import { useI18nContext } from '../../i18n'; import { usePaletteContext } from '../../theme'; import { FlatListFactory } from '../../ui/FlatList'; import { SingleLineText } from '../../ui/Text'; import { Avatar } from '../Avatar'; import { BottomSheetNameMenu } from '../BottomSheetMenu/BottomSheetNameMenu'; import { useCloseMenu } from '../hooks/useCloseMenu'; import { useDataPriority } from '../hooks/useDataPriority'; import { useMessageThreadMemberListMoreActions } from '../hooks/useMessageThreadMemberListMoreActions'; import { useFlatList } from '../List'; import { EmptyPlaceholder, ErrorPlaceholder, LoadingPlaceholder, } from '../Placeholder'; import { TopNavigationBar, TopNavigationBarLeft } from '../TopNavigationBar'; import { ContextNameMenuRef } from '../types'; import { gRequestMaxThreadCount } from './const'; import type { MessageThreadMemberListItemProps, MessageThreadMemberListProps, ThreadMemberModel, } from './types'; /** * Message Thread Member List Component. */ export function MessageThreadMemberList(props: MessageThreadMemberListProps) { const FlatList = React.useMemo( () => FlatListFactory(), [] ); const { containerStyle, navigationBarVisible, customNavigationBar, onBack } = props; const { data, onMore, reachedThreshold, bounces, listState, menuRef, onClickedItem, onRequestCloseMenu, } = useMessageThreadMemberList(props); const { tr } = useI18nContext(); const { getColor } = useColors(); return ( {navigationBarVisible !== false ? ( customNavigationBar ? ( <>{customNavigationBar} ) : ( } Right={} /> ) ) : null} ) => { const { item } = info; return ; }} keyExtractor={(item: MessageThreadMemberListItemProps) => { return item.model.id; }} onEndReached={onMore} onEndReachedThreshold={reachedThreshold} bounces={bounces} // !!! This effect does not work well when inserting the first element without scrolling. // maintainVisibleContentPosition={{ // minIndexForVisible: 0, // autoscrollToTopThreshold: 10, // }} ListEmptyComponent={EmptyPlaceholder} ListErrorComponent={ listState === 'error' ? ( { // todo: }} /> ) : null } ListLoadingComponent={ listState === 'loading' ? : null } /> ); } function useMessageThreadMemberList(props: MessageThreadMemberListProps) { const { testMode, thread, onClickedItem } = props; const flatListProps = useFlatList({ listState: testMode === 'only-ui' ? 'normal' : 'normal', enableMore: true, }); const { dataRef, setData } = flatListProps; const [reachedThreshold] = React.useState(0.5); const hasNoMoreRef = React.useRef(false); const currentCursorRef = React.useRef(''); const bounces = React.useRef(true).current; const im = useChatContext(); const menuRef = React.useRef({} as any); const alertRef = React.useRef(null); const { closeMenu } = useCloseMenu({ menuRef }); const groupOwnerRef = React.useRef(''); const { getContactInfo } = useDataPriority({}); const onClickedKickMember = React.useCallback( (threadId: string, memberId: string) => { im.removeMemberFromThread({ threadId: threadId, userId: memberId, }); }, [im] ); const { onShowMessageThreadMemberListMoreActions } = useMessageThreadMemberListMoreActions({ thread, menuRef, alertRef, }); const _onClickedItem = React.useCallback( (data?: ThreadMemberModel) => { if (data) { if (onClickedItem) { const ret = onClickedItem(data); if (ret === true) { if ( (thread.owner === im.userId || groupOwnerRef.current === im.userId) && data.id !== im.userId ) { onShowMessageThreadMemberListMoreActions({ threadId: thread.threadId, memberId: data.id, onClickedKickMember: onClickedKickMember, groupOwner: groupOwnerRef.current, }); } } } else { if ( (thread.owner === im.userId || groupOwnerRef.current === im.userId) && data.id !== im.userId ) { onShowMessageThreadMemberListMoreActions({ threadId: thread.threadId, memberId: data.id, onClickedKickMember: onClickedKickMember, groupOwner: groupOwnerRef.current, }); } } } }, [ im.userId, onClickedItem, onClickedKickMember, onShowMessageThreadMemberListMoreActions, thread, ] ); const requestMore = React.useCallback(() => { if (thread) { im.fetchMembersFromThread({ threadId: thread.threadId, cursor: currentCursorRef.current, pageSize: gRequestMaxThreadCount, onResult: (res) => { if (res.isOk && res.value?.list) { if (res.value.list.length < gRequestMaxThreadCount) { hasNoMoreRef.current = true; } currentCursorRef.current = res.value.cursor; if (res.value) { res.value.list.forEach((item) => { dataRef.current.push({ model: { id: item, name: getContactInfo(item).name, avatar: getContactInfo(item).avatar, isOwner: thread.owner === item, type: 'user', }, }); }); setData([...dataRef.current]); } } }, }); } }, [dataRef, getContactInfo, im, setData, thread]); const _onMore = React.useCallback(() => { if (hasNoMoreRef.current === true) { return; } if (dataRef.current.length < 10) { return; } requestMore(); }, [dataRef, requestMore]); const init = React.useCallback(() => { if (thread && thread.parentId) { im.getGroupInfo({ groupId: thread.parentId, onResult: (res) => { if (res.isOk && res.value) { groupOwnerRef.current = res.value.owner; } }, }); } }, [im, thread]); React.useEffect(() => { init(); }, [init]); React.useEffect(() => { currentCursorRef.current = ''; requestMore(); }, [requestMore]); return { ...flatListProps, reachedThreshold, onMore: _onMore, bounces, menuRef, onRequestCloseMenu: closeMenu, onClickedItem: _onClickedItem, }; } function ListItemRender(props: MessageThreadMemberListItemProps) { const { model } = props; const { id, name, avatar } = model; const {} = useI18nContext(); const { colors } = usePaletteContext(); const { getColor } = useColors({ t1: { light: colors.neutral[1], dark: colors.neutral[98], }, t2: { light: colors.neutral[5], dark: colors.neutral[6], }, count: { light: colors.primary[5], dark: colors.primary[6], }, }); const _onClicked = React.useCallback(() => { // onClickedItem?.(model); }, []); return ( {name === undefined || name.length === 0 ? id : name} {/* {isOwner === true ? ( {tr('_uikit_thread_owner')} ) : null} */} ); }