diff --git a/ui/src/components/FaileSafeModeOverlay.tsx b/ui/src/components/FaileSafeModeOverlay.tsx
index c0163747..774be169 100644
--- a/ui/src/components/FaileSafeModeOverlay.tsx
+++ b/ui/src/components/FaileSafeModeOverlay.tsx
@@ -1,9 +1,10 @@
import { useState } from "react";
import { ExclamationTriangleIcon } from "@heroicons/react/24/solid";
import { motion, AnimatePresence } from "framer-motion";
+import { LuInfo } from "react-icons/lu";
import { Button } from "@/components/Button";
-import { GridCard } from "@components/Card";
+import Card, { GridCard } from "@components/Card";
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
import { useDeviceUiNavigation } from "@/hooks/useAppNavigation";
import { useVersion } from "@/hooks/useVersion";
@@ -30,19 +31,47 @@ function OverlayContent({ children }: OverlayContentProps) {
);
}
+interface TooltipProps {
+ readonly children: React.ReactNode;
+ readonly text: string;
+ readonly show: boolean;
+}
+
+function Tooltip({ children, text, show }: TooltipProps) {
+ if (!show) {
+ return <>{children}>;
+ }
+
+
+ return (
+
+ {children}
+
+
+
+
+ {text}
+
+
+
+
+ );
+}
+
export function FailSafeModeOverlay({ reason }: FailSafeModeOverlayProps) {
const { send } = useJsonRpc();
const { navigateTo } = useDeviceUiNavigation();
const { appVersion } = useVersion();
const { systemVersion } = useDeviceStore();
const [isDownloadingLogs, setIsDownloadingLogs] = useState(false);
+ const [hasDownloadedLogs, setHasDownloadedLogs] = useState(false);
const getReasonCopy = () => {
switch (reason) {
case "video":
return {
message:
- "We've detected an issue with the video capture process. Your device is still running and accessible, but video streaming is temporarily unavailable. You can reboot to attempt recovery, report the issue, or downgrade to the last stable version.",
+ "We've detected an issue with the video capture process. Your device is still running and accessible, but video streaming is temporarily unavailable.",
};
default:
return {
@@ -80,13 +109,14 @@ export function FailSafeModeOverlay({ reason }: FailSafeModeOverlayProps) {
document.body.removeChild(a);
URL.revokeObjectURL(url);
- notifications.success("Recovery logs downloaded successfully");
+ notifications.success("Crash logs downloaded successfully");
+ setHasDownloadedLogs(true);
// Open GitHub issue
const issueBody = `## Issue Description
-The ${reason} process encountered an error and recovery mode was activated.
+The \`${reason}\` process encountered an error and fail safe mode was activated.
-**Reason:** ${reason}
+**Reason:** \`${reason}\`
**Timestamp:** ${new Date().toISOString()}
**App Version:** ${appVersion || "Unknown"}
**System Version:** ${systemVersion || "Unknown"}
@@ -95,6 +125,9 @@ The ${reason} process encountered an error and recovery mode was activated.
Please attach the recovery logs file that was downloaded to your computer:
\`${filename}\`
+> [!NOTE]
+> Please omit any sensitive information from the logs.
+
## Additional Context
[Please describe what you were doing when this occurred]`;
@@ -114,7 +147,7 @@ Please attach the recovery logs file that was downloaded to your computer:
return (
Fail safe mode activated
{message}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/ui/src/routes/devices.$id.settings.general.reboot.tsx b/ui/src/routes/devices.$id.settings.general.reboot.tsx
index db0e0530..bb7daddd 100644
--- a/ui/src/routes/devices.$id.settings.general.reboot.tsx
+++ b/ui/src/routes/devices.$id.settings.general.reboot.tsx
@@ -1,28 +1,39 @@
import { useNavigate } from "react-router";
-import { useCallback } from "react";
+import { useCallback, useState } from "react";
import { useJsonRpc } from "@/hooks/useJsonRpc";
import { Button } from "@components/Button";
+import LoadingSpinner from "../components/LoadingSpinner";
+import { useDeviceUiNavigation } from "../hooks/useAppNavigation";
+
export default function SettingsGeneralRebootRoute() {
const navigate = useNavigate();
const { send } = useJsonRpc();
+ const [isRebooting, setIsRebooting] = useState(false);
+ const { navigateTo } = useDeviceUiNavigation();
- const onConfirmUpdate = useCallback(() => {
+ const onConfirmUpdate = useCallback(async () => {
+ setIsRebooting(true);
// This is where we send the RPC to the golang binary
- send("reboot", {force: true});
- }, [send]);
+ send("reboot", { force: true });
+
+ await new Promise(resolve => setTimeout(resolve, 5000));
+ navigateTo("/");
+ }, [navigateTo, send]);
{
/* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */
}
- return