The display will wake up when the connection state changes, or when touched.
-
+
-
+
- {
- // Revalidate the current route to refresh the local device status and dependent UI components
- revalidator.revalidate();
- setIsLocalAuthDialogOpen(x);
- }}
- />
);
}
diff --git a/ui/src/main.tsx b/ui/src/main.tsx
index dd47073..18872f1 100644
--- a/ui/src/main.tsx
+++ b/ui/src/main.tsx
@@ -30,6 +30,7 @@ import WelcomeLocalPasswordRoute from "./routes/welcome-local.password";
import { CLOUD_API } from "./ui.config";
import OtherSessionRoute from "./routes/devices.$id.other-session";
import UpdateRoute from "./routes/devices.$id.update";
+import LocalAuthRoute from "./routes/devices.$id.local-auth";
export const isOnDevice = import.meta.env.MODE === "device";
export const isInCloud = !isOnDevice;
@@ -86,6 +87,10 @@ if (isOnDevice) {
path: "update",
element: ,
},
+ {
+ path: "local-auth",
+ element: ,
+ },
],
},
@@ -138,6 +143,10 @@ if (isOnDevice) {
path: "update",
element: ,
},
+ {
+ path: "local-auth",
+ element: ,
+ },
],
},
{
diff --git a/ui/src/routes/devices.$id.local-auth.tsx b/ui/src/routes/devices.$id.local-auth.tsx
new file mode 100644
index 0000000..4476aa2
--- /dev/null
+++ b/ui/src/routes/devices.$id.local-auth.tsx
@@ -0,0 +1,365 @@
+import { GridCard } from "@/components/Card";
+import { useState } from "react";
+import { Button } from "@components/Button";
+import LogoBlueIcon from "@/assets/logo-blue.svg";
+import LogoWhiteIcon from "@/assets/logo-white.svg";
+import { InputFieldWithLabel } from "@/components/InputField";
+import api from "@/api";
+import { useLocalAuthModalStore } from "@/hooks/stores";
+import { useNavigate } from "react-router-dom";
+
+export default function LocalAuthRoute() {
+ const navigate = useNavigate();
+
+ return (
+
+ {/* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */}
+
+ );
+}
+
+export function Dialog({ onClose }: { onClose: (open: boolean) => void }) {
+ const { modalView, setModalView } = useLocalAuthModalStore();
+ const [error, setError] = useState(null);
+
+ const handleCreatePassword = async (password: string, confirmPassword: string) => {
+ if (password === "") {
+ setError("Please enter a password");
+ return;
+ }
+
+ if (password !== confirmPassword) {
+ setError("Passwords do not match");
+ return;
+ }
+
+ try {
+ const res = await api.POST("/auth/password-local", { password });
+ if (res.ok) {
+ setModalView("creationSuccess");
+ } else {
+ const data = await res.json();
+ setError(data.error || "An error occurred while setting the password");
+ }
+ } catch (error) {
+ setError("An error occurred while setting the password");
+ }
+ };
+
+ const handleUpdatePassword = async (
+ oldPassword: string,
+ newPassword: string,
+ confirmNewPassword: string,
+ ) => {
+ if (newPassword !== confirmNewPassword) {
+ setError("Passwords do not match");
+ return;
+ }
+
+ if (oldPassword === "") {
+ setError("Please enter your old password");
+ return;
+ }
+
+ if (newPassword === "") {
+ setError("Please enter a new password");
+ return;
+ }
+
+ try {
+ const res = await api.PUT("/auth/password-local", {
+ oldPassword,
+ newPassword,
+ });
+
+ if (res.ok) {
+ setModalView("updateSuccess");
+ } else {
+ const data = await res.json();
+ setError(data.error || "An error occurred while changing the password");
+ }
+ } catch (error) {
+ setError("An error occurred while changing the password");
+ }
+ };
+
+ const handleDeletePassword = async (password: string) => {
+ if (password === "") {
+ setError("Please enter your current password");
+ return;
+ }
+
+ try {
+ const res = await api.DELETE("/auth/local-password", { password });
+ if (res.ok) {
+ setModalView("deleteSuccess");
+ } else {
+ const data = await res.json();
+ setError(data.error || "An error occurred while disabling the password");
+ }
+ } catch (error) {
+ setError("An error occurred while disabling the password");
+ }
+ };
+
+ return (
+
+