From 351c479a2d4458995a89cc1464e8dc7b3118f551 Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Wed, 14 Aug 2024 15:24:25 +0530 Subject: [PATCH] Camera Feed: Persist last accessed position or preset --- src/Components/CameraFeed/CameraFeed.tsx | 5 +- src/Components/CameraFeed/routes.ts | 20 +++++- .../ConsultationFeedTab.tsx | 66 ++++++++++++++++--- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/src/Components/CameraFeed/CameraFeed.tsx b/src/Components/CameraFeed/CameraFeed.tsx index 7c5cf7a8a19..b6ac336259e 100644 --- a/src/Components/CameraFeed/CameraFeed.tsx +++ b/src/Components/CameraFeed/CameraFeed.tsx @@ -12,6 +12,7 @@ import FeedControls from "./FeedControls"; import FeedWatermark from "./FeedWatermark"; import useFullscreen from "../../Common/hooks/useFullscreen"; import useBreakpoints from "../../Common/hooks/useBreakpoints"; +import { GetPresetsResponse } from "./routes"; interface Props { children?: React.ReactNode; @@ -66,7 +67,7 @@ export default function CameraFeed(props: Props) { async function getPresets(cb: (presets: Record) => void) { const { res, data } = await props.operate({ type: "get_presets" }); if (res?.ok && data) { - cb((data as { result: Record }).result); + cb((data as GetPresetsResponse).result); } } getPresets(props.onCameraPresetsObtained); @@ -125,9 +126,9 @@ export default function CameraFeed(props: Props) { }} onReset={resetStream} onMove={async (data) => { - props.onMove?.(); setState("moving"); const { res } = await props.operate({ type: "relative_move", data }); + props.onMove?.(); setTimeout(() => { setState((state) => (state === "moving" ? undefined : state)); }, 4000); diff --git a/src/Components/CameraFeed/routes.ts b/src/Components/CameraFeed/routes.ts index f3374a1776d..482dc515eb3 100644 --- a/src/Components/CameraFeed/routes.ts +++ b/src/Components/CameraFeed/routes.ts @@ -1,11 +1,27 @@ import { Type } from "../../Redux/api"; -import { OperationAction } from "./useOperateCamera"; +import { OperationAction, PTZPayload } from "./useOperateCamera"; + +export type GetStatusResponse = { + result: { + position: PTZPayload; + moveStatus: { + panTilt: "IDLE" | "MOVING"; + zoom: "IDLE" | "MOVING"; + }; + error: string; + utcTime: string; + }; +}; + +export type GetPresetsResponse = { + result: Record; +}; export const FeedRoutes = { operateAsset: { path: "/api/v1/asset/{id}/operate_assets/", method: "POST", - TRes: Type(), + TRes: Type(), TBody: Type<{ action: OperationAction }>(), }, } as const; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx index d8d4f8b3d28..f7d81a56edf 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx @@ -20,12 +20,14 @@ import ConfirmDialog from "../../Common/ConfirmDialog"; import useBreakpoints from "../../../Common/hooks/useBreakpoints"; import { Warn } from "../../../Utils/Notifications"; import { useTranslation } from "react-i18next"; +import { GetStatusResponse } from "../../CameraFeed/routes"; export const ConsultationFeedTab = (props: ConsultationTabProps) => { const { t } = useTranslation(); const authUser = useAuthUser(); const facility = useSlug("facility"); const bed = props.consultationData.current_bed?.bed_object; + const feedStateSessionKey = getFeedStateKey(props.consultationId); const [asset, setAsset] = useState(); const [preset, setPreset] = useState(); @@ -65,11 +67,23 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { obj.meta.type !== "boundary", ); - const lastPresetId = sessionStorage.getItem( - getFeedPresetKey(props.consultationId), - ); + const lastStateJSON = sessionStorage.getItem(feedStateSessionKey); const preset = - presets.find((obj) => obj.id === lastPresetId) ?? presets[0]; + (() => { + if (lastStateJSON) { + const lastState = JSON.parse(lastStateJSON) as LastFeedState; + if (lastState.type === "preset") { + return presets.find((obj) => obj.id === lastState.value); + } + if (lastState.type === "position") { + return { + ...presets[0], + id: "", + meta: { ...presets[0].meta, position: lastState.value }, + }; + } + } + })() ?? presets[0]; if (preset) { setPreset(preset); @@ -112,9 +126,15 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { }, [!!bed, loading, !!asset, divRef.current]); useEffect(() => { - const feedPresetKey = getFeedPresetKey(props.consultationId); - if (preset) { - sessionStorage.setItem(feedPresetKey, preset.id); + const feedPresetKey = getFeedStateKey(props.consultationId); + if (preset?.id) { + sessionStorage.setItem( + feedPresetKey, + JSON.stringify({ + type: "preset", + value: preset.id, + } satisfies LastAccessedPreset), + ); } }, [preset, props.consultationId]); @@ -148,7 +168,21 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { key={key} asset={asset} preset={preset?.meta.position} - onMove={() => setHasMoved(true)} + onMove={() => { + setHasMoved(true); + setTimeout(async () => { + const { data } = await operate({ type: "get_status" }); + if (data) { + sessionStorage.setItem( + feedStateSessionKey, + JSON.stringify({ + type: "position", + value: (data as GetStatusResponse).result.position, + } satisfies LastAccessedPosition), + ); + } + }, 3000); + }} operate={operate} onStreamError={() => { triggerGoal("Camera Feed Viewed", { @@ -226,6 +260,18 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { ); }; -const getFeedPresetKey = (consultationId: string) => { - return `encounterFeedPreset[${consultationId}]`; +type LastAccessedPreset = { + type: "preset"; + value: string; +}; + +type LastAccessedPosition = { + type: "position"; + value: PTZPayload; +}; + +type LastFeedState = LastAccessedPosition | LastAccessedPreset; + +const getFeedStateKey = (consultationId: string) => { + return `encounterFeedState[${consultationId}]`; };