import React, {type JSX, useCallback} from 'react' import { Dimensions, type GestureResponderEvent, type StyleProp, View, type ViewStyle, } from 'react-native' import {type AppBskyGraphDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {cleanError} from '#/lib/strings/errors' import {logger} from '#/logger' import {useModalControls} from '#/state/modals' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useListMembersQuery} from '#/state/queries/list-members' import {useSession} from '#/state/session' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' import {List, type ListRef} from '#/view/com/util/List' import {ProfileCardFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonText} from '#/components/Button' import {ListFooter} from '#/components/Lists' import * as ProfileCard from '#/components/ProfileCard' import type * as bsky from '#/types/bsky' const LOADING_ITEM = {_reactKey: '__loading__'} const EMPTY_ITEM = {_reactKey: '__empty__'} const ERROR_ITEM = {_reactKey: '__error__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} export function ListMembers({ list, style, scrollElRef, onScrolledDownChange, onPressTryAgain, renderHeader, renderEmptyState, testID, headerOffset = 0, desktopFixedHeightOffset, }: { list: string style?: StyleProp scrollElRef?: ListRef onScrolledDownChange: (isScrolledDown: boolean) => void onPressTryAgain?: () => void renderHeader: () => JSX.Element renderEmptyState: () => JSX.Element testID?: string headerOffset?: number desktopFixedHeightOffset?: number }) { const t = useTheme() const {_} = useLingui() const [isRefreshing, setIsRefreshing] = React.useState(false) const {openModal} = useModalControls() const {currentAccount} = useSession() const moderationOpts = useModerationOpts() const { data, isFetching, isFetched, isError, error, refetch, fetchNextPage, hasNextPage, isFetchingNextPage, } = useListMembersQuery(list) const isEmpty = !isFetching && !data?.pages[0].items.length const isOwner = currentAccount && data?.pages[0].list.creator.did === currentAccount.did const items = React.useMemo(() => { let items: any[] = [] if (isFetched) { if (isEmpty && isError) { items = items.concat([ERROR_ITEM]) } if (isEmpty) { items = items.concat([EMPTY_ITEM]) } else if (data) { for (const page of data.pages) { items = items.concat(page.items) } } if (!isEmpty && isError) { items = items.concat([LOAD_MORE_ERROR_ITEM]) } } else if (isFetching) { items = items.concat([LOADING_ITEM]) } return items }, [isFetched, isEmpty, isError, data, isFetching]) // events // = const onRefresh = React.useCallback(async () => { setIsRefreshing(true) try { await refetch() } catch (err) { logger.error('Failed to refresh lists', {message: err}) } setIsRefreshing(false) }, [refetch, setIsRefreshing]) const onEndReached = React.useCallback(async () => { if (isFetching || !hasNextPage || isError) return try { await fetchNextPage() } catch (err) { logger.error('Failed to load more lists', {message: err}) } }, [isFetching, hasNextPage, isError, fetchNextPage]) const onPressRetryLoadMore = React.useCallback(() => { fetchNextPage() }, [fetchNextPage]) const onPressEditMembership = React.useCallback( (e: GestureResponderEvent, profile: bsky.profile.AnyProfileView) => { e.preventDefault() openModal({ name: 'user-add-remove-lists', subject: profile.did, displayName: profile.displayName || profile.handle, handle: profile.handle, }) }, [openModal], ) // rendering // = const renderItem = React.useCallback( ({item}: {item: any}) => { if (item === EMPTY_ITEM) { return renderEmptyState() } else if (item === ERROR_ITEM) { return ( ) } else if (item === LOAD_MORE_ERROR_ITEM) { return ( ) } else if (item === LOADING_ITEM) { return } const profile = (item as AppBskyGraphDefs.ListItemView).subject if (!moderationOpts) return null return ( {isOwner && ( )} ) }, [ renderEmptyState, error, onPressTryAgain, onPressRetryLoadMore, moderationOpts, isOwner, onPressEditMembership, _, t, ], ) const renderFooter = useCallback(() => { if (isEmpty) return null return ( ) }, [ hasNextPage, error, isFetchingNextPage, fetchNextPage, isEmpty, headerOffset, ]) return ( item.subject?.did || item._reactKey} renderItem={renderItem} ListHeaderComponent={!isEmpty ? renderHeader : undefined} ListFooterComponent={renderFooter} refreshing={isRefreshing} onRefresh={onRefresh} headerOffset={headerOffset} contentContainerStyle={{ minHeight: Dimensions.get('window').height * 1.5, }} onScrolledDownChange={onScrolledDownChange} onEndReached={onEndReached} onEndReachedThreshold={0.6} removeClippedSubviews={true} desktopFixedHeight={desktopFixedHeightOffset || true} /> ) }