namespace tracking and creation

This commit is contained in:
Emmaline Autumn 2024-10-19 12:40:53 -06:00
parent 0941690f91
commit 0517e7c2e2
9 changed files with 141 additions and 4 deletions

View File

@ -120,6 +120,33 @@ router.route("/api/pack")
return new Response(JSON.stringify({ packName }), { status: 200 });
});
router.route("/api/pack/namespaces")
.get(async () => {
using store = new BearMetalStore();
const namespaces = Array.from(Deno.readDirSync(store.get("packlocation")))
.filter((dir) => dir.isDirectory).map((dir) => dir.name);
return new Response(JSON.stringify(namespaces), { status: 200 });
})
.post(async (req) => {
using store = new BearMetalStore();
const namespace = await req.text();
if (!namespace) {
return new Response("Namespace is required", { status: 400 });
}
const namespaceRx = /^[a-zA-Z0-9_\-]+$/;
if (!namespaceRx.test(namespace)) {
return new Response(
"Namespace must only contain letters, numbers, underscores, and dashes",
{ status: 400 },
);
}
await ensureDir(store.get("packlocation") + "/" + namespace);
return new Response(null, { status: 200 });
});
router.route("/api/packs")
.get(async () => {
using store = new BearMetalStore();

View File

@ -7,7 +7,7 @@ export function App() {
<BrowserRouter>
<Routes>
<Route path="/" Component={Home} />
<Route path="/editor" Component={Editor} />
<Route path="/editor/*" Component={Editor} />
</Routes>
</BrowserRouter>
);

3
src/atoms/namespace.ts Normal file
View File

@ -0,0 +1,3 @@
import { atom } from "jotai";
export const namespaceAtom = atom<string>("");

View File

@ -0,0 +1,62 @@
import { useState } from "preact/hooks";
import { fetchJson } from "../../util/fetchJson.ts";
import { Loader } from "../loader.tsx";
import { Modal } from "../modal.tsx";
import useSwr from "swr";
export const NamespaceModal = ({ close }: { close: () => void }) => {
const { data: namespaces, isLoading } = useSwr<string[]>(
"/api/pack/namespaces",
fetchJson,
);
const [namespace, setNamespace] = useState("");
const [invalid, setInvalid] = useState("");
const createNamespace = async (ns?: string) => {
const res = await fetch("/api/pack/namespaces", {
method: "POST",
body: ns ?? namespace,
});
if (res.status === 200) {
return close();
}
setInvalid(await res.text());
};
return (
<Modal>
{isLoading
? <Loader msg="Checking existing namespaces..." />
: (
<div class="flex flex-col gap-2">
<p>Create a new namespace</p>
{!namespaces?.includes("minecraft") &&
(
<button
class="w-full"
onClick={() => createNamespace("minecraft")}
>
Create default minecraft namespace
</button>
)}
<form
onSubmit={(e) => {
e.preventDefault();
createNamespace();
}}
class="flex gap-2"
>
<input
type="text"
value={namespace}
onInput={(e) => setNamespace((e.target as any).value)}
/>
<button type="submit">Create</button>
</form>
</div>
)}
</Modal>
);
};

View File

@ -0,0 +1,3 @@
export const Selector = () => {
return <div>Selector</div>;
};

View File

@ -1,4 +1,4 @@
import { classList } from "../classes.ts";
import { classList } from "../util/classes.ts";
interface IProps {
msg?: string;

2
src/util/fetchJson.ts Normal file
View File

@ -0,0 +1,2 @@
export const fetchJson = (url: string, init?: RequestInit) =>
fetch(url, init).then((res) => res.json());

View File

@ -3,11 +3,23 @@ import { Modal } from "../components/modal.tsx";
import { LabelledHr } from "../components/labelledHr.tsx";
import { PacksList } from "../components/packsList.tsx";
import { PackInfo } from "../components/packInfo.tsx";
import { useAtom } from "jotai";
import { namespaceAtom } from "../atoms/namespace.ts";
import { Route, Routes } from "react-router-dom";
import { Selector } from "../components/editor/selector.tsx";
import { NamespaceModal } from "../components/editor/namespaceModal.tsx";
export const Editor = () => {
const [packName, setPackName] = useState("");
const [loading, setLoading] = useState(true);
const [namespaces, setNamespaces] = useState<string[]>([]);
const [namespace, setNamespace] = useAtom(namespaceAtom);
const fetchNamespaces = async () => {
const res = await fetch("/api/pack/namespaces");
const json = await res.json();
setNamespaces(json);
};
useEffect(() => {
document.title = "BearMetal Packer";
@ -15,6 +27,7 @@ export const Editor = () => {
setPackName(json.packName);
setLoading(false);
});
fetchNamespaces();
}, []);
const setPackNameThing = async (event: SubmitEvent) => {
@ -56,9 +69,11 @@ export const Editor = () => {
);
}
const [showNamespaceModal, setShowNamespaceModal] = useState(false);
return (
<div class="flex h-full">
<div class="w-1/4 p-4 bg-primary-600">
<div class="w-1/4 p-4 bg-mixed-400">
<div class="text-center">
<h1 class="text-2xl">BearMetalPacker</h1>
<PackInfo packName={packName} />
@ -67,14 +82,39 @@ export const Editor = () => {
<div>
<ul class="w-full">
{namespaces.map((namespace) => (
<li class="cursor-pointer even:bg-black/5" // onClick={() => setNamespace(namespace)}
<li
class="text-lg cursor-pointer even:bg-black/5"
onClick={() => setNamespace(namespace)}
>
{namespace}
</li>
))}
<li class="mt-4">
<button onClick={() => setShowNamespaceModal(true)}>
New Namespace
</button>
</li>
</ul>
{showNamespaceModal && (
<NamespaceModal
close={() => {
setShowNamespaceModal(false);
fetchNamespaces();
}}
/>
)}
</div>
</div>
<div>
{!namespace ? <div>No namespace set</div> : (
<div>
<h3 class="text-lg">Namespace: {namespace}</h3>
<Routes>
<Route path="/editor" Component={Selector} />
</Routes>
</div>
)}
</div>
</div>
);
};