From 28e5d9d406516ca694cd4614b096ac6497671259 Mon Sep 17 00:00:00 2001 From: Mauro Eijsenring Date: Fri, 18 Oct 2024 11:43:36 +0200 Subject: [PATCH] ui: contract page mobile friendly (#144) --- .../contracts/class-and-instance-tables.tsx | 79 ------------------ .../explorer-ui/src/components/info-badge.tsx | 4 +- .../src/components/table-badge.tsx | 28 +++++++ .../src/pages/block-details/index.tsx | 22 ++--- .../explorer-ui/src/pages/block/index.tsx | 9 ++- .../pages/contract-class-details/index.tsx | 77 ++++++++++++++++++ .../util.ts | 0 .../index.tsx | 15 +--- .../util.ts | 0 .../explorer-ui/src/pages/contract/index.tsx | 78 ++++++++++++++++++ .../explorer-ui/src/pages/contract/util.ts | 36 +++++++++ .../pages/contracts/class-details/index.tsx | 58 ------------- .../explorer-ui/src/pages/tx-effect/index.tsx | 54 +++++++++++++ .../src/routes/contracts/classes.$id.lazy.tsx | 2 +- .../src/routes/contracts/index.lazy.tsx | 81 +------------------ .../contracts/instances.$address.lazy.tsx | 2 +- .../src/routes/tx-effects/index.lazy.tsx | 54 +------------ services/explorer-ui/src/service/constants.ts | 9 ++- 18 files changed, 302 insertions(+), 306 deletions(-) delete mode 100644 services/explorer-ui/src/components/contracts/class-and-instance-tables.tsx create mode 100644 services/explorer-ui/src/components/table-badge.tsx create mode 100644 services/explorer-ui/src/pages/contract-class-details/index.tsx rename services/explorer-ui/src/pages/{contracts/class-details => contract-class-details}/util.ts (100%) rename services/explorer-ui/src/pages/{contracts/instance-details => contract-instance-details}/index.tsx (84%) rename services/explorer-ui/src/pages/{contracts/instance-details => contract-instance-details}/util.ts (100%) create mode 100644 services/explorer-ui/src/pages/contract/index.tsx create mode 100644 services/explorer-ui/src/pages/contract/util.ts delete mode 100644 services/explorer-ui/src/pages/contracts/class-details/index.tsx create mode 100644 services/explorer-ui/src/pages/tx-effect/index.tsx diff --git a/services/explorer-ui/src/components/contracts/class-and-instance-tables.tsx b/services/explorer-ui/src/components/contracts/class-and-instance-tables.tsx deleted file mode 100644 index 15128d33..00000000 --- a/services/explorer-ui/src/components/contracts/class-and-instance-tables.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { ContractClassesTable } from "./classes/table"; -import { ContractInstancesTable } from "./instances/table"; -import { useContractClasses, useLatestContractInstances } from "~/hooks"; -import { contractClassSchema } from "./classes/schema"; -import { contractInstanceSchema } from "./instances/schema"; - -export const ContractClassesAndInstancesTable = ({ - classesTitle, - contractClassesData, - instancesTitle, - contractInstancesData, -}: { - classesTitle: string; - contractClassesData: ReturnType; - instancesTitle: string; - contractInstancesData: ReturnType; -}) => { - const { - data: classesData, - isLoading: isLoadingClasses, - error: errorClasses, - } = contractClassesData; - const { - data: instancesData, - isLoading: isLoadingInstances, - error: errorInstances, - } = contractInstancesData; - - const contractClasses = classesData?.map((contractClass) => - contractClassSchema.parse({ - blockHash: contractClass.blockHash, - contractClassId: contractClass.contractClassId, - version: contractClass.version, - artifactHash: contractClass.artifactHash, - privateFunctionsRoot: contractClass.privateFunctionsRoot, - }) - ); - - const contractInstances = instancesData?.map((contractInstance) => - contractInstanceSchema.parse({ - address: contractInstance.address, - blockHash: contractInstance.blockHash, - blockHeight: contractInstance.blockHeight, - version: contractInstance.version, - contractClassId: contractInstance.contractClassId, - publicKeysHash: contractInstance.publicKeysHash, - deployer: contractInstance.deployer, - }) - ); - - return ( -
-
-

{classesTitle}

- {isLoadingClasses ? ( -

Loading...

- ) : errorClasses ? ( -

{errorClasses.message}

- ) : !contractClasses ? ( -

No data

- ) : ( - - )} -
-
-

{instancesTitle}

- {isLoadingInstances ? ( -

Loading...

- ) : errorInstances ? ( -

{errorInstances.message}

- ) : !contractInstances ? ( -

No data

- ) : ( - - )} -
-
- ); -}; diff --git a/services/explorer-ui/src/components/info-badge.tsx b/services/explorer-ui/src/components/info-badge.tsx index 6e8a7d6e..3cb3054a 100644 --- a/services/explorer-ui/src/components/info-badge.tsx +++ b/services/explorer-ui/src/components/info-badge.tsx @@ -21,8 +21,8 @@ export const InfoBadge: FC = ({ return (
-

{title}

-

{text}

+

{title}

+

{text}

); }; diff --git a/services/explorer-ui/src/components/table-badge.tsx b/services/explorer-ui/src/components/table-badge.tsx new file mode 100644 index 00000000..4a0682f7 --- /dev/null +++ b/services/explorer-ui/src/components/table-badge.tsx @@ -0,0 +1,28 @@ +import { FC, useMemo } from "react"; + +interface TableBadgeProps { + title: string; + isLoading: boolean; + error: Error | null; + children: React.ReactNode; +} + +export const TableBadge: FC = ({ + title, + isLoading, + error, + children, +}) => { + const text = useMemo(() => { + if (isLoading) return "Loading"; + if (error) return error.message; + return undefined; + }, [isLoading, error]); + + return ( +
+

{title}

+ {!text ? children :

{text}

} +
+ ); +}; diff --git a/services/explorer-ui/src/pages/block-details/index.tsx b/services/explorer-ui/src/pages/block-details/index.tsx index b9e8d215..cabae0a3 100644 --- a/services/explorer-ui/src/pages/block-details/index.tsx +++ b/services/explorer-ui/src/pages/block-details/index.tsx @@ -35,30 +35,18 @@ export const BlockDetails: FC = () => { (API Endpoint) - -
-
- -
-
- -
- -
+
- -
diff --git a/services/explorer-ui/src/pages/block/index.tsx b/services/explorer-ui/src/pages/block/index.tsx index 2ea9fa46..0904e388 100644 --- a/services/explorer-ui/src/pages/block/index.tsx +++ b/services/explorer-ui/src/pages/block/index.tsx @@ -23,12 +23,15 @@ export const Blocks: FC = () => { if (error) return

{error.message}

; if (!latestBlocks) return

No data

; const averageBlockTimeFormatted = formatDuration( - Number(avarageBlockTime) / 1000 + Number(avarageBlockTime) / 1000, ); return ( -
-

Latest Blocks

+
+
+

Blocks

+

Blocks

+
{ + const { id } = useParams({ + from: "/contracts/classes/$id", + }); + const { + data: classesData, + isLoading: isLoadingClasses, + error: errorClasses, + } = useContractClasses(id); + const { + data: instancesData, + isLoading: isLoadingInstances, + error: errorInstances, + } = useContractInstance(id); + + if (!id) return
No classId
; + + const apiEndpointUrl = `${API_URL}/${aztecExplorer.getL2ContractClasses(id)}`; + + return ( +
+
+
+
+

Contract class details

+ + (API Endpoint) + +
+
+
+ +
+
+
+
+ + {classesData && ( + + )} + + + + {instancesData && ( + + )} + +
+
+
+ ); +}; diff --git a/services/explorer-ui/src/pages/contracts/class-details/util.ts b/services/explorer-ui/src/pages/contract-class-details/util.ts similarity index 100% rename from services/explorer-ui/src/pages/contracts/class-details/util.ts rename to services/explorer-ui/src/pages/contract-class-details/util.ts diff --git a/services/explorer-ui/src/pages/contracts/instance-details/index.tsx b/services/explorer-ui/src/pages/contract-instance-details/index.tsx similarity index 84% rename from services/explorer-ui/src/pages/contracts/instance-details/index.tsx rename to services/explorer-ui/src/pages/contract-instance-details/index.tsx index bf470eeb..5504b1f5 100644 --- a/services/explorer-ui/src/pages/contracts/instance-details/index.tsx +++ b/services/explorer-ui/src/pages/contract-instance-details/index.tsx @@ -6,16 +6,9 @@ import { API_URL, aztecExplorer } from "~/service/constants"; import { getContractData } from "./util"; export const ContractInstanceDetails: FC = () => { - let address = ""; - try { - const params = useParams({ - from: "/contracts/instances/$address", - }); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - address = params.address; - } catch (e) { - // TODO - } + const { address } = useParams({ + from: "/contracts/instances/$address", + }); const { data: contractInstanceDetails, isLoading, @@ -28,7 +21,7 @@ export const ContractInstanceDetails: FC = () => { if (!contractInstanceDetails) return
No data
; const apiEndpointUrl = `${API_URL}/${aztecExplorer.getL2ContractInstance( - address + address, )}`; return ( diff --git a/services/explorer-ui/src/pages/contracts/instance-details/util.ts b/services/explorer-ui/src/pages/contract-instance-details/util.ts similarity index 100% rename from services/explorer-ui/src/pages/contracts/instance-details/util.ts rename to services/explorer-ui/src/pages/contract-instance-details/util.ts diff --git a/services/explorer-ui/src/pages/contract/index.tsx b/services/explorer-ui/src/pages/contract/index.tsx new file mode 100644 index 00000000..fc7d731d --- /dev/null +++ b/services/explorer-ui/src/pages/contract/index.tsx @@ -0,0 +1,78 @@ +import { FC } from "react"; +import { InfoBadge } from "~/components/info-badge"; +import { useLatestContractClasses, useLatestContractInstances } from "~/hooks"; +import { useTotalContracts, useTotalContractsLast24h } from "~/hooks/stats"; +import { mapContractClasses, mapContractInstances } from "./util"; +import { ContractInstancesTable } from "~/components/contracts/instances/table"; +import { ContractClassesTable } from "~/components/contracts/classes/table"; +import { TableBadge } from "~/components/table-badge"; + +export const Contracts: FC = () => { + const { + data: classesData, + isLoading: isLoadingClasses, + error: errorClasses, + } = useLatestContractClasses(); + const { + data: instancesData, + isLoading: isLoadingInstances, + error: errorInstances, + } = useLatestContractInstances(); + const { + data: totalAmountOfContracts, + isLoading: loadingAmountContracts, + error: errorAmountContracts, + } = useTotalContracts(); + const { + data: totalAmountOfContracts24h, + isLoading: loadingAmountContracts24h, + error: errorAmountContracts24h, + } = useTotalContractsLast24h(); + + return ( +
+
+

All contracts

+

All contracts

+
+
+ + +
+ +
+ + {classesData && ( + + )} + + + + {instancesData && ( + + )} + +
+
+ ); +}; diff --git a/services/explorer-ui/src/pages/contract/util.ts b/services/explorer-ui/src/pages/contract/util.ts new file mode 100644 index 00000000..eafa5fe0 --- /dev/null +++ b/services/explorer-ui/src/pages/contract/util.ts @@ -0,0 +1,36 @@ +import { + ChicmozL2ContractClassRegisteredEvent, + ChicmozL2ContractInstanceDeluxe, +} from "@chicmoz-pkg/types"; +import { contractClassSchema } from "~/components/contracts/classes/schema"; +import { contractInstanceSchema } from "~/components/contracts/instances/schema"; + +export const mapContractClasses = ( + classesData: ChicmozL2ContractClassRegisteredEvent[], +) => { + return classesData.map((contractClass) => + contractClassSchema.parse({ + blockHash: contractClass.blockHash, + contractClassId: contractClass.contractClassId, + version: contractClass.version, + artifactHash: contractClass.artifactHash, + privateFunctionsRoot: contractClass.privateFunctionsRoot, + }), + ); +}; + +export const mapContractInstances = ( + instancesData: ChicmozL2ContractInstanceDeluxe[], +) => { + return instancesData.map((contractInstance) => + contractInstanceSchema.parse({ + address: contractInstance.address, + blockHash: contractInstance.blockHash, + blockHeight: contractInstance.blockHeight, + version: contractInstance.version, + contractClassId: contractInstance.contractClassId, + publicKeysHash: contractInstance.publicKeysHash, + deployer: contractInstance.deployer, + }), + ); +}; diff --git a/services/explorer-ui/src/pages/contracts/class-details/index.tsx b/services/explorer-ui/src/pages/contracts/class-details/index.tsx deleted file mode 100644 index 7e7ac9cd..00000000 --- a/services/explorer-ui/src/pages/contracts/class-details/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useParams } from "@tanstack/react-router"; -import { type FC } from "react"; -import { KeyValueDisplay } from "~/components/info-display/key-value-display"; -import { useContractClasses, useDeployedContractInstances } from "~/hooks"; -import { API_URL, aztecExplorer } from "~/service/constants"; -import { getContractData } from "./util"; -import { ContractClassesAndInstancesTable } from "~/components/contracts/class-and-instance-tables"; - -export const ContractClassDetails: FC = () => { - let classId = ""; - try { - const params = useParams({ - from: "/contracts/classes/$id", - }); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - classId = params.id; - } catch (e) { - // TODO - } - const contractClassVersions = useContractClasses(classId); - const contractDeployedInstances = useDeployedContractInstances(classId); - - if (!classId) return
No classId
; - if (contractClassVersions.isLoading) return
Loading...
; - if (contractClassVersions.error) return
Error
; - if (!contractClassVersions.data) return
No data
; - - const apiEndpointUrl = `${API_URL}/${aztecExplorer.getL2ContractClasses( - classId - )}`; - return ( -
-
-
-
-

Contract class details

- - (API Endpoint) - -
-
-
- -
-
-
- -
-
- ); -}; diff --git a/services/explorer-ui/src/pages/tx-effect/index.tsx b/services/explorer-ui/src/pages/tx-effect/index.tsx new file mode 100644 index 00000000..4536d8b1 --- /dev/null +++ b/services/explorer-ui/src/pages/tx-effect/index.tsx @@ -0,0 +1,54 @@ +import { FC } from "react"; +import { InfoBadge } from "~/components/info-badge"; +import { getTxEffectTableObj } from "~/components/tx-effects/tx-effects-schema"; +import { TxEffectsTable } from "~/components/tx-effects/tx-effects-table"; +import { useLatestBlocks } from "~/hooks"; +import { useTotalTxEffects, useTotalTxEffectsLast24h } from "~/hooks/stats"; + +export const TxEffects: FC = () => { + const { data: latestBlocks, isLoading, error } = useLatestBlocks(); + const { + data: totalTxEffects, + isLoading: loadingTotalEffects, + error: errorTotalEffects, + } = useTotalTxEffects(); + const { + data: totalTxEffects24h, + isLoading: loadingTotalEffects24h, + error: errorTotalEffects24h, + } = useTotalTxEffectsLast24h(); + + if (isLoading) return

Loading...

; + if (error) return

{error.message}

; + if (!latestBlocks) return

No data

; + + const latestTxEffects = latestBlocks.flatMap((block) => { + return block.body.txEffects.map((txEffect) => + getTxEffectTableObj(txEffect, block), + ); + }); + + return ( +
+
+

All tx-effects

+

All tx-effects

+
+
+ + +
+ +
+ ); +}; diff --git a/services/explorer-ui/src/routes/contracts/classes.$id.lazy.tsx b/services/explorer-ui/src/routes/contracts/classes.$id.lazy.tsx index f77810da..a8c4c5ab 100644 --- a/services/explorer-ui/src/routes/contracts/classes.$id.lazy.tsx +++ b/services/explorer-ui/src/routes/contracts/classes.$id.lazy.tsx @@ -1,5 +1,5 @@ import { createLazyFileRoute } from "@tanstack/react-router"; -import { ContractClassDetails } from "~/pages/contracts/class-details"; +import { ContractClassDetails } from "~/pages/contract-class-details"; export const Route = createLazyFileRoute("/contracts/classes/$id")({ component: ContractClassDetails, diff --git a/services/explorer-ui/src/routes/contracts/index.lazy.tsx b/services/explorer-ui/src/routes/contracts/index.lazy.tsx index efe29c22..08e29483 100644 --- a/services/explorer-ui/src/routes/contracts/index.lazy.tsx +++ b/services/explorer-ui/src/routes/contracts/index.lazy.tsx @@ -1,81 +1,6 @@ -import { Outlet, createLazyFileRoute, useParams } from "@tanstack/react-router"; -import { ContractClassesAndInstancesTable } from "~/components/contracts/class-and-instance-tables"; -import { InfoBadge } from "~/components/info-badge"; -import { useLatestContractClasses, useLatestContractInstances } from "~/hooks"; -import { useTotalContracts, useTotalContractsLast24h } from "~/hooks/stats"; +import { createLazyFileRoute } from "@tanstack/react-router"; +import { Contracts } from "~/pages/contract"; export const Route = createLazyFileRoute("/contracts/")({ - component: TxEffects, + component: Contracts, }); - -function TxEffects() { - const latestClassesData = useLatestContractClasses(); - const latestInstancesData = useLatestContractInstances(); - const { - data: totalAmountOfContracts, - isLoading: loadingAmountContracts, - error: errorAmountContracts, - } = useTotalContracts(); - const { - data: totalAmountOfContracts24h, - isLoading: loadingAmountContracts24h, - error: errorAmountContracts24h, - } = useTotalContractsLast24h(); - - let isAddress = false; - let isClass = false; - let isIndex = false; - try { - useParams({ from: "/contracts/instances/$address" }); - isAddress = true; - } catch (e) { - // TODO - } - try { - useParams({ from: "/contracts/classes/$id" }); - isClass = true; - } catch (e) { - // TODO - } - isIndex = !isAddress && !isClass; - - const text = { - title: isIndex - ? "All Contracts" - : isClass - ? "Contract Class Details" - : "Contract Instance Details", - }; - return ( -
-

{text.title}

- {isIndex ? ( - <> -
- - -
- - - - ) : ( - - )} -
- ); -} diff --git a/services/explorer-ui/src/routes/contracts/instances.$address.lazy.tsx b/services/explorer-ui/src/routes/contracts/instances.$address.lazy.tsx index 2e33c72e..0d659b04 100644 --- a/services/explorer-ui/src/routes/contracts/instances.$address.lazy.tsx +++ b/services/explorer-ui/src/routes/contracts/instances.$address.lazy.tsx @@ -1,5 +1,5 @@ import { createLazyFileRoute } from "@tanstack/react-router"; -import { ContractInstanceDetails } from "~/pages/contracts/instance-details"; +import { ContractInstanceDetails } from "~/pages/contract-instance-details"; export const Route = createLazyFileRoute("/contracts/instances/$address")({ component: ContractInstanceDetails, diff --git a/services/explorer-ui/src/routes/tx-effects/index.lazy.tsx b/services/explorer-ui/src/routes/tx-effects/index.lazy.tsx index 5b2c078b..cb85a629 100644 --- a/services/explorer-ui/src/routes/tx-effects/index.lazy.tsx +++ b/services/explorer-ui/src/routes/tx-effects/index.lazy.tsx @@ -1,58 +1,6 @@ import { createLazyFileRoute } from "@tanstack/react-router"; -import { InfoBadge } from "~/components/info-badge"; -import { getTxEffectTableObj } from "~/components/tx-effects/tx-effects-schema"; -import { TxEffectsTable } from "~/components/tx-effects/tx-effects-table"; -import { useLatestBlocks } from "~/hooks"; -import { useTotalTxEffects, useTotalTxEffectsLast24h } from "~/hooks/stats"; +import { TxEffects } from "~/pages/tx-effect"; export const Route = createLazyFileRoute("/tx-effects/")({ component: TxEffects, }); - -function TxEffects() { - const { data: latestBlocks, isLoading, error } = useLatestBlocks(); - const { - data: totalTxEffects, - isLoading: loadingTotalEffects, - error: errorTotalEffects, - } = useTotalTxEffects(); - const { - data: totalTxEffects24h, - isLoading: loadingTotalEffects24h, - error: errorTotalEffects24h, - } = useTotalTxEffectsLast24h(); - - if (isLoading) return

Loading...

; - if (error) return

{error.message}

; - if (!latestBlocks) return

No data

; - - const latestTxEffects = latestBlocks.flatMap((block) => { - return block.body.txEffects.map((txEffect) => - getTxEffectTableObj(txEffect, block) - ); - }); - - return ( -
-
-

All tx-effects

-

All tx-effects

-
-
- - -
- -
- ); -} diff --git a/services/explorer-ui/src/service/constants.ts b/services/explorer-ui/src/service/constants.ts index 821742f0..134c3486 100644 --- a/services/explorer-ui/src/service/constants.ts +++ b/services/explorer-ui/src/service/constants.ts @@ -9,9 +9,12 @@ export const aztecExplorer = { getL2TxEffectByHeightAndIndex: (height: number, index: number) => `l2/blocks/${height}/txEffects/${index}`, getL2TxEffectsByHeightRange: "", - getL2ContractClassByIdAndVersion: (classId: string, version: string) => `l2/contract-classes/${classId}/versions/${version}`, - getL2ContractClasses: (classId?: string) => classId ? `l2/contract-classes/${classId}` : "l2/contract-classes", - getL2ContractInstance: (address: string) => `l2/contract-instances/${address}`, + getL2ContractClassByIdAndVersion: (classId: string, version: string) => + `l2/contract-classes/${classId}/versions/${version}`, + getL2ContractClasses: (classId?: string) => + classId ? `l2/contract-classes/${classId}` : "l2/contract-classes", + getL2ContractInstance: (address: string) => + `l2/contract-instances/${address}`, getL2ContractInstances: "l2/contract-instances", getL2ContractInstancesByBlockHash: (hash: string) => `l2/blocks/${hash}/contract-instances`,