diff --git a/web/src/AppBar.jsx b/web/src/AppBar.jsx index f71f4124f..9f4a750ee 100644 --- a/web/src/AppBar.jsx +++ b/web/src/AppBar.jsx @@ -8,16 +8,16 @@ import DarkModeIcon from './icons/DarkMode'; import SettingsIcon from './icons/Settings'; import FrigateRestartIcon from './icons/FrigateRestart'; import Prompt from './components/Prompt'; -import { useDarkMode, useViewMode } from './context'; +import { useDarkMode, useUserView } from './context'; import { useCallback, useRef, useState } from 'preact/hooks'; import { useRestart } from './api/ws'; -import { ViewModeTypes } from './components/ViewOptionEnum' +import { UserViewTypes } from './context/UserViewTypes' export default function AppBar() { const [showMoreMenu, setShowMoreMenu] = useState(false); const [showDialog, setShowDialog] = useState(false); const [showDialogWait, setShowDialogWait] = useState(false); - const { currentViewMode, setViewMode } = useViewMode(); + const { currentUserView, setUserView } = useUserView(); const { setDarkMode } = useDarkMode(); const { send: sendRestart } = useRestart(); @@ -29,12 +29,12 @@ export default function AppBar() { [setDarkMode, setShowMoreMenu] ); - const handleSetViewMode = useCallback( + const handleSetUserView = useCallback( (value) => { - setViewMode(value); + setUserView(value); setShowMoreMenu(false); }, - [setViewMode, setShowMoreMenu] + [setUserView, setShowMoreMenu] ); const moreRef = useRef(null); @@ -75,10 +75,10 @@ export default function AppBar() { diff --git a/web/src/Sidebar.jsx b/web/src/Sidebar.jsx index c19c9a2f0..38ea4ccc8 100644 --- a/web/src/Sidebar.jsx +++ b/web/src/Sidebar.jsx @@ -4,7 +4,7 @@ import { Match } from 'preact-router/match'; import { memo } from 'preact/compat'; import { ENV } from './env'; import { useMemo } from 'preact/hooks' -import ViewOption from './components/ViewOption' +import UserViewer from './components/UserViewer' import useSWR from 'swr'; import NavigationDrawer, { Destination, Separator } from './components/NavigationDrawer'; @@ -47,17 +47,17 @@ export default function Sidebar() { - + - - + + - - + + - +
{ENV !== 'production' ? ( diff --git a/web/src/app.tsx b/web/src/app.tsx index 95f530982..fd110c74c 100644 --- a/web/src/app.tsx +++ b/web/src/app.tsx @@ -6,7 +6,7 @@ import AppBar from './AppBar'; import Cameras from './routes/Cameras'; import { Router } from 'preact-router'; import Sidebar from './Sidebar'; -import { DarkModeProvider, DrawerProvider, ViewModeProvider } from './context'; +import { DarkModeProvider, DrawerProvider, UserViewProvider } from './context'; import useSWR from 'swr'; export default function App() { @@ -16,7 +16,7 @@ export default function App() { return ( - +
{!config ? ( @@ -48,7 +48,7 @@ export default function App() {
)}
- + ); diff --git a/web/src/components/UserViewer.jsx b/web/src/components/UserViewer.jsx new file mode 100644 index 000000000..98fb55da1 --- /dev/null +++ b/web/src/components/UserViewer.jsx @@ -0,0 +1,12 @@ +import { h } from 'preact'; +import { useUserView } from '../context'; +import { UserViewTypes } from '../context/UserViewTypes'; + +export default function UserViewer({children, requiredmode }) { + const { currentUserView } = useUserView(); + + return currentUserView >= UserViewTypes[requiredmode] ? ( + <>{children} + + ) : null; +} \ No newline at end of file diff --git a/web/src/components/ViewOption.jsx b/web/src/components/ViewOption.jsx deleted file mode 100644 index 3fb3f276a..000000000 --- a/web/src/components/ViewOption.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import { h } from 'preact'; -import { useViewMode } from '../context'; -import { ViewModeTypes } from './ViewOptionEnum'; - -export default function ViewOption({children, requiredmode }) { - const { currentViewMode } = useViewMode(); - - return currentViewMode >= ViewModeTypes[requiredmode] ? ( - <>{children} - - ) : null; -} \ No newline at end of file diff --git a/web/src/components/__tests__/ViewOption.test.jsx b/web/src/components/__tests__/UserViewer.test.jsx similarity index 60% rename from web/src/components/__tests__/ViewOption.test.jsx rename to web/src/components/__tests__/UserViewer.test.jsx index 5592aff41..2ec888d7e 100644 --- a/web/src/components/__tests__/ViewOption.test.jsx +++ b/web/src/components/__tests__/UserViewer.test.jsx @@ -1,27 +1,27 @@ import { h } from 'preact'; import { render, screen, waitFor } from '@testing-library/preact'; import { set as setData } from 'idb-keyval'; -import ViewOption from '../ViewOption'; -import { ViewModeProvider } from '../../context'; -import { ViewModeTypes } from '../ViewOptionEnum'; +import UserViewer from '../UserViewer'; +import { UserViewProvider } from '../../context'; +import { UserViewTypes } from '../../context/UserViewTypes'; import * as WS from '../../api/ws'; -describe('ViewOption', () => { +describe('UserViewer', () => { beforeEach(() => { vi.spyOn(WS, 'WsProvider').mockImplementation(({ children }) => children); }); test('make sure children are visible with same modes', async () => { - const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1); - const maxViewModeHR = ViewModeTypes[maxViewMode]; + const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1); + const maxViewModeHR = UserViewTypes[maxViewMode]; setData('view-mode', maxViewMode); render( - - + +
stuff
-
-
+ + ); const el = await screen.findByTestId('children'); @@ -29,16 +29,16 @@ describe('ViewOption', () => { }); test('make sure children are visible with max viewmode, and a small requiredmode', async () => { - const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1); - const lowViewModeHR = ViewModeTypes[1]; + const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1); + const lowViewModeHR = UserViewTypes[1]; setData('view-mode', maxViewMode); render( - - + +
stuff
-
-
+ + ); const el = await screen.findByTestId('children'); @@ -46,17 +46,17 @@ describe('ViewOption', () => { }); test('make sure children are hidden, due to failed requiredmode', async () => { - const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1); - const maxViewModeHR = ViewModeTypes[maxViewMode]; + const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1); + const maxViewModeHR = UserViewTypes[maxViewMode]; setData('view-mode', '0'); render( - - + +
stuff
-
+
bugfix
-
+ ); //without this, the test will always pass, even if setData('view-mode', '2') ... really strange behavior diff --git a/web/src/components/ViewOptionEnum.tsx b/web/src/context/UserViewTypes.tsx similarity index 56% rename from web/src/components/ViewOptionEnum.tsx rename to web/src/context/UserViewTypes.tsx index 5cb9c7a54..1fa51702f 100644 --- a/web/src/components/ViewOptionEnum.tsx +++ b/web/src/context/UserViewTypes.tsx @@ -1,5 +1,5 @@ -export enum ViewModeTypes { +export enum UserViewTypes { "user", "advanced", "admin", - } \ No newline at end of file +} \ No newline at end of file diff --git a/web/src/context/__tests__/index.test.jsx b/web/src/context/__tests__/index.test.jsx index 3c13c2db5..df4a8dbde 100644 --- a/web/src/context/__tests__/index.test.jsx +++ b/web/src/context/__tests__/index.test.jsx @@ -1,7 +1,7 @@ import { h } from 'preact'; import { set as setData, get as getData } from 'idb-keyval'; -import { DarkModeProvider, useDarkMode, usePersistence, ViewModeProvider, useViewMode } from '..'; -import { ViewModeTypes } from '../../components/ViewOptionEnum' +import { DarkModeProvider, useDarkMode, usePersistence, UserViewProvider, useUserView } from '..'; +import { UserViewTypes } from '../../context/UserViewTypes' import { fireEvent, render, screen } from 'testing-library'; import { useCallback } from 'preact/hooks'; import * as WS from '../../api/ws'; @@ -197,9 +197,9 @@ describe('usePersistence', () => { }); }); -function ViewModeChecker() { - const { currentViewMode } = useViewMode(); - return
{currentViewMode}
; +function UserViewChecker() { + const { currentUserView } = useUserView(); + return
{currentUserView}
; } describe('ViewMode', () => { @@ -211,24 +211,24 @@ describe('ViewMode', () => { setData('view-mode', null); render( - - - + + + ); - const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1); + const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1); const el = await screen.findByTestId(maxViewMode); expect(el).toBeInTheDocument(); }); - Object.keys(ViewModeTypes).filter((v) => !isNaN(Number(v))).map(key => - test(`uses a viewmode option that is stored in idb - ${ViewModeTypes[key]}`, async () => { + Object.keys(UserViewTypes).filter((v) => !isNaN(Number(v))).map(key => + test(`uses a viewmode option that is stored in idb - ${UserViewTypes[key]}`, async () => { setData('view-mode', key); render( - - - + + + ); const el = await screen.findByTestId(key); @@ -236,26 +236,26 @@ describe('ViewMode', () => { }) ) - test('update viewmode live, using setViewMode from context and verify idb save', async () => { + test('update viewmode live, using setUserView from context and verify idb save', async () => { setData('view-mode', null); function Updater() { - const { setViewMode } = useViewMode(); + const { setUserView } = useUserView(); const handleClick = useCallback((value) => { - setViewMode(value.button.toString()); - }, [setViewMode]); + setUserView(value.button.toString()); + }, [setUserView]); return
click me
; } render( - - + + - + ); const button = await screen.findByText('click me'); - const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1); + const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1); fireEvent.click(button, {button: '0'}); const minmode = await screen.findByTestId('0'); diff --git a/web/src/context/index.jsx b/web/src/context/index.jsx index 18fbf8d1d..46b6cec09 100644 --- a/web/src/context/index.jsx +++ b/web/src/context/index.jsx @@ -1,19 +1,19 @@ import { h, createContext } from 'preact'; import { get as getData, set as setData } from 'idb-keyval'; import { useCallback, useContext, useEffect, useLayoutEffect, useState } from 'preact/hooks'; -import { ViewModeTypes } from '../components/ViewOptionEnum'; +import { UserViewTypes } from './UserViewTypes'; -const ViewMode = createContext(""); +const UserView = createContext(""); -export function ViewModeProvider({ children, config }) { - const [currentViewMode, setCurrentViewMode] = useState(null); +export function UserViewProvider({ children, config }) { + const [currentUserView, setCurrentUserView] = useState(null); - const setViewMode = useCallback( + const setUserView = useCallback( (value) => { setData('view-mode', value); - setCurrentViewMode(value); + setCurrentUserView(value); }, - [setCurrentViewMode] + [setCurrentUserView] ); useEffect(() => { @@ -21,25 +21,25 @@ export function ViewModeProvider({ children, config }) { let viewmode = await getData('view-mode'); if(viewmode == null) { - const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1).toString(); - const configValue = config ? ViewModeTypes[config.ui.viewmode].toString() : maxViewMode; + const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1).toString(); + const configValue = config ? UserViewTypes[config.ui.viewmode].toString() : maxViewMode; viewmode = configValue; } - setViewMode(viewmode); + setUserView(viewmode); } load(); - }, [config, setViewMode]); + }, [config, setUserView]); - return !currentViewMode ? null : ( - {children} + return !currentUserView ? null : ( + {children} ); } -export function useViewMode() { - return useContext(ViewMode); +export function useUserView() { + return useContext(UserView); } const DarkMode = createContext(null); diff --git a/web/src/routes/Camera.jsx b/web/src/routes/Camera.jsx index bb580dde6..a02f3c812 100644 --- a/web/src/routes/Camera.jsx +++ b/web/src/routes/Camera.jsx @@ -17,7 +17,7 @@ import WebRtcPlayer from '../components/WebRtcPlayer'; import '../components/MsePlayer'; import CameraControlPanel from '../components/CameraControlPanel'; import { baseUrl } from '../api/baseUrl'; -import ViewOption from '../components/ViewOption' +import UserViewer from '../components/UserViewer' const emptyObject = Object.freeze({}); @@ -110,9 +110,9 @@ export default function Camera({ camera }) { label="Regions" labelPosition="after" /> - + Mask & Zone creator - + ) : null; diff --git a/web/src/routes/Cameras.jsx b/web/src/routes/Cameras.jsx index cc3aa658c..980e4402b 100644 --- a/web/src/routes/Cameras.jsx +++ b/web/src/routes/Cameras.jsx @@ -8,8 +8,8 @@ import MotionIcon from '../icons/Motion'; import SnapshotIcon from '../icons/Snapshot'; import { useAudioState, useDetectState, useRecordingsState, useSnapshotsState } from '../api/ws'; import { useMemo } from 'preact/hooks'; -import { useViewMode } from '../context' -import { ViewModeTypes } from '../components/ViewOptionEnum'; +import { useUserView } from '../context' +import { UserViewTypes } from '../context/UserViewTypes'; import useSWR from 'swr'; export default function Cameras() { @@ -101,14 +101,14 @@ function Camera({ name, config }) { [config, audioValue, sendAudio, detectValue, sendDetect, recordValue, sendRecordings, snapshotValue, sendSnapshots] ); - const { currentViewMode } = useViewMode(); + const { currentUserView } = useUserView(); return ( = ViewModeTypes["admin"] ? icons : []} + icons={!currentUserView || currentUserView >= UserViewTypes["admin"] ? icons : []} media={} /> ); diff --git a/web/src/routes/Events.jsx b/web/src/routes/Events.jsx index 6d882e090..6a3009066 100644 --- a/web/src/routes/Events.jsx +++ b/web/src/routes/Events.jsx @@ -30,7 +30,7 @@ import TimeAgo from '../components/TimeAgo'; import Timepicker from '../components/TimePicker'; import TimelineSummary from '../components/TimelineSummary'; import TimelineEventOverlay from '../components/TimelineEventOverlay'; -import ViewOption from '../components/ViewOption'; +import UserViewer from '../components/UserViewer'; const API_LIMIT = 25; @@ -658,13 +658,13 @@ export default function Events({ path, ...props }) { )}
- + onDelete(e, event.id, event.retain_indefinitely)} /> - + go2rtc {go2rtc && `${go2rtc.version} `} - + dashboard - + )}