/** * Users tab - user list with infinite scroll * Query keys include selectedServerId for proper cache isolation per media server */ import { View, FlatList, RefreshControl, Pressable, ActivityIndicator } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useInfiniteQuery } from '@tanstack/react-query'; import { useRouter } from 'expo-router'; import { api } from '@/lib/api'; import { useMediaServer } from '@/providers/MediaServerProvider'; import { Text } from '@/components/ui/text'; import { Card } from '@/components/ui/card'; import { UserAvatar } from '@/components/ui/user-avatar'; import { cn } from '@/lib/utils'; import { colors } from '@/lib/theme'; import type { ServerUserWithIdentity } from '@tracearr/shared'; const PAGE_SIZE = 50; function TrustScoreBadge({ score }: { score: number }) { const variant = score < 50 ? 'destructive' : score < 75 ? 'warning' : 'success'; return ( {score} ); } function UserCard({ user, onPress }: { user: ServerUserWithIdentity; onPress: () => void }) { return ( {user.username} {user.role === 'owner' ? 'Owner' : 'User'} ); } export default function UsersScreen() { const router = useRouter(); const { selectedServerId } = useMediaServer(); const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch, isRefetching, } = useInfiniteQuery({ queryKey: ['users', selectedServerId], queryFn: ({ pageParam = 1 }) => api.users.list({ page: pageParam, pageSize: PAGE_SIZE, serverId: selectedServerId ?? undefined }), initialPageParam: 1, getNextPageParam: (lastPage: { page: number; totalPages: number }) => { if (lastPage.page < lastPage.totalPages) { return lastPage.page + 1; } return undefined; }, }); // Flatten all pages into single array const users = data?.pages.flatMap((page) => page.data) || []; const total = data?.pages[0]?.total || 0; const handleEndReached = () => { if (hasNextPage && !isFetchingNextPage) { void fetchNextPage(); } }; return ( item.id} renderItem={({ item }) => ( router.push(`/user/${item.id}` as never)} /> )} contentContainerClassName="p-4 pt-3" onEndReached={handleEndReached} onEndReachedThreshold={0.5} refreshControl={ } ListHeaderComponent={ Users {total} {total === 1 ? 'user' : 'users'} } ListFooterComponent={ isFetchingNextPage ? ( ) : null } ListEmptyComponent={ 0 No Users Users will appear here after syncing with your media server } /> ); }