From fa0f393cbb351812927aa30a3944e7c4e85cc653 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Mon, 29 Jul 2024 17:05:06 +0200 Subject: [PATCH] Clan-app: edit clan, memoize active clan --- pkgs/clan-cli/clan_cli/__init__.py | 4 +- pkgs/webview-ui/app/src/App.tsx | 17 +- pkgs/webview-ui/app/src/layout/header.tsx | 46 +++-- pkgs/webview-ui/app/src/layout/layout.tsx | 4 +- .../app/src/routes/clan/editClan.tsx | 175 ++++++++++++++++++ pkgs/webview-ui/app/src/routes/disk/view.tsx | 6 +- .../app/src/routes/settings/index.tsx | 112 ++++++----- 7 files changed, 286 insertions(+), 78 deletions(-) create mode 100644 pkgs/webview-ui/app/src/routes/clan/editClan.tsx diff --git a/pkgs/clan-cli/clan_cli/__init__.py b/pkgs/clan-cli/clan_cli/__init__.py index 29acb1e59..e155a04d9 100644 --- a/pkgs/clan-cli/clan_cli/__init__.py +++ b/pkgs/clan-cli/clan_cli/__init__.py @@ -7,10 +7,10 @@ from types import ModuleType # These imports are unused, but necessary for @API.register to run once. from clan_cli.api import directory, mdns_discovery, modules from clan_cli.arg_actions import AppendOptionAction -from clan_cli.clan import show +from clan_cli.clan import show, update # API endpoints that are not used in the cli. -__all__ = ["directory", "mdns_discovery", "modules"] +__all__ = ["directory", "mdns_discovery", "modules", "update"] from . import ( backups, diff --git a/pkgs/webview-ui/app/src/App.tsx b/pkgs/webview-ui/app/src/App.tsx index 9c056ae8c..016cff09f 100644 --- a/pkgs/webview-ui/app/src/App.tsx +++ b/pkgs/webview-ui/app/src/App.tsx @@ -1,4 +1,4 @@ -import { createSignal, type Component } from "solid-js"; +import { createEffect, createSignal, type Component } from "solid-js"; import { Layout } from "./layout/layout"; import { Route, Router } from "./Routes"; import { Toaster } from "solid-toast"; @@ -7,9 +7,20 @@ import { makePersisted } from "@solid-primitives/storage"; // Some global state const [route, setRoute] = createSignal("machines"); +createEffect(() => { + console.log(route()); +}); + export { route, setRoute }; -const [activeURI, setActiveURI] = createSignal(null); +const [activeURI, setActiveURI] = makePersisted( + createSignal(null), + { + name: "activeURI", + storage: localStorage, + } +); + export { activeURI, setActiveURI }; const [clanList, setClanList] = makePersisted(createSignal([]), { @@ -17,8 +28,6 @@ const [clanList, setClanList] = makePersisted(createSignal([]), { storage: localStorage, }); -clanList() && setActiveURI(clanList()[0]); - export { clanList, setClanList }; const App: Component = () => { diff --git a/pkgs/webview-ui/app/src/layout/header.tsx b/pkgs/webview-ui/app/src/layout/header.tsx index bc9234d75..1bae01663 100644 --- a/pkgs/webview-ui/app/src/layout/header.tsx +++ b/pkgs/webview-ui/app/src/layout/header.tsx @@ -1,15 +1,20 @@ import { createQuery } from "@tanstack/solid-query"; import { activeURI, setRoute } from "../App"; import { callApi } from "../api"; -import { Show } from "solid-js"; +import { Accessor, createEffect, Show } from "solid-js"; -export const Header = () => { - const { isLoading, data } = createQuery(() => ({ - queryKey: [`${activeURI()}:meta`], +interface HeaderProps { + clan_dir: Accessor; +} +export const Header = (props: HeaderProps) => { + const { clan_dir } = props; + + const query = createQuery(() => ({ + queryKey: [clan_dir(), "meta"], queryFn: async () => { - const currUri = activeURI(); - if (currUri) { - const result = await callApi("show_clan_meta", { uri: currUri }); + const curr = clan_dir(); + if (curr) { + const result = await callApi("show_clan_meta", { uri: curr }); if (result.status === "error") throw new Error("Failed to fetch data"); return result.data; } @@ -29,16 +34,25 @@ export const Header = () => {
-
-
-
- C - - {(name) => {name()}} - + + {(meta) => ( +
+
+
+ {meta().name.slice(0, 1)} +
+
-
-
+ )} + + + + {(meta) => [ + {meta().name}, + {meta()?.description}, + ]} + +
diff --git a/pkgs/webview-ui/app/src/layout/layout.tsx b/pkgs/webview-ui/app/src/layout/layout.tsx index 84412fc9f..616faa57d 100644 --- a/pkgs/webview-ui/app/src/layout/layout.tsx +++ b/pkgs/webview-ui/app/src/layout/layout.tsx @@ -1,7 +1,7 @@ import { Component, JSXElement, Show } from "solid-js"; import { Header } from "./header"; import { Sidebar } from "../Sidebar"; -import { clanList, route, setRoute } from "../App"; +import { activeURI, clanList, route, setRoute } from "../App"; interface LayoutProps { children: JSXElement; @@ -18,7 +18,7 @@ export const Layout: Component = (props) => { />
-
+
{props.children}
diff --git a/pkgs/webview-ui/app/src/routes/clan/editClan.tsx b/pkgs/webview-ui/app/src/routes/clan/editClan.tsx new file mode 100644 index 000000000..6494d1e7d --- /dev/null +++ b/pkgs/webview-ui/app/src/routes/clan/editClan.tsx @@ -0,0 +1,175 @@ +import { OperationResponse, callApi, pyApi } from "@/src/api"; +import { Accessor, Show, Switch, Match } from "solid-js"; +import { + SubmitHandler, + createForm, + required, + reset, +} from "@modular-forms/solid"; +import toast from "solid-toast"; +import { createQuery } from "@tanstack/solid-query"; + +type CreateForm = Meta; + +interface EditClanFormProps { + directory: Accessor; + done: () => void; +} +export const EditClanForm = (props: EditClanFormProps) => { + const { directory } = props; + const details = createQuery(() => ({ + queryKey: [directory(), "meta"], + queryFn: async () => { + const result = await callApi("show_clan_meta", { uri: directory() }); + if (result.status === "error") throw new Error("Failed to fetch data"); + return result.data; + }, + })); + + return ( + + + {(data) => ( + + )} + + + ); +}; + +interface FinalEditClanFormProps { + initial: CreateForm; + directory: string; + done: () => void; +} +export const FinalEditClanForm = (props: FinalEditClanFormProps) => { + const [formStore, { Form, Field }] = createForm({ + initialValues: props.initial, + }); + + const handleSubmit: SubmitHandler = async (values, event) => { + await toast.promise( + (async () => { + await callApi("update_clan_meta", { + options: { + directory: props.directory, + meta: values, + }, + }); + })(), + { + loading: "Updating clan...", + success: "Clan Successfully updated", + error: "Failed to update clan", + } + ); + props.done(); + }; + + return ( +
+
+ + {(field, props) => ( + <> +
+ + group + + } + > + {(icon) => ( + Clan Logo + )} + +
+ + )} +
+
+ + {(field, props) => ( + + )} + + + {(field, props) => ( + + )} + + { +
+ +
+ } +
+
+
+ ); +}; + +type Meta = Extract< + OperationResponse<"show_clan_meta">, + { status: "success" } +>["data"]; diff --git a/pkgs/webview-ui/app/src/routes/disk/view.tsx b/pkgs/webview-ui/app/src/routes/disk/view.tsx index c9798c119..43e2f4aff 100644 --- a/pkgs/webview-ui/app/src/routes/disk/view.tsx +++ b/pkgs/webview-ui/app/src/routes/disk/view.tsx @@ -2,24 +2,24 @@ import { callApi } from "@/src/api"; import { activeURI } from "@/src/App"; import { createQuery } from "@tanstack/solid-query"; import { createEffect } from "solid-js"; +import toast from "solid-toast"; export function DiskView() { const query = createQuery(() => ({ - queryKey: ["disk", activeURI], + queryKey: ["disk", activeURI()], queryFn: async () => { const currUri = activeURI(); if (currUri) { // Example of calling an API const result = await callApi("get_inventory", { base_path: currUri }); if (result.status === "error") throw new Error("Failed to fetch data"); - return result.data; } }, })); createEffect(() => { // Example debugging the data - console.log(query.data); + console.log(query); }); return (
diff --git a/pkgs/webview-ui/app/src/routes/settings/index.tsx b/pkgs/webview-ui/app/src/routes/settings/index.tsx index 1c83c41ab..8fa7869f7 100644 --- a/pkgs/webview-ui/app/src/routes/settings/index.tsx +++ b/pkgs/webview-ui/app/src/routes/settings/index.tsx @@ -12,18 +12,19 @@ import { setRoute, clanList, } from "@/src/App"; -import { createEffect, createSignal, For, Show } from "solid-js"; +import { + createEffect, + createSignal, + For, + Match, + Setter, + Show, + Switch, +} from "solid-js"; import { createQuery } from "@tanstack/solid-query"; import { useFloating } from "@/src/floating"; -import { - arrow, - autoUpdate, - flip, - hide, - offset, - shift, - size, -} from "@floating-ui/dom"; +import { autoUpdate, flip, hide, offset, shift } from "@floating-ui/dom"; +import { EditClanForm } from "../clan/editClan"; export const registerClan = async () => { try { @@ -51,9 +52,10 @@ export const registerClan = async () => { interface ClanDetailsProps { clan_dir: string; + setEditURI: Setter; } const ClanDetails = (props: ClanDetailsProps) => { - const { clan_dir } = props; + const { clan_dir, setEditURI } = props; const details = createQuery(() => ({ queryKey: [clan_dir, "meta"], @@ -66,7 +68,6 @@ const ClanDetails = (props: ClanDetailsProps) => { const [reference, setReference] = createSignal(); const [floating, setFloating] = createSignal(); - const [arrowEl, setArrowEl] = createSignal(); // `position` is a reactive object. const position = useFloating(reference, floating, { @@ -92,6 +93,14 @@ const ClanDetails = (props: ClanDetailsProps) => {
+
-
Clan URI
+
{clan_dir}
-
- {details.data?.name} -
+
{details.data?.name}
- {clan_dir}
} - > -
- {details.data?.description} -
+ +
{details.data?.description}
); }; export const Settings = () => { + const [editURI, setEditURI] = createSignal(null); + return (
-
-
-
Registered Clans
- -
-
- - {(value) => } - -
-
+ + + {(uri) => ( + { + setEditURI(null); + }} + /> + )} + + +
+
+
Registered Clans
+ +
+
+ + {(value) => ( + + )} + +
+
+
+
); };