diff --git a/web/src/components/filter/ReviewActionGroup.tsx b/web/src/components/filter/ReviewActionGroup.tsx index 5e1a93185..c3df52881 100644 --- a/web/src/components/filter/ReviewActionGroup.tsx +++ b/web/src/components/filter/ReviewActionGroup.tsx @@ -1,8 +1,10 @@ -import { LuCheckSquare, LuFileUp, LuTrash } from "react-icons/lu"; +import { FaCircleCheck } from "react-icons/fa6"; import { useCallback } from "react"; import axios from "axios"; import { Button } from "../ui/button"; import { isDesktop } from "react-device-detect"; +import { FaCompactDisc } from "react-icons/fa"; +import { HiTrash } from "react-icons/hi"; type ReviewActionGroupProps = { selectedReviews: string[]; @@ -21,8 +23,7 @@ export default function ReviewActionGroup({ }, [setSelectedReviews]); const onMarkAsReviewed = useCallback(async () => { - const idList = selectedReviews.join(","); - await axios.post(`reviews/viewed`, { ids: idList }); + await axios.post(`reviews/viewed`, { ids: selectedReviews }); setSelectedReviews([]); pullLatestData(); }, [selectedReviews, setSelectedReviews, pullLatestData]); @@ -36,16 +37,20 @@ export default function ReviewActionGroup({ return (
-
-
{`${selectedReviews.length} selected | `}
- +
{selectedReviews.length == 1 && ( )}
diff --git a/web/src/components/icons/LiveIcons.tsx b/web/src/components/icons/LiveIcons.tsx new file mode 100644 index 000000000..dc491b612 --- /dev/null +++ b/web/src/components/icons/LiveIcons.tsx @@ -0,0 +1,42 @@ +type LiveIconProps = { + layout?: "list" | "grid"; +}; + +export function LiveGridIcon({ layout }: LiveIconProps) { + return ( +
+
+
+
+
+
+
+
+
+
+
+ ); +} + +export function LiveListIcon({ layout }: LiveIconProps) { + return ( +
+
+
+
+ ); +} diff --git a/web/src/context/providers.tsx b/web/src/context/providers.tsx index efcf2a10b..b239aff47 100644 --- a/web/src/context/providers.tsx +++ b/web/src/context/providers.tsx @@ -13,7 +13,7 @@ function providers({ children }: TProvidersProps) { return ( - + {children} diff --git a/web/src/context/theme-provider.tsx b/web/src/context/theme-provider.tsx index 26d6ef244..70dfaced1 100644 --- a/web/src/context/theme-provider.tsx +++ b/web/src/context/theme-provider.tsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useEffect, useState } from "react"; +import { createContext, useContext, useEffect, useMemo, useState } from "react"; type Theme = "dark" | "light" | "system"; type ColorScheme = @@ -41,6 +41,7 @@ type ThemeProviderProps = { type ThemeProviderState = { theme: Theme; + systemTheme?: Theme; colorScheme: ColorScheme; setTheme: (theme: Theme) => void; setColorScheme: (colorScheme: ColorScheme) => void; @@ -48,6 +49,7 @@ type ThemeProviderState = { const initialState: ThemeProviderState = { theme: "system", + systemTheme: undefined, colorScheme: "theme-default", setTheme: () => null, setColorScheme: () => null, @@ -86,6 +88,16 @@ export function ThemeProvider({ } }); + const systemTheme = useMemo(() => { + if (theme != "system") { + return undefined; + } + + return window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"; + }, [theme]); + useEffect(() => { //localStorage.removeItem(storageKey); //console.log(localStorage.getItem(storageKey)); @@ -95,21 +107,17 @@ export function ThemeProvider({ root.classList.add(theme, colorScheme); - if (theme === "system") { - const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") - .matches - ? "dark" - : "light"; - + if (systemTheme) { root.classList.add(systemTheme); return; } root.classList.add(theme); - }, [theme, colorScheme]); + }, [theme, colorScheme, systemTheme]); const value = { theme, + systemTheme, colorScheme, setTheme: (theme: Theme) => { localStorage.setItem(storageKey, JSON.stringify({ theme, colorScheme })); diff --git a/web/src/pages/ConfigEditor.tsx b/web/src/pages/ConfigEditor.tsx index cbfa17a74..eca6b8fc8 100644 --- a/web/src/pages/ConfigEditor.tsx +++ b/web/src/pages/ConfigEditor.tsx @@ -19,7 +19,7 @@ function ConfigEditor() { const { data: config } = useSWR("config/raw"); - const { theme } = useTheme(); + const { theme, systemTheme } = useTheme(); const [error, setError] = useState(); const editorRef = useRef(null); @@ -107,7 +107,7 @@ function ConfigEditor() { language: "yaml", model: modelRef.current, scrollBeyondLastLine: false, - theme: theme == "dark" ? "vs-dark" : "vs-light", + theme: (systemTheme || theme) == "dark" ? "vs-dark" : "vs-light", }); } diff --git a/web/src/views/live/LiveDashboardView.tsx b/web/src/views/live/LiveDashboardView.tsx index 071a9d71e..d5a7401c5 100644 --- a/web/src/views/live/LiveDashboardView.tsx +++ b/web/src/views/live/LiveDashboardView.tsx @@ -1,6 +1,7 @@ import { useFrigateReviews } from "@/api/ws"; import Logo from "@/components/Logo"; import { CameraGroupSelector } from "@/components/filter/CameraGroupSelector"; +import { LiveGridIcon, LiveListIcon } from "@/components/icons/LiveIcons"; import { AnimatedEventThumbnail } from "@/components/image/AnimatedEventThumbnail"; import BirdseyeLivePlayer from "@/components/player/BirdseyeLivePlayer"; import LivePlayer from "@/components/player/LivePlayer"; @@ -12,7 +13,6 @@ import { CameraConfig, FrigateConfig } from "@/types/frigateConfig"; import { ReviewSegment } from "@/types/review"; import { useCallback, useEffect, useMemo, useState } from "react"; import { isDesktop, isMobile, isSafari } from "react-device-detect"; -import { CiGrid2H, CiGrid31 } from "react-icons/ci"; import useSWR from "swr"; type LiveDashboardViewProps = { @@ -89,26 +89,26 @@ export default function LiveDashboardView({