Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(explorer): dozer integration #3185

Merged
merged 83 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
c8db0bf
initial chain name as url refactoring
karooolis Sep 13, 2024
67d7279
fix nav urls
karooolis Sep 13, 2024
c8c26b8
redirect chain name
karooolis Sep 13, 2024
a0d2f83
use wagmi config from useConfig
karooolis Sep 13, 2024
632cd23
use chain id from url params
karooolis Sep 13, 2024
552a1c2
dynamically set anvil connectors
karooolis Sep 13, 2024
b7e2520
check isAnvil in ConnectButton
karooolis Sep 13, 2024
03e36e1
check isAnvil in ConnectButton
karooolis Sep 13, 2024
c6c1252
useChain helper
karooolis Sep 13, 2024
af9dfe1
useChain helper
karooolis Sep 13, 2024
f91da90
fix 404 and error pages, add favicon
karooolis Sep 16, 2024
a0fa830
remove isAnvil helper
karooolis Sep 16, 2024
025af62
add chain id + name validation
karooolis Sep 16, 2024
3009ef4
use asserts for chain checking
karooolis Sep 16, 2024
ea4573b
typecheck chain id within router
karooolis Sep 16, 2024
9c64ece
typecheck chain id within router
karooolis Sep 16, 2024
fafc6d4
memoize wagmi config
karooolis Sep 16, 2024
99c2069
move provider, remove redundant layout
karooolis Sep 16, 2024
748929e
cleanup
karooolis Sep 16, 2024
359bc50
explicitly add favicon
karooolis Sep 16, 2024
9b0a018
dozer demo setup
karooolis Sep 17, 2024
dd6e63c
Merge branch 'main' into kumpis/dozer-api-logs
karooolis Sep 17, 2024
8c0a2dc
remove stash
karooolis Sep 17, 2024
b18924b
remove usePrevious
karooolis Sep 17, 2024
471d994
Merge branch 'main' into kumpis/dozer-api-logs
karooolis Sep 17, 2024
0e0679e
remove DataExplorer
karooolis Sep 17, 2024
4f29768
cleanup
karooolis Sep 17, 2024
5d9d739
unfreeze lockfile
karooolis Sep 17, 2024
7a38195
add anvil connector, standardize tables viewer + selector
karooolis Sep 18, 2024
38c9922
unfreeze lockfile
karooolis Sep 18, 2024
3a948d1
rename keysTuple to keyTuple
karooolis Sep 18, 2024
61e73fe
cleanup
karooolis Sep 18, 2024
54a1796
update tables route
karooolis Sep 18, 2024
cf30d82
update useEffect condition
karooolis Sep 18, 2024
db37a2a
Merge branch 'main' into kumpis/dozer-api-logs
karooolis Sep 19, 2024
a1dee57
Merge branch 'main' into kumpis/dozer-api-logs
karooolis Sep 19, 2024
4cef84e
sort by internal mud namespaces
karooolis Sep 19, 2024
73e2807
snake-case sql table names
karooolis Sep 19, 2024
5a9fdb1
mud internal tables as non-editable
karooolis Sep 19, 2024
ff51675
string[] type for mudInternalTables
karooolis Sep 19, 2024
77fa429
fix table select, update query
karooolis Sep 19, 2024
1b9f4e6
update query
karooolis Sep 19, 2024
d5a6ff2
styling for no table
karooolis Sep 19, 2024
a047099
remove unused common const
karooolis Sep 20, 2024
fde1868
Create cyan-chefs-obey.md
karooolis Sep 20, 2024
af22d2e
Merge branch 'main' into kumpis/dozer-api-logs
karooolis Sep 20, 2024
391a980
Update packages/explorer/src/common.ts
karooolis Sep 20, 2024
39bf564
Merge branch 'kumpis/dozer-api-logs' of github.com:latticexyz/mud int…
karooolis Sep 20, 2024
957ef6c
update internal tables references
karooolis Sep 20, 2024
24e4593
use fetchRecords helper
karooolis Sep 20, 2024
46a39a3
cleanup sqlite
karooolis Sep 20, 2024
f5985a0
fix sqlite useDeployedTablesQuery
karooolis Sep 20, 2024
a8bfbe8
move tables decoding to routes, standardize queries (wip)
karooolis Sep 20, 2024
387bd53
move queries logic into api
karooolis Sep 23, 2024
b3f3952
Merge branch 'main' into kumpis/dozer-api-logs
karooolis Sep 23, 2024
bf1e15c
cleanup
karooolis Sep 23, 2024
1070c7f
fix paths
karooolis Sep 23, 2024
ce2f026
undo mprocs changes
karooolis Sep 23, 2024
5a491a5
update changelog
karooolis Sep 23, 2024
997f87a
use proper-cased column names
karooolis Sep 23, 2024
f3d46aa
handle empty dozer result
karooolis Sep 23, 2024
5a61d95
Merge branch 'main' into kumpis/dozer-api-logs
karooolis Sep 23, 2024
9d0cb6c
rename explorer
karooolis Sep 23, 2024
dcdc424
update tables query cache keys
karooolis Sep 23, 2024
f1f5de4
update dozer response type
karooolis Sep 23, 2024
e7f9608
wip
karooolis Sep 24, 2024
ff4b074
refactor to match sqlite and dozer endpoints
karooolis Sep 24, 2024
d8fb9c5
Merge branch 'main' into kumpis/dozer-api-logs
karooolis Sep 24, 2024
fca3362
correct dozer fetch tables query
karooolis Sep 24, 2024
87fcb7c
various fixes and improvements
karooolis Sep 24, 2024
a669ecb
better table viewer loading + queries handling
karooolis Sep 24, 2024
43e02aa
only show keys that are in schema
karooolis Sep 24, 2024
bf25702
keep query on reload
karooolis Sep 24, 2024
a4a1d24
add missing query dep, editable table cell cleanup
karooolis Sep 24, 2024
aa68217
Update packages/explorer/src/app/(explorer)/hooks/useIndexerApiUrl.ts
karooolis Sep 24, 2024
ed2c113
use indexer helper, update changeset, move utils
karooolis Sep 25, 2024
ea41f8d
Update packages/explorer/src/app/(explorer)/api/utils/decodeTable.ts
karooolis Sep 25, 2024
312bb10
cleanup editable table cell
karooolis Sep 25, 2024
3f06da6
use protocol-parser values to derive keySchema & valueSchema
karooolis Sep 25, 2024
0e65cb8
rename deployed table
karooolis Sep 25, 2024
a132bc1
use getSchemaTypes helper
karooolis Sep 25, 2024
594e162
use getKeyTuple
karooolis Sep 25, 2024
6b8a21a
rename back to table
karooolis Sep 25, 2024
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/cyan-chefs-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/explorer": patch
---

Hosted SQL API integration has been added for Redstone and Garnet.
karooolis marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions packages/explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"dependencies": {
"@hookform/resolvers": "^3.9.0",
"@latticexyz/common": "workspace:*",
"@latticexyz/config": "workspace:*",
"@latticexyz/protocol-parser": "workspace:*",
"@latticexyz/schema-type": "workspace:*",
"@latticexyz/store": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { RainbowKitProvider, darkTheme } from "@rainbow-me/rainbowkit";
import "@rainbow-me/rainbowkit/styles.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { getDefaultAnvilConnectors } from "../../../../../connectors/anvil";
import { useChain } from "../../../../../hooks/useChain";
import { useChain } from "../../../hooks/useChain";

const queryClient = new QueryClient();

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,39 @@ import { Hex } from "viem";
import { useAccount, useConfig } from "wagmi";
import { ChangeEvent, useState } from "react";
import { encodeField, getFieldIndex } from "@latticexyz/protocol-parser/internal";
import { SchemaAbiType } from "@latticexyz/schema-type/internal";
import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { waitForTransactionReceipt, writeContract } from "@wagmi/core";
import { Checkbox } from "../../../../../../components/ui/Checkbox";
import { useChain } from "../../../../../../hooks/useChain";
import { camelCase, cn } from "../../../../../../lib/utils";
import { TableConfig } from "../../../../../api/table/route";
import { DeployedTable } from "../../../../api/utils/decodeTable";
import { useChain } from "../../../../hooks/useChain";
import { cn } from "../../../../lib/utils";

type Props = {
name: string;
value: string;
keyTuple: string[];
config: TableConfig;
deployedTable: DeployedTable;
};

export function EditableTableCell({ name, config, keyTuple, value: defaultValue }: Props) {
export function EditableTableCell({ name, deployedTable, keyTuple, value: defaultValue }: Props) {
const [value, setValue] = useState<unknown>(defaultValue);
const wagmiConfig = useConfig();
const queryClient = useQueryClient();
const { worldAddress } = useParams();
const { id: chainId } = useChain();
const account = useAccount();

const [value, setValue] = useState<unknown>(defaultValue);

const tableId = config?.table_id;
const fieldType = config?.value_schema[camelCase(name)] as SchemaAbiType;
const tableId = deployedTable?.tableId;
const valueSchema = deployedTable?.valueSchema;
const type = valueSchema[name]?.type;

const { mutate, isPending } = useMutation({
mutationFn: async (newValue: unknown) => {
const fieldIndex = getFieldIndex(config?.value_schema, camelCase(name));
const encodedField = encodeField(fieldType, newValue);
const fieldIndex = getFieldIndex(
Object.fromEntries(Object.entries(valueSchema).map(([key, value]) => [key, value.type])),
name,
);
const encodedField = encodeField(type, newValue);
const txHash = await writeContract(wagmiConfig, {
abi: IBaseWorldAbi,
address: worldAddress as Hex,
Expand Down Expand Up @@ -86,7 +87,7 @@ export function EditableTableCell({ name, config, keyTuple, value: defaultValue
}
};

if (fieldType === "bool") {
if (type === "bool") {
return (
<>
<Checkbox
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import { useParams } from "next/navigation";
import { parseAsString, useQueryState } from "nuqs";
import { Hex } from "viem";
import { anvil } from "viem/chains";
import { useEffect } from "react";
import { useChain } from "../../../../hooks/useChain";
import { getTableName } from "../../../../lib/getTableName";
import { useDeployedTablesQuery } from "../../../../queries/useDeployedTablesQuery";
import { useTableDataQuery } from "../../../../queries/useTableDataQuery";
import { SQLEditor } from "./SQLEditor";
import { TableSelector } from "./TableSelector";
import { TablesViewer } from "./TablesViewer";

export function Explorer() {
const { worldAddress } = useParams();
const { id: chainId } = useChain();
const [query, setQuery] = useQueryState("query", parseAsString.withDefault(""));
const [selectedTableId] = useQueryState("tableId");

const { data: deployedTables } = useDeployedTablesQuery();
const deployedTable = deployedTables?.find(({ tableId }) => tableId === selectedTableId);
const { data: tableData, isLoading, isFetched } = useTableDataQuery({ deployedTable, query });

useEffect(() => {
if (!deployedTable) return;

const tableName = getTableName(deployedTable, worldAddress as Hex, chainId);
if (chainId === anvil.id) {
setQuery(`SELECT * FROM "${tableName}"`);
} else {
setQuery(`SELECT ${Object.keys(deployedTable.schema).join(", ")} FROM ${tableName}`);
}
}, [chainId, setQuery, selectedTableId, deployedTable, worldAddress]);

return (
<>
{chainId !== anvil.id && <SQLEditor />}
<TableSelector tables={deployedTables} />
<TablesViewer deployedTable={deployedTable} tableData={tableData} isLoading={isLoading || !isFetched} />
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { PlayIcon } from "lucide-react";
import { useQueryState } from "nuqs";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { Button } from "../../../../../../components/ui/Button";
import { Form, FormControl, FormField, FormItem } from "../../../../../../components/ui/Form";
import { Input } from "../../../../../../components/ui/Input";

export function SQLEditor() {
const [query, setQuery] = useQueryState("query");
const form = useForm({
defaultValues: {
query: query || "",
},
});

const handleSubmit = form.handleSubmit((data) => {
setQuery(data.query);
});

useEffect(() => {
form.reset({ query: query || "" });
}, [query, form]);

return (
<Form {...form}>
<form onSubmit={handleSubmit}>
<div className="relative">
<FormField
name="query"
render={({ field }) => (
<FormItem>
<FormControl>
<Input {...field} className="pr-[90px]" disabled={!query} />
</FormControl>
</FormItem>
)}
/>

<Button className="absolute right-1 top-1 h-8 px-4" type="submit">
<PlayIcon className="mr-1.5 h-3 w-3" /> Run
</Button>
</div>
</form>
</Form>
);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Check, ChevronsUpDown, Lock } from "lucide-react";
import { useParams } from "next/navigation";
import { CheckIcon, ChevronsUpDownIcon, Link2Icon, Link2OffIcon } from "lucide-react";
import { useQueryState } from "nuqs";
import { Hex } from "viem";
import { useEffect, useState } from "react";
import { internalTableNames } from "@latticexyz/store-sync/sqlite";
import { useState } from "react";
import { useEffect } from "react";
import { Button } from "../../../../../../components/ui/Button";
import {
Command,
Expand All @@ -14,59 +13,77 @@ import {
CommandList,
} from "../../../../../../components/ui/Command";
import { Popover, PopoverContent, PopoverTrigger } from "../../../../../../components/ui/Popover";
import { cn } from "../../../../../../lib/utils";
import { DeployedTable } from "../../../../api/utils/decodeTable";
import { cn } from "../../../../lib/utils";

type Props = {
tables: string[];
};
function TableSelectorItem({
table,
selected,
asOption,
}: {
table: DeployedTable;
selected: boolean;
asOption?: boolean;
}) {
const { type, name, namespace } = table;
return (
<div className="flex items-center">
{asOption && <CheckIcon className={cn("mr-2 h-4 w-4", selected ? "opacity-100" : "opacity-0")} />}
{type === "offchainTable" && <Link2OffIcon className="mr-2 inline-block opacity-70" size={14} />}
{type === "table" && <Link2Icon className="mr-2 inline-block opacity-70" size={14} />}
{name} {namespace && <span className="ml-2 opacity-70">({namespace})</span>}
</div>
);
}

export function TableSelector({ tables }: Props) {
export function TableSelector({ tables }: { tables?: DeployedTable[] }) {
const [selectedTableId, setTableId] = useQueryState("tableId");
const [open, setOpen] = useState(false);
const { worldAddress } = useParams();
const selectedTable = tables?.find(({ tableId }) => tableId === selectedTableId);

useEffect(() => {
if (!selectedTableId && tables.length > 0) {
setTableId(tables[0] as Hex);
if (!selectedTableId && Array.isArray(tables) && tables.length > 0) {
setTableId(tables[0].tableId);
}
}, [selectedTableId, setTableId, tables]);

return (
<div className="w-full py-4">
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button variant="outline" role="combobox" aria-expanded={open} className="w-full justify-between">
{selectedTableId
? tables.find((tableId) => tableId === selectedTableId)?.replace(`${worldAddress}__`, "")
: "Select table..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-full justify-between"
disabled={!tables}
>
{selectedTable && (
<TableSelectorItem table={selectedTable} selected={selectedTableId === selectedTable.tableId} />
)}
{!selectedTable && <span className="opacity-50">Select table...</span>}
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>

<PopoverContent className="w-[--radix-popover-trigger-width] p-0">
<Command>
<CommandInput placeholder="Search tables..." className="font-mono" />
<CommandList>
<CommandEmpty>No framework found.</CommandEmpty>
<CommandEmpty className="py-4 text-center font-mono text-sm">No table found.</CommandEmpty>
<CommandGroup>
{tables.map((tableId) => {
{tables?.map((table) => {
return (
<CommandItem
key={tableId}
value={tableId}
key={table.tableId}
value={table.tableId}
onSelect={(newTableId) => {
setTableId(newTableId as Hex);
setOpen(false);
}}
className="font-mono"
>
<Check
className={cn("mr-2 h-4 w-4", selectedTableId === tableId ? "opacity-100" : "opacity-0")}
/>
{(internalTableNames as string[]).includes(tableId) && (
<Lock className="mr-2 inline-block opacity-70" size={14} />
)}
{tableId.replace(`${worldAddress}__`, "")}
<TableSelectorItem table={table} selected={selectedTableId === table.tableId} asOption />
</CommandItem>
);
})}
Expand Down
Loading
Loading