this isnt going well

uh oh
This commit is contained in:
cowbot92
2023-12-03 15:46:10 -05:00
parent 4b3e35b0e7
commit 1d8ceac867
6 changed files with 210 additions and 257 deletions

View File

@@ -1,143 +0,0 @@
import { filter, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { classes } from 'common/react';
import { createSearch } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { Button, ByondUi, Flex, Input, Section } from '../components';
import { Window } from '../layouts';
/**
* Returns previous and next camera names relative to the currently
* active camera.
*/
export const prevNextCamera = (cameras, activeCamera) => {
if (!activeCamera) {
return [];
}
const index = cameras.findIndex(camera => (
camera.name === activeCamera.name
));
return [
cameras[index - 1]?.name,
cameras[index + 1]?.name,
];
};
/**
* Camera selector.
*
* Filters cameras, applies search terms and sorts the alphabetically.
*/
export const selectCameras = (cameras, searchText = '') => {
const testSearch = createSearch(searchText, camera => camera.name);
return flow([
// Null camera filter
filter(camera => camera?.name),
// Optional search term
searchText && filter(testSearch),
// Slightly expensive, but way better than sorting in BYOND
sortBy(camera => camera.name),
])(cameras);
};
export const CameraConsole = (props, context) => {
const { act, data } = useBackend(context);
const { mapRef, activeCamera } = data;
const cameras = selectCameras(data.cameras);
const [
prevCameraName,
nextCameraName,
] = prevNextCamera(cameras, activeCamera);
return (
<Window
width={870}
height={708}
resizable>
<div className="CameraConsole__left">
<Window.Content scrollable>
<CameraConsoleContent />
</Window.Content>
</div>
<div className="CameraConsole__right">
<div className="CameraConsole__toolbar">
<b>Camera: </b>
{activeCamera
&& activeCamera.name
|| '—'}
</div>
<div className="CameraConsole__toolbarRight">
<Button
icon="chevron-left"
disabled={!prevCameraName}
onClick={() => act('switch_camera', {
name: prevCameraName,
})} />
<Button
icon="chevron-right"
disabled={!nextCameraName}
onClick={() => act('switch_camera', {
name: nextCameraName,
})} />
</div>
<ByondUi
className="CameraConsole__map"
params={{
id: mapRef,
type: 'map',
}} />
</div>
</Window>
);
};
export const CameraConsoleContent = (props, context) => {
const { act, data } = useBackend(context);
const [
searchText,
setSearchText,
] = useLocalState(context, 'searchText', '');
const { activeCamera } = data;
const cameras = selectCameras(data.cameras, searchText);
return (
<Flex
direction={"column"}
height="100%">
<Flex.Item>
<Input
autoFocus
fluid
mt={1}
placeholder="Search for a camera"
onInput={(e, value) => setSearchText(value)} />
</Flex.Item>
<Flex.Item
height="100%">
<Section
fill
scrollable>
{cameras.map(camera => (
// We're not using the component here because performance
// would be absolutely abysmal (50+ ms for each re-render).
<div
key={camera.name}
title={camera.name}
className={classes([
'Button',
'Button--fluid',
'Button--color--transparent',
'Button--ellipsis',
activeCamera
&& camera.name === activeCamera.name
&& 'Button--selected',
])}
onClick={() => [act('switch_camera', {
name: camera.name,
}), document.getElementsByClassName('CameraConsole__left')[0].focus()]}>
{camera.name}
</div>
))}
</Section>
</Flex.Item>
</Flex>
);
};

View File

@@ -0,0 +1,198 @@
import { filter, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { BooleanLike, classes } from 'common/react';
import { createSearch } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { Button, ByondUi, Input, NoticeBox, Section, Stack } from '../components';
import { Window } from '../layouts';
type Data = {
can_spy: BooleanLike;
mapRef: string;
cameras: Camera[];
activeCamera: Camera & { status: BooleanLike };
network: string[];
};
type Camera = {
name: string;
};
/**
* Returns previous and next camera names relative to the currently
* active camera.
*/
const prevNextCamera = (
cameras: Camera[],
activeCamera: Camera & { status: BooleanLike }
) => {
if (!activeCamera) {
return [];
}
const index = cameras.findIndex(
(camera) => camera?.name === activeCamera.name
);
return [cameras[index - 1]?.name, cameras[index + 1]?.name];
};
/**
* Camera selector.
*
* Filters cameras, applies search terms and sorts the alphabetically.
*/
const selectCameras = (cameras: Camera[], searchText = ''): Camera[] => {
const testSearch = createSearch(searchText, (camera: Camera) => camera.name);
return flow([
// Null camera filter
filter((camera: Camera) => !!camera?.name), // Optional search term
searchText && filter(testSearch),
// Slightly expensive, but way better than sorting in BYOND
sortBy((camera: Camera) => camera.name),
])(cameras);
};
export const CameraConsole = (props, context) => {
return (
<Window width={850} height={708}>
<Window.Content>
<CameraContent />
</Window.Content>
</Window>
);
};
export const CameraContent = (props, context) => {
return (
<Stack fill>
<Stack.Item grow>
<CameraSelector />
</Stack.Item>
<Stack.Item grow={3}>
<CameraControls />
</Stack.Item>
</Stack>
);
};
const CameraSelector = (props, context) => {
const { act, data } = useBackend<Data>(context);
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
const { activeCamera } = data;
const cameras = selectCameras(data.cameras, searchText);
return (
<Stack fill vertical>
<Stack.Item>
<Input
autoFocus
fluid
mt={1}
placeholder="Search for a camera"
onInput={(e, value) => setSearchText(value)}
/>
</Stack.Item>
<Stack.Item grow>
<Section fill scrollable>
{cameras.map((camera) => (
// We're not using the component here because performance
// would be absolutely abysmal (50+ ms for each re-render).
<div
key={camera.name}
title={camera.name}
className={classes([
'candystripe',
'Button',
'Button--fluid',
'Button--color--transparent',
'Button--ellipsis',
activeCamera &&
camera.name === activeCamera.name &&
'Button--selected',
])}
onClick={() =>
act('switch_camera', {
name: camera.name,
})
}>
{camera.name}
</div>
))}
</Section>
</Stack.Item>
</Stack>
);
};
const CameraControls = (props, context) => {
const { act, data } = useBackend<Data>(context);
const { activeCamera, can_spy, mapRef } = data;
const cameras = selectCameras(data.cameras);
const [prevCameraName, nextCameraName] = prevNextCamera(
cameras,
activeCamera
);
return (
<Section fill>
<Stack fill vertical>
<Stack.Item>
<Stack fill>
<Stack.Item grow>
{activeCamera?.name ? (
<NoticeBox info>{activeCamera.name}</NoticeBox>
) : (
<NoticeBox danger>No input signal</NoticeBox>
)}
</Stack.Item>
<Stack.Item>
{!!can_spy && (
<Button
icon="magnifying-glass"
tooltip="Track Person"
onClick={() => act('start_tracking')}
/>
)}
</Stack.Item>
<Stack.Item>
<Button
icon="chevron-left"
disabled={!prevCameraName}
onClick={() =>
act('switch_camera', {
name: prevCameraName,
})
}
/>
</Stack.Item>
<Stack.Item>
<Button
icon="chevron-right"
disabled={!nextCameraName}
onClick={() =>
act('switch_camera', {
name: nextCameraName,
})
}
/>
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item grow>
<ByondUi
height="100%"
width="100%"
params={{
id: mapRef,
type: 'map',
}}
/>
</Stack.Item>
</Stack>
</Section>
);
};

View File

@@ -1,60 +0,0 @@
import { filter, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { classes } from 'common/react';
import { createSearch } from 'common/string';
import { Fragment } from 'inferno';
import { useBackend, useLocalState } from '../backend';
import { Button, ByondUi, Input, Section } from '../components';
import { NtosWindow } from '../layouts';
import { prevNextCamera, selectCameras, CameraConsoleContent } from './CameraConsole';
import { logger } from "../logging";
export const NtosSecurEye = (props, context) => {
const { act, data, config } = useBackend(context);
const { PC_device_theme, mapRef, activeCamera } = data;
const cameras = selectCameras(data.cameras);
const [
prevCameraName,
nextCameraName,
] = prevNextCamera(cameras, activeCamera);
return (
<NtosWindow
width={800}
height={600}
theme={PC_device_theme}>
<NtosWindow.Content>
<div className="CameraConsole__left">
<CameraConsoleContent />
</div>
<div className="CameraConsole__right">
<div className="CameraConsole__toolbar">
<b>Camera: </b>
{activeCamera
&& activeCamera.name
|| '—'}
</div>
<div className="CameraConsole__toolbarRight">
<Button
icon="chevron-left"
disabled={!prevCameraName}
onClick={() => act('switch_camera', {
name: prevCameraName,
})} />
<Button
icon="chevron-right"
disabled={!nextCameraName}
onClick={() => act('switch_camera', {
name: nextCameraName,
})} />
</div>
<ByondUi
className="CameraConsole__map"
params={{
id: mapRef,
type: 'map',
}} />
</div>
</NtosWindow.Content>
</NtosWindow>
);
};

View File

@@ -0,0 +1,12 @@
import { NtosWindow } from '../layouts';
import { CameraContent } from './CameraConsole';
export const NtosSecurEye = (props) => {
return (
<NtosWindow width={800} height={600}>
<NtosWindow.Content>
<CameraContent />
</NtosWindow.Content>
</NtosWindow>
);
};

View File

@@ -1,53 +0,0 @@
@use '../base.scss';
$background-color: rgba(0, 0, 0, 0.33) !default;
.CameraConsole__left {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: base.em(220px);
}
.CameraConsole__right {
position: absolute;
top: 0;
bottom: 0;
left: base.em(220px);
right: 0;
background-color: $background-color;
}
.CameraConsole__toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2em;
line-height: 2em;
margin: 0.25em 1em 0;
}
.CameraConsole__toolbarRight {
position: absolute;
top: 0;
right: 0;
height: 2em;
line-height: 2em;
margin: 0.33em 0.5em 0;
}
.CameraConsole__map {
position: absolute;
top: base.em(26px);
bottom: 0;
left: 0;
right: 0;
margin: 0.5em;
text-align: center;
.NoticeBox {
margin-top: calc(50% - 2em);
}
}

View File

@@ -48,7 +48,6 @@
// Interfaces
@include meta.load-css('./interfaces/AlertModal.scss');
@include meta.load-css('./interfaces/CameraConsole.scss');
@include meta.load-css('./interfaces/InspectorBooth.scss');
@include meta.load-css('./interfaces/ListInput.scss');
@include meta.load-css('./interfaces/HellishRunes.scss');