diff --git a/web/src/screens/Logs.tsx b/web/src/screens/Logs.tsx index e23fdd4..450ea27 100644 --- a/web/src/screens/Logs.tsx +++ b/web/src/screens/Logs.tsx @@ -1,65 +1,129 @@ import { useEffect, useRef, useState } from "react"; +import { ExclamationIcon } from "@heroicons/react/solid"; + import { APIClient } from "../api/APIClient"; -import {ExclamationIcon} from "@heroicons/react/solid"; +import { Checkbox } from "../components/Checkbox"; +import { classNames } from "../utils"; +import { SettingsContext } from "../utils/Context"; type LogEvent = { - time: string; - level: string; - message: string; + time: string; + level: string; + message: string; +}; + +type LogLevel = "TRACE" | "DEBUG" | "INFO" | "ERROR"; + +const LogColors: Record = { + "TRACE": "text-purple-300", + "DEBUG": "text-yellow-500", + "INFO": "text-green-500", + "ERROR": "text-red-500", }; export const Logs = () => { - const messagesEndRef = useRef(null); - const [logs, setLogs] = useState([]); + const [settings, setSettings] = SettingsContext.use(); - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: "auto" }) + const messagesEndRef = useRef(null); + const [logs, setLogs] = useState([]); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: "auto" }); + } + + useEffect(() => { + const es = APIClient.events.logs(); + + es.onmessage = (event) => { + const newData = JSON.parse(event.data) as LogEvent; + setLogs((prevState) => [...prevState, newData]); + + if (settings.scrollOnNewLog) + scrollToBottom(); } - useEffect(() => { - const es = APIClient.events.logs() + return () => es.close(); + }, [setLogs, settings]); - es.onmessage = (event) => { - const d = JSON.parse(event.data) as LogEvent; - setLogs(prevState => ([...prevState, d])); - scrollToBottom(); - } - return () => { - es.close(); - } - }, [setLogs]); + const onSetValue = ( + key: "scrollOnNewLog" | "indentLogLines" | "hideWrappedText", + newValue: boolean + ) => setSettings((prevState) => ({ + ...prevState, + [key]: newValue + })); - return ( -
-
-
-

Logs

-
-
-
-
-
-
-
- {logs.map((a, idx) => ( -

- {a.time} - {a.level === "TRACE" && {a.level}} - {a.level === "DEBUG" && {a.level}} - {a.level === "INFO" && {a.level} } - {a.level === "ERROR" && {a.level}} - {a.message} -

- ))} -
-
-
-
-
- ) + return ( +
+
+
+

Logs

+
+
+
+
+
+
+ onSetValue("scrollOnNewLog", newValue)} + /> + onSetValue("indentLogLines", newValue)} + /> + onSetValue("hideWrappedText", newValue)} + /> +
+ {logs.map((a, idx) => ( +
+ + {a.time} + + {a.level in LogColors ? ( + + {a.level} + {' '} + + ) : null} + + {a.message} + +
+ ))} +
+
+
+
+
+ ) } diff --git a/web/src/utils/Context.ts b/web/src/utils/Context.ts index a1967cf..bd1fe30 100644 --- a/web/src/utils/Context.ts +++ b/web/src/utils/Context.ts @@ -2,65 +2,72 @@ import { newRidgeState } from "react-ridge-state"; export const InitializeGlobalContext = () => { - const auth_ctx = localStorage.getItem("auth"); - if (auth_ctx) - AuthContext.set(JSON.parse(auth_ctx)); - - const settings_ctx = localStorage.getItem("settings"); - if (settings_ctx) { - SettingsContext.set(JSON.parse(settings_ctx)); - } else { - // Only check for light theme, otherwise dark theme is the default - SettingsContext.set((state) => ({ - ...state, - darkTheme: !( - window.matchMedia !== undefined && - window.matchMedia("(prefers-color-scheme: light)").matches - ) - })); - } + const auth_ctx = localStorage.getItem("auth"); + if (auth_ctx) + AuthContext.set(JSON.parse(auth_ctx)); + + const settings_ctx = localStorage.getItem("settings"); + if (settings_ctx) { + SettingsContext.set(JSON.parse(settings_ctx)); + } else { + // Only check for light theme, otherwise dark theme is the default + SettingsContext.set((state) => ({ + ...state, + darkTheme: !( + window.matchMedia !== undefined && + window.matchMedia("(prefers-color-scheme: light)").matches + ) + })); + } } interface AuthInfo { - username: string; - isLoggedIn: boolean; + username: string; + isLoggedIn: boolean; } export const AuthContext = newRidgeState( - { - username: "", - isLoggedIn: false - }, - { - onSet: (new_state) => { - try { - localStorage.setItem("auth", JSON.stringify(new_state)); - } catch (e) { - console.log("An error occurred while trying to modify the local auth context state."); - console.log("Error:", e); - } - } + { + username: "", + isLoggedIn: false + }, + { + onSet: (new_state) => { + try { + localStorage.setItem("auth", JSON.stringify(new_state)); + } catch (e) { + console.log("An error occurred while trying to modify the local auth context state."); + console.log("Error:", e); + } } + } ); interface SettingsType { - debug: boolean; - darkTheme: boolean; + debug: boolean; + darkTheme: boolean; + scrollOnNewLog: boolean; + indentLogLines: boolean; + hideWrappedText: boolean; } export const SettingsContext = newRidgeState( { - debug: false, - darkTheme: true + debug: false, + darkTheme: true, + scrollOnNewLog: false, + indentLogLines: false, + hideWrappedText: false }, { onSet: (new_state) => { try { if (new_state.darkTheme) { - document.documentElement.classList.add("dark"); + document.documentElement.classList.add("dark"); } else { - document.documentElement.classList.remove("dark"); + document.documentElement.classList.remove("dark"); } + localStorage.setItem("settings", JSON.stringify(new_state)); } catch (e) { console.log("An error occurred while trying to modify the local settings context state.");