From 270ed2af6c8766671069bce7abe60071240a982f Mon Sep 17 00:00:00 2001 From: filip Date: Tue, 28 Jan 2025 12:29:49 +0100 Subject: [PATCH 1/6] first --- .../src/components/option-buttons/index.tsx | 106 ++++++++++++++++++ .../pages/contract-class-details/index.tsx | 6 +- .../src/pages/contract-class-details/tabs.tsx | 94 ---------------- .../src/pages/tx-effect-details/index.tsx | 15 ++- .../src/pages/tx-effect-details/tabs.tsx | 95 ---------------- .../src/pages/tx-effect-details/utils.ts | 29 ++--- 6 files changed, 125 insertions(+), 220 deletions(-) create mode 100644 services/explorer-ui/src/components/option-buttons/index.tsx delete mode 100644 services/explorer-ui/src/pages/contract-class-details/tabs.tsx delete mode 100644 services/explorer-ui/src/pages/tx-effect-details/tabs.tsx diff --git a/services/explorer-ui/src/components/option-buttons/index.tsx b/services/explorer-ui/src/components/option-buttons/index.tsx new file mode 100644 index 00000000..046c7a06 --- /dev/null +++ b/services/explorer-ui/src/components/option-buttons/index.tsx @@ -0,0 +1,106 @@ +import { CustomTooltip } from "~/components/custom-tooltip"; +import { + Button, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + Separator, +} from "~/components/ui"; + +export type OptionItem = { + id: string; + label: string; +}; + +export type OptionItems = OptionItem[]; + +type ExtractOptionId = T[number]["id"]; + +type OptionButtonProps = { + options: T; + availableOptions: Record; // TODO: this should work: Record, boolean> + onOptionSelect: (option: ExtractOptionId) => void; + selectedItem: ExtractOptionId; +}; + +export const OptionButtons = ({ + options, + availableOptions, + onOptionSelect, + selectedItem, +}: OptionButtonProps) => { + return ( + <> +
+ {options.map((option, key) => { + const isAvailable = availableOptions[option.id]; + if (!isAvailable) { + return ( + + + + ); + } + return ( +
+ + {selectedItem === option.id && ( + + )} +
+ ); + })} +
+
+ +
+ + ); +}; diff --git a/services/explorer-ui/src/pages/contract-class-details/index.tsx b/services/explorer-ui/src/pages/contract-class-details/index.tsx index cb555050..e1e48419 100644 --- a/services/explorer-ui/src/pages/contract-class-details/index.tsx +++ b/services/explorer-ui/src/pages/contract-class-details/index.tsx @@ -3,6 +3,7 @@ import { useState, type FC } from "react"; import { ContractClassesTable } from "~/components/contracts/classes/table"; import { ContractInstancesTable } from "~/components/contracts/instances/table"; import { KeyValueDisplay } from "~/components/info-display/key-value-display"; +import { OptionButtons } from "~/components/option-buttons"; import { useContractClassPrivateFunctions, useContractClassUnconstrainedFunctions, @@ -12,7 +13,6 @@ import { } from "~/hooks"; import { mapContractClasses, mapContractInstances } from "../contract/util"; import { contractClassTabs, type TabId } from "./constants"; -import { OptionButtons } from "./tabs"; import { getContractClassKeyValueData } from "./util"; export const ContractClassDetails: FC = () => { @@ -77,8 +77,8 @@ export const ContractClassDetails: FC = () => { diff --git a/services/explorer-ui/src/pages/contract-class-details/tabs.tsx b/services/explorer-ui/src/pages/contract-class-details/tabs.tsx deleted file mode 100644 index 108a4fe6..00000000 --- a/services/explorer-ui/src/pages/contract-class-details/tabs.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { Tab, tabId } from "./constants"; -import { - Button, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, - Separator, -} from "~/components/ui"; -import { CustomTooltip } from "~/components/custom-tooltip"; - -export type OptionButtonProps = { - availableData: Record; - requiredOptions: Tab[]; - onOptionSelect: (option: string) => void; - selectedItem: tabId; -}; - -export const OptionButtons: React.FC = ({ - availableData, - requiredOptions, - onOptionSelect, - selectedItem, -}) => { - // Check if an option is available in the record - const isOptionAvailable = (option: string) => - option in availableData && - availableData[option] !== undefined && - availableData[option] !== null; - - return ( - <> -
- {requiredOptions.map((option, key) => { - const isAvailable = isOptionAvailable(option.id); - - if (!isAvailable) { - return ( - - - - ); - } - return ( -
- - {selectedItem === option.id && ( - - )} -
- ); - })} -
-
- -
- - ); -}; diff --git a/services/explorer-ui/src/pages/tx-effect-details/index.tsx b/services/explorer-ui/src/pages/tx-effect-details/index.tsx index 90a767a0..e914aa14 100644 --- a/services/explorer-ui/src/pages/tx-effect-details/index.tsx +++ b/services/explorer-ui/src/pages/tx-effect-details/index.tsx @@ -1,9 +1,9 @@ import { useParams } from "@tanstack/react-router"; import { useEffect, useState, type FC } from "react"; import { KeyValueDisplay } from "~/components/info-display/key-value-display"; +import { OptionButtons } from "~/components/option-buttons"; import { useGetTxEffectByHash, useSubTitle } from "~/hooks"; import { txEffectTabs, type TabId } from "./constants"; -import { OptionButtons } from "./tabs"; import { getTxEffectData, mapTxEffectsData } from "./utils"; const naiveDecode = (data: Buffer): string => { @@ -27,7 +27,7 @@ const naiveDecode = (data: Buffer): string => { }; export const TxEffectDetails: FC = () => { - const [selectedTab, setSelectedTab] = useState("unencryptedLogs"); + const [selectedTab, setSelectedTab] = useState("nullifiers"); const { hash } = useParams({ from: "/tx-effects/$hash", }); @@ -47,14 +47,14 @@ export const TxEffectDetails: FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [txEffects]); - const getSelectedItem = (value: string) => { + const onSelectChange = (value: string) => { setSelectedTab(value as TabId); }; if (!hash)
No txEffect hash
; if (isLoading) return
Loading...
; if (error) return
Error
; - if (!txEffects) return
No data
; + if (!txEffects || !selectedTab) return
No data
; return (
@@ -67,10 +67,9 @@ export const TxEffectDetails: FC = () => {
diff --git a/services/explorer-ui/src/pages/tx-effect-details/tabs.tsx b/services/explorer-ui/src/pages/tx-effect-details/tabs.tsx deleted file mode 100644 index 67afb888..00000000 --- a/services/explorer-ui/src/pages/tx-effect-details/tabs.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { TxEffectDataType } from "./utils"; -import { Tab, tabId } from "./constants"; -import { - Button, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, - Separator, -} from "~/components/ui"; -import { CustomTooltip } from "~/components/custom-tooltip"; - -export type OptionButtonProps = { - availableData: Record; - requiredOptions: Tab[]; - onOptionSelect: (option: string) => void; - selectedItem: tabId; -}; - -export const OptionButtons: React.FC = ({ - availableData, - requiredOptions, - onOptionSelect, - selectedItem, -}) => { - // Check if an option is available in the record - const isOptionAvailable = (option: string) => - option in availableData && - availableData[option] !== undefined && - availableData[option] !== null; - - return ( - <> -
- {requiredOptions.map((option, key) => { - const isAvailable = isOptionAvailable(option.id); - - if (!isAvailable) { - return ( - - - - ); - } - return ( -
- - {selectedItem === option.id && ( - - )} -
- ); - })} -
-
- -
- - ); -}; diff --git a/services/explorer-ui/src/pages/tx-effect-details/utils.ts b/services/explorer-ui/src/pages/tx-effect-details/utils.ts index e51c0b09..29b330d3 100644 --- a/services/explorer-ui/src/pages/tx-effect-details/utils.ts +++ b/services/explorer-ui/src/pages/tx-effect-details/utils.ts @@ -43,26 +43,15 @@ export const getTxEffectData = (data: ChicmozL2TxEffectDeluxe) => [ export const mapTxEffectsData = ( data?: ChicmozL2TxEffectDeluxe -): Record => { - if (!data) return {}; - - const effectsMap: Record = { - privateLogs: data.privateLogs.length ? data.privateLogs : undefined, - unencryptedLogs: data.unencryptedLogs?.functionLogs?.filter( +): Record => { + return { + privateLogs: !!data?.privateLogs?.length, + unencryptedLogs: !!data?.unencryptedLogs?.functionLogs?.filter( (log) => log.logs.length > 0 - ).length - ? data.unencryptedLogs.functionLogs - : undefined, - nullifiers: data.nullifiers?.length ? data.nullifiers : undefined, - noteHashes: data.noteHashes?.length ? data.noteHashes : undefined, - l2ToL1Msgs: data.l2ToL1Msgs?.length ? data.l2ToL1Msgs : undefined, - publicDataWrites: data.publicDataWrites?.length - ? data.publicDataWrites - : undefined, + ).length, + nullifiers: !!data?.nullifiers?.length, + noteHashes: !!data?.noteHashes?.length, + l2ToL1Msgs: !!data?.l2ToL1Msgs?.length, + publicDataWrites: !!data?.publicDataWrites?.length, }; - - // Filter out undefined values - return Object.fromEntries( - Object.entries(effectsMap).filter(([_, value]) => value !== undefined) - ); }; From af74efa44d6855f69851e900ed095817e6b9931e Mon Sep 17 00:00:00 2001 From: filip Date: Tue, 28 Jan 2025 12:42:43 +0100 Subject: [PATCH 2/6] included block details --- .../src/pages/block-details/constants.ts | 19 ++++++++++ .../src/pages/block-details/index.tsx | 35 +++++++++++-------- .../pages/contract-class-details/index.tsx | 2 +- 3 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 services/explorer-ui/src/pages/block-details/constants.ts diff --git a/services/explorer-ui/src/pages/block-details/constants.ts b/services/explorer-ui/src/pages/block-details/constants.ts new file mode 100644 index 00000000..9a439fa5 --- /dev/null +++ b/services/explorer-ui/src/pages/block-details/constants.ts @@ -0,0 +1,19 @@ +import { z } from "zod"; + +export type tabId = "txEffects" | "contracts"; + +export const tabIds = ["txEffects", "contracts"] as const; + +export const tabIdSchema = z.enum(tabIds); +export type TabId = z.infer; + +export const tabSchema = z.object({ + id: tabIdSchema, + label: z.string(), +}); +export type Tab = z.infer; + +export const blockDetailsTabs: Tab[] = [ + { id: "txEffects", label: "Transaction effects" }, + { id: "contracts", label: "Contracts" }, +]; diff --git a/services/explorer-ui/src/pages/block-details/index.tsx b/services/explorer-ui/src/pages/block-details/index.tsx index b2345995..ed5d8aa7 100644 --- a/services/explorer-ui/src/pages/block-details/index.tsx +++ b/services/explorer-ui/src/pages/block-details/index.tsx @@ -1,13 +1,14 @@ import { useParams } from "@tanstack/react-router"; -import { type FC } from "react"; +import { useState, type FC } from "react"; import { KeyValueDisplay } from "~/components/info-display/key-value-display"; +import { OptionButtons } from "~/components/option-buttons"; import { TxEffectsTable } from "~/components/tx-effects/tx-effects-table"; -import { Button } from "~/components/ui"; import { useGetBlockByIdentifier, useGetTxEffectsByBlockHeight, useSubTitle, } from "~/hooks"; +import { blockDetailsTabs, type TabId } from "./constants"; import { getBlockDetails, getTxEffects } from "./util"; export const BlockDetails: FC = () => { @@ -15,6 +16,10 @@ export const BlockDetails: FC = () => { from: "/blocks/$blockNumber", }); useSubTitle(`Block ${blockNumber}`); + const [selectedTab, setSelectedTab] = useState("txEffects"); + const onOptionSelect = (value: string) => { + setSelectedTab(value as TabId); + }; const { data: latestBlock, isLoading, @@ -28,7 +33,6 @@ export const BlockDetails: FC = () => { error: txEffectsError, } = useGetTxEffectsByBlockHeight(height); - //TODO: Check for better solution if (!latestBlock) return
No block hash
; return ( @@ -37,19 +41,22 @@ export const BlockDetails: FC = () => {

Block Details

-
+
-
- -
-
+
+ +
+ {selectedTab === "txEffects" && ( { } error={error ?? txEffectsError} /> -
+ )}
diff --git a/services/explorer-ui/src/pages/contract-class-details/index.tsx b/services/explorer-ui/src/pages/contract-class-details/index.tsx index e1e48419..76dbb93e 100644 --- a/services/explorer-ui/src/pages/contract-class-details/index.tsx +++ b/services/explorer-ui/src/pages/contract-class-details/index.tsx @@ -16,11 +16,11 @@ import { contractClassTabs, type TabId } from "./constants"; import { getContractClassKeyValueData } from "./util"; export const ContractClassDetails: FC = () => { - const [selectedTab, setSelectedTab] = useState("contractVersions"); const { id, version } = useParams({ from: "/contracts/classes/$id/versions/$version", }); useSubTitle(`Ctrct cls ${id}`); + const [selectedTab, setSelectedTab] = useState("contractVersions"); const onOptionSelect = (value: string) => { setSelectedTab(value as TabId); }; From 072df8be53a61090e76fe93b50fac20da171ea24 Mon Sep 17 00:00:00 2001 From: filip Date: Tue, 28 Jan 2025 14:09:25 +0100 Subject: [PATCH 3/6] cleanup --- .../src/components/option-buttons/index.tsx | 52 +++++++++---------- .../src/pages/tx-effect-details/index.tsx | 14 +---- 2 files changed, 27 insertions(+), 39 deletions(-) diff --git a/services/explorer-ui/src/components/option-buttons/index.tsx b/services/explorer-ui/src/components/option-buttons/index.tsx index 046c7a06..61c1e497 100644 --- a/services/explorer-ui/src/components/option-buttons/index.tsx +++ b/services/explorer-ui/src/components/option-buttons/index.tsx @@ -25,6 +25,22 @@ type OptionButtonProps = { selectedItem: ExtractOptionId; }; +const withAvailableTooltip = ( + isAvailable: boolean, + key: number, + children: React.ReactNode +) => { + if (!isAvailable) { + return ( + + {children} + + ); + } + + return
{children}
; +}; + export const OptionButtons = ({ options, availableOptions, @@ -33,40 +49,24 @@ export const OptionButtons = ({ }: OptionButtonProps) => { return ( <> -
+
{options.map((option, key) => { const isAvailable = availableOptions[option.id]; - if (!isAvailable) { - return ( - - - - ); - } - return ( -
+ return withAvailableTooltip( + isAvailable, + key, +
{selectedItem === option.id && ( - + )}
); diff --git a/services/explorer-ui/src/pages/tx-effect-details/index.tsx b/services/explorer-ui/src/pages/tx-effect-details/index.tsx index e914aa14..b0e9d85d 100644 --- a/services/explorer-ui/src/pages/tx-effect-details/index.tsx +++ b/services/explorer-ui/src/pages/tx-effect-details/index.tsx @@ -1,5 +1,5 @@ import { useParams } from "@tanstack/react-router"; -import { useEffect, useState, type FC } from "react"; +import { useState, type FC } from "react"; import { KeyValueDisplay } from "~/components/info-display/key-value-display"; import { OptionButtons } from "~/components/option-buttons"; import { useGetTxEffectByHash, useSubTitle } from "~/hooks"; @@ -35,18 +35,6 @@ export const TxEffectDetails: FC = () => { const { data: txEffects, isLoading, error } = useGetTxEffectByHash(hash); const txEffectData = mapTxEffectsData(txEffects); - useEffect(() => { - // check for the first avalible tab with data - if (txEffects) { - const firstAvailableTab = txEffectTabs.find( - (tab) => tab.id in txEffectData - ); - - if (firstAvailableTab) setSelectedTab(firstAvailableTab.id); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [txEffects]); - const onSelectChange = (value: string) => { setSelectedTab(value as TabId); }; From 0eb2325a2ead47f6425d232dfaea636d1265eee7 Mon Sep 17 00:00:00 2001 From: filip Date: Tue, 28 Jan 2025 14:17:44 +0100 Subject: [PATCH 4/6] stylish --- .../src/components/option-buttons/index.tsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/services/explorer-ui/src/components/option-buttons/index.tsx b/services/explorer-ui/src/components/option-buttons/index.tsx index 61c1e497..27b81945 100644 --- a/services/explorer-ui/src/components/option-buttons/index.tsx +++ b/services/explorer-ui/src/components/option-buttons/index.tsx @@ -61,7 +61,7 @@ export const OptionButtons = ({ onClick={() => onOptionSelect(option.id)} disabled={!isAvailable} variant="default" - className="hover:bg-slate-500" + className="hover:bg-slate-500 border border-input" > {option.label} @@ -88,15 +88,6 @@ export const OptionButtons = ({ {option.label} )) - // requiredOptions.map((tab, key) => ( - // - // {tab.label} - // - //)) } From 44d288ddfb1a21c45ccf1609e068978dcc51e74f Mon Sep 17 00:00:00 2001 From: filip Date: Tue, 28 Jan 2025 14:27:33 +0100 Subject: [PATCH 5/6] fixed bad check --- .../explorer-ui/src/pages/contract-class-details/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/explorer-ui/src/pages/contract-class-details/index.tsx b/services/explorer-ui/src/pages/contract-class-details/index.tsx index 76dbb93e..d05768b1 100644 --- a/services/explorer-ui/src/pages/contract-class-details/index.tsx +++ b/services/explorer-ui/src/pages/contract-class-details/index.tsx @@ -48,11 +48,13 @@ export const ContractClassDetails: FC = () => { privateFunctions: !contractClassPrivateFunctionsHookRes.isLoading && !contractClassPrivateFunctionsHookRes.error && - !!contractClassPrivateFunctionsHookRes.data, + !!contractClassPrivateFunctionsHookRes.data && + !!contractClassPrivateFunctionsHookRes.data.length, unconstrainedFunctions: !contractClassUnconstrainedFunctionsHookRes.isLoading && !contractClassUnconstrainedFunctionsHookRes.error && - !!contractClassUnconstrainedFunctionsHookRes.data, + !!contractClassUnconstrainedFunctionsHookRes.data && + !!contractClassUnconstrainedFunctionsHookRes.data.length, }; if (!id) return
No classId
; From 586d2bc6792327598a9c54b64e643f79941740f7 Mon Sep 17 00:00:00 2001 From: filip Date: Wed, 29 Jan 2025 10:48:38 +0100 Subject: [PATCH 6/6] comment --- services/explorer-ui/src/components/option-buttons/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/services/explorer-ui/src/components/option-buttons/index.tsx b/services/explorer-ui/src/components/option-buttons/index.tsx index 27b81945..a802f47b 100644 --- a/services/explorer-ui/src/components/option-buttons/index.tsx +++ b/services/explorer-ui/src/components/option-buttons/index.tsx @@ -30,6 +30,7 @@ const withAvailableTooltip = ( key: number, children: React.ReactNode ) => { + // TODO: refactor to use CustomTooltip component instead? if (!isAvailable) { return (