From 2c7ae5c85c06f02aa4ad7d5bcfdece6b9ad752f5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Jul 2025 19:57:09 +0000 Subject: [PATCH 1/3] Add pagination, search, and loading state to AllSupportedWallets component Co-authored-by: joaquim.verges --- .../components/others/AllSupportedWallets.tsx | 213 ++++++++++++++++-- 1 file changed, 191 insertions(+), 22 deletions(-) diff --git a/apps/portal/src/components/others/AllSupportedWallets.tsx b/apps/portal/src/components/others/AllSupportedWallets.tsx index c9982d4c726..9e994971ca7 100644 --- a/apps/portal/src/components/others/AllSupportedWallets.tsx +++ b/apps/portal/src/components/others/AllSupportedWallets.tsx @@ -1,4 +1,7 @@ +"use client"; + import Image from "next/image"; +import { useState, useEffect, useMemo } from "react"; import { getAllWalletsList, getWalletInfo, @@ -6,6 +9,9 @@ import { } from "thirdweb/wallets"; import { DocLink, InlineCode } from "../Document"; import { Table, TBody, Td, Th, Tr } from "../Document/Table"; +import { Input } from "../ui/input"; +import { Button } from "../ui/button"; +import { ChevronLeftIcon, ChevronRightIcon, SearchIcon } from "lucide-react"; const specialWallets: { [key in WalletId]?: boolean; @@ -14,39 +20,202 @@ const specialWallets: { smart: true, }; -export async function AllSupportedWallets() { - const wallets = await getAllWalletsList(); +const ITEMS_PER_PAGE = 20; - return ( - - - - - - - - {wallets +interface WalletInfo { + id: string; + name: string; +} + +export function AllSupportedWallets() { + const [wallets, setWallets] = useState([]); + const [loading, setLoading] = useState(true); + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + + useEffect(() => { + async function loadWallets() { + try { + const allWallets = await getAllWalletsList(); + const filteredWallets = allWallets .filter((w) => !(w.id in specialWallets)) - .map((w) => { - return ( - + .map((w) => ({ + id: w.id, + name: w.name, + })); + setWallets(filteredWallets); + } catch (error) { + console.error("Failed to load wallets:", error); + } finally { + setLoading(false); + } + } + + loadWallets(); + }, []); + + const filteredWallets = useMemo(() => { + if (!searchQuery) return wallets; + + const query = searchQuery.toLowerCase(); + return wallets.filter( + (wallet) => + wallet.name.toLowerCase().includes(query) || + wallet.id.toLowerCase().includes(query) + ); + }, [wallets, searchQuery]); + + const totalPages = Math.ceil(filteredWallets.length / ITEMS_PER_PAGE); + const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; + const endIndex = startIndex + ITEMS_PER_PAGE; + const currentWallets = filteredWallets.slice(startIndex, endIndex); + + // Reset to first page when search changes + useEffect(() => { + setCurrentPage(1); + }, [searchQuery]); + + const handlePreviousPage = () => { + setCurrentPage((prev) => Math.max(prev - 1, 1)); + }; + + const handleNextPage = () => { + setCurrentPage((prev) => Math.min(prev + 1, totalPages)); + }; + + const handlePageClick = (page: number) => { + setCurrentPage(page); + }; + + if (loading) { + return ( +
+
Loading wallets...
+
+ ); + } + + return ( +
+ {/* Search Input */} +
+ + setSearchQuery(e.target.value)} + className="pl-10" + /> +
+ + {/* Results count */} +
+ {filteredWallets.length === wallets.length + ? `Showing ${filteredWallets.length} wallets` + : `Found ${filteredWallets.length} of ${wallets.length} wallets`} +
+ + {/* Table */} +
Wallet ID
+ + + + + + + {currentWallets.length === 0 ? ( + + + + ) : ( + currentWallets.map((wallet) => ( + - ); - })} - -
WalletID
+ {searchQuery ? "No wallets found matching your search." : "No wallets available."} +
- - {w.name} + + {wallet.name} - +
+ )) + )} + + + + {/* Pagination */} + {totalPages > 1 && ( +
+
+ Page {currentPage} of {totalPages} + {filteredWallets.length > 0 && ( + + (showing {startIndex + 1}-{Math.min(endIndex, filteredWallets.length)} of {filteredWallets.length}) + + )} +
+ +
+ + + {/* Page numbers */} +
+ {Array.from({ length: Math.min(5, totalPages) }, (_, i) => { + let pageNumber; + + if (totalPages <= 5) { + pageNumber = i + 1; + } else if (currentPage <= 3) { + pageNumber = i + 1; + } else if (currentPage >= totalPages - 2) { + pageNumber = totalPages - 4 + i; + } else { + pageNumber = currentPage - 2 + i; + } + + return ( + + ); + })} +
+ + +
+
+ )} + ); } From 90dea67670b3ce0cde448f84889caf9e352d098d Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Fri, 25 Jul 2025 20:19:49 +1200 Subject: [PATCH 2/3] fixes --- .../components/others/AllSupportedWallets.tsx | 121 ++++++++++-------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/apps/portal/src/components/others/AllSupportedWallets.tsx b/apps/portal/src/components/others/AllSupportedWallets.tsx index 9e994971ca7..de8bcf00459 100644 --- a/apps/portal/src/components/others/AllSupportedWallets.tsx +++ b/apps/portal/src/components/others/AllSupportedWallets.tsx @@ -1,17 +1,23 @@ "use client"; +import { + QueryClient, + QueryClientProvider, + useQuery, +} from "@tanstack/react-query"; +import { ChevronLeftIcon, ChevronRightIcon, SearchIcon } from "lucide-react"; import Image from "next/image"; -import { useState, useEffect, useMemo } from "react"; +import { useMemo, useState } from "react"; import { getAllWalletsList, getWalletInfo, type WalletId, } from "thirdweb/wallets"; -import { DocLink, InlineCode } from "../Document"; +import { DocLink } from "../Document/DocLink"; +import { InlineCode } from "../Document/InlineCode"; import { Table, TBody, Td, Th, Tr } from "../Document/Table"; -import { Input } from "../ui/input"; import { Button } from "../ui/button"; -import { ChevronLeftIcon, ChevronRightIcon, SearchIcon } from "lucide-react"; +import { Input } from "../ui/input"; const specialWallets: { [key in WalletId]?: boolean; @@ -22,46 +28,44 @@ const specialWallets: { const ITEMS_PER_PAGE = 20; -interface WalletInfo { - id: string; - name: string; -} +const queryClient = new QueryClient(); export function AllSupportedWallets() { - const [wallets, setWallets] = useState([]); - const [loading, setLoading] = useState(true); + return ( + + + + ); +} + +function AllSupportedWalletsContent() { const [searchQuery, setSearchQuery] = useState(""); const [currentPage, setCurrentPage] = useState(1); - useEffect(() => { - async function loadWallets() { - try { - const allWallets = await getAllWalletsList(); - const filteredWallets = allWallets - .filter((w) => !(w.id in specialWallets)) - .map((w) => ({ - id: w.id, - name: w.name, - })); - setWallets(filteredWallets); - } catch (error) { - console.error("Failed to load wallets:", error); - } finally { - setLoading(false); - } - } - - loadWallets(); - }, []); + const { data: wallets, isLoading: loading } = useQuery({ + queryKey: ["allWalletsList"], + queryFn: async () => { + const allWallets = await getAllWalletsList(); + return allWallets + .filter((w) => !(w.id in specialWallets)) + .map((w) => ({ + id: w.id, + name: w.name, + })); + }, + staleTime: 1000 * 60 * 5, // 5 minutes + }); const filteredWallets = useMemo(() => { - if (!searchQuery) return wallets; - + if (!searchQuery) return wallets || []; + if (!wallets) return []; + + setCurrentPage(1); const query = searchQuery.toLowerCase(); return wallets.filter( (wallet) => wallet.name.toLowerCase().includes(query) || - wallet.id.toLowerCase().includes(query) + wallet.id.toLowerCase().includes(query), ); }, [wallets, searchQuery]); @@ -70,11 +74,6 @@ export function AllSupportedWallets() { const endIndex = startIndex + ITEMS_PER_PAGE; const currentWallets = filteredWallets.slice(startIndex, endIndex); - // Reset to first page when search changes - useEffect(() => { - setCurrentPage(1); - }, [searchQuery]); - const handlePreviousPage = () => { setCurrentPage((prev) => Math.max(prev - 1, 1)); }; @@ -99,7 +98,7 @@ export function AllSupportedWallets() {
{/* Search Input */}
- + - {filteredWallets.length === wallets.length + {filteredWallets.length === wallets?.length ? `Showing ${filteredWallets.length} wallets` - : `Found ${filteredWallets.length} of ${wallets.length} wallets`} + : `Found ${filteredWallets.length} of ${wallets?.length} wallets`}
{/* Table */} @@ -126,8 +125,13 @@ export function AllSupportedWallets() { {currentWallets.length === 0 ? ( - - {searchQuery ? "No wallets found matching your search." : "No wallets available."} + + {searchQuery + ? "No wallets found matching your search." + : "No wallets available."} ) : ( @@ -155,14 +159,16 @@ export function AllSupportedWallets() { {totalPages > 1 && (
- Page {currentPage} of {totalPages} + Page {currentPage} of {totalPages} {filteredWallets.length > 0 && ( - (showing {startIndex + 1}-{Math.min(endIndex, filteredWallets.length)} of {filteredWallets.length}) + (showing {startIndex + 1}- + {Math.min(endIndex, filteredWallets.length)} of{" "} + {filteredWallets.length}) )}
- +
{/* Page numbers */}
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => { - let pageNumber; - + let pageNumber: number; + if (totalPages <= 5) { pageNumber = i + 1; } else if (currentPage <= 3) { @@ -210,7 +216,7 @@ export function AllSupportedWallets() { disabled={currentPage === totalPages} > Next - +
@@ -219,8 +225,19 @@ export function AllSupportedWallets() { ); } -async function WalletImage(props: { id: WalletId }) { - const img = await getWalletInfo(props.id, true); +function WalletImage(props: { id: WalletId }) { + const { data: img } = useQuery({ + queryKey: ["wallet-image", props.id], + queryFn: () => getWalletInfo(props.id, true), + staleTime: 1000 * 60 * 60 * 24, // 24 hours + }); + + if (!img) { + return ( +
+ ); + } + return ( ); From f0424ad8a239ff444137a0d0cd6ca9418b073cb1 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Fri, 25 Jul 2025 20:52:44 +1200 Subject: [PATCH 3/3] fix --- apps/portal/src/components/others/AllSupportedWallets.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/portal/src/components/others/AllSupportedWallets.tsx b/apps/portal/src/components/others/AllSupportedWallets.tsx index de8bcf00459..81150e640f7 100644 --- a/apps/portal/src/components/others/AllSupportedWallets.tsx +++ b/apps/portal/src/components/others/AllSupportedWallets.tsx @@ -125,10 +125,7 @@ function AllSupportedWalletsContent() { {currentWallets.length === 0 ? ( - + {searchQuery ? "No wallets found matching your search." : "No wallets available."}