ViewMode renamed to UserView in code

This commit is contained in:
spacebares 2023-07-07 14:38:46 -04:00
parent 889a629a9e
commit 97a5363c84
13 changed files with 105 additions and 105 deletions

View File

@ -8,16 +8,16 @@ import DarkModeIcon from './icons/DarkMode';
import SettingsIcon from './icons/Settings'; import SettingsIcon from './icons/Settings';
import FrigateRestartIcon from './icons/FrigateRestart'; import FrigateRestartIcon from './icons/FrigateRestart';
import Prompt from './components/Prompt'; import Prompt from './components/Prompt';
import { useDarkMode, useViewMode } from './context'; import { useDarkMode, useUserView } from './context';
import { useCallback, useRef, useState } from 'preact/hooks'; import { useCallback, useRef, useState } from 'preact/hooks';
import { useRestart } from './api/ws'; import { useRestart } from './api/ws';
import { ViewModeTypes } from './components/ViewOptionEnum' import { UserViewTypes } from './context/UserViewTypes'
export default function AppBar() { export default function AppBar() {
const [showMoreMenu, setShowMoreMenu] = useState(false); const [showMoreMenu, setShowMoreMenu] = useState(false);
const [showDialog, setShowDialog] = useState(false); const [showDialog, setShowDialog] = useState(false);
const [showDialogWait, setShowDialogWait] = useState(false); const [showDialogWait, setShowDialogWait] = useState(false);
const { currentViewMode, setViewMode } = useViewMode(); const { currentUserView, setUserView } = useUserView();
const { setDarkMode } = useDarkMode(); const { setDarkMode } = useDarkMode();
const { send: sendRestart } = useRestart(); const { send: sendRestart } = useRestart();
@ -29,12 +29,12 @@ export default function AppBar() {
[setDarkMode, setShowMoreMenu] [setDarkMode, setShowMoreMenu]
); );
const handleSetViewMode = useCallback( const handleSetUserView = useCallback(
(value) => { (value) => {
setViewMode(value); setUserView(value);
setShowMoreMenu(false); setShowMoreMenu(false);
}, },
[setViewMode, setShowMoreMenu] [setUserView, setShowMoreMenu]
); );
const moreRef = useRef(null); const moreRef = useRef(null);
@ -75,10 +75,10 @@ export default function AppBar() {
<MenuItem icon={SettingsIcon} label="Viewmode:"> <MenuItem icon={SettingsIcon} label="Viewmode:">
<select <select
className="py-0.5 pr-8" className="py-0.5 pr-8"
value={currentViewMode} value={currentUserView}
onChange={(e) => handleSetViewMode(e.target.value)} onChange={(e) => handleSetUserView(e.target.value)}
> >
{ Object.keys(ViewModeTypes).filter((v) => !isNaN(Number(v))).map(key => <option key={key} value={key}>{ViewModeTypes[key]}</option>) } { Object.keys(UserViewTypes).filter((v) => !isNaN(Number(v))).map(key => <option key={key} value={key}>{UserViewTypes[key]}</option>) }
</select> </select>
</MenuItem> </MenuItem>
<MenuSeparator /> <MenuSeparator />

View File

@ -4,7 +4,7 @@ import { Match } from 'preact-router/match';
import { memo } from 'preact/compat'; import { memo } from 'preact/compat';
import { ENV } from './env'; import { ENV } from './env';
import { useMemo } from 'preact/hooks' import { useMemo } from 'preact/hooks'
import ViewOption from './components/ViewOption' import UserViewer from './components/UserViewer'
import useSWR from 'swr'; import useSWR from 'swr';
import NavigationDrawer, { Destination, Separator } from './components/NavigationDrawer'; import NavigationDrawer, { Destination, Separator } from './components/NavigationDrawer';
@ -47,17 +47,17 @@ export default function Sidebar() {
<Destination href="/events" text="Events" /> <Destination href="/events" text="Events" />
<Destination href="/exports" text="Exports" /> <Destination href="/exports" text="Exports" />
<Separator /> <Separator />
<ViewOption requiredmode="advanced"> <UserViewer requiredmode="advanced">
<Destination href="/storage" text="Storage" /> <Destination href="/storage" text="Storage" />
<Destination href="/system" text="System" /> <Destination href="/system" text="System" />
</ViewOption> </UserViewer>
<ViewOption requiredmode="admin"> <UserViewer requiredmode="admin">
<Destination href="/config" text="Config" /> <Destination href="/config" text="Config" />
</ViewOption> </UserViewer>
<ViewOption requiredmode="advanced"> <UserViewer requiredmode="advanced">
<Destination href="/logs" text="Logs" /> <Destination href="/logs" text="Logs" />
<Separator /> <Separator />
</ViewOption> </UserViewer>
<div className="flex flex-grow" /> <div className="flex flex-grow" />
{ENV !== 'production' ? ( {ENV !== 'production' ? (
<Fragment> <Fragment>

View File

@ -6,7 +6,7 @@ import AppBar from './AppBar';
import Cameras from './routes/Cameras'; import Cameras from './routes/Cameras';
import { Router } from 'preact-router'; import { Router } from 'preact-router';
import Sidebar from './Sidebar'; import Sidebar from './Sidebar';
import { DarkModeProvider, DrawerProvider, ViewModeProvider } from './context'; import { DarkModeProvider, DrawerProvider, UserViewProvider } from './context';
import useSWR from 'swr'; import useSWR from 'swr';
export default function App() { export default function App() {
@ -16,7 +16,7 @@ export default function App() {
return ( return (
<DarkModeProvider> <DarkModeProvider>
<DrawerProvider> <DrawerProvider>
<ViewModeProvider config={config}> <UserViewProvider config={config}>
<div data-testid="app" className="w-full"> <div data-testid="app" className="w-full">
<AppBar /> <AppBar />
{!config ? ( {!config ? (
@ -48,7 +48,7 @@ export default function App() {
</div> </div>
)} )}
</div> </div>
</ViewModeProvider> </UserViewProvider>
</DrawerProvider> </DrawerProvider>
</DarkModeProvider> </DarkModeProvider>
); );

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,27 +1,27 @@
import { h } from 'preact'; import { h } from 'preact';
import { render, screen, waitFor } from '@testing-library/preact'; import { render, screen, waitFor } from '@testing-library/preact';
import { set as setData } from 'idb-keyval'; import { set as setData } from 'idb-keyval';
import ViewOption from '../ViewOption'; import UserViewer from '../UserViewer';
import { ViewModeProvider } from '../../context'; import { UserViewProvider } from '../../context';
import { ViewModeTypes } from '../ViewOptionEnum'; import { UserViewTypes } from '../../context/UserViewTypes';
import * as WS from '../../api/ws'; import * as WS from '../../api/ws';
describe('ViewOption', () => { describe('UserViewer', () => {
beforeEach(() => { beforeEach(() => {
vi.spyOn(WS, 'WsProvider').mockImplementation(({ children }) => children); vi.spyOn(WS, 'WsProvider').mockImplementation(({ children }) => children);
}); });
test('make sure children are visible with same modes', async () => { test('make sure children are visible with same modes', async () => {
const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1); const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1);
const maxViewModeHR = ViewModeTypes[maxViewMode]; const maxViewModeHR = UserViewTypes[maxViewMode];
setData('view-mode', maxViewMode); setData('view-mode', maxViewMode);
render( render(
<ViewModeProvider> <UserViewProvider>
<ViewOption requiredmode={maxViewModeHR}> <UserViewer requiredmode={maxViewModeHR}>
<div data-testid='children'>stuff</div> <div data-testid='children'>stuff</div>
</ViewOption> </UserViewer>
</ViewModeProvider> </UserViewProvider>
); );
const el = await screen.findByTestId('children'); 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 () => { test('make sure children are visible with max viewmode, and a small requiredmode', async () => {
const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1); const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1);
const lowViewModeHR = ViewModeTypes[1]; const lowViewModeHR = UserViewTypes[1];
setData('view-mode', maxViewMode); setData('view-mode', maxViewMode);
render( render(
<ViewModeProvider> <UserViewProvider>
<ViewOption requiredmode={lowViewModeHR}> <UserViewer requiredmode={lowViewModeHR}>
<div data-testid='children'>stuff</div> <div data-testid='children'>stuff</div>
</ViewOption> </UserViewer>
</ViewModeProvider> </UserViewProvider>
); );
const el = await screen.findByTestId('children'); const el = await screen.findByTestId('children');
@ -46,17 +46,17 @@ describe('ViewOption', () => {
}); });
test('make sure children are hidden, due to failed requiredmode', async () => { test('make sure children are hidden, due to failed requiredmode', async () => {
const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1); const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1);
const maxViewModeHR = ViewModeTypes[maxViewMode]; const maxViewModeHR = UserViewTypes[maxViewMode];
setData('view-mode', '0'); setData('view-mode', '0');
render( render(
<ViewModeProvider> <UserViewProvider>
<ViewOption requiredmode={maxViewModeHR}> <UserViewer requiredmode={maxViewModeHR}>
<div data-testid='children'>stuff</div> <div data-testid='children'>stuff</div>
</ViewOption> </UserViewer>
<div>bugfix</div> <div>bugfix</div>
</ViewModeProvider> </UserViewProvider>
); );
//without this, the test will always pass, even if setData('view-mode', '2') ... really strange behavior //without this, the test will always pass, even if setData('view-mode', '2') ... really strange behavior

View File

@ -1,4 +1,4 @@
export enum ViewModeTypes { export enum UserViewTypes {
"user", "user",
"advanced", "advanced",
"admin", "admin",

View File

@ -1,7 +1,7 @@
import { h } from 'preact'; import { h } from 'preact';
import { set as setData, get as getData } from 'idb-keyval'; import { set as setData, get as getData } from 'idb-keyval';
import { DarkModeProvider, useDarkMode, usePersistence, ViewModeProvider, useViewMode } from '..'; import { DarkModeProvider, useDarkMode, usePersistence, UserViewProvider, useUserView } from '..';
import { ViewModeTypes } from '../../components/ViewOptionEnum' import { UserViewTypes } from '../../context/UserViewTypes'
import { fireEvent, render, screen } from 'testing-library'; import { fireEvent, render, screen } from 'testing-library';
import { useCallback } from 'preact/hooks'; import { useCallback } from 'preact/hooks';
import * as WS from '../../api/ws'; import * as WS from '../../api/ws';
@ -197,9 +197,9 @@ describe('usePersistence', () => {
}); });
}); });
function ViewModeChecker() { function UserViewChecker() {
const { currentViewMode } = useViewMode(); const { currentUserView } = useUserView();
return <div data-testid={currentViewMode}>{currentViewMode}</div>; return <div data-testid={currentUserView}>{currentUserView}</div>;
} }
describe('ViewMode', () => { describe('ViewMode', () => {
@ -211,24 +211,24 @@ describe('ViewMode', () => {
setData('view-mode', null); setData('view-mode', null);
render( render(
<ViewModeProvider> <UserViewProvider>
<ViewModeChecker /> <UserViewChecker />
</ViewModeProvider> </UserViewProvider>
); );
const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1); const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1);
const el = await screen.findByTestId(maxViewMode); const el = await screen.findByTestId(maxViewMode);
expect(el).toBeInTheDocument(); expect(el).toBeInTheDocument();
}); });
Object.keys(ViewModeTypes).filter((v) => !isNaN(Number(v))).map(key => Object.keys(UserViewTypes).filter((v) => !isNaN(Number(v))).map(key =>
test(`uses a viewmode option that is stored in idb - ${ViewModeTypes[key]}`, async () => { test(`uses a viewmode option that is stored in idb - ${UserViewTypes[key]}`, async () => {
setData('view-mode', key); setData('view-mode', key);
render( render(
<ViewModeProvider> <UserViewProvider>
<ViewModeChecker /> <UserViewChecker />
</ViewModeProvider> </UserViewProvider>
); );
const el = await screen.findByTestId(key); 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); setData('view-mode', null);
function Updater() { function Updater() {
const { setViewMode } = useViewMode(); const { setUserView } = useUserView();
const handleClick = useCallback((value) => { const handleClick = useCallback((value) => {
setViewMode(value.button.toString()); setUserView(value.button.toString());
}, [setViewMode]); }, [setUserView]);
return <div onClick={handleClick}>click me</div>; return <div onClick={handleClick}>click me</div>;
} }
render( render(
<ViewModeProvider> <UserViewProvider>
<ViewModeChecker /> <UserViewChecker />
<Updater /> <Updater />
</ViewModeProvider> </UserViewProvider>
); );
const button = await screen.findByText('click me'); 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'}); fireEvent.click(button, {button: '0'});
const minmode = await screen.findByTestId('0'); const minmode = await screen.findByTestId('0');

View File

@ -1,19 +1,19 @@
import { h, createContext } from 'preact'; import { h, createContext } from 'preact';
import { get as getData, set as setData } from 'idb-keyval'; import { get as getData, set as setData } from 'idb-keyval';
import { useCallback, useContext, useEffect, useLayoutEffect, useState } from 'preact/hooks'; 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 }) { export function UserViewProvider({ children, config }) {
const [currentViewMode, setCurrentViewMode] = useState(null); const [currentUserView, setCurrentUserView] = useState(null);
const setViewMode = useCallback( const setUserView = useCallback(
(value) => { (value) => {
setData('view-mode', value); setData('view-mode', value);
setCurrentViewMode(value); setCurrentUserView(value);
}, },
[setCurrentViewMode] [setCurrentUserView]
); );
useEffect(() => { useEffect(() => {
@ -21,25 +21,25 @@ export function ViewModeProvider({ children, config }) {
let viewmode = await getData('view-mode'); let viewmode = await getData('view-mode');
if(viewmode == null) { if(viewmode == null) {
const maxViewMode = (Object.keys(ViewModeTypes).filter(isNaN).length-1).toString(); const maxViewMode = (Object.keys(UserViewTypes).filter(isNaN).length-1).toString();
const configValue = config ? ViewModeTypes[config.ui.viewmode].toString() : maxViewMode; const configValue = config ? UserViewTypes[config.ui.viewmode].toString() : maxViewMode;
viewmode = configValue; viewmode = configValue;
} }
setViewMode(viewmode); setUserView(viewmode);
} }
load(); load();
}, [config, setViewMode]); }, [config, setUserView]);
return !currentViewMode ? null : ( return !currentUserView ? null : (
<ViewMode.Provider value={{ currentViewMode, setViewMode }}>{children}</ViewMode.Provider> <UserView.Provider value={{ currentUserView, setUserView }}>{children}</UserView.Provider>
); );
} }
export function useViewMode() { export function useUserView() {
return useContext(ViewMode); return useContext(UserView);
} }
const DarkMode = createContext(null); const DarkMode = createContext(null);

View File

@ -17,7 +17,7 @@ import WebRtcPlayer from '../components/WebRtcPlayer';
import '../components/MsePlayer'; import '../components/MsePlayer';
import CameraControlPanel from '../components/CameraControlPanel'; import CameraControlPanel from '../components/CameraControlPanel';
import { baseUrl } from '../api/baseUrl'; import { baseUrl } from '../api/baseUrl';
import ViewOption from '../components/ViewOption' import UserViewer from '../components/UserViewer'
const emptyObject = Object.freeze({}); const emptyObject = Object.freeze({});
@ -110,9 +110,9 @@ export default function Camera({ camera }) {
label="Regions" label="Regions"
labelPosition="after" labelPosition="after"
/> />
<ViewOption requiredmode="admin"> <UserViewer requiredmode="admin">
<Link href={`/cameras/${camera}/editor`}>Mask & Zone creator</Link> <Link href={`/cameras/${camera}/editor`}>Mask & Zone creator</Link>
</ViewOption> </UserViewer>
</div> </div>
) : null; ) : null;

View File

@ -8,8 +8,8 @@ import MotionIcon from '../icons/Motion';
import SnapshotIcon from '../icons/Snapshot'; import SnapshotIcon from '../icons/Snapshot';
import { useAudioState, useDetectState, useRecordingsState, useSnapshotsState } from '../api/ws'; import { useAudioState, useDetectState, useRecordingsState, useSnapshotsState } from '../api/ws';
import { useMemo } from 'preact/hooks'; import { useMemo } from 'preact/hooks';
import { useViewMode } from '../context' import { useUserView } from '../context'
import { ViewModeTypes } from '../components/ViewOptionEnum'; import { UserViewTypes } from '../context/UserViewTypes';
import useSWR from 'swr'; import useSWR from 'swr';
export default function Cameras() { export default function Cameras() {
@ -101,14 +101,14 @@ function Camera({ name, config }) {
[config, audioValue, sendAudio, detectValue, sendDetect, recordValue, sendRecordings, snapshotValue, sendSnapshots] [config, audioValue, sendAudio, detectValue, sendDetect, recordValue, sendRecordings, snapshotValue, sendSnapshots]
); );
const { currentViewMode } = useViewMode(); const { currentUserView } = useUserView();
return ( return (
<Card <Card
buttons={buttons} buttons={buttons}
href={href} href={href}
header={cleanName} header={cleanName}
icons={!currentViewMode || currentViewMode >= ViewModeTypes["admin"] ? icons : []} icons={!currentUserView || currentUserView >= UserViewTypes["admin"] ? icons : []}
media={<CameraImage camera={name} stretch />} media={<CameraImage camera={name} stretch />}
/> />
); );

View File

@ -30,7 +30,7 @@ import TimeAgo from '../components/TimeAgo';
import Timepicker from '../components/TimePicker'; import Timepicker from '../components/TimePicker';
import TimelineSummary from '../components/TimelineSummary'; import TimelineSummary from '../components/TimelineSummary';
import TimelineEventOverlay from '../components/TimelineEventOverlay'; import TimelineEventOverlay from '../components/TimelineEventOverlay';
import ViewOption from '../components/ViewOption'; import UserViewer from '../components/UserViewer';
const API_LIMIT = 25; const API_LIMIT = 25;
@ -658,13 +658,13 @@ export default function Events({ path, ...props }) {
)} )}
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<ViewOption requiredmode="admin"> <UserViewer requiredmode="admin">
<Delete <Delete
className="h-6 w-6 cursor-pointer" className="h-6 w-6 cursor-pointer"
stroke="#f87171" stroke="#f87171"
onClick={(e) => onDelete(e, event.id, event.retain_indefinitely)} onClick={(e) => onDelete(e, event.id, event.retain_indefinitely)}
/> />
</ViewOption> </UserViewer>
<Download <Download
className="h-6 w-6 mt-auto" className="h-6 w-6 mt-auto"

View File

@ -12,7 +12,7 @@ import Dialog from '../components/Dialog';
import TimeAgo from '../components/TimeAgo'; import TimeAgo from '../components/TimeAgo';
import copy from 'copy-to-clipboard'; import copy from 'copy-to-clipboard';
import { About } from '../icons/About'; import { About } from '../icons/About';
import ViewOption from '../components/ViewOption'; import UserViewer from '../components/UserViewer';
const emptyObject = Object.freeze({}); const emptyObject = Object.freeze({});
@ -99,7 +99,7 @@ export default function System() {
{config && ( {config && (
<span class="p-1"> <span class="p-1">
go2rtc {go2rtc && `${go2rtc.version} `} go2rtc {go2rtc && `${go2rtc.version} `}
<ViewOption requiredmode="admin"> <UserViewer requiredmode="admin">
<Link <Link
className="text-blue-500 hover:underline" className="text-blue-500 hover:underline"
target="_blank" target="_blank"
@ -108,7 +108,7 @@ export default function System() {
> >
dashboard dashboard
</Link> </Link>
</ViewOption> </UserViewer>
</span> </span>
)} )}
</div> </div>