diff --git a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNav.css b/pkgs/clan-app/ui/src/components/Sidebar/Sidebar.css similarity index 66% rename from pkgs/clan-app/ui/src/components/Sidebar/SidebarNav.css rename to pkgs/clan-app/ui/src/components/Sidebar/Sidebar.css index f448138ab..7884eae7d 100644 --- a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNav.css +++ b/pkgs/clan-app/ui/src/components/Sidebar/Sidebar.css @@ -1,5 +1,5 @@ div.sidebar { - @apply h-full w-auto max-w-60 border-none; + @apply w-60 border-none; & > div.header { } diff --git a/pkgs/clan-app/ui/src/components/Sidebar/Sidebar.stories.tsx b/pkgs/clan-app/ui/src/components/Sidebar/Sidebar.stories.tsx new file mode 100644 index 000000000..daa870dba --- /dev/null +++ b/pkgs/clan-app/ui/src/components/Sidebar/Sidebar.stories.tsx @@ -0,0 +1,157 @@ +import type { Meta, StoryObj } from "@kachurun/storybook-solid"; +import { + createMemoryHistory, + MemoryRouter, + Route, + RouteSectionProps, +} from "@solidjs/router"; +import { Sidebar } from "@/src/components/Sidebar/Sidebar"; +import { Suspense } from "solid-js"; +import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"; +import { addClanURI, resetStore } from "@/src/stores/clan"; +import { SolidQueryDevtools } from "@tanstack/solid-query-devtools"; +import { encodeBase64 } from "@/src/hooks/clan"; + +const defaultClanURI = "/home/brian/clans/my-clan"; + +const queryData = { + "/home/brian/clans/my-clan": { + details: { + name: "Brian's Clan", + uri: "/home/brian/clans/my-clan", + }, + machines: { + europa: { + name: "Europa", + machineClass: "nixos", + }, + ganymede: { + name: "Ganymede", + machineClass: "nixos", + }, + }, + }, + "/home/brian/clans/davhau": { + details: { + name: "Dave's Clan", + uri: "/home/brian/clans/davhau", + }, + machines: { + callisto: { + name: "Callisto", + machineClass: "nixos", + }, + amalthea: { + name: "Amalthea", + machineClass: "nixos", + }, + }, + }, + "/home/brian/clans/mic92": { + details: { + name: "Mic92's Clan", + uri: "/home/brian/clans/mic92", + }, + machines: { + thebe: { + name: "Thebe", + machineClass: "nixos", + }, + sponde: { + name: "Sponde", + machineClass: "nixos", + }, + }, + }, +}; + +const staticSections = [ + { + title: "Links", + links: [ + { label: "GitHub", path: "https://github.com/brian-the-dev" }, + { label: "Twitter", path: "https://twitter.com/brian_the_dev" }, + { + label: "LinkedIn", + path: "https://www.linkedin.com/in/brian-the-dev/", + }, + { + label: "Instagram", + path: "https://www.instagram.com/brian_the_dev/", + }, + ], + }, +]; + +const meta: Meta = { + title: "Components/Sidebar", + component: Sidebar, + render: () => { + // set history to point to our test clan + const history = createMemoryHistory(); + history.set({ value: `/clans/${encodeBase64(defaultClanURI)}` }); + + // reset local storage and then add each clan + resetStore(); + + Object.keys(queryData).forEach((uri) => addClanURI(uri)); + + return ( +
+ {props.children}} + > + } + > + +

Machine

} + /> +
+
+ +
+ ); + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: {}, + decorators: [ + (Story: StoryObj) => { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + staleTime: Infinity, + }, + }, + }); + + Object.entries(queryData).forEach(([clanURI, clan]) => { + queryClient.setQueryData( + ["clans", encodeBase64(clanURI), "details"], + clan.details, + ); + queryClient.setQueryData( + ["clans", encodeBase64(clanURI), "machines"], + clan.machines || {}, + ); + }); + + return ( + + + + ); + }, + ], +}; diff --git a/pkgs/clan-app/ui/src/components/Sidebar/Sidebar.tsx b/pkgs/clan-app/ui/src/components/Sidebar/Sidebar.tsx new file mode 100644 index 000000000..475b94b50 --- /dev/null +++ b/pkgs/clan-app/ui/src/components/Sidebar/Sidebar.tsx @@ -0,0 +1,28 @@ +import "./Sidebar.css"; +import { SidebarHeader } from "@/src/components/Sidebar/SidebarHeader"; +import { SidebarBody } from "@/src/components/Sidebar/SidebarBody"; + +export interface LinkProps { + path: string; + label?: string; +} + +export interface SectionProps { + title: string; + links: LinkProps[]; +} + +export interface SidebarProps { + staticSections?: SectionProps[]; +} + +export const Sidebar = (props: SidebarProps) => { + return ( + <> + + + ); +}; diff --git a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNavBody.css b/pkgs/clan-app/ui/src/components/Sidebar/SidebarBody.css similarity index 100% rename from pkgs/clan-app/ui/src/components/Sidebar/SidebarNavBody.css rename to pkgs/clan-app/ui/src/components/Sidebar/SidebarBody.css diff --git a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNavBody.tsx b/pkgs/clan-app/ui/src/components/Sidebar/SidebarBody.tsx similarity index 58% rename from pkgs/clan-app/ui/src/components/Sidebar/SidebarNavBody.tsx rename to pkgs/clan-app/ui/src/components/Sidebar/SidebarBody.tsx index debbed175..ccd762208 100644 --- a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNavBody.tsx +++ b/pkgs/clan-app/ui/src/components/Sidebar/SidebarBody.tsx @@ -1,46 +1,60 @@ -import "./SidebarNavBody.css"; +import "./SidebarBody.css"; import { A } from "@solidjs/router"; import { Accordion } from "@kobalte/core/accordion"; import Icon from "../Icon/Icon"; import { Typography } from "@/src/components/Typography/Typography"; -import { - MachineProps, - SidebarNavProps, -} from "@/src/components/Sidebar/SidebarNav"; -import { For } from "solid-js"; +import { For, Suspense } from "solid-js"; import { MachineStatus } from "@/src/components/MachineStatus/MachineStatus"; +import { buildMachinePath, useClanURI } from "@/src/hooks/clan"; +import { useMachinesQuery } from "@/src/queries/queries"; +import { SidebarProps } from "./Sidebar"; + +interface MachineProps { + clanURI: string; + machineID: string; + name: string; + status: MachineStatus; + serviceCount: number; +} const MachineRoute = (props: MachineProps) => ( -
- + ); -export const SidebarNavBody = (props: SidebarNavProps) => { - const sectionLabels = props.extraSections.map((section) => section.label); +export const SidebarBody = (props: SidebarProps) => { + const clanURI = useClanURI(); + const machineList = useMachinesQuery(clanURI); + + const sectionLabels = (props.staticSections || []).map( + (section) => section.title, + ); // controls which sections are open by default // we want them all to be open by default @@ -75,21 +89,27 @@ export const SidebarNavBody = (props: SidebarNavProps) => { - + + + - + {(section) => ( - + { inverted={true} color="tertiary" > - {section.label} + {section.title} .clan-icon { + @apply flex justify-center items-center; @apply rounded-full bg-inv-4 w-7 h-7; } } diff --git a/pkgs/clan-app/ui/src/components/Sidebar/SidebarHeader.tsx b/pkgs/clan-app/ui/src/components/Sidebar/SidebarHeader.tsx new file mode 100644 index 000000000..d22c75fc3 --- /dev/null +++ b/pkgs/clan-app/ui/src/components/Sidebar/SidebarHeader.tsx @@ -0,0 +1,107 @@ +import "./SidebarHeader.css"; +import Icon from "@/src/components/Icon/Icon"; +import { DropdownMenu } from "@kobalte/core/dropdown-menu"; +import { useNavigate } from "@solidjs/router"; +import { Typography } from "../Typography/Typography"; +import { createSignal, For, Suspense } from "solid-js"; +import { useAllClanDetailsQuery } from "@/src/queries/queries"; +import { navigateToClan, useClanURI } from "@/src/hooks/clan"; +import { clanURIs } from "@/src/stores/clan"; + +export const SidebarHeader = () => { + const navigate = useNavigate(); + + const [open, setOpen] = createSignal(false); + + // get information about the current active clan + const clanURI = useClanURI(); + const allClans = useAllClanDetailsQuery(clanURIs()); + + const activeClan = () => allClans.find(({ data }) => data?.uri === clanURI); + + return ( + + ); +}; diff --git a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNav.stories.tsx b/pkgs/clan-app/ui/src/components/Sidebar/SidebarNav.stories.tsx deleted file mode 100644 index ba0a3c12a..000000000 --- a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNav.stories.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import type { Meta, StoryObj } from "@kachurun/storybook-solid"; -import { - createMemoryHistory, - MemoryRouter, - Route, - RouteSectionProps, -} from "@solidjs/router"; -import { - SidebarNav, - SidebarNavProps, -} from "@/src/components/Sidebar/SidebarNav"; -import { Suspense } from "solid-js"; -import { StoryContext } from "@kachurun/storybook-solid-vite"; - -const sidebarNavProps: SidebarNavProps = { - clanLinks: [ - { label: "Brian's Clan", path: "/clans/1" }, - { label: "Dave's Clan", path: "/clans/2" }, - { label: "Mic92's Clan", path: "/clans/3" }, - ], - clanDetail: { - label: "Brian's Clan", - settingsPath: "/clans/1/settings", - machines: [ - { - label: "Backup & Home", - path: "/clans/1/machine/backup", - serviceCount: 3, - status: "Online", - }, - { - label: "Raspberry Pi", - path: "/clans/1/machine/pi", - serviceCount: 1, - status: "Offline", - }, - { - label: "Mom's Laptop", - path: "/clans/1/machine/moms-laptop", - serviceCount: 2, - status: "Installed", - }, - { - label: "Dad's Laptop", - path: "/clans/1/machine/dads-laptop", - serviceCount: 4, - status: "Not Installed", - }, - ], - }, - extraSections: [ - { - label: "Tools", - links: [ - { label: "Borgbackup", path: "/clans/1/service/borgbackup" }, - { label: "Syncthing", path: "/clans/1/service/syncthing" }, - { label: "Mumble", path: "/clans/1/service/mumble" }, - { label: "Minecraft", path: "/clans/1/service/minecraft" }, - ], - }, - { - label: "Links", - links: [ - { label: "GitHub", path: "https://github.com/brian-the-dev" }, - { label: "Twitter", path: "https://twitter.com/brian_the_dev" }, - { - label: "LinkedIn", - path: "https://www.linkedin.com/in/brian-the-dev/", - }, - { - label: "Instagram", - path: "https://www.instagram.com/brian_the_dev/", - }, - ], - }, - ], -}; - -const meta: Meta = { - title: "Components/Sidebar/Nav", - component: SidebarNav, - render: (_: never, context: StoryContext) => { - const history = createMemoryHistory(); - history.set({ value: "/clans/1/machine/backup" }); - - return ( -
- ( - - - - )} - > - <>} /> - -
- ); - }, -}; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = { - args: {}, -}; diff --git a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNav.tsx b/pkgs/clan-app/ui/src/components/Sidebar/SidebarNav.tsx deleted file mode 100644 index 78281dab6..000000000 --- a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNav.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import "./SidebarNav.css"; -import { SidebarNavHeader } from "@/src/components/Sidebar/SidebarNavHeader"; -import { SidebarNavBody } from "@/src/components/Sidebar/SidebarNavBody"; -import { MachineStatus } from "@/src/components/MachineStatus/MachineStatus"; - -export interface LinkProps { - path: string; - label?: string; -} - -export interface SectionProps { - label: string; - links: LinkProps[]; -} - -export interface MachineProps { - label: string; - path: string; - status: MachineStatus; - serviceCount: number; -} - -export interface ClanLinkProps { - label: string; - path: string; -} - -export interface ClanProps { - label: string; - settingsPath: string; - machines: MachineProps[]; -} - -export interface SidebarNavProps { - clanDetail: ClanProps; - clanLinks: ClanLinkProps[]; - extraSections: SectionProps[]; -} - -export const SidebarNav = (props: SidebarNavProps) => { - return ( - - ); -}; diff --git a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNavHeader.tsx b/pkgs/clan-app/ui/src/components/Sidebar/SidebarNavHeader.tsx deleted file mode 100644 index 6140958c1..000000000 --- a/pkgs/clan-app/ui/src/components/Sidebar/SidebarNavHeader.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import "./SidebarNavHeader.css"; -import Icon from "@/src/components/Icon/Icon"; -import { DropdownMenu } from "@kobalte/core/dropdown-menu"; -import { useNavigate } from "@solidjs/router"; -import { Typography } from "../Typography/Typography"; -import { createSignal, For } from "solid-js"; -import { ClanLinkProps, ClanProps } from "@/src/components/Sidebar/SidebarNav"; - -export interface SidebarHeaderProps { - clanDetail: ClanProps; - clanLinks: ClanLinkProps[]; -} - -export const SidebarNavHeader = (props: SidebarHeaderProps) => { - const navigate = useNavigate(); - - const [open, setOpen] = createSignal(false); - - const firstChar = props.clanDetail.label.charAt(0); - - return ( - - ); -}; diff --git a/pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.css b/pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.css index 649366633..13dacc11c 100644 --- a/pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.css +++ b/pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.css @@ -1,5 +1,5 @@ div.sidebar-pane { - @apply h-full w-auto max-w-60 border-none; + @apply w-full max-w-60 border-none; & > div.header { @apply flex items-center justify-between px-3 py-2 rounded-t-[0.5rem]; diff --git a/pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.stories.tsx b/pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.stories.tsx index ec3ade505..135c7dc0a 100644 --- a/pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.stories.tsx +++ b/pkgs/clan-app/ui/src/components/Sidebar/SidebarPane.stories.tsx @@ -11,7 +11,7 @@ import { Checkbox } from "@/src/components/Form/Checkbox"; import { Combobox } from "../Form/Combobox"; const meta: Meta = { - title: "Components/Sidebar/Pane", + title: "Components/SidebarPane", component: SidebarPane, }; diff --git a/pkgs/clan-app/ui/src/hooks/clan.ts b/pkgs/clan-app/ui/src/hooks/clan.ts index 4930d7e6e..3d5ff0946 100644 --- a/pkgs/clan-app/ui/src/hooks/clan.ts +++ b/pkgs/clan-app/ui/src/hooks/clan.ts @@ -2,6 +2,9 @@ import { callApi } from "@/src/hooks/api"; import { addClanURI, setActiveClanURI } from "@/src/stores/clan"; import { Params, Navigator, useParams } from "@solidjs/router"; +export const encodeBase64 = (value: string) => window.btoa(value); +export const decodeBase64 = (value: string) => window.atob(value); + export const selectClanFolder = async () => { const req = callApi("get_clan_folder", {}); const res = await req.result; @@ -20,12 +23,43 @@ export const selectClanFolder = async () => { throw new Error("Illegal state exception"); }; -export const navigateToClan = (navigate: Navigator, uri: string) => { - navigate("/clans/" + window.btoa(uri)); +export const buildClanPath = (clanURI: string) => { + return "/clans/" + encodeBase64(clanURI); +}; + +export const buildMachinePath = (clanURI: string, machineID: string) => { + return ( + "/clans/" + encodeBase64(clanURI) + "/machines/" + encodeBase64(machineID) + ); +}; + +export const navigateToClan = (navigate: Navigator, clanURI: string) => { + const path = buildClanPath(clanURI); + console.log("Navigating to clan", clanURI, path); + navigate(path); +}; + +export const navigateToMachine = ( + navigate: Navigator, + clanURI: string, + machineID: string, +) => { + const path = buildMachinePath(clanURI, machineID); + console.log("Navigating to machine", clanURI, machineID, path); + navigate(path); }; export const clanURIParam = (params: Params) => { - return window.atob(params.clanURI); + return decodeBase64(params.clanURI); }; export const useClanURI = () => clanURIParam(useParams()); + +export const machineIDParam = (params: Params) => { + return decodeBase64(params.machineID); +}; + +export const useMachineID = (): string => { + const params = useParams(); + return machineIDParam(params); +}; diff --git a/pkgs/clan-app/ui/src/index.tsx b/pkgs/clan-app/ui/src/index.tsx index 44751ca0b..4bcf66ad9 100644 --- a/pkgs/clan-app/ui/src/index.tsx +++ b/pkgs/clan-app/ui/src/index.tsx @@ -17,11 +17,14 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) { "Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?", ); } +if (import.meta.env.DEV) { + console.log("Development mode"); +} render( () => ( - {import.meta.env.DEV && } + {import.meta.env.DEV && } {Routes} ), diff --git a/pkgs/clan-app/ui/src/queries/queries.ts b/pkgs/clan-app/ui/src/queries/queries.ts index e4446eaf5..38e880400 100644 --- a/pkgs/clan-app/ui/src/queries/queries.ts +++ b/pkgs/clan-app/ui/src/queries/queries.ts @@ -1,20 +1,18 @@ -import { useQuery, UseQueryResult } from "@tanstack/solid-query"; +import { useQueries, useQuery, UseQueryResult } from "@tanstack/solid-query"; import { callApi, SuccessData } from "../hooks/api"; +import { encodeBase64 } from "@/src/hooks/clan"; +export type ClanDetails = SuccessData<"get_clan_details">; export type ListMachines = SuccessData<"list_machines">; export type MachinesQueryResult = UseQueryResult; -interface MachinesQueryParams { - clanURI: string; -} - -export const useMachinesQuery = (props: MachinesQueryParams) => +export const useMachinesQuery = (clanURI: string) => useQuery(() => ({ - queryKey: ["clans", props.clanURI, "machines"], + queryKey: ["clans", encodeBase64(clanURI), "machines"], queryFn: async () => { const api = callApi("list_machines", { flake: { - identifier: props.clanURI, + identifier: clanURI, }, }); const result = await api.result; @@ -25,3 +23,54 @@ export const useMachinesQuery = (props: MachinesQueryParams) => return result.data; }, })); + +export const useClanDetailsQuery = (clanURI: string) => + useQuery(() => ({ + queryKey: ["clans", encodeBase64(clanURI), "details"], + queryFn: async () => { + const call = callApi("get_clan_details", { + flake: { + identifier: clanURI, + }, + }); + const result = await call.result; + + if (result.status === "error") { + // todo should we create some specific error types? + console.error("Error fetching clan details:", result.errors); + throw new Error(result.errors[0].message); + } + + return { + uri: clanURI, + ...result.data, + }; + }, + })); + +export const useAllClanDetailsQuery = (clanURIs: string[]) => + useQueries(() => ({ + queries: clanURIs.map((clanURI) => ({ + queryKey: ["clans", encodeBase64(clanURI), "details"], + enabled: !!clanURI, + queryFn: async () => { + const call = callApi("get_clan_details", { + flake: { + identifier: clanURI, + }, + }); + const result = await call.result; + + if (result.status === "error") { + // todo should we create some specific error types? + console.error("Error fetching clan details:", result.errors); + throw new Error(result.errors[0].message); + } + + return { + uri: clanURI, + ...result.data, + }; + }, + })), + })); diff --git a/pkgs/clan-app/ui/src/routes/Clan/Clan.css b/pkgs/clan-app/ui/src/routes/Clan/Clan.css index d74a2c313..b205e8873 100644 --- a/pkgs/clan-app/ui/src/routes/Clan/Clan.css +++ b/pkgs/clan-app/ui/src/routes/Clan/Clan.css @@ -11,3 +11,14 @@ .create-modal { @apply min-w-96; } + +.sidebar-container { +} + +div.sidebar { + @apply absolute top-10 bottom-20 left-4 w-60; +} + +div.sidebar-pane { + @apply absolute top-12 bottom-20 left-[16.5rem] w-64; +} diff --git a/pkgs/clan-app/ui/src/routes/Clan/Clan.tsx b/pkgs/clan-app/ui/src/routes/Clan/Clan.tsx index 0ebd50b98..d449eaf0e 100644 --- a/pkgs/clan-app/ui/src/routes/Clan/Clan.tsx +++ b/pkgs/clan-app/ui/src/routes/Clan/Clan.tsx @@ -13,19 +13,14 @@ import "./Clan.css"; import { Modal } from "@/src/components/Modal/Modal"; import { TextInput } from "@/src/components/Form/TextInput"; import { createForm, FieldValues, reset } from "@modular-forms/solid"; +import { Sidebar } from "@/src/components/Sidebar/Sidebar"; export const Clan: Component = (props) => { return ( <> -
- {props.children} -
- + + {props.children} + ); }; @@ -89,7 +84,7 @@ const MockCreateMachine = (props: MockProps) => { ); }; -const ClanSceneController = () => { +const ClanSceneController = (props: RouteSectionProps) => { const clanURI = useClanURI(); const [dialogHandlers, setDialogHandlers] = createSignal<{ @@ -232,7 +227,7 @@ const SceneDataProvider = (props: { clanURI: string; children: (sceneData: { query: MachinesQueryResult }) => JSX.Element; }) => { - const machinesQuery = useMachinesQuery({ clanURI: props.clanURI }); + const machinesQuery = useMachinesQuery(props.clanURI); // This component can be used to provide scene data or context if needed return props.children({ query: machinesQuery }); diff --git a/pkgs/clan-app/ui/src/routes/Machine/Machine.tsx b/pkgs/clan-app/ui/src/routes/Machine/Machine.tsx new file mode 100644 index 000000000..c85576231 --- /dev/null +++ b/pkgs/clan-app/ui/src/routes/Machine/Machine.tsx @@ -0,0 +1,19 @@ +import { RouteSectionProps, useNavigate } from "@solidjs/router"; +import { SidebarPane } from "@/src/components/Sidebar/SidebarPane"; +import { navigateToClan, useClanURI, useMachineID } from "@/src/hooks/clan"; + +export const Machine = (props: RouteSectionProps) => { + const navigate = useNavigate(); + const clanURI = useClanURI(); + + const onClose = () => { + // go back to clan route + navigateToClan(navigate, clanURI); + }; + + return ( + +

Hello world

+
+ ); +}; diff --git a/pkgs/clan-app/ui/src/routes/index.tsx b/pkgs/clan-app/ui/src/routes/index.tsx index a624b9e8a..167b31356 100644 --- a/pkgs/clan-app/ui/src/routes/index.tsx +++ b/pkgs/clan-app/ui/src/routes/index.tsx @@ -1,6 +1,7 @@ import type { RouteDefinition } from "@solidjs/router/dist/types"; import { Onboarding } from "@/src/routes/Onboarding/Onboarding"; import { Clan } from "@/src/routes/Clan/Clan"; +import { Machine } from "@/src/routes/Machine/Machine"; export const Routes: RouteDefinition[] = [ { @@ -21,25 +22,14 @@ export const Routes: RouteDefinition[] = [ }, { path: "/:clanURI", + component: Clan, children: [ { path: "/", - component: Clan, }, { - path: "/machines", - children: [ - { - path: "/", - component: () =>

Machines (Index)

, - }, - { - path: "/:machineID", - component: (props) => ( -

Machine ID: {props.params.machineID}

- ), - }, - ], + path: "/machines/:machineID", + component: Machine, }, ], }, diff --git a/pkgs/clan-app/ui/src/stores/clan.ts b/pkgs/clan-app/ui/src/stores/clan.ts index 7b88a9545..c9cb52264 100644 --- a/pkgs/clan-app/ui/src/stores/clan.ts +++ b/pkgs/clan-app/ui/src/stores/clan.ts @@ -20,6 +20,14 @@ const [store, setStore] = makePersisted( }, ); +const resetStore = () => { + setStore({ + clanURIs: [], + activeClanURI: undefined, + sceneData: {}, + }); +}; + /** * Retrieves the active clan URI from the store. * @@ -92,4 +100,5 @@ export { clanURIs, addClanURI, removeClanURI, + resetStore, }; diff --git a/pkgs/clan-cli/clan_cli/nixpkgs b/pkgs/clan-cli/clan_cli/nixpkgs new file mode 120000 index 000000000..917ef9505 --- /dev/null +++ b/pkgs/clan-cli/clan_cli/nixpkgs @@ -0,0 +1 @@ +/nix/store/q012qk78pwldxl3qjy09nwrx9jlamivm-nixpkgs \ No newline at end of file