From acd8f48b20c7c8a63c61c5b0dae5ccbabb3eefb5 Mon Sep 17 00:00:00 2001 From: karooolis Date: Fri, 4 Oct 2024 18:24:40 +0300 Subject: [PATCH 01/14] entrance page --- .../(explorer)/[chainName]/worlds/page.tsx | 66 ++++++++++++++++--- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/page.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/page.tsx index 4358639446..f4dad27c87 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/page.tsx +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/page.tsx @@ -1,15 +1,61 @@ -import { notFound, redirect } from "next/navigation"; +"use client"; -export const dynamic = "force-dynamic"; +import { useRouter } from "next/navigation"; +import * as z from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Button } from "../../../../components/ui/Button"; +import { Form, FormControl, FormField, FormItem } from "../../../../components/ui/Form"; +import { Input } from "../../../../components/ui/Input"; -type Props = { - params: { - chainName: string; - }; -}; +const formSchema = z.object({ + worldAddress: z.string().min(1, { + message: "World address is required.", + }), +}); export default function WorldsPage({ params }: Props) { - const worldAddress = process.env.WORLD_ADDRESS; - if (worldAddress) return redirect(`/${params.chainName}/worlds/${worldAddress}`); - return notFound(); + const router = useRouter(); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + worldAddress: "", + }, + }); + + function onSubmit(values: z.infer) { + router.push(`/${params.chainName}/worlds/${values.worldAddress}`); + } + + return ( +
+

Worlds Explorer

+ +
+ + ( + + + + + + )} + /> + +
+ + +
+ + +
+ ); } From 665b880b6f193efe0929a068edf711b9febeec88 Mon Sep 17 00:00:00 2001 From: karooolis Date: Mon, 7 Oct 2024 11:35:48 +0300 Subject: [PATCH 02/14] wip --- .../[chainName]/worlds/WorldsForm.tsx | 189 ++++++++++++++++++ .../(explorer)/[chainName]/worlds/page.tsx | 79 ++------ .../explorer/src/app/(explorer)/layout.tsx | 2 +- 3 files changed, 211 insertions(+), 59 deletions(-) create mode 100644 packages/explorer/src/app/(explorer)/[chainName]/worlds/WorldsForm.tsx diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/WorldsForm.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/WorldsForm.tsx new file mode 100644 index 0000000000..a303797b82 --- /dev/null +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/WorldsForm.tsx @@ -0,0 +1,189 @@ +"use client"; + +import { Address } from "viem"; +import * as z from "zod"; +import { Command as CommandPrimitive } from "cmdk"; +import { useRef, useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Button } from "../../../../components/ui/Button"; +import { Command, CommandGroup, CommandItem, CommandList } from "../../../../components/ui/Command"; +import { Form, FormControl, FormField, FormItem } from "../../../../components/ui/Form"; +import { Input } from "../../../../components/ui/Input"; + +function Icon() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +const formSchema = z.object({ + worldAddress: z.string().min(1, { + message: "World address is required.", + }), +}); + +export function WorldsForm({ worlds }: { worlds: Address[] }) { + const inputRef = useRef(null); + const [open, setOpen] = useState(false); + const [searchValue, setSearchValue] = useState(""); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + worldAddress: "", + }, + }); + + function onSubmit(values: z.infer) { + // router.push(`/${params.chainName}/worlds/${values.worldAddress}`); + } + + function onRandomWorld() { + if (worlds.length > 0) { + // const randomWorld = worlds[Math.floor(Math.random() * worlds.length)]; + // router.push(`/${params.chainName}/worlds/${randomWorld.address}`); + } + } + + return ( +
+

+ Worlds Explorer +

+ + +
+ +
+
+ {/* setSearchValue(event.target.value)} + onBlur={() => setOpen(false)} + onFocus={() => setOpen(true)} + /> */} + + setOpen(false)} + onFocus={() => setOpen(true)} + placeholder="Enter world address..." + // className="ml-2 flex-1 bg-transparent font-mono outline-none placeholder:text-muted-foreground" + > + + +
+ +
+ + {open ? ( +
+ + {worlds?.map((world) => { + return ( + { + event.preventDefault(); + event.stopPropagation(); + }} + onSelect={(value) => { + setSearchValue(value); + setOpen(false); + }} + className="cursor-pointer font-mono" + > + {world} + + ); + })} + +
+ ) : null} +
+
+
+ +
+ + +
+
+ +
+
+ ); +} diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/page.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/page.tsx index f4dad27c87..3dc3d8ea0f 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/page.tsx +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/page.tsx @@ -1,61 +1,24 @@ -"use client"; - -import { useRouter } from "next/navigation"; -import * as z from "zod"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { Button } from "../../../../components/ui/Button"; -import { Form, FormControl, FormField, FormItem } from "../../../../components/ui/Form"; -import { Input } from "../../../../components/ui/Input"; - -const formSchema = z.object({ - worldAddress: z.string().min(1, { - message: "World address is required.", - }), -}); - -export default function WorldsPage({ params }: Props) { - const router = useRouter(); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - worldAddress: "", - }, - }); - - function onSubmit(values: z.infer) { - router.push(`/${params.chainName}/worlds/${values.worldAddress}`); +import { Address } from "viem"; +import { WorldsForm } from "./WorldsForm"; + +type ApiResponse = { + items: { + address: { + hash: Address; + }; + }[]; +}; + +async function fetchWorlds(): Promise { + const response = await fetch("https://explorer.redstone.xyz/api/v2/mud/worlds"); + if (!response.ok) { + throw new Error("Failed to fetch worlds"); } + const data: ApiResponse = await response.json(); + return data.items.map((world) => world.address.hash); +} - return ( -
-

Worlds Explorer

- -
- - ( - - - - - - )} - /> - -
- - -
- - -
- ); +export default async function WorldsPage({ params }: { params: { chainName: string } }) { + const worlds = await fetchWorlds(); + return ; } diff --git a/packages/explorer/src/app/(explorer)/layout.tsx b/packages/explorer/src/app/(explorer)/layout.tsx index 97595f9cf9..0251727abf 100644 --- a/packages/explorer/src/app/(explorer)/layout.tsx +++ b/packages/explorer/src/app/(explorer)/layout.tsx @@ -30,7 +30,7 @@ export default function RootLayout({ -
{children}
+
{children}
From e5ba2359172cc44c8b74a2d5f93fe1e3b6c4ce10 Mon Sep 17 00:00:00 2001 From: karooolis Date: Tue, 8 Oct 2024 12:56:30 +0300 Subject: [PATCH 03/14] submit entry page form, correct fetch urls --- .../[chainName]/worlds/WorldsForm.tsx | 159 ++++++------------ .../(explorer)/[chainName]/worlds/page.tsx | 23 ++- .../src/app/(explorer)/hooks/useWorldUrl.ts | 4 +- .../src/app/(explorer)/utils/getWorldUrl.ts | 5 + 4 files changed, 80 insertions(+), 111 deletions(-) create mode 100644 packages/explorer/src/app/(explorer)/utils/getWorldUrl.ts diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/WorldsForm.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/WorldsForm.tsx index a303797b82..0418019eb8 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/WorldsForm.tsx +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/WorldsForm.tsx @@ -1,141 +1,86 @@ "use client"; -import { Address } from "viem"; +import Image from "next/image"; +import { useParams, useRouter } from "next/navigation"; +import { Address, isAddress } from "viem"; import * as z from "zod"; import { Command as CommandPrimitive } from "cmdk"; -import { useRef, useState } from "react"; +import { useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Button } from "../../../../components/ui/Button"; import { Command, CommandGroup, CommandItem, CommandList } from "../../../../components/ui/Command"; -import { Form, FormControl, FormField, FormItem } from "../../../../components/ui/Form"; +import { Form, FormControl, FormField, FormItem, FormMessage } from "../../../../components/ui/Form"; import { Input } from "../../../../components/ui/Input"; - -function Icon() { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -} +import mudLogo from "../../icon.svg"; +import { getWorldUrl } from "../../utils/getWorldUrl"; const formSchema = z.object({ - worldAddress: z.string().min(1, { - message: "World address is required.", - }), + worldAddress: z + .string() + .refine((value) => isAddress(value), { + message: "Invalid world address", + }) + .transform((value): Address => value as Address), }); export function WorldsForm({ worlds }: { worlds: Address[] }) { - const inputRef = useRef(null); + const router = useRouter(); + const { chainName } = useParams(); const [open, setOpen] = useState(false); - const [searchValue, setSearchValue] = useState(""); const form = useForm>({ resolver: zodResolver(formSchema), - defaultValues: { - worldAddress: "", - }, + reValidateMode: "onChange", }); - function onSubmit(values: z.infer) { - // router.push(`/${params.chainName}/worlds/${values.worldAddress}`); + function onSubmit({ worldAddress }: z.infer) { + router.push(getWorldUrl(chainName as string, worldAddress)); } - function onRandomWorld() { + function onLuckyWorld() { if (worlds.length > 0) { - // const randomWorld = worlds[Math.floor(Math.random() * worlds.length)]; - // router.push(`/${params.chainName}/worlds/${randomWorld.address}`); + const luckyAddress = worlds[Math.floor(Math.random() * worlds.length)]; + router.push(getWorldUrl(chainName as string, luckyAddress)); } } return (

- Worlds Explorer + MUD logo Worlds Explorer

-
- {/* setSearchValue(event.target.value)} - onBlur={() => setOpen(false)} - onFocus={() => setOpen(true)} - /> */} - - setOpen(false)} - onFocus={() => setOpen(true)} - placeholder="Enter world address..." - // className="ml-2 flex-1 bg-transparent font-mono outline-none placeholder:text-muted-foreground" - > - - -
+ ( + + + { + field.onChange(value); + }} + onBlur={() => { + field.onBlur(); + setOpen(false); + }} + onFocus={() => setOpen(true)} + placeholder="Enter world address..." + > + + + + + + )} + />
@@ -152,7 +97,9 @@ export function WorldsForm({ worlds }: { worlds: Address[] }) { event.stopPropagation(); }} onSelect={(value) => { - setSearchValue(value); + form.setValue("worldAddress", value as Address, { + shouldValidate: true, + }); setOpen(false); }} className="cursor-pointer font-mono" @@ -169,13 +116,13 @@ export function WorldsForm({ worlds }: { worlds: Address[] }) {
-