From 462fc195913f95f5fb8ec4dd556999c579c12f74 Mon Sep 17 00:00:00 2001 From: Alois Date: Tue, 7 Apr 2026 15:57:51 +0200 Subject: [PATCH] Fixed login, added clear storage button --- .../web/src/components/screens/login/form.tsx | 24 ++- apps/web/src/routes/screens/login.tsx | 2 +- packages/ui/src/cmp/alert-dialog.tsx | 185 ++++++++++++++++++ packages/ui/src/screens/error.tsx | 3 +- packages/ui/src/screens/loading.tsx | 3 +- packages/ui/src/screens/util.tsx | 64 +++++- 6 files changed, 270 insertions(+), 11 deletions(-) create mode 100644 packages/ui/src/cmp/alert-dialog.tsx diff --git a/apps/web/src/components/screens/login/form.tsx b/apps/web/src/components/screens/login/form.tsx index 3fa8fa7..0b09889 100644 --- a/apps/web/src/components/screens/login/form.tsx +++ b/apps/web/src/components/screens/login/form.tsx @@ -3,7 +3,7 @@ import { Input } from "@tensamin/ui/cmp/input"; import { Label } from "@tensamin/ui/cmp/label"; import { useStorage } from "@tensamin/storage/context"; import { log, toast } from "@tensamin/shared/log"; -import { Upload } from "lucide-react"; +import { File } from "lucide-react"; import * as React from "react"; import { z } from "zod"; @@ -34,7 +34,17 @@ function parseTuFileContent(rawFileContent: string): { userId: number; privateKey: string; } { - if (rawFileContent.length !== 92 || !rawFileContent.includes("::")) { + if (rawFileContent.trim().length === 0) { + throw new Error("File is empty"); + } else if (!rawFileContent.includes("::")) { + throw new Error("Invalid file"); + } else if (rawFileContent.split("::").length !== 2) { + throw new Error("Invalid file"); + } else if (rawFileContent.split("::")[0].length === 0) { + throw new Error("Invalid file"); + } else if (rawFileContent.split("::")[1].length === 0) { + throw new Error("Invalid file"); + } else if (isNaN(Number(rawFileContent.split("::")[0]))) { throw new Error("Invalid file"); } @@ -77,7 +87,7 @@ export default function Form() { location.href = "/"; } catch (error) { - log(0, "Login", "red", error); + log(0, "login", "red", error); toast("error", "Failed to load file"); } }, @@ -115,7 +125,7 @@ export default function Form() { const parse = fetchedUser.shape.data.safeParse(data); if (!parse.success) { - log(0, "Login", "red", "Invalid response from server", parse.error); + log(0, "login", "red", "Invalid response from server", parse.error); toast("error", "Invalid response from server"); return; } @@ -128,7 +138,7 @@ export default function Form() { location.href = "/"; } catch (error) { - log(0, "Login", "red", error); + log(0, "login", "red", error); toast("error", "Failed to fetch user data"); } }, @@ -141,8 +151,8 @@ export default function Form() { onClick={() => uploadRef.current?.click()} className="flex flex-col gap-3 cursor-pointer w-55 aspect-square bg-input/13 hover:bg-input/30 transition-all duration-300 ease-in-out border-3 items-center justify-center rounded-lg" > - -

Upload .tu file

+ +

Select .tu file

+
diff --git a/packages/ui/src/cmp/alert-dialog.tsx b/packages/ui/src/cmp/alert-dialog.tsx new file mode 100644 index 0000000..90d1446 --- /dev/null +++ b/packages/ui/src/cmp/alert-dialog.tsx @@ -0,0 +1,185 @@ +import * as React from "react" +import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog" + +import { cn } from "../lib/utils" +import { Button } from "./button" + +function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) { + return +} + +function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) { + return ( + + ) +} + +function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: AlertDialogPrimitive.Backdrop.Props) { + return ( + + ) +} + +function AlertDialogContent({ + className, + size = "default", + ...props +}: AlertDialogPrimitive.Popup.Props & { + size?: "default" | "sm" +}) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogMedia({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( +
+ ); } diff --git a/packages/ui/src/screens/util.tsx b/packages/ui/src/screens/util.tsx index adc4c45..584296c 100644 --- a/packages/ui/src/screens/util.tsx +++ b/packages/ui/src/screens/util.tsx @@ -1,13 +1,19 @@ +import { cn } from "../lib/utils"; import Controls from "@tensamin/tauri/controls"; export default function CreateScreen({ children, + className, }: { children: React.ReactNode; + className?: string; }) { return (
<> @@ -17,3 +23,59 @@ export default function CreateScreen({
); } + +import { Button } from "../cmp/button"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "../cmp/alert-dialog"; +import { useIsMobile } from "../lib/utils"; + +export function StorageDestoryer() { + const isMobile = useIsMobile(); + return ( +
+ + + Clear storage + + } + /> + + + + Are you sure you want to clear storage? + + + This action cannot be undone. It will clear all your settings and + credentials. + + + + { + localStorage.clear(); + for (const db of await indexedDB.databases()) { + indexedDB.deleteDatabase(db.name!); + } + location.reload(); + }} + > + Continue + + Cancel + + + +
+ ); +}