Skip to content

Commit

Permalink
Add global nft search (#46)
Browse files Browse the repository at this point in the history
* Add global nft search

* Rename feature

* Fix legal footer links
  • Loading branch information
nikitayutanov authored Jul 1, 2024
1 parent 08d2cf8 commit 5ee1f64
Show file tree
Hide file tree
Showing 17 changed files with 115 additions and 31 deletions.
3 changes: 1 addition & 2 deletions frontend/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CopyButton, FilterButton, LinkButton } from './buttons';
import { Form, Input, Checkbox, Radio, Select, Textarea } from './form';
import { withAccount, withApi, withMarketplaceConfig } from './hocs';
import { InfoCard, InfoCardProps } from './info-card';
import { PriceInput, SearchInput } from './inputs';
import { PriceInput } from './inputs';
import {
Container,
Footer,
Expand Down Expand Up @@ -34,7 +34,6 @@ export {
InfoCard,
NFTActionFormModal,
PriceInfoCard,
SearchInput,
FilterButton,
Breadcrumbs,
Form,
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/components/inputs/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { PriceInput } from './price-input';
import { SearchInput } from './search-input';

export { PriceInput, SearchInput };
export { PriceInput };
3 changes: 0 additions & 3 deletions frontend/src/components/inputs/search-input/index.ts

This file was deleted.

14 changes: 0 additions & 14 deletions frontend/src/components/inputs/search-input/search-input.tsx

This file was deleted.

6 changes: 1 addition & 5 deletions frontend/src/components/layout/footer/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ const LIST = [
},
{
heading: 'Legal',
links: [
{ text: 'Community Guidelines', href: '#' },
{ text: 'Terms', href: '#' },
{ text: 'Privacy policy', href: '#' },
],
links: [{ text: 'Privacy policy', href: 'https://vara.network/privacy-policy' }],
},
];

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/layout/header/header.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
}

.container,
.search,
.buttons,
.wallet {
display: flex;
Expand All @@ -19,6 +20,7 @@
gap: 12px;
}

.search,
.wallet {
gap: 24px;
}
6 changes: 5 additions & 1 deletion frontend/src/components/layout/header/header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useAccount } from '@gear-js/react-hooks';

import { ROUTE } from '@/consts';
import { NFTSearch } from '@/features/nft-search';
import { Wallet, AccountBalance } from '@/features/wallet';

import { LinkButton } from '../../buttons';
Expand All @@ -15,7 +16,10 @@ function Header() {
return (
<header className={styles.header}>
<Container className={styles.container}>
<Logo />
<div className={styles.search}>
<Logo />
<NFTSearch />
</div>

<div className={styles.wallet}>
<AccountBalance />
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/features/nft-search/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { NFTSearch } from './nft-search';

export { NFTSearch };
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { NFTSearch } from './nft-search';

export { NFTSearch };
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';

import { Input } from '@/components';
import { ROUTE } from '@/consts';

import SearchSVG from '../../assets/search.svg?react';
import { FIELD_NAME, SCHEMA } from '../../consts';
import { useNFTSearchParam } from '../../hooks';

function NFTSearch() {
const param = useNFTSearchParam();

const form = useForm({ values: { [FIELD_NAME.QUERY]: param.value }, resolver: zodResolver(SCHEMA) });
const navigate = useNavigate();

const handleSubmit = form.handleSubmit(({ query }) => {
const search = query ? param.set(query) : param.reset();

navigate({ pathname: ROUTE.NFTS, search });
});

return (
<FormProvider {...form}>
<form onSubmit={handleSubmit}>
<Input name={FIELD_NAME.QUERY} icon={SearchSVG} label="NFT name/Account address" size="small" />
</form>
</FormProvider>
);
}

export { NFTSearch };
11 changes: 11 additions & 0 deletions frontend/src/features/nft-search/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { z } from 'zod';

const FIELD_NAME = {
QUERY: 'query',
};

const SCHEMA = z.object({
[FIELD_NAME.QUERY]: z.string().trim(),
});

export { FIELD_NAME, SCHEMA };
24 changes: 24 additions & 0 deletions frontend/src/features/nft-search/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useSearchParams } from 'react-router-dom';

import { FIELD_NAME } from './consts';

function useNFTSearchParam() {
const [params] = useSearchParams();
const value = params.get(FIELD_NAME.QUERY) || '';

const set = (query: string) => {
params.set(FIELD_NAME.QUERY, query);

return params.toString();
};

const reset = () => {
params.delete(FIELD_NAME.QUERY);

return '';
};

return { value, set, reset };
}

export { useNFTSearchParam };
4 changes: 4 additions & 0 deletions frontend/src/features/nft-search/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { NFTSearch } from './components';
import { useNFTSearchParam } from './hooks';

export { NFTSearch, useNFTSearchParam };
4 changes: 2 additions & 2 deletions frontend/src/pages/lists/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ function useTotalNFTsCount(where: NftWhereInput) {
return [totalCount, isReady, refetch] as const;
}

function useNFTs(owner: string, collectionId?: string) {
const where = useMemo(() => getNftFilters(owner, collectionId), [owner, collectionId]);
function useNFTs(owner: string, collectionId: string | undefined, query?: string) {
const where = useMemo(() => getNftFilters(owner, collectionId, query), [owner, collectionId, query]);
const [totalCount, isTotalCountReady, refetchTotalNFTsCount] = useTotalNFTsCount(where);

const { data, isFetching, fetchNextPage, refetch } = useInfiniteQuery({
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/pages/lists/lists.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import CollectionCardSkeletonSVG from '@/features/collections/assets/collection-
import NFTCardSkeletonSVG from '@/features/collections/assets/nft-card-skeleton.svg?react';
import { AccountFilter, GridSize, useAccountFilter, useGridSize } from '@/features/lists';
import { GRID_SIZE } from '@/features/lists/consts';
import { useNFTSearchParam } from '@/features/nft-search';
import { cx } from '@/utils';

import { useCollections, useNFTs } from './hooks';
Expand All @@ -23,10 +24,16 @@ function Lists() {

const { gridSize, setGridSize } = useGridSize();
const { accountFilterValue, accountFilterAddress, setAccountFilterValue } = useAccountFilter();
const nftSearchParam = useNFTSearchParam();

const [collections, collectionsCount, hasMoreCollections, isCollectionsQueryReady, fetchCollections] =
useCollections(accountFilterAddress);
const [nfts, nftsCount, hasMoreNFTs, isNFTsQueryReady, fetchNFTs] = useNFTs(accountFilterAddress);

const [nfts, nftsCount, hasMoreNFTs, isNFTsQueryReady, fetchNFTs] = useNFTs(
accountFilterAddress,
undefined,
nftSearchParam.value,
);

const renderTabs = () =>
TABS.map(({ to, text }, index) => {
Expand Down
18 changes: 17 additions & 1 deletion frontend/src/pages/lists/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { decodeAddress } from '@gear-js/api';

import { CollectionWhereInput, NftWhereInput } from '@/graphql/graphql';

const getCollectionFilters = (admin: string) => (admin ? { admin_contains: admin } : {}) as CollectionWhereInput;

const getNftFilters = (owner: string | null, collectionId: string | undefined) => {
const getNftFilters = (owner: string | null, collectionId: string | undefined, query: string | undefined) => {
const where = {} as NftWhereInput;

if (owner) {
Expand All @@ -13,6 +15,20 @@ const getNftFilters = (owner: string | null, collectionId: string | undefined) =
where.collection = { id_eq: collectionId } as CollectionWhereInput;
}

if (query) {
try {
const accountAddress = decodeAddress(query);

if (owner) {
where.AND = [{ owner_eq: accountAddress } as NftWhereInput];
} else {
where.owner_eq = accountAddress;
}
} catch {
where.name_containsInsensitive = query;
}
}

return where;
};

Expand Down

0 comments on commit 5ee1f64

Please sign in to comment.