From 3d3a585f684774e4ed3cc215157402181b9a8f73 Mon Sep 17 00:00:00 2001 From: alanrsoares Date: Mon, 8 Jan 2024 18:41:02 -0300 Subject: [PATCH 1/3] feat: add pagination to my interchain tokens page --- apps/maestro/src/config/app.ts | 8 --- apps/maestro/src/config/env.ts | 3 +- .../RecentTransactionsTable.tsx | 71 ++++++++++++------- .../axelarscan/getCosmosChainConfigs.ts | 5 +- .../interchainToken/getMyInterchainTokens.ts | 19 ++++- .../TokenDetailsSection.tsx | 2 +- .../pages/InterchainTokensPage/TokenList.tsx | 64 +++++++++++++++-- 7 files changed, 129 insertions(+), 43 deletions(-) diff --git a/apps/maestro/src/config/app.ts b/apps/maestro/src/config/app.ts index 5ba8470c3..2470619d8 100644 --- a/apps/maestro/src/config/app.ts +++ b/apps/maestro/src/config/app.ts @@ -1,12 +1,4 @@ -import { uniq } from "rambda"; - export const APP_NAME = "Interchain Token Service"; export const APP_TITLE = "Interchain Token Service. Build once, run anywhere."; -export const DISABLED_CHAINS = uniq( - String(process.env.NEXT_PUBLIC_DISABLED_CHAINS) - .split(",") - .map((chain) => chain.trim()) -); - export const IS_STAGING = process.env.NEXT_PUBLIC_SITE_URL?.includes("staging"); diff --git a/apps/maestro/src/config/env.ts b/apps/maestro/src/config/env.ts index b7d55d99a..93f635c64 100644 --- a/apps/maestro/src/config/env.ts +++ b/apps/maestro/src/config/env.ts @@ -1,6 +1,6 @@ import { Maybe } from "@axelarjs/utils"; -import { map, split, trim } from "rambda"; +import { map, split, trim, uniq } from "rambda"; export const NEXT_PUBLIC_E2E_ENABLED = process.env.NEXT_PUBLIC_E2E_ENABLED === "true"; @@ -69,6 +69,7 @@ export const NEXT_PUBLIC_DISABLED_CHAINS = Maybe.of( process.env.NEXT_PUBLIC_DISABLED_CHAINS ) .map(split(",")) + .map(uniq) .mapOr([], map(trim)); export const NEXT_PUBLIC_DISABLED_WALLET_IDS = Maybe.of( diff --git a/apps/maestro/src/features/RecentTransactions/RecentTransactionsTable.tsx b/apps/maestro/src/features/RecentTransactions/RecentTransactionsTable.tsx index 3117bd340..91b0d7b3d 100644 --- a/apps/maestro/src/features/RecentTransactions/RecentTransactionsTable.tsx +++ b/apps/maestro/src/features/RecentTransactions/RecentTransactionsTable.tsx @@ -75,31 +75,12 @@ export const RecentTransactionsTable: FC = ({ hasNextPage || hasPrevPage ? ( -
-
- - - -
-
+
) : null, @@ -196,4 +177,44 @@ const TransactionRow: FC<{ ); }; +type PaginationProps = { + page: number; + hasNextPage: boolean; + hasPrevPage: boolean; + onPageChange: (page: number) => void; +}; + +const Pagination: FC = ({ + page, + onPageChange, + hasNextPage, + hasPrevPage, +}) => ( +
+
+ + + +
+
+); + export default RecentTransactionsTable; diff --git a/apps/maestro/src/server/routers/axelarscan/getCosmosChainConfigs.ts b/apps/maestro/src/server/routers/axelarscan/getCosmosChainConfigs.ts index dfb909043..08834bcc8 100644 --- a/apps/maestro/src/server/routers/axelarscan/getCosmosChainConfigs.ts +++ b/apps/maestro/src/server/routers/axelarscan/getCosmosChainConfigs.ts @@ -1,12 +1,13 @@ import { TRPCError } from "@trpc/server"; -import { DISABLED_CHAINS, IS_STAGING } from "~/config/app"; +import { IS_STAGING } from "~/config/app"; +import { NEXT_PUBLIC_DISABLED_CHAINS } from "~/config/env"; import { publicProcedure } from "~/server/trpc"; export const getCosmosChainConfigs = publicProcedure.query(async ({ ctx }) => { try { const { cosmos } = await ctx.services.axelarscan.getChainConfigs({ - disabledChains: DISABLED_CHAINS, + disabledChains: NEXT_PUBLIC_DISABLED_CHAINS, isStaging: IS_STAGING, }); diff --git a/apps/maestro/src/server/routers/interchainToken/getMyInterchainTokens.ts b/apps/maestro/src/server/routers/interchainToken/getMyInterchainTokens.ts index aa440fb91..8b874f783 100644 --- a/apps/maestro/src/server/routers/interchainToken/getMyInterchainTokens.ts +++ b/apps/maestro/src/server/routers/interchainToken/getMyInterchainTokens.ts @@ -10,9 +10,12 @@ export const getMyInterchainTokens = protectedProcedure z.object({ // only for cache invalidation on account change sessionAddress: hex40Literal(), + // pagination + limit: z.number().optional().default(10), + offset: z.number().optional().default(0), }) ) - .query(async ({ ctx }) => { + .query(async ({ ctx, input }) => { invariant(ctx.session?.address, "Missing session address"); const tokenRecords = @@ -20,8 +23,20 @@ export const getMyInterchainTokens = protectedProcedure ctx.session?.address ); - return tokenRecords.sort( + const sorted = tokenRecords.sort( // sort by creation date newest to oldest (a, b) => Number(b.createdAt?.getTime()) - Number(a.createdAt?.getTime()) ); + + const totalPages = Math.ceil(sorted.length / input.limit); + const pageIndex = Math.floor(input.offset / input.limit); + + // return paginated results + const page = sorted.slice(input.offset, input.offset + input.limit); + + return { + items: page, + totalPages, + pageIndex, + }; }); diff --git a/apps/maestro/src/ui/pages/InterchainTokenDetailsPage/TokenDetailsSection.tsx b/apps/maestro/src/ui/pages/InterchainTokenDetailsPage/TokenDetailsSection.tsx index 5e820b404..faf335199 100644 --- a/apps/maestro/src/ui/pages/InterchainTokenDetailsPage/TokenDetailsSection.tsx +++ b/apps/maestro/src/ui/pages/InterchainTokenDetailsPage/TokenDetailsSection.tsx @@ -45,7 +45,7 @@ const TokenDetailsSection: FC = (props) => { {maskAddress(tokenId)} diff --git a/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx b/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx index 13b6824b8..08188236e 100644 --- a/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx +++ b/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx @@ -1,6 +1,6 @@ -import { Card, CopyToClipboardButton } from "@axelarjs/ui"; +import { Button, Card, CopyToClipboardButton } from "@axelarjs/ui"; import { maskAddress, Maybe } from "@axelarjs/utils"; -import { useMemo, type FC } from "react"; +import { useMemo, useState, type FC } from "react"; import Link from "next/link"; import { filter, map } from "rambda"; @@ -20,14 +20,60 @@ function getChainNameSlug(chainId: number) { return chain?.axelarChainName.toLowerCase() ?? ""; } +type PaginationProps = { + page: number; + hasNextPage: boolean; + hasPrevPage: boolean; + onPageChange: (page: number) => void; +}; + +const Pagination: FC = ({ + page, + onPageChange, + hasNextPage, + hasPrevPage, +}) => ( +
+
+ + + +
+
+); + type TokenListProps = { sessionAddress?: `0x${string}`; }; +const PAGE_LIMIT = 12; const TokenList: FC = ({ sessionAddress }) => { + const [pageIndex, setPageIndex] = useState(0); + const offset = pageIndex * PAGE_LIMIT; + const { data } = useGetMyInterchainTokensQuery( { sessionAddress: sessionAddress as `0x${string}`, + limit: PAGE_LIMIT, + offset, }, { suspense: true, @@ -37,9 +83,13 @@ const TokenList: FC = ({ sessionAddress }) => { const { computed } = useEVMChainConfigsQuery(); + const maybeTokens = Maybe.of(data).map((data) => data.items); + + const totalPages = Maybe.of(data).mapOr(0, (data) => data.totalPages); + const filteredTokens = useMemo( () => - Maybe.of(data) + maybeTokens .map( map( (token) => @@ -50,7 +100,7 @@ const TokenList: FC = ({ sessionAddress }) => { [], filter(([token, chain]) => Boolean(token) && Boolean(chain)) ), - [computed.indexedById, data] + [computed.indexedById, maybeTokens] ); return ( @@ -102,6 +152,12 @@ const TokenList: FC = ({ sessionAddress }) => { ); })} + 0} + /> ); }; From 55abea3ba5e5e06fe53d1df51ad44ab03f44b5f8 Mon Sep 17 00:00:00 2001 From: alanrsoares Date: Mon, 8 Jan 2024 19:37:33 -0300 Subject: [PATCH 2/3] chore: whistespace --- apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx b/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx index 08188236e..ae20eb1ed 100644 --- a/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx +++ b/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx @@ -60,10 +60,11 @@ const Pagination: FC = ({ ); +const PAGE_LIMIT = 12; + type TokenListProps = { sessionAddress?: `0x${string}`; }; -const PAGE_LIMIT = 12; const TokenList: FC = ({ sessionAddress }) => { const [pageIndex, setPageIndex] = useState(0); From b1f4bcc10699da4f0306c97ee3609ca8f1561ca1 Mon Sep 17 00:00:00 2001 From: alanrsoares Date: Tue, 9 Jan 2024 11:17:00 -0300 Subject: [PATCH 3/3] refactor: extract Pagination component --- .../RecentTransactionsTable.tsx | 43 +----------------- .../ui/components/Pagination/Pagination.tsx | 44 +++++++++++++++++++ .../src/ui/components/Pagination/index.ts | 2 + .../pages/InterchainTokensPage/TokenList.tsx | 43 +----------------- 4 files changed, 50 insertions(+), 82 deletions(-) create mode 100644 apps/maestro/src/ui/components/Pagination/Pagination.tsx create mode 100644 apps/maestro/src/ui/components/Pagination/index.ts diff --git a/apps/maestro/src/features/RecentTransactions/RecentTransactionsTable.tsx b/apps/maestro/src/features/RecentTransactions/RecentTransactionsTable.tsx index 91b0d7b3d..fcf7874be 100644 --- a/apps/maestro/src/features/RecentTransactions/RecentTransactionsTable.tsx +++ b/apps/maestro/src/features/RecentTransactions/RecentTransactionsTable.tsx @@ -1,4 +1,4 @@ -import { Button, ExternalLinkIcon, Table, Tooltip } from "@axelarjs/ui"; +import { ExternalLinkIcon, Table, Tooltip } from "@axelarjs/ui"; import { maskAddress } from "@axelarjs/utils"; import { useEffect, useMemo, useState, type FC } from "react"; import Link from "next/link"; @@ -8,6 +8,7 @@ import { type Address } from "wagmi"; import { NEXT_PUBLIC_EXPLORER_URL } from "~/config/env"; import { trpc } from "~/lib/trpc"; import type { RecentTransactionsOutput } from "~/server/routers/gmp/getRecentTransactions"; +import Pagination from "~/ui/components/Pagination"; import { CONTRACT_METHODS_LABELS } from "./RecentTransactions"; import { type ContractMethod } from "./types"; @@ -177,44 +178,4 @@ const TransactionRow: FC<{ ); }; -type PaginationProps = { - page: number; - hasNextPage: boolean; - hasPrevPage: boolean; - onPageChange: (page: number) => void; -}; - -const Pagination: FC = ({ - page, - onPageChange, - hasNextPage, - hasPrevPage, -}) => ( -
-
- - - -
-
-); - export default RecentTransactionsTable; diff --git a/apps/maestro/src/ui/components/Pagination/Pagination.tsx b/apps/maestro/src/ui/components/Pagination/Pagination.tsx new file mode 100644 index 000000000..821104e74 --- /dev/null +++ b/apps/maestro/src/ui/components/Pagination/Pagination.tsx @@ -0,0 +1,44 @@ +import { Button } from "@axelarjs/ui"; +import type { FC } from "react"; + +type PaginationProps = { + page: number; + hasNextPage: boolean; + hasPrevPage: boolean; + onPageChange: (page: number) => void; +}; + +const Pagination: FC = ({ + page, + onPageChange, + hasNextPage, + hasPrevPage, +}) => ( +
+
+ + + +
+
+); + +export default Pagination; diff --git a/apps/maestro/src/ui/components/Pagination/index.ts b/apps/maestro/src/ui/components/Pagination/index.ts new file mode 100644 index 000000000..8c0d55617 --- /dev/null +++ b/apps/maestro/src/ui/components/Pagination/index.ts @@ -0,0 +1,2 @@ +export { default } from "./Pagination"; +export * from "./Pagination"; diff --git a/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx b/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx index ae20eb1ed..0d71596dd 100644 --- a/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx +++ b/apps/maestro/src/ui/pages/InterchainTokensPage/TokenList.tsx @@ -1,4 +1,4 @@ -import { Button, Card, CopyToClipboardButton } from "@axelarjs/ui"; +import { Card, CopyToClipboardButton } from "@axelarjs/ui"; import { maskAddress, Maybe } from "@axelarjs/utils"; import { useMemo, useState, type FC } from "react"; import Link from "next/link"; @@ -9,6 +9,7 @@ import { WAGMI_CHAIN_CONFIGS } from "~/config/wagmi"; import { trpc } from "~/lib/trpc"; import { useEVMChainConfigsQuery } from "~/services/axelarscan/hooks"; import { ChainIcon } from "~/ui/components/EVMChainsDropdown"; +import Pagination from "~/ui/components/Pagination"; import Page from "~/ui/layouts/Page"; const useGetMyInterchainTokensQuery = @@ -20,46 +21,6 @@ function getChainNameSlug(chainId: number) { return chain?.axelarChainName.toLowerCase() ?? ""; } -type PaginationProps = { - page: number; - hasNextPage: boolean; - hasPrevPage: boolean; - onPageChange: (page: number) => void; -}; - -const Pagination: FC = ({ - page, - onPageChange, - hasNextPage, - hasPrevPage, -}) => ( -
-
- - - -
-
-); - const PAGE_LIMIT = 12; type TokenListProps = {