mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-25 06:35:56 -05:00
prettify code
This commit is contained in:
parent
a787703a31
commit
8036aa40b7
6294
ui/pnpm-lock.yaml
generated
6294
ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -76,12 +76,14 @@ function MoonfireMenu(props: Props) {
|
||||
</Box>
|
||||
)}
|
||||
<Tooltip title="Toggle theme">
|
||||
<IconButton
|
||||
onClick={changeTheme}
|
||||
color="inherit"
|
||||
size="small"
|
||||
>
|
||||
{choosenTheme === CurrentMode.Light ? <Brightness7 /> : choosenTheme === CurrentMode.Dark ? <Brightness2 /> : <BrightnessAuto />}
|
||||
<IconButton onClick={changeTheme} color="inherit" size="small">
|
||||
{choosenTheme === CurrentMode.Light ? (
|
||||
<Brightness7 />
|
||||
) : choosenTheme === CurrentMode.Dark ? (
|
||||
<Brightness2 />
|
||||
) : (
|
||||
<BrightnessAuto />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
{props.loginState !== "unknown" && props.loginState !== "logged-in" && (
|
||||
|
@ -76,7 +76,9 @@ const DisplaySelector = (props: Props) => {
|
||||
<Checkbox
|
||||
checked={props.trimStartAndEnd}
|
||||
size="small"
|
||||
onChange={(event) => props.setTrimStartAndEnd(event.target.checked)}
|
||||
onChange={(event) =>
|
||||
props.setTrimStartAndEnd(event.target.checked)
|
||||
}
|
||||
name="trim-start-and-end"
|
||||
color="secondary"
|
||||
/>
|
||||
@ -90,7 +92,9 @@ const DisplaySelector = (props: Props) => {
|
||||
<Checkbox
|
||||
checked={props.timestampTrack}
|
||||
size="small"
|
||||
onChange={(event) => props.setTimestampTrack(event.target.checked)}
|
||||
onChange={(event) =>
|
||||
props.setTimestampTrack(event.target.checked)
|
||||
}
|
||||
name="timestamp-track"
|
||||
color="secondary"
|
||||
/>
|
||||
|
@ -90,8 +90,7 @@ const StreamMultiSelector = ({ toplevel, selected, setSelected }: Props) => {
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Card
|
||||
>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Box
|
||||
component="table"
|
||||
|
@ -400,7 +400,9 @@ const TimerangeSelector = ({
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<FormLabel sx={{ mt: 1 }} component="legend">To</FormLabel>
|
||||
<FormLabel sx={{ mt: 1 }} component="legend">
|
||||
To
|
||||
</FormLabel>
|
||||
<RadioGroup
|
||||
row
|
||||
value={days.endType}
|
||||
|
@ -15,7 +15,7 @@ import Fullscreen from "@mui/icons-material/Fullscreen";
|
||||
export interface Layout {
|
||||
className: string;
|
||||
cameras: number;
|
||||
name: string
|
||||
name: string;
|
||||
}
|
||||
|
||||
// These class names must match useStyles rules (below).
|
||||
@ -24,7 +24,7 @@ const LAYOUTS: Layout[] = [
|
||||
{ className: "dual", cameras: 2, name: "2" },
|
||||
{ className: "main-plus-five", cameras: 6, name: "Main + 5" },
|
||||
{ className: "two-by-two", cameras: 4, name: "2x2" },
|
||||
{ className: "three-by-three", cameras: 9, name: "3x3" }
|
||||
{ className: "three-by-three", cameras: 9, name: "3x3" },
|
||||
];
|
||||
const MAX_CAMERAS = 9;
|
||||
|
||||
@ -114,9 +114,9 @@ const Multiview = (props: MultiviewProps) => {
|
||||
const [selected, updateSelected] = useReducer(
|
||||
selectedReducer,
|
||||
searchParams.has("cams")
|
||||
? JSON.parse(searchParams.get("cams") || "") :
|
||||
localStorage.getItem("camsSelected") !== null ?
|
||||
JSON.parse(localStorage.getItem("camsSelected") || "")
|
||||
? JSON.parse(searchParams.get("cams") || "")
|
||||
: localStorage.getItem("camsSelected") !== null
|
||||
? JSON.parse(localStorage.getItem("camsSelected") || "")
|
||||
: Array(MAX_CAMERAS).fill(null)
|
||||
);
|
||||
|
||||
@ -124,7 +124,8 @@ const Multiview = (props: MultiviewProps) => {
|
||||
* Save previously selected cameras to local storage.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (searchParams.has("cams")) localStorage.setItem("camsSelected", (searchParams.get("cams") || ""));
|
||||
if (searchParams.has("cams"))
|
||||
localStorage.setItem("camsSelected", searchParams.get("cams") || "");
|
||||
}, [searchParams]);
|
||||
|
||||
const outerRef = React.useRef<HTMLDivElement>(null);
|
||||
@ -148,7 +149,6 @@ const Multiview = (props: MultiviewProps) => {
|
||||
}
|
||||
}, [outerRef]);
|
||||
|
||||
|
||||
const monoviews = selected.slice(0, layout.cameras).map((e, i) => {
|
||||
// When a camera is selected, use the camera's index as the key.
|
||||
// This allows swapping cameras' positions without tearing down their
|
||||
@ -196,11 +196,23 @@ const Multiview = (props: MultiviewProps) => {
|
||||
}}
|
||||
>
|
||||
<Tooltip title="Toggle full screen">
|
||||
<IconButton size="small" sx={{
|
||||
position: 'fixed', background: 'rgba(50,50,50,0.4) !important', transition: '0.2s', opacity: '0.4', bottom: 10, right: 10, zIndex: 9, color: "#fff", ":hover": {
|
||||
opacity: '1'
|
||||
}
|
||||
}} onClick={handleFullScreen}>
|
||||
<IconButton
|
||||
size="small"
|
||||
sx={{
|
||||
position: "fixed",
|
||||
background: "rgba(50,50,50,0.4) !important",
|
||||
transition: "0.2s",
|
||||
opacity: "0.4",
|
||||
bottom: 10,
|
||||
right: 10,
|
||||
zIndex: 9,
|
||||
color: "#fff",
|
||||
":hover": {
|
||||
opacity: "1",
|
||||
},
|
||||
}}
|
||||
onClick={handleFullScreen}
|
||||
>
|
||||
<Fullscreen />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
@ -227,12 +239,12 @@ const Multiview = (props: MultiviewProps) => {
|
||||
gridTemplateColumns: {
|
||||
xs: "100%",
|
||||
sm: "100%",
|
||||
md: "repeat(2, calc(100% / 2))"
|
||||
md: "repeat(2, calc(100% / 2))",
|
||||
},
|
||||
gridTemplateRows: {
|
||||
xs: "50%",
|
||||
sm: "50%",
|
||||
md: "repeat(1, calc(100% / 1))"
|
||||
md: "repeat(1, calc(100% / 1))",
|
||||
},
|
||||
},
|
||||
"&.two-by-two": {
|
||||
|
@ -19,15 +19,21 @@ export interface LiveProps {
|
||||
const Live = ({ cameras, Frame }: LiveProps) => {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
|
||||
|
||||
const [multiviewLayoutIndex, setMultiviewLayoutIndex] = useState(
|
||||
Number.parseInt(searchParams.get("layout") || localStorage.getItem("multiviewLayoutIndex") || "0", 10)
|
||||
Number.parseInt(
|
||||
searchParams.get("layout") ||
|
||||
localStorage.getItem("multiviewLayoutIndex") ||
|
||||
"0",
|
||||
10
|
||||
)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchParams.has("layout"))
|
||||
localStorage.setItem("multiviewLayoutIndex", (searchParams.get("layout") || "0"));
|
||||
localStorage.setItem(
|
||||
"multiviewLayoutIndex",
|
||||
searchParams.get("layout") || "0"
|
||||
);
|
||||
}, [searchParams]);
|
||||
|
||||
if ("MediaSource" in window === false) {
|
||||
|
@ -118,7 +118,7 @@ const Login = ({ open, onSuccess, handleClose }: Props) => {
|
||||
</DialogTitle>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||
<TextField
|
||||
id="username"
|
||||
label="Username"
|
||||
|
@ -18,14 +18,20 @@ import { useReducer } from "react";
|
||||
import { LoginState } from "../App";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function Header({ loginState, logout, setChangePasswordOpen, activityMenuPart, setLoginState, toplevel }:
|
||||
{
|
||||
loginState: LoginState,
|
||||
logout: () => void,
|
||||
setChangePasswordOpen: React.Dispatch<React.SetStateAction<boolean>>,
|
||||
activityMenuPart?: JSX.Element,
|
||||
setLoginState: React.Dispatch<React.SetStateAction<LoginState>>,
|
||||
toplevel: api.ToplevelResponse | null
|
||||
export default function Header({
|
||||
loginState,
|
||||
logout,
|
||||
setChangePasswordOpen,
|
||||
activityMenuPart,
|
||||
setLoginState,
|
||||
toplevel,
|
||||
}: {
|
||||
loginState: LoginState;
|
||||
logout: () => void;
|
||||
setChangePasswordOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
activityMenuPart?: JSX.Element;
|
||||
setLoginState: React.Dispatch<React.SetStateAction<LoginState>>;
|
||||
toplevel: api.ToplevelResponse | null;
|
||||
}) {
|
||||
const [showMenu, toggleShowMenu] = useReducer((m: boolean) => !m, false);
|
||||
|
||||
@ -90,5 +96,5 @@ export default function Header({ loginState, logout, setChangePasswordOpen, acti
|
||||
</List>
|
||||
</Drawer>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
@ -9,19 +9,21 @@ import React, { createContext } from "react";
|
||||
export enum CurrentMode {
|
||||
Auto = 0,
|
||||
Light = 1,
|
||||
Dark = 2
|
||||
Dark = 2,
|
||||
}
|
||||
|
||||
interface ThemeProps {
|
||||
changeTheme: () => void,
|
||||
currentTheme: 'dark' | 'light',
|
||||
choosenTheme: CurrentMode
|
||||
changeTheme: () => void;
|
||||
currentTheme: "dark" | "light";
|
||||
choosenTheme: CurrentMode;
|
||||
}
|
||||
|
||||
export const ThemeContext = createContext<ThemeProps>({
|
||||
currentTheme: window.matchMedia("(prefers-color-scheme: dark)").matches ? 'dark' : 'light',
|
||||
currentTheme: window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light",
|
||||
changeTheme: () => {},
|
||||
choosenTheme: CurrentMode.Auto
|
||||
choosenTheme: CurrentMode.Auto,
|
||||
});
|
||||
|
||||
const ThemeMode = ({ children }: { children: JSX.Element }): JSX.Element => {
|
||||
@ -40,21 +42,33 @@ const ThemeMode = ({ children }: { children: JSX.Element }): JSX.Element => {
|
||||
return matches;
|
||||
};
|
||||
|
||||
const detectedSystemColorScheme = useMediaQuery("(prefers-color-scheme: dark)") ? "dark" : "light";
|
||||
const detectedSystemColorScheme = useMediaQuery(
|
||||
"(prefers-color-scheme: dark)"
|
||||
)
|
||||
? "dark"
|
||||
: "light";
|
||||
|
||||
const changeTheme = React.useCallback(() => {
|
||||
setMode(mode === 'dark' ? 'light' : mode === 'light' ? 'system' : 'dark')
|
||||
setMode(mode === "dark" ? "light" : mode === "light" ? "system" : "dark");
|
||||
}, [mode, setMode]);
|
||||
|
||||
const currentTheme = mode === 'system' ? detectedSystemColorScheme : (mode ?? detectedSystemColorScheme);
|
||||
const choosenTheme = mode === 'dark' ? CurrentMode.Dark : mode === 'light' ? CurrentMode.Light : CurrentMode.Auto;
|
||||
const currentTheme =
|
||||
mode === "system"
|
||||
? detectedSystemColorScheme
|
||||
: mode ?? detectedSystemColorScheme;
|
||||
const choosenTheme =
|
||||
mode === "dark"
|
||||
? CurrentMode.Dark
|
||||
: mode === "light"
|
||||
? CurrentMode.Light
|
||||
: CurrentMode.Auto;
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ changeTheme, currentTheme, choosenTheme }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeMode;
|
||||
|
||||
|
@ -3,7 +3,10 @@
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
|
||||
|
||||
import CssBaseline from "@mui/material/CssBaseline";
|
||||
import { Experimental_CssVarsProvider, experimental_extendTheme } from "@mui/material/styles";
|
||||
import {
|
||||
Experimental_CssVarsProvider,
|
||||
experimental_extendTheme,
|
||||
} from "@mui/material/styles";
|
||||
import StyledEngineProvider from "@mui/material/StyledEngineProvider";
|
||||
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
|
||||
import "@fontsource/roboto";
|
||||
@ -22,15 +25,15 @@ const themeExtended = experimental_extendTheme({
|
||||
dark: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#000000"
|
||||
main: "#000000",
|
||||
},
|
||||
secondary: {
|
||||
main: "#e65100"
|
||||
}
|
||||
}
|
||||
main: "#e65100",
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const container = document.getElementById("root");
|
||||
const root = createRoot(container!);
|
||||
root.render(
|
||||
|
@ -7,11 +7,11 @@
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import "@testing-library/jest-dom";
|
||||
import { vi } from 'vitest'
|
||||
import { vi } from "vitest";
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
writable: true,
|
||||
value: vi.fn().mockImplementation(query => ({
|
||||
value: vi.fn().mockImplementation((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
@ -21,4 +21,4 @@ Object.defineProperty(window, 'matchMedia', {
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
})),
|
||||
})
|
||||
});
|
||||
|
@ -11,9 +11,13 @@ const target = process.env.PROXY_TARGET ?? "http://localhost:8080/";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), viteCompression(), viteLegacyPlugin({
|
||||
targets: ['defaults', 'fully supports es6-module'],
|
||||
})],
|
||||
plugins: [
|
||||
react(),
|
||||
viteCompression(),
|
||||
viteLegacyPlugin({
|
||||
targets: ["defaults", "fully supports es6-module"],
|
||||
}),
|
||||
],
|
||||
server: {
|
||||
proxy: {
|
||||
"/api": {
|
||||
|
Loading…
Reference in New Issue
Block a user