Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(collection): add token id search #158

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ const SmallGridContainer = forwardRef<
SmallGridContainer.displayName = "SmallGridContainer";

interface CollectionItemsDataGridViewProps {
collectionTokens: CollectionToken[];
items: CollectionToken[];
viewType: ViewType;
onEndReached: () => void;
}

export default function CollectionItemsDataGridView({
collectionTokens,
items,
viewType,
onEndReached,
}: CollectionItemsDataGridViewProps) {
const components = {
List: viewType === "large-grid" ? LargeGridContainer : SmallGridContainer,
Expand All @@ -60,12 +62,14 @@ export default function CollectionItemsDataGridView({
return (
<div className="mb-6">
<VirtuosoGrid
initialItemCount={collectionTokens.length}
totalCount={collectionTokens.length}
initialTopMostItemIndex={0}
overscan={400}
totalCount={items.length}
useWindowScroll
components={components}
endReached={onEndReached}
itemContent={(index) => {
const token = collectionTokens[index];
const token = items[index];

if (!token) {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
"use client";

import { useMemo } from "react";
import { useSuspenseInfiniteQuery } from "@tanstack/react-query";
import { useEffect, useMemo } from "react";

import type { ViewType } from "../../../../components/view-type-toggle-group";
import type { ViewType } from "~/components/view-type-toggle-group";
import type {
CollectionSortBy,
CollectionSortDirection,
CollectionTokensApiResponse,
} from "~/lib/getCollectionTokens";
import type { CollectionToken, Filters } from "~/types";
import useInfiniteWindowScroll from "~/hooks/useInfiniteWindowScroll";
import { getCollectionTokens } from "~/lib/getCollectionTokens";
import useCollectionTokens from "~/hooks/useCollectionTokens";
import CollectionItemsDataGridView from "./collection-items-data-grid-view";
import CollectionItemsDataListView from "./collection-items-data-list-view";

Expand All @@ -21,59 +18,78 @@ interface CollectionItemsDataProps {
sortDirection: CollectionSortDirection;
viewType: ViewType;
filters: Filters;
searchQuery: string;
}

export default function CollectionItemsData({
function CollectionItemsData({
collectionAddress,
sortBy,
sortDirection,
viewType,
filters,
searchQuery,
}: CollectionItemsDataProps) {
const {
data: infiniteData,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useSuspenseInfiniteQuery({
queryKey: [
"collectionTokens",
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useCollectionTokens({
collectionAddress,
sortDirection,
sortBy,
collectionAddress,
filters,
],
refetchInterval: 10_000,
getNextPageParam: (lastPage: CollectionTokensApiResponse) =>
lastPage.next_page,
initialPageParam: undefined as number | undefined,
queryFn: ({ pageParam }) =>
getCollectionTokens({
collectionAddress,
page: pageParam,
sortDirection,
sortBy,
filters,
}),
});

useInfiniteWindowScroll({
fetchNextPage,
hasNextPage: !!hasNextPage,
isFetchingNextPage,
});
});

const collectionTokens: CollectionToken[] = useMemo(
() => infiniteData.pages.flatMap((page) => page.data),
[infiniteData],
const items: CollectionToken[] = useMemo(
() =>
data.pages
.flatMap((page) => page.data)
.filter((token) => token.token_id.includes(searchQuery)),
[data.pages, searchQuery],
);

console.log("isFetchingNextPage", isFetchingNextPage);

useEffect(() => {
if (items.length > 0 || !hasNextPage || isFetchingNextPage) {
return;
}

const run = async () => {
console.log("fetchNextPage 1");
await fetchNextPage();
};

void run();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [items]);

const handleEndReached = async () => {
if (isFetchingNextPage || !hasNextPage) {
return;
}

console.log("fetchNextPage 2");
await fetchNextPage();
};

if (items.length === 0) {
return (
<div className="p-5">
<div className="text-xl font-semibold">No items found.</div>
<div className="mb-5 text-muted-foreground">
Try updating your search criteria to find what you're looking for.
</div>
</div>
);
}

return viewType === "list" ? (
<CollectionItemsDataListView collectionTokens={collectionTokens} />
<CollectionItemsDataListView collectionTokens={items} />
) : (
<CollectionItemsDataGridView
collectionTokens={collectionTokens}
items={items}
onEndReached={handleEndReached}
viewType={viewType}
/>
);
}

export default CollectionItemsData;
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import ViewTypeToggleButton from "../../../../components/view-type-toggle-button
import ViewTypeToggleGroup from "../../../../components/view-type-toggle-group";
import CollectionItemsSortingSelect from "./collection-item-sorting-select";

// import LiveResultsIndicator from "./live-results-indicator";

interface CollectionItemsToolbarProps {
setSortBy: (sortBy: CollectionSortBy) => void;
setSortDirection: (sortDirection: CollectionSortDirection) => void;
Expand All @@ -27,11 +25,11 @@ interface CollectionItemsToolbarProps {
toggleFiltersPanel: () => void;
viewType: ViewType;
setViewType: (viewType: ViewType) => void;
totalTokensCount: number;
filtersPanelOpen: boolean;
filtersDialogOpen: boolean;
openFiltersDialog: () => void;
filtersCount: number;
searchQuery: string;
setSearchQuery: (query: string) => void;
}

export default function CollectionItemsToolbar({
Expand All @@ -42,11 +40,11 @@ export default function CollectionItemsToolbar({
toggleFiltersPanel,
viewType,
setViewType,
// totalTokensCount,
filtersPanelOpen,
// filtersDialogOpen,
openFiltersDialog,
filtersCount,
searchQuery,
setSearchQuery,
}: CollectionItemsToolbarProps) {
const isDesktop = useMediaQuery("(min-width: 1024px)");

Expand All @@ -67,8 +65,13 @@ export default function CollectionItemsToolbar({
</span>
)}
</Button>
{/* <LiveResultsIndicator totalCount={totalTokensCount} /> */}
<SearchInput className="flex-1" placeholder="Search item" />
<SearchInput
className="flex-1"
placeholder="Search item"
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.currentTarget.value)}
/>
<CollectionItemsSortingSelect
className="hidden lg:block"
sortBy={sortBy}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { Suspense, useCallback, useEffect, useState } from "react";
import { parseAsJson, useQueryState } from "nuqs";
import { useDebounceValue } from "usehooks-ts";

import type { ViewType } from "~/components/view-type-toggle-group";
import type { Filters } from "~/types";
Expand Down Expand Up @@ -29,10 +30,11 @@ const isValidViewType = (value: string): value is ViewType =>

export default function CollectionItems({
collectionAddress,
collectionTokenCount,
}: CollectionProps) {
const [filtersPanelOpen, setFiltersPanelOpen] = useState(false);
const [filtersDialogOpen, setFiltersDialogOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
const [debouncedSearchQuery] = useDebounceValue(searchQuery, 500);
const [viewType, setViewType] = useState<ViewType>("large-grid");
const [sortDirection, setSortDirection] = useQueryState(
collectionSortDirectionKey,
Expand Down Expand Up @@ -125,12 +127,12 @@ export default function CollectionItems({
setSortBy={setSortBy}
viewType={viewType}
setViewType={handleViewTypeChange}
totalTokensCount={collectionTokenCount}
toggleFiltersPanel={toggleFiltersPanel}
filtersPanelOpen={filtersPanelOpen}
openFiltersDialog={() => setFiltersDialogOpen(true)}
filtersDialogOpen={filtersDialogOpen}
filtersCount={filtersCount}
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
/>
<CollectionItemsFiltersTraits
traits={filters.traits}
Expand All @@ -147,6 +149,7 @@ export default function CollectionItems({
sortBy={sortBy}
viewType={viewType}
filters={filters}
searchQuery={debouncedSearchQuery}
/>
</Suspense>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
import { Ethereum } from "@ark-market/ui/icons";
"use client";

import { ETH } from "~/constants/tokens";
import useBalance from "~/hooks/useBalance";
import { parseEther } from "viem";

import { Skeleton } from "@ark-market/ui/skeleton";

import usePortfolioStats from "~/hooks/usePortfolioStats";
import usePrices from "~/hooks/usePrices";

interface PortfolioValueProps {
address?: string;
address: string;
}

export default function PortfolioValue({ address }: PortfolioValueProps) {
const { data: ethBalance, isPending } = useBalance({ address, token: ETH });
const { convertInUsd, isLoading: isLoadingPrices } = usePrices();
const ethBalanceInUsd = convertInUsd({ amount: ethBalance?.value });
const { data } = usePortfolioStats({ address });
const { convertInUsd, isLoading } = usePrices();

if (isLoadingPrices || isPending) {
return null;
}
const totalValueInUsd = convertInUsd({
amount: parseEther(data.total_value),
});

return (
<div className="flex rounded-lg bg-card px-2.5 py-2">
<div className="flex items-center gap-1">
<div className="flex flex-col">
<p className="text-sm text-secondary-foreground">Portfolio value</p>
<p className="text-md flex items-center space-x-1.5 font-semibold">
<Ethereum />
<div className="text-xl">
{ethBalance?.rounded}{" "}
<span className="text-secondary-foreground">ETH</span>
<div className="flex h-16 min-w-64 items-center rounded-lg bg-card px-3.5">
<div className="flex w-full flex-col gap-1">
<div className="text-sm leading-none text-secondary-foreground">
Portfolio value
</div>
<div className="flex items-end justify-between gap-5">
<div className="text-md flex gap-1 text-lg font-semibold leading-none">
{data.total_value}{" "}
<div className="text-secondary-foreground">ETH</div>
</div>
{isLoading ? (
<Skeleton className="h-4 w-20" />
) : (
<div className="leading-none text-muted-foreground">
${totalValueInUsd}
</div>
</p>
)}
</div>
</div>

<div className="ml-10 hidden items-end sm:flex">
<span className="text-muted-foreground">${ethBalanceInUsd}</span>
</div>
</div>
);
}
54 changes: 54 additions & 0 deletions apps/arkmarket/src/hooks/useCollectionTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useSuspenseInfiniteQuery } from "@tanstack/react-query";

import type {
CollectionSortBy,
CollectionSortDirection,
CollectionTokensApiResponse,
} from "~/lib/getCollectionTokens";
import type { Filters } from "~/types";
import { getCollectionTokens } from "~/lib/getCollectionTokens";

interface UseCollectionTokensParams {
collectionAddress: string;
sortDirection: CollectionSortDirection;
sortBy: CollectionSortBy;
filters: Filters;
}

export default function useCollectionTokens({
collectionAddress,
sortDirection,
sortBy,
filters,
}: UseCollectionTokensParams) {
const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } =
useSuspenseInfiniteQuery({
refetchInterval: 10_000,
initialPageParam: undefined,
queryKey: [
"collectionTokens",
collectionAddress,
sortDirection,
sortBy,
filters,
],
queryFn: ({ pageParam }) =>
getCollectionTokens({
collectionAddress,
page: pageParam,
sortDirection,
sortBy,
filters,
}),
getNextPageParam: (lastPage: CollectionTokensApiResponse) =>
lastPage.next_page,
});

return {
data,
fetchNextPage,
hasNextPage,
isFetching,
isFetchingNextPage,
};
}
Loading
Loading