Skip to content

Commit

Permalink
Merge pull request #135 from penumbra-zone/ibc-prefetch-suspense-comp…
Browse files Browse the repository at this point in the history
…onents

Add prefetching + Suspense to IBC pages
  • Loading branch information
ejmg authored Jul 15, 2024
2 parents 762c018 + e2e3af0 commit 80bc3d3
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 208 deletions.
84 changes: 20 additions & 64 deletions src/app/ibc/channel/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"use client";

import { type FC } from "react";
import axios from "axios";
import { useQuery } from "@tanstack/react-query";
import Link from "next/link";
import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
import { Channel } from "@/components/ibc/Channel";
import { getQueryClient } from "@/lib/utils";
import { getIbcChannel } from "@/components/ibc/Channel/getIbcChannel";

interface PageProps {
params: {
Expand All @@ -13,70 +12,27 @@ interface PageProps {

const Page : FC<PageProps> = ({ params }) => {
const { id: channelId } = params;
const { data, isFetched, isError } = useQuery({
queryFn: async () => {
console.log(`Fetching: GET /api/ibc/channel/${channelId}`);
const { data } = await axios.get(`/api/ibc/channel?q=${channelId}`);
console.log("Fetched result:", data);
return data as { connectionId: string, clientId: string, consensusHeight: string, recentTxs: string[]};
// TODO: enforce validation
// const result = IbcChannelValidator.safeParse(data);
},
queryKey: ["IbcChannel", channelId],
retry: false,
const queryClient = getQueryClient();

const endpoint = "/api/ibc/channel";
const queryName = "IbcChannel";
const errorMessage = "Failed to query IBC Channel by id. Please try again.";
queryClient.prefetchQuery({
queryFn: () => getIbcChannel({ endpoint, channelId }),
queryKey: [queryName, channelId],
meta: {
errorMessage: "Failed to query IBC Channel by id. Please try again.",
errorMessage,
},
});

if (isError) {
return (
<div className="py-5 flex justify-center">
<h1 className="text-4xl font-semibold">No results found.</h1>
</div>
);
}

return (
<div className="bg-primary rounded-sm shadow-md">
{isFetched ? (
<div className="flex flex-col items-center gap-5 pt-5">
<h1 className="sm:text-2xl text-lg font-bold">IBC Channel</h1>
{// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
data ? (
<div className="sm:w-11/12 w-full">
<div className="flex flex-col justify-start p-5 gap-y-5 w-full">
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Channel ID</p>
<pre className="sm:w-0 w-full">{channelId}</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Client ID</p>
<Link href={`/ibc/client/${data.clientId}`} className="underline sm:w-0 w-full"><pre>{data.clientId}</pre></Link>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Counterparty Height</p>
<pre className="sm:w-0 w-full">{data.consensusHeight}</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Connection IDs</p>
<Link href={`/ibc/connection/${data.connectionId}`} className="underline sm:w-0 w-full"><pre>{data.connectionId}</pre></Link>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Recent Transactions</p>
<div className="sm:w-5/6 w-full">
{data.recentTxs.map(( hash , i) => <Link href={`/transaction/${hash}`} key={i} className="underline"><pre className="sm:font-base font-sm overflow-hidden overflow-ellipsis">{hash}</pre></Link>)}
</div>
</div>
</div>
</div>
) : (
<p>No results</p>
)}
</div>
) : (
<p>loading...</p>
)}
<div className="bg-primary flex flex-col gap-5 pt-5 items-center ">
<h1 className="font-medium">IBC Channel</h1>
<HydrationBoundary state={dehydrate(queryClient)}>
<div className="sm:w-11/12 w-full">
<Channel {...{endpoint, queryName, channelId}}/>
</div>
</HydrationBoundary>
</div>
);
};
Expand Down
89 changes: 21 additions & 68 deletions src/app/ibc/client/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"use client";

import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import Link from "next/link";
import { Client } from "@/components/ibc/Client";
import { getIbcClient } from "@/components/ibc/Client/getIbcClient";
import { getQueryClient } from "@/lib/utils";
import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
import { type FC } from "react";

interface PageProps {
Expand All @@ -12,74 +11,28 @@ interface PageProps {
}

const Page : FC<PageProps> = ({ params }) => {
const { id } = params;
const { data , isFetched, isError } = useQuery({
queryFn: async () => {
console.log("Fetching: GET /api/ibc/client");
const { data } = await axios.get(`/api/ibc/client?q=${id}`);
console.log("Fetched result:", data);
// TODO: enforce validation
// const result = IbcClientValidator.safeParse(data);
return data as Array<{ channels: string[], connection_id: string, client_id: string, events: string }>;
},
queryKey: ["IbcClient", id],
retry: false,
const { id: clientId } = params;
const queryClient = getQueryClient();

const endpoint = "/api/ibc/client";
const queryName = "IbcClient";
const errorMessage = "Failed to query IBC Client by id. Please try again.";
queryClient.prefetchQuery({
queryFn: () => getIbcClient({ endpoint, clientId }),
queryKey: [queryName, clientId],
meta: {
errorMessage: "Failed to query IBC Client by id. Please try again.",
errorMessage,
},
});

// Not the best.
if (isError || data?.length === undefined) {
return (
<div className="py-5 flex justify-center">
<h1 className="text-4xl font-semibold">No results found.</h1>
</div>
);
}
// ... Still Not The Best.
const { client_id: clientId, connection_id: connectionId, channels, events: eventsJSON} = data[0];
const events: Array<{ key: string, value: string }> = JSON.parse(eventsJSON);
const consensusHeight = events.find(({ key }) => key === "consensus_height")?.value ?? "NONE";
const clientType = events.find(({ key }) => key === "client_type")?.value ?? "NONE";
// TODO: Do we just want a header and not the whole transaction decoded? packets?
// const header = events.find(({ key }) => key === "header")?.value ?? "NONE";

return (
<div className="bg-primary rounded-sm shadow-md">
{isFetched ? (
<div className="flex flex-col items-center gap-5 pt-5">
<h1 className="sm:text-2xl text-lg font-bold">IBC Client</h1>
<div className="sm:w-11/12 w-full">
<div className="flex flex-col justify-start p-5 gap-y-5 w-full">
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Client ID</p>
<pre className="sm:w-0 w-full">{clientId}</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Client Type</p>
<pre className="sm:w-0 w-full">{clientType}</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Counterparty Height</p>
<pre className="sm:w-0 w-full">{consensusHeight}</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Connection IDs</p>
<Link href={`/ibc/connection/${connectionId}`} className="underline sm:w-0 w-full"><pre>{connectionId}</pre></Link>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Channel IDs</p>
<div className="">
{channels.map(( channelId, i ) => <Link href={`/ibc/channel/${channelId}`} key={i} className="underline sm:w-0 w-full"><pre>{channelId}</pre></Link>)}
</div>
</div>
</div>
</div>
</div>
) : (
<p>loading...</p>
)}
<div className="bg-primary flex flex-col gap-5 pt-5 items-center ">
<h1 className="font-medium">IBC Client</h1>
<HydrationBoundary state={dehydrate(queryClient)}>
<div className="sm:w-11/12 w-full">
<Client {...{endpoint, queryName, clientId}}/>
</div>
</HydrationBoundary>
</div>
);
};
Expand Down
76 changes: 22 additions & 54 deletions src/app/ibc/connection/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"use client";

import { type FC } from "react";
import axios from "axios";
import { useQuery } from "@tanstack/react-query";
import Link from "next/link";
import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
import { Connection } from "@/components/ibc/Connection";
import { getIbcConnection } from "@/components/ibc/Connection/getIbcConnection";
import { getQueryClient } from "@/lib/utils";

interface PageProps {
params: {
Expand All @@ -12,60 +11,29 @@ interface PageProps {
}

const Page : FC<PageProps> = ({ params }) => {
const { id } = params;
const { data, isFetched, isError } = useQuery({
queryFn: async () => {
console.log(`Fetching: GET /api/ibc/connection/${id}`);
const { data } = await axios.get(`/api/ibc/connection?q=${id}`);
console.log("Fetched result:", data);
return data as Array<{ connection_id: string, client_id: string, channel_ids: string[] }>;
// TODO: enforce validation
// const result = IbcConnectionValidator.safeParse(data);
},
queryKey: ["IbcConnection", id],
retry: false,
const { id: connectionId } = params;

const queryClient = getQueryClient();

const endpoint = "/api/ibc/connection";
const queryName = "IbcConnection";
const errorMessage = "Failed to query IBC Connection by id. Please try again.";
queryClient.prefetchQuery({
queryFn: () => getIbcConnection({ endpoint, connectionId }),
queryKey: ["IbcConnection", connectionId],
meta: {
errorMessage: "Failed to query IBC Connection by id. Please try again.",
errorMessage,
},
});

if (isError || !data) {
return (
<div className="py-5 flex justify-center">
<h1 className="text-4xl font-semibold">No results found.</h1>
</div>
);
}

const { connection_id: connectionId, client_id: clientId, channel_ids: channelIds } = data[0];

return (
<div className="bg-primary rounded-sm shadow-md">
{isFetched ? (
<div className="flex flex-col items-center gap-5 pt-5">
<h1 className="sm:text-2xl text-lg font-bold">IBC Connection</h1>
<div className="sm:w-11/12 w-full">
<div className="flex flex-col justify-start p-5 gap-y-5 w-full">
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Connection ID</p>
<pre className="sm:w-0 w-full">{connectionId}</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Client ID</p>
<Link href={`/ibc/client/${clientId}`} className="underline sm:w-0 w-full"><pre>{clientId}</pre></Link>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full font-semibold">Channel IDs</p>
<div className="">
{channelIds.map(( channelId, i ) => <Link href={`/ibc/channel/${channelId}`} key={i} className="underline sm:w-0 w-full"><pre>{channelId}</pre></Link>)}
</div>
</div>
</div>
</div>
</div>
) : (
<p>loading...</p>
)}
<div className="bg-primary flex flex-col gap-5 pt-5 items-center ">
<h1 className="font-medium">IBC Connection</h1>
<HydrationBoundary state={dehydrate(queryClient)}>
<div className="sm:w-11/12 w-full">
<Connection {...{endpoint, queryName, connectionId}}/>
</div>
</HydrationBoundary>
</div>
);
};
Expand Down
55 changes: 33 additions & 22 deletions src/components/Transaction/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,41 @@ export const Transaction : FC<TransactionProps> = ({ endpoint, queryName, hash }

const [ txResult, penumbraTx ] = data;
return (
<div>
<div className="flex flex-wrap justify-between sm:p-5 p-2 sm:gap-y-10 gap-y-5 w-full">
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full sm:text-lg font-semibold">Hash</p>
{/* TODO: this width on smaller screens is pretty arbitrary and there's a few instances of this now through the codebase. revisit and implement as consistent tailwind variables. */}
{/* TODO: also at the point where JS should be used for copying to clipboard which mitigates most text overflow issues and opens up stylings for improvement. */}
<pre className="sm:w-5/6 w-[300px] sm:text-lg break-all whitespace-pre-wrap">{txResult.tx_hash}</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full sm:text-lg font-semibold">Block Height</p>
<Link href={`/block/${txResult.height}`}><pre className="underline sm:w-0 sm:text-lg w-full">{txResult.height.toString()}</pre></Link>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full sm:text-lg font-semibold">Timestamp</p>
<pre className="sm:w-0 w-full sm:text-lg">{txResult.created_at}</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 sm:shrink-0 w-full sm:text-lg font-semibold">Transaction Data</p>
<pre className="sm:w-5/6 w-full break-all whitespace-pre-wrap text-xs p-1 bg-slate-100">
<JsonView data={penumbraTx as object} shouldExpandNode={allExpanded} clickToExpandNode={true} style={defaultStyles}/>
<div className="flex flex-wrap justify-between sm:p-5 p-2 sm:gap-y-10 gap-y-5 w-full">
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full sm:text-lg font-semibold">Hash</p>
{/* TODO: this width on smaller screens is pretty arbitrary and there's a few instances of this now through the codebase. revisit and implement as consistent tailwind variables. */}
{/* TODO: also at the point where JS should be used for copying to clipboard which mitigates most text overflow issues and opens up stylings for improvement. */}
<pre className="sm:w-5/6 w-[300px] sm:text-lg break-all whitespace-pre-wrap">
{txResult.tx_hash}
</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full sm:text-lg font-semibold">Block Height</p>
<Link href={`/block/${txResult.height}`}>
<pre className="underline sm:w-0 sm:text-lg w-full">
{txResult.height.toString()}
</pre>
</div>
<TransactionView tx={penumbraTx}/>
</Link>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 w-full sm:text-lg font-semibold">Timestamp</p>
<pre className="sm:w-0 w-full sm:text-lg">{txResult.created_at}</pre>
</div>
<div className="flex flex-wrap justify-start w-full">
<p className="sm:w-1/6 sm:shrink-0 w-full sm:text-lg font-semibold">
Transaction Data
</p>
<pre className="sm:w-5/6 w-full break-all whitespace-pre-wrap text-xs p-1 bg-slate-100">
<JsonView
data={penumbraTx as object}
shouldExpandNode={allExpanded}
clickToExpandNode={true}
style={defaultStyles}
/>
</pre>
</div>
<TransactionView tx={penumbraTx} />
</div>
);
};
5 changes: 5 additions & 0 deletions src/components/ibc/Channel/getIbcChannel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function getIbcChannel({ endpoint, channelId } : { endpoint: string, channelId: string}) {
console.log(`Fetching: GET ${endpoint}?q=${channelId}`);
const res = await fetch(`http://localhost:3000${endpoint}?q=${channelId}`, { method: "GET" });
return await res.json();
}
Loading

0 comments on commit 80bc3d3

Please sign in to comment.