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 (
+
+ )
+}
+
+function AlertDialogCancel({
+ className,
+ variant = "outline",
+ size = "default",
+ ...props
+}: AlertDialogPrimitive.Close.Props &
+ Pick, "variant" | "size">) {
+ return (
+ }
+ {...props}
+ />
+ )
+}
+
+export {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogMedia,
+ AlertDialogOverlay,
+ AlertDialogPortal,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+}
diff --git a/packages/ui/src/screens/error.tsx b/packages/ui/src/screens/error.tsx
index 8d5cfa4..b5e93d3 100644
--- a/packages/ui/src/screens/error.tsx
+++ b/packages/ui/src/screens/error.tsx
@@ -1,5 +1,5 @@
import Link from "../link";
-import CreateScreen from "./util";
+import CreateScreen, { StorageDestoryer } from "./util";
/**
* Executes Screen.
@@ -14,6 +14,7 @@ export default function Screen(props: { error: string; description: string }) {
{props.description}
+
);
}
diff --git a/packages/ui/src/screens/loading.tsx b/packages/ui/src/screens/loading.tsx
index b1c2720..0c26834 100644
--- a/packages/ui/src/screens/loading.tsx
+++ b/packages/ui/src/screens/loading.tsx
@@ -1,5 +1,5 @@
import * as React from "react";
-import CreateScreen from "./util";
+import CreateScreen, { StorageDestoryer } from "./util";
const DELAY = 250;
@@ -79,6 +79,7 @@ export default function Screen(props: ScreenProps) {
}}
/>
+
);
}
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
+
+
+
+
+ );
+}