Skip to content

Commit

Permalink
fix(explorer): various fixes (#3299)
Browse files Browse the repository at this point in the history
  • Loading branch information
karooolis authored Oct 17, 2024
1 parent e0c4b4c commit 9a43e87
Show file tree
Hide file tree
Showing 13 changed files with 111 additions and 66 deletions.
11 changes: 11 additions & 0 deletions .changeset/soft-bears-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@latticexyz/explorer": patch
---

- Not found page if invalid chain name.
- Only show selector for worlds if options exist.
- Remove "future time" from transactions table.
- Improved layout for Interact tab.
- Wrap long args in transactions table.
- New tables polling.
- Add logs (regression).
19 changes: 19 additions & 0 deletions packages/explorer/src/app/(explorer)/[chainName]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use client";

import { notFound } from "next/navigation";
import { isValidChainName } from "../../../common";

type Props = {
params: {
chainName: string;
};
children: React.ReactNode;
};

export default function ChainLayout({ params: { chainName }, children }: Props) {
if (!isValidChainName(chainName)) {
return notFound();
}

return children;
}
4 changes: 2 additions & 2 deletions packages/explorer/src/app/(explorer)/[chainName]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ type Props = {
};
};

export default async function ChainPage({ params }: Props) {
return redirect(`/${params.chainName}/worlds`);
export default async function ChainPage({ params: { chainName } }: Props) {
return redirect(`/${chainName}/worlds`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function WorldsForm({ worlds }: { worlds: Address[] }) {

<div className="relative">
<CommandList>
{open ? (
{open && worlds.length > 0 ? (
<div className="absolute top-3 z-10 max-h-[200px] w-full overflow-y-auto rounded-md border bg-popover text-popover-foreground outline-none animate-in">
<CommandGroup>
{worlds?.map((world) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,22 @@ export function Form() {
);

return (
<div className="flex min-h-full">
<div className="w-[320px] flex-shrink-0">
<div className="sticky top-2 pr-4">
<h4 className="py-4 text-xs font-semibold uppercase opacity-70">Jump to:</h4>
<>
<div className="-mr-1g -ml-1 flex gap-x-4 overflow-y-hidden">
<div className="w-[320px] flex-shrink-0 overflow-y-auto border-r pl-1">
<div className="pr-4">
<h4 className="py-4 text-xs font-semibold uppercase opacity-70">Jump to:</h4>
<Input
type="text"
placeholder="Filter functions..."
value={deferredFilterValue}
onChange={(evt) => {
setFilterValue(evt.target.value);
}}
/>
</div>

<Input
type="text"
placeholder="Filter functions..."
value={deferredFilterValue}
onChange={(evt) => {
setFilterValue(evt.target.value);
}}
/>

<ul
className="mt-4 max-h-max space-y-2 overflow-y-auto pb-4"
style={{
maxHeight: "calc(100vh - 160px)",
}}
>
<ul className="mt-4 max-h-max space-y-2 overflow-y-auto pb-4">
{!isFetched &&
Array.from({ length: 10 }).map((_, index) => {
return (
Expand Down Expand Up @@ -77,25 +73,25 @@ export function Form() {
})}
</ul>
</div>
</div>

<div className="min-h-full w-full border-l pl-4">
{!isFetched && (
<>
<Skeleton className="h-[100px]" />
<Separator className="my-4" />
<Skeleton className="h-[100px]" />
<Separator className="my-4" />
<Skeleton className="h-[100px]" />
<Separator className="my-4" />
<Skeleton className="h-[100px]" />
</>
)}
<div className="w-full overflow-y-auto pl-1 pr-1">
{!isFetched && (
<>
<Skeleton className="h-[100px]" />
<Separator className="my-4" />
<Skeleton className="h-[100px]" />
<Separator className="my-4" />
<Skeleton className="h-[100px]" />
<Separator className="my-4" />
<Skeleton className="h-[100px]" />
</>
)}

{filteredFunctions?.map((abi) => {
return <FunctionField key={JSON.stringify(abi)} abi={abi as AbiFunction} />;
})}
{filteredFunctions?.map((abi) => {
return <FunctionField key={JSON.stringify(abi)} abi={abi as AbiFunction} />;
})}
</div>
</div>
</div>
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
"use client";

import { notFound } from "next/navigation";
import { Address } from "viem";
import { isValidChainName } from "../../../../../common";
import { Navigation } from "../../../../../components/Navigation";
import { Providers } from "./Providers";
import { TransactionsWatcher } from "./observe/TransactionsWatcher";

export default function WorldLayout({ children }: { children: React.ReactNode }) {
type Props = {
params: {
chainName: string;
worldAddress: Address;
};
children: React.ReactNode;
};

export default function WorldLayout({ params: { chainName }, children }: Props) {
if (!isValidChainName(chainName)) {
return notFound();
}

return (
<Providers>
<Navigation />
<div className="flex h-screen flex-col">
<Navigation />
{children}
</div>
<TransactionsWatcher />
{children}
</Providers>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ export function TransactionTableRow({ row }: { row: Row<ObservedTransaction> })
<div className="flex items-start gap-x-4">
<h3 className="w-[45px] flex-shrink-0 text-2xs font-bold uppercase">Inputs</h3>
{Array.isArray(data.functionData?.args) && data.functionData?.args.length > 0 ? (
<div className="flex-grow border border-white/20 p-2">
<div className="min-w-0 flex-grow border border-white/20 p-2">
{data.functionData?.args?.map((arg, idx) => (
<div key={idx} className="flex">
<span className="flex-shrink-0 text-xs text-white/60">arg {idx + 1}:</span>
<span className="ml-2 whitespace-pre-wrap text-xs">
<span className="ml-2 break-all text-xs">
{typeof arg === "object" && arg !== null ? JSON.stringify(arg, null, 2) : String(arg)}
</span>
</div>
Expand All @@ -113,8 +113,8 @@ export function TransactionTableRow({ row }: { row: Row<ObservedTransaction> })
<>
<Separator className="my-5" />
<div className="flex items-start gap-x-4">
<h3 className="inline-block w-[45px] text-2xs font-bold uppercase">Logs</h3>
{Array.isArray(logs) && logs.length > 10 ? (
<h3 className="inline-block w-[45px] flex-shrink-0 text-2xs font-bold uppercase">Logs</h3>
{Array.isArray(logs) && logs.length > 0 ? (
<div className="flex-grow break-all border border-white/20 p-2 pb-3">
<ul>
{logs.map((log, idx) => {
Expand All @@ -128,7 +128,7 @@ export function TransactionTableRow({ row }: { row: Row<ObservedTransaction> })
{Object.entries(args).map(([key, value]) => (
<li key={key} className="mt-1 flex">
<span className="flex-shrink-0 text-xs text-white/60">{key}: </span>
<span className="ml-2 text-xs">{value as never}</span>
<span className="ml-2 break-all text-xs">{value as never}</span>
</li>
))}
</ul>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { redirect } from "next/navigation";
import { supportedChainName } from "../../../../../common";

type Props = {
params: {
chainName: string;
chainName: supportedChainName;
worldAddress: string;
};
};

export default async function WorldPage({ params }: Props) {
const { chainName, worldAddress } = params;
export default async function WorldPage({ params: { chainName, worldAddress } }: Props) {
return redirect(`/${chainName}/worlds/${worldAddress}/explore`);
}
15 changes: 6 additions & 9 deletions packages/explorer/src/app/(explorer)/[chainName]/worlds/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { Address } from "viem";
import { supportedChains, validateChainName } from "../../../../common";
import { supportedChainName, supportedChains } from "../../../../common";
import { indexerForChainId } from "../../utils/indexerForChainId";
import { WorldsForm } from "./WorldsForm";

Expand All @@ -13,9 +13,7 @@ type ApiResponse = {
}[];
};

async function fetchWorlds(chainName: string): Promise<Address[]> {
validateChainName(chainName);

async function fetchWorlds(chainName: supportedChainName): Promise<Address[]> {
const chain = supportedChains[chainName];
const indexer = indexerForChainId(chain.id);
let worldsApiUrl: string | null = null;
Expand Down Expand Up @@ -49,15 +47,14 @@ async function fetchWorlds(chainName: string): Promise<Address[]> {

type Props = {
params: {
chainName: string;
chainName: supportedChainName;
};
};

export default async function WorldsPage({ params }: Props) {
const worlds = await fetchWorlds(params.chainName);
export default async function WorldsPage({ params: { chainName } }: Props) {
const worlds = await fetchWorlds(chainName);
if (worlds.length === 1) {
return redirect(`/${params.chainName}/worlds/${worlds[0]}`);
return redirect(`/${chainName}/worlds/${worlds[0]}`);
}

return <WorldsForm worlds={worlds} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ export function useTablesQuery() {
})
.sort(({ namespace }) => (internalNamespaces.includes(namespace) ? 1 : -1));
},
refetchInterval: 5000,
});
}
4 changes: 0 additions & 4 deletions packages/explorer/src/app/(explorer)/utils/timeAgo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ export function timeAgo(timestamp: bigint) {
const currentTimestampSeconds = Math.floor(Date.now() / 1000);
const diff = currentTimestampSeconds - Number(timestamp);

if (diff < 0) {
return "in the future";
}

for (const unit of units) {
if (diff >= unit.limit) {
const unitsAgo = Math.floor(diff / unit.inSeconds);
Expand Down
12 changes: 10 additions & 2 deletions packages/explorer/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,22 @@ export const chainIdToName = Object.fromEntries(
Object.entries(supportedChains).map(([chainName, chain]) => [chain.id, chainName]),
) as Record<supportedChainId, supportedChainName>;

export function isValidChainId(chainId: unknown): chainId is supportedChainId {
return typeof chainId === "number" && chainId in chainIdToName;
}

export function isValidChainName(name: unknown): name is supportedChainName {
return typeof name === "string" && name in supportedChains;
}

export function validateChainId(chainId: unknown): asserts chainId is supportedChainId {
if (!(typeof chainId === "number" && chainId in chainIdToName)) {
if (!isValidChainId(chainId)) {
throw new Error(`Invalid chain ID. Supported chains are: ${Object.keys(chainIdToName).join(", ")}.`);
}
}

export function validateChainName(name: unknown): asserts name is supportedChainName {
if (!(typeof name === "string" && name in supportedChains)) {
if (!isValidChainName(name)) {
throw new Error(`Invalid chain name. Supported chains are: ${Object.keys(supportedChains).join(", ")}.`);
}
}
2 changes: 1 addition & 1 deletion packages/explorer/src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function NavigationLink({ href, children }: { href: string; children: React.Reac
export function Navigation() {
const { data, isFetched } = useWorldAbiQuery();
return (
<div className="mb-8">
<div className="pb-4">
<div className="flex items-center justify-between">
<div className="-mx-3 flex">
<NavigationLink href="explore">Explore</NavigationLink>
Expand Down

0 comments on commit 9a43e87

Please sign in to comment.