ProposalsV3List.tsx
1 import { Box, Paper, Skeleton, Stack } from '@mui/material'; 2 import { useState } from 'react'; 3 import InfiniteScroll from 'react-infinite-scroller'; 4 import { NoSearchResults } from 'src/components/NoSearchResults'; 5 import { Proposal, useProposals } from 'src/hooks/governance/useProposals'; 6 import { useProposalsSearch } from 'src/hooks/governance/useProposalsSearch'; 7 8 import { ProposalListHeader } from './ProposalListHeader'; 9 import { ProposalV3ListItem } from './ProposalV3ListItem'; 10 import { stringToState } from './StateBadge'; 11 import { VoteBar } from './VoteBar'; 12 13 export const ProposalsV3List = () => { 14 const [proposalFilter, setProposalFilter] = useState<string>('all'); 15 const filterState = stringToState(proposalFilter); 16 17 const [searchTerm, setSearchTerm] = useState<string>(''); 18 19 const { results: searchResults, loading: loadingSearchResults } = useProposalsSearch(searchTerm); 20 21 const { data, isFetching: loadingProposals, fetchNextPage, hasNextPage } = useProposals(); 22 23 let listItems: Proposal[] = []; 24 if (searchTerm && searchResults.length > 0) { 25 listItems = searchResults; 26 } 27 28 if (!searchTerm && data) { 29 data.pages.forEach((page) => listItems.push(...page.proposals)); 30 } 31 32 if (proposalFilter !== 'all') { 33 listItems = listItems.filter((proposal) => proposal.badgeState === filterState); 34 } 35 36 return ( 37 <Paper> 38 <ProposalListHeader 39 proposalFilter={proposalFilter} 40 handleProposalFilterChange={setProposalFilter} 41 handleSearchQueryChange={setSearchTerm} 42 /> 43 {listItems.length > 0 ? ( 44 <InfiniteScroll loadMore={() => fetchNextPage()} hasMore={hasNextPage}> 45 {listItems.map((proposal) => ( 46 <ProposalV3ListItem key={proposal.subgraphProposal.id} proposal={proposal} /> 47 ))} 48 {loadingProposals && 49 Array.from({ length: 5 }).map((_, i) => <ProposalListSkeleton key={i} />)} 50 </InfiniteScroll> 51 ) : ((!loadingSearchResults && searchTerm) || 52 (!loadingProposals && proposalFilter !== 'all')) && 53 listItems.length === 0 ? ( 54 <NoSearchResults searchTerm={searchTerm} /> 55 ) : ( 56 Array.from({ length: 4 }).map((_, i) => <ProposalListSkeleton key={i} />) 57 )} 58 </Paper> 59 ); 60 }; 61 62 const ProposalListSkeleton = () => { 63 return ( 64 <Box 65 sx={{ 66 p: 6, 67 display: 'flex', 68 flexWrap: 'wrap', 69 justifyContent: 'space-between', 70 borderBottom: (theme) => `1px solid ${theme.palette.divider}`, 71 }} 72 > 73 <Stack 74 direction="row" 75 sx={{ 76 width: { 77 xs: '100%', 78 lg: '70%', 79 }, 80 pr: { xs: 0, lg: 8 }, 81 display: 'flex', 82 flexDirection: 'column', 83 justifyContent: 'space-between', 84 }} 85 > 86 <Stack 87 direction="column" 88 sx={{ 89 width: { 90 xs: '100%', 91 lg: '70%', 92 }, 93 pr: { xs: 0, lg: 8 }, 94 display: 'flex', 95 flexDirection: 'column', 96 gap: { xs: 3, lg: 6 }, 97 }} 98 > 99 <Skeleton variant="rectangular" height={22} width={220} /> 100 <Skeleton variant="rectangular" height={24} width={350} /> 101 </Stack> 102 </Stack> 103 <Stack 104 flexGrow={1} 105 direction="column" 106 justifyContent="center" 107 sx={{ 108 pl: { xs: 0, lg: 18 }, 109 mt: { xs: 7, lg: 0 }, 110 }} 111 > 112 <VoteBar yae percent={0} votes={0} sx={{ mb: 4 }} loading /> 113 <VoteBar percent={0} votes={0} loading /> 114 </Stack> 115 </Box> 116 ); 117 };