Skip to content

Commit

Permalink
Enhancements to Location Live Feed (#6726)
Browse files Browse the repository at this point in the history
* Page: support for auto collapse sidebar

* Adds utilities and `useOperateCamera` hook

* Adds reusable component: `NetworkSignal`

* stash

* Update camera controls

* rename files

* refactor

* refactor location filter

* Add location select popup

* Update location select logic

* update reponsiveness

* hide non working filters

* adjust z-index
  • Loading branch information
rithviknishad authored Dec 7, 2023
1 parent a6ed2bc commit 745d4f8
Show file tree
Hide file tree
Showing 34 changed files with 1,394 additions and 1,366 deletions.
57 changes: 57 additions & 0 deletions src/CAREUI/display/NetworkSignal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { classNames } from "../../Utils/utils";
import CareIcon from "../icons/CareIcon";

interface Props {
/**
* Strength of the signal, from 0 to 3
*
* undefined: Error
* 0: No signal
* 1: Weak signal
* 2: Medium signal
* 3: Strong signal
*/
strength?: number;
children?: React.ReactNode;
}

export default function NetworkSignal({ strength, children }: Props) {
return (
<div
className={classNames(
"flex items-center", // Strength colors
strength === 0 && "text-danger-500",
strength === 1 && "text-danger-500",
strength === 2 && "text-warning-500",
strength === 3 && "text-primary-500"
)}
>
<div className="flex items-end gap-0.5 p-2">
{strength === undefined ? (
<CareIcon
icon="l-exclamation-triangle"
className="text-lg text-danger-500"
/>
) : (
Array.from({ length: 3 }, (_, i) => (
<div
key={i}
className={classNames(
"w-1 rounded-sm",

// Heights
i === 0 && "h-[5px]",
i === 1 && "h-[10px]",
i === 2 && "h-[15px]",

// Whether to infill with strength color or not
strength > i ? "bg-current" : "bg-zinc-600"
)}
/>
))
)}
</div>
{children}
</div>
);
}
49 changes: 49 additions & 0 deletions src/CAREUI/interactive/KeyboardShortcut.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import useKeyboardShortcut from "use-keyboard-shortcut";
import { classNames, isAppleDevice } from "../../Utils/utils";

interface Props {
children: React.ReactNode;
shortcut: string[];
onTrigger: () => void;
shortcutSeperator?: string;
helpText?: string;
tooltipClassName?: string;
}

export default function KeyboardShortcut(props: Props) {
useKeyboardShortcut(props.shortcut, props.onTrigger, {
overrideSystem: true,
});

return (
<div className="tooltip">
{props.children}
<span
className={classNames(
"tooltip-text flex items-center gap-0.5 text-xs",
props.tooltipClassName || "tooltip-bottom"
)}
>
<span className="px-1 font-bold">{props.helpText}</span>
<kbd className="hidden items-center px-1.5 font-sans font-medium text-zinc-300 shadow md:inline-flex">
{getShortcutKeyDescription(props.shortcut).join(" + ")}
</kbd>
</span>
</div>
);
}

const SHORTCUT_KEY_MAP = {
Meta: "⌘",
Shift: "⇧Shift",
Alt: "⌥Alt",
Control: isAppleDevice ? "⌃Ctrl" : "Ctrl",
ArrowUp: "↑",
ArrowDown: "↓",
ArrowLeft: "←",
ArrowRight: "→",
} as Record<string, string>;

export const getShortcutKeyDescription = (shortcut: string[]) => {
return shortcut.map((key) => SHORTCUT_KEY_MAP[key] || key);
};
43 changes: 43 additions & 0 deletions src/CAREUI/misc/Fullscreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect, useRef } from "react";

interface Props {
className?: string;
fullscreenClassName?: string;
children: React.ReactNode;
fullscreen: boolean;
onExit: () => void;
}

export default function Fullscreen(props: Props) {
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
if (props.fullscreen) {
ref.current?.requestFullscreen();
} else {
document.exitFullscreen();
}
}, [props.fullscreen]);

useEffect(() => {
const listener = () => {
if (!document.fullscreenElement) {
props.onExit();
}
};

document.addEventListener("fullscreenchange", listener);
return () => {
document.removeEventListener("fullscreenchange", listener);
};
}, [props.onExit]);

return (
<div
ref={ref}
className={props.fullscreen ? props.fullscreenClassName : props.className}
>
{props.children}
</div>
);
}
7 changes: 7 additions & 0 deletions src/Common/hooks/useFeedPTZ.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Deprecated. Use `useOperateAsset` instead.
*
* Preserving for backwards compatibility and preventing merge conflict with a
* co-related PR. Will be removed in the future.
*/

import { operateAsset } from "../../Redux/actions";

export interface IAsset {
Expand Down
45 changes: 0 additions & 45 deletions src/Common/hooks/useMSEplayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ export interface IAsset {
middlewareHostname: string;
}

interface PTZPayload {
x: number;
y: number;
zoom: number;
}

interface UseMSEMediaPlayerOption {
config: IAsset;
url?: string;
Expand Down Expand Up @@ -40,16 +34,6 @@ export interface IOptions {
onSuccess?: (resp: any) => void;
onError?: (err: any) => void;
}

enum PTZ {
Up = "up",
Down = "down",
Left = "left",
Right = "right",
ZoomIn = "zoomIn",
ZoomOut = "zoomOut",
}

const stopStream =
({
middlewareHostname,
Expand All @@ -69,38 +53,9 @@ const stopStream =
.catch((err) => options.onError && options.onError(err));
};

export const getPTZPayload = (action: PTZ): PTZPayload => {
let x = 0;
let y = 0;
let zoom = 0;
switch (action) {
case PTZ.Up:
y = 0.1;
break;
case PTZ.Down:
y = -0.1;
break;
case PTZ.Left:
x = -0.1;
break;
case PTZ.Right:
x = 0.1;
break;
case PTZ.ZoomIn:
zoom = 0.1;
break;
case PTZ.ZoomOut:
zoom = -0.1;
break;
}

return { x, y, zoom };
};

/**
* MSE player utility
*/

const Utf8ArrayToStr = (array: string | any[] | Uint8Array) => {
let out, i, c;
let char2, char3;
Expand Down
1 change: 1 addition & 0 deletions src/Components/Assets/AssetTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface AssetLocationObject {
id: string;
name: string;
};
middleware_address?: string;
}

export enum AssetType {
Expand Down
77 changes: 77 additions & 0 deletions src/Components/CameraFeed/AssetBedSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Fragment } from "react";
import useSlug from "../../Common/hooks/useSlug";
import routes from "../../Redux/api";
import useQuery from "../../Utils/request/useQuery";
import { AssetBedModel, AssetData } from "../Assets/AssetTypes";
import { BedModel } from "../Facility/models";
import { Listbox, Transition } from "@headlessui/react";
import CareIcon from "../../CAREUI/icons/CareIcon";

interface Props {
asset?: AssetData;
bed?: BedModel;
value?: AssetBedModel;
onChange?: (value: AssetBedModel) => void;
}

export default function AssetBedSelect(props: Props) {
const facility = useSlug("facility");

const { data, loading } = useQuery(routes.listAssetBeds, {
query: {
limit: 100,
facility,
asset: props.asset?.id,
bed: props.bed?.id,
},
});

const selected = props.value;

return (
<Listbox value={selected} onChange={props.onChange} disabled={loading}>
<div className="relative">
<Listbox.Button className="relative w-full cursor-default pr-6 text-right text-xs text-zinc-400 focus:outline-none disabled:cursor-not-allowed disabled:bg-transparent disabled:text-zinc-700 sm:text-sm">
<span className="block truncate">
{selected?.bed_object.name ?? "No Preset"}
</span>
<span className="pointer-events-none absolute inset-y-0 right-0 mt-1 flex items-center">
<CareIcon icon="l-angle-down" className="text-lg text-zinc-500" />
</span>
</Listbox.Button>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute z-20 mt-1 max-h-48 w-full overflow-auto rounded-b-lg bg-zinc-900/75 py-1 text-base shadow-lg ring-1 ring-white/5 backdrop-blur-sm focus:outline-none sm:text-sm md:max-h-60">
{data?.results.map((obj) => (
<Listbox.Option
key={obj.id}
className={({ active }) =>
`relative cursor-default select-none px-2 py-1 ${
active ? "bg-zinc-700 text-white" : "text-zinc-400"
}`
}
value={obj}
>
{({ selected }) => (
<>
<span
className={`block truncate text-xs md:text-sm ${
selected ? "font-bold text-white" : "font-normal"
}`}
>
{obj.bed_object.name}: {obj.meta.preset_name}
</span>
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
);
}
Loading

0 comments on commit 745d4f8

Please sign in to comment.