Skip to content

Commit

Permalink
Merge pull request #477 from liteflow-labs/collection-and-trait-filte…
Browse files Browse the repository at this point in the history
…r-improvement

Collection and trait filter improvement
  • Loading branch information
NicolasMahe authored Nov 21, 2023
2 parents 5e8d209 + eea86a2 commit 24144c5
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 202 deletions.
50 changes: 33 additions & 17 deletions components/Filter/FilterAsset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Button,
Checkbox,
CheckboxGroup,
Divider,
Flex,
Heading,
Stack,
Expand All @@ -15,7 +16,7 @@ import {
} from '@chakra-ui/react'
import { NextPage } from 'next'
import useTranslation from 'next-translate/useTranslation'
import { useCallback, useEffect, useState } from 'react'
import { useCallback, useEffect, useMemo } from 'react'
import { FormProvider, useForm } from 'react-hook-form'
import { Filter, OfferFilter } from '../../hooks/useAssetFilterFromQuery'
import useEnvironment from '../../hooks/useEnvironment'
Expand Down Expand Up @@ -85,15 +86,28 @@ const FilterAsset: NextPage<Props> = ({
} = formValues
const filterResult = watch()

const collection = useMemo(() => {
if (currentCollection) {
return {
chainId: currentCollection.chainId,
address: currentCollection.address,
}
}
if (filterResult.collection) {
const [chainId, address] = filterResult.collection.split('-')
if (!chainId || !address) return
return {
chainId: parseInt(chainId, 10),
address,
}
}
}, [currentCollection, filterResult.collection])

useEffect(() => {
reset(filter)
return () => reset(NoFilter)
}, [reset, filter])

const [collection, setCollection] = useState<
{ chainId: number; address: string } | undefined
>(currentCollection)

const propagateFilter = useCallback(
(data: Partial<Filter> = {}) =>
onFilterChange({ ...filterResult, ...data }),
Expand Down Expand Up @@ -196,18 +210,20 @@ const FilterAsset: NextPage<Props> = ({
formValues={formValues}
onFilterChange={propagateFilter}
/>
<FilterByCollection
formValues={formValues}
selectedCollection={currentCollection}
onCollectionChange={setCollection}
onFilterChange={propagateFilter}
/>
<FilterByTrait
collection={collection}
filter={filter}
formValues={formValues}
onFilterChange={onFilterChange}
/>
{!currentCollection && (
<FilterByCollection
formValues={formValues}
onFilterChange={propagateFilter}
/>
)}
{currentCollection && <Divider mb={4} />}
{collection && (
<FilterByTrait
collection={collection}
formValues={formValues}
onFilterChange={onFilterChange}
/>
)}
</FormProvider>
</Accordion>
</Stack>
Expand Down
163 changes: 85 additions & 78 deletions components/Filter/FilterBy/Collection.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,58 @@
import { NetworkStatus } from '@apollo/client'
import {
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Button,
Heading,
SkeletonCircle,
SkeletonText,
Stack,
Text,
VStack,
useToast,
} from '@chakra-ui/react'
import useTranslation from 'next-translate/useTranslation'
import { FC, useMemo } from 'react'
import { FC, useCallback, useMemo, useState } from 'react'
import { UseFormReturn } from 'react-hook-form'
import { concatToQuery } from '../../../concat'
import {
CollectionFilter,
StringFilter,
useSearchCollectionQuery,
} from '../../../graphql'
import { Filter } from '../../../hooks/useAssetFilterFromQuery'
import { formatError } from '../../../utils'
import CollectionListItem from '../../Collection/ListItem'
import List, { ListItem } from '../../List/List'
import SearchInput from '../../SearchInput'

type Props = {
formValues: UseFormReturn<Filter, any, undefined>
selectedCollection?: {
chainId: number
address: string
name: string
image: string | null
floorPrice: {
valueInRef: string
refCode: string
} | null
totalVolume: {
valueInRef: string
refCode: string
}
}
onCollectionChange: (data?: { chainId: number; address: string }) => void
onFilterChange: (data?: Partial<Filter>) => void
}

const PAGINATION_LIMIT = 8

const FilterByCollection: FC<Props> = ({
formValues: { setValue, watch },
selectedCollection,
onCollectionChange,
onFilterChange,
}) => {
const { t } = useTranslation('components')
const toast = useToast()
const [offset, setOffset] = useState(0)

const filterResult = watch()

const { data: collectionData } = useSearchCollectionQuery({
const {
data: collectionData,
fetchMore,
networkStatus,
} = useSearchCollectionQuery({
variables: {
limit: 8,
offset: 0,
offset: 0, // the offset change must be done when calling the fetchMore function to concat queries' results
limit: PAGINATION_LIMIT,
filter: {
name: {
includesInsensitive: filterResult.collectionSearch || '',
Expand All @@ -66,64 +62,59 @@ const FilterByCollection: FC<Props> = ({
: {}),
} as CollectionFilter,
},
skip: !!selectedCollection,
ssr: false,
notifyOnNetworkStatusChange: true,
})
const collections = collectionData?.collections?.nodes
const hasNextPage = collectionData?.collections?.pageInfo.hasNextPage

const collection = useMemo(() => {
if (selectedCollection) return selectedCollection
if (!filterResult.collection) {
onCollectionChange(undefined)
return
const loadMore = useCallback(async () => {
const newOffset = offset + PAGINATION_LIMIT
try {
await fetchMore({
variables: { offset: newOffset },
updateQuery: concatToQuery('collections'),
})
setOffset(newOffset)
} catch (e) {
toast({
title: formatError(e),
status: 'error',
})
}
}, [fetchMore, offset, toast])

const collection = useMemo(() => {
if (!filterResult.collection) return
const [chainId, address] = filterResult.collection.split('-')
if (!chainId || !address) return
const collection = collections?.find(
(x) => x.address === address && x.chainId === parseInt(chainId, 10),
)
onCollectionChange(
collection ? { chainId: collection.chainId, address } : undefined,
)
return collection
}, [
collections,
filterResult.collection,
selectedCollection,
onCollectionChange,
])
}, [collections, filterResult.collection])

return collection ? (
<AccordionItem>
<AccordionButton>
<Heading variant="heading2" flex="1" textAlign="left">
{selectedCollection
? t('filters.assets.properties.label')
: t('filters.assets.properties.labelWithCollection')}
{t('filters.assets.properties.labelWithCollection')}
</Heading>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>
<Stack spacing={3}>
{!selectedCollection && (
<CollectionListItem
width="full"
borderColor="brand.500"
bgColor="brand.50"
borderWidth="1px"
borderRadius="md"
padding={2}
textAlign="left"
cursor="pointer"
onClick={() => onFilterChange({ collection: null, traits: [] })}
collection={collection}
closable
/>
)}
<SearchInput
placeholder={t('filters.assets.properties.search.placeholder')}
name="propertySearch"
onReset={() => setValue('propertySearch', '')}
<CollectionListItem
width="full"
borderColor="brand.500"
bgColor="brand.50"
borderWidth="1px"
borderRadius="md"
padding={2}
textAlign="left"
cursor="pointer"
onClick={() => onFilterChange({ collection: null, traits: [] })}
collection={collection}
closable
/>
</Stack>
</AccordionPanel>
Expand All @@ -145,7 +136,7 @@ const FilterByCollection: FC<Props> = ({
/>
<List>
{!collections ? (
new Array(8)
new Array(PAGINATION_LIMIT)
.fill(0)
.map((_, index) => (
<ListItem
Expand All @@ -158,24 +149,40 @@ const FilterByCollection: FC<Props> = ({
/>
))
) : collections.length > 0 ? (
collections.map((collection) => (
<CollectionListItem
key={`${collection.chainId}-${collection.address}`}
cursor={'pointer'}
rounded="xl"
transition={'background-color 0.3s ease-in-out'}
_hover={{
bgColor: 'brand.50',
}}
onClick={() =>
onFilterChange({
collection: `${collection.chainId}-${collection.address}`,
traits: [],
})
}
collection={collection}
/>
))
<>
{collections.map((collection) => (
<CollectionListItem
key={`${collection.chainId}-${collection.address}`}
cursor={'pointer'}
rounded="xl"
transition={'background-color 0.3s ease-in-out'}
_hover={{
bgColor: 'brand.50',
}}
onClick={() =>
onFilterChange({
collection: `${collection.chainId}-${collection.address}`,
traits: [],
})
}
collection={collection}
/>
))}
{hasNextPage && (
<Button
size="sm"
variant="outline"
isLoading={networkStatus === NetworkStatus.fetchMore}
onClick={loadMore}
width="fit-content"
mx="auto"
>
<Text as="span" isTruncated>
{t('filters.collections.more')}
</Text>
</Button>
)}
</>
) : (
<VStack spacing={1}>
<Text variant="subtitle2" color="gray.800" as="span">
Expand Down
Loading

0 comments on commit 24144c5

Please sign in to comment.