Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mighty-pillows-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ensadmin": patch
---

Make the Record Resolution Inspector URL shareable by driving name state from query params, with cross-navigation links between name profiles and record inspection.
34 changes: 26 additions & 8 deletions apps/ensadmin/src/app/@actions/name/page.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,49 @@
"use client";

import { getEnsManagerNameDetailsUrl } from "@namehash/namehash-ui";
import { ScanSearch } from "lucide-react";
import Link from "next/link";
import { useSearchParams } from "next/navigation";

import type { Name } from "@ensnode/ensnode-sdk";

import { ExternalLinkWithIcon } from "@/components/link";
Comment thread
notrab marked this conversation as resolved.
Comment thread
notrab marked this conversation as resolved.
import { getRecordResolutionRelativePath } from "@/components/name-links";
import { Button } from "@/components/ui/button";
import { useNamespace } from "@/hooks/async/use-namespace";
import { useRawConnectionUrlParam } from "@/hooks/use-connection-url-param";

export default function ActionsNamePage() {
const searchParams = useSearchParams();
const nameParam = searchParams.get("name");

const name = nameParam ? (decodeURIComponent(nameParam) as Name) : null;
Comment thread
notrab marked this conversation as resolved.
const name = nameParam ? (nameParam as Name) : null;

const { data: namespace } = useNamespace();
const { retainCurrentRawConnectionUrlParam } = useRawConnectionUrlParam();

const ensAppProfileUrl = name && namespace ? getEnsManagerNameDetailsUrl(name, namespace) : null;
Comment thread
notrab marked this conversation as resolved.
if (!name) return null;

if (!ensAppProfileUrl) return null;
const inspectRecordsHref = retainCurrentRawConnectionUrlParam(
getRecordResolutionRelativePath(name),
);
const ensAppProfileUrl = namespace ? getEnsManagerNameDetailsUrl(name, namespace) : null;

return (
<Button variant="link" size="sm" asChild>
<ExternalLinkWithIcon href={ensAppProfileUrl.toString()}>
View in ENS App
</ExternalLinkWithIcon>
</Button>
<div className="flex items-center gap-2">
<Button variant="link" size="sm" asChild>
<Link href={inspectRecordsHref} className="inline-flex items-center gap-1">
Inspect Records
<ScanSearch size={12} />
</Link>
</Button>
{ensAppProfileUrl && (
<Button variant="link" size="sm" asChild>
<ExternalLinkWithIcon href={ensAppProfileUrl.toString()}>
View in ENS App
</ExternalLinkWithIcon>
</Button>
)}
</div>
);
}
40 changes: 38 additions & 2 deletions apps/ensadmin/src/app/@breadcrumbs/inspect/records/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,45 @@
import { BreadcrumbItem, BreadcrumbPage } from "@/components/ui/breadcrumb";
"use client";

import { NameDisplay } from "@namehash/namehash-ui";
import { useSearchParams } from "next/navigation";

import type { Name } from "@ensnode/ensnode-sdk";

import {
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { useRawConnectionUrlParam } from "@/hooks/use-connection-url-param";

export default function Page() {
const searchParams = useSearchParams();
const nameParam = searchParams.get("name");
Comment thread
vercel[bot] marked this conversation as resolved.
Outdated
const { retainCurrentRawConnectionUrlParam } = useRawConnectionUrlParam();
const recordsBaseHref = retainCurrentRawConnectionUrlParam("/inspect/records");

const name = nameParam ? (nameParam as Name) : null;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
Comment thread
notrab marked this conversation as resolved.
Outdated
Comment thread
notrab marked this conversation as resolved.
Outdated

if (name) {
return (
<>
<BreadcrumbItem>
<BreadcrumbLink href={recordsBaseHref}>Record Resolution</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>
<NameDisplay name={name} />
</BreadcrumbPage>
</BreadcrumbItem>
</>
);
}

return (
<BreadcrumbItem>
<BreadcrumbPage>Records Resolution</BreadcrumbPage>
<BreadcrumbPage>Record Resolution</BreadcrumbPage>
</BreadcrumbItem>
);
}
2 changes: 1 addition & 1 deletion apps/ensadmin/src/app/@breadcrumbs/name/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function Page() {
const { retainCurrentRawConnectionUrlParam } = useRawConnectionUrlParam();
const exploreNamesBaseHref = retainCurrentRawConnectionUrlParam("/name");

const name = nameParam ? (decodeURIComponent(nameParam) as Name) : null;
Comment thread
notrab marked this conversation as resolved.
const name = nameParam ? (nameParam as Name) : null;

return (
<BreadcrumbsGroup name="ENS Explorer">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ export function RenderRequestsOutput<KEY extends string>({
dataKey,
accelerated,
unaccelerated,
headerActions,
}: {
dataKey: KEY;
accelerated: QueryResult<KEY>;
unaccelerated: QueryResult<KEY>;
headerActions?: React.ReactNode;
}) {
const [tab, setTab] = useState("accelerated");

Expand Down Expand Up @@ -91,7 +93,10 @@ export function RenderRequestsOutput<KEY extends string>({
{/* Response Card */}
<Card className="w-full">
<CardHeader>
<CardTitle>ENSNode Response</CardTitle>
<CardTitle className="flex flex-row items-center justify-between gap-4">
<span>ENSNode Response</span>
{headerActions}
</CardTitle>
</CardHeader>
<CardContent className="max-h-[30rem] overflow-scroll">
{(() => {
Expand Down
30 changes: 30 additions & 0 deletions apps/ensadmin/src/app/inspect/_components/resolve-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Button } from "@/components/ui/button";

interface ResolveButtonProps {
canResolve: boolean;
hasChanged: boolean;
onRefetch: () => void;
onNavigate: () => void;
}

export function ResolveButton({
canResolve,
hasChanged,
onRefetch,
onNavigate,
}: ResolveButtonProps) {
return (
<Button
disabled={!canResolve}
onClick={() => {
if (hasChanged) {
onNavigate();
} else {
onRefetch();
}
}}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
lightwalker-eth marked this conversation as resolved.
>
Resolve
</Button>
);
}
51 changes: 37 additions & 14 deletions apps/ensadmin/src/app/inspect/primary-name/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";

import { AddressDisplay, getChainName } from "@namehash/namehash-ui";
import { useSearchParams } from "next/navigation";
import { useMemo, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import { useDebouncedValue } from "rooks";
import { type Address, isAddress } from "viem";

Expand All @@ -16,8 +16,8 @@ import { usePrimaryName } from "@ensnode/ensnode-react";
import { getNamespaceSpecificValue } from "@ensnode/ensnode-sdk";

import { RenderRequestsOutput } from "@/app/inspect/_components/render-requests-output";
import { ResolveButton } from "@/app/inspect/_components/resolve-button";
import { Pill } from "@/components/pill";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
Expand All @@ -29,6 +29,7 @@ import {
SelectValue,
} from "@/components/ui/select";
import { useActiveNamespace } from "@/hooks/active/use-active-namespace";
import { useRawConnectionUrlParam } from "@/hooks/use-connection-url-param";

import { EXAMPLE_ADDRESSES } from "../_lib/example-addresses";

Expand All @@ -47,20 +48,39 @@ const getENSIP19SupportedChainIds = (namespace: ENSNamespaceId) =>
// TODO: use shadcn/form, react-hook-form, and zod to make all of this nicer aross the board
// TODO: sync form state to query params, current just defaulting is supported
export default function ResolvePrimaryNameInspector() {
const router = useRouter();
const searchParams = useSearchParams();
const { retainCurrentRawConnectionUrlParam } = useRawConnectionUrlParam();

const namespace = useActiveNamespace();
const exampleAddresses = useMemo(
() => getNamespaceSpecificValue(namespace, EXAMPLE_ADDRESSES),
[namespace],
);

const [address, setAddress] = useState(
searchParams.get("address") || exampleAddresses[0].address,
);
const [chainId, setChainId] = useState(searchParams.get("chainId") || "1");
const addressFromQuery = searchParams.get("address")?.trim() || null;
const chainIdFromQuery = searchParams.get("chainId") || "1";

const [address, setAddress] = useState(addressFromQuery || exampleAddresses[0].address);
const [chainId, setChainId] = useState(chainIdFromQuery);
const [debouncedAddress] = useDebouncedValue(address, 150);

useEffect(() => {
setAddress(addressFromQuery ?? "");
}, [addressFromQuery]);
Comment thread
notrab marked this conversation as resolved.
Outdated

useEffect(() => {
setChainId(chainIdFromQuery);
}, [chainIdFromQuery]);
Comment thread
notrab marked this conversation as resolved.

const navigateToAddress = (addr: string, chain: string) => {
Comment thread
notrab marked this conversation as resolved.
Outdated
setAddress(addr);
setChainId(chain);
const path = `/inspect/primary-name?address=${encodeURIComponent(addr)}&chainId=${encodeURIComponent(chain)}`;
const href = retainCurrentRawConnectionUrlParam(path);
router.push(href);
};

const additionalChainIds = getENSIP19SupportedChainIds(namespace);

const canQuery = !!debouncedAddress && isAddress(debouncedAddress);
Expand Down Expand Up @@ -144,22 +164,25 @@ export default function ResolvePrimaryNameInspector() {
<span className="text-sm font-medium leading-none">Examples:</span>
{/* -mx-6 px-6 insets the scroll container against card for prettier scrolling */}
<div className="flex flex-row overflow-x-scroll gap-2 no-scrollbar -mx-6 px-6">
{exampleAddresses.map(({ address, name }) => (
{exampleAddresses.map(({ address: exampleAddress, name }) => (
<Pill
key={address}
onClick={() => {
setAddress(address);
}}
key={exampleAddress}
onClick={() => navigateToAddress(exampleAddress, chainId)}
className="font-mono"
>
<AddressDisplay address={address} /> ({name})
<AddressDisplay address={exampleAddress} /> ({name})
</Pill>
))}
</div>
</div>
</CardContent>
<CardFooter>
<Button onClick={() => refetch()}>Refresh</Button>
<ResolveButton
canResolve={isAddress(address.trim())}
hasChanged={address.trim() !== addressFromQuery || chainId !== chainIdFromQuery}
onRefetch={refetch}
onNavigate={() => navigateToAddress(address.trim(), chainId)}
/>
</CardFooter>
</Card>

Expand Down
45 changes: 34 additions & 11 deletions apps/ensadmin/src/app/inspect/primary-names/page.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,55 @@
"use client";

import { AddressDisplay } from "@namehash/namehash-ui";
import { useSearchParams } from "next/navigation";
import { useMemo, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import { useDebouncedValue } from "rooks";
import type { Address } from "viem";
import { type Address, isAddress } from "viem";

import { usePrimaryNames } from "@ensnode/ensnode-react";
import { getNamespaceSpecificValue } from "@ensnode/ensnode-sdk";

import { RenderRequestsOutput } from "@/app/inspect/_components/render-requests-output";
import { ResolveButton } from "@/app/inspect/_components/resolve-button";
import { Pill } from "@/components/pill";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useActiveNamespace } from "@/hooks/active/use-active-namespace";
import { useRawConnectionUrlParam } from "@/hooks/use-connection-url-param";

import { EXAMPLE_ADDRESSES } from "../_lib/example-addresses";

// TODO: showcase current ENSNode configuration and viable acceleration pathways?
// TODO: use shadcn/form, react-hook-form, and zod to make all of this nicer aross the board
// TODO: sync form state to query params, current just defaulting is supported
export default function ResolvePrimaryNameInspector() {
const router = useRouter();
const searchParams = useSearchParams();
const { retainCurrentRawConnectionUrlParam } = useRawConnectionUrlParam();

const namespace = useActiveNamespace();
const exampleAddresses = useMemo(
() => getNamespaceSpecificValue(namespace, EXAMPLE_ADDRESSES),
[namespace],
);

const [address, setAddress] = useState(
searchParams.get("address") || exampleAddresses[0].address,
);
const addressFromQuery = searchParams.get("address")?.trim() || null;

const [address, setAddress] = useState(addressFromQuery || exampleAddresses[0].address);
const [debouncedAddress] = useDebouncedValue(address, 150);

useEffect(() => {
setAddress(addressFromQuery ?? "");
}, [addressFromQuery]);
Comment thread
notrab marked this conversation as resolved.
Outdated

const navigateToAddress = (addr: string) => {
Comment thread
notrab marked this conversation as resolved.
Outdated
setAddress(addr);
const path = `/inspect/primary-names?address=${encodeURIComponent(addr)}`;
const href = retainCurrentRawConnectionUrlParam(path);
router.push(href);
};

const canQuery =
!!debouncedAddress && debouncedAddress.length > 0 && debouncedAddress.startsWith("0x");

Expand Down Expand Up @@ -99,16 +113,25 @@ export default function ResolvePrimaryNameInspector() {
<span className="text-sm font-medium leading-none">Examples:</span>
{/* -mx-6 px-6 insets the scroll container against card for prettier scrolling */}
<div className="flex flex-row overflow-x-scroll gap-2 no-scrollbar -mx-6 px-6">
{exampleAddresses.map(({ address, name }) => (
<Pill key={address} onClick={() => setAddress(address)} className="font-mono">
<AddressDisplay address={address} /> ({name})
{exampleAddresses.map(({ address: exampleAddress, name }) => (
<Pill
key={exampleAddress}
onClick={() => navigateToAddress(exampleAddress)}
className="font-mono"
>
<AddressDisplay address={exampleAddress} /> ({name})
</Pill>
))}
</div>
</div>
</CardContent>
<CardFooter>
<Button onClick={() => refetch()}>Refresh</Button>
<ResolveButton
canResolve={isAddress(address.trim())}
hasChanged={address.trim() !== addressFromQuery}
onRefetch={refetch}
onNavigate={() => navigateToAddress(address.trim())}
/>
</CardFooter>
</Card>

Expand Down
Loading
Loading