Skip to content

Commit

Permalink
Adds support for "Still watching" prompt to prevent users from idling…
Browse files Browse the repository at this point in the history
… when watching stream (#8440)
  • Loading branch information
rithviknishad authored Sep 4, 2024
1 parent 28b02d9 commit 4c61baa
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 20 deletions.
6 changes: 6 additions & 0 deletions .example.env
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ REACT_KASP_FULL_STRING=Karunya Arogya Suraksha Padhathi
REACT_SAMPLE_FORMAT_ASSET_IMPORT=/asset-import-template.xlsx
REACT_SAMPLE_FORMAT_EXTERNAL_RESULT_IMPORT=/External-Results-Template.csv

# Camera feed, still watching idle timeout (in seconds; default: 180)
REACT_STILL_WATCHING_IDLE_TIMEOUT=

# Camera feed, still watching prompt duration (in seconds; default: 30)
REACT_STILL_WATCHING_PROMPT_DURATION=

# Feature flags
REACT_ENABLE_HCX=true
REACT_ENABLE_ABDM=true
Expand Down
9 changes: 9 additions & 0 deletions care.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ const careConfig = {

wartimeShifting: env.REACT_WARTIME_SHIFTING === "true",

stillWatching: {
idleTimeout: env.REACT_STILL_WATCHING_IDLE_TIMEOUT
? parseInt(env.REACT_STILL_WATCHING_IDLE_TIMEOUT)
: 3 * 60,
promptDuration: env.REACT_STILL_WATCHING_PROMPT_DURATION
? parseInt(env.REACT_STILL_WATCHING_PROMPT_DURATION)
: 30,
},

auth: {
tokenRefreshInterval: env.REACT_JWT_TOKEN_REFRESH_INTERVAL
? parseInt(env.REACT_JWT_TOKEN_REFRESH_INTERVAL)
Expand Down
29 changes: 16 additions & 13 deletions src/Components/CameraFeed/CentralLiveMonitoring/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Fullscreen from "../../../CAREUI/misc/Fullscreen";
import useBreakpoints from "../../../Common/hooks/useBreakpoints";
import { useQueryParams } from "raviger";
import LiveMonitoringFilters from "./LiveMonitoringFilters";
import StillWatching from "../StillWatching";

export default function CentralLiveMonitoring(props: { facilityId: string }) {
const [isFullscreen, setFullscreen] = useState(false);
Expand Down Expand Up @@ -59,19 +60,21 @@ export default function CentralLiveMonitoring(props: { facilityId: string }) {
No Camera present in this location or facility.
</div>
) : (
<Fullscreen
fullscreenClassName="h-screen overflow-auto"
fullscreen={isFullscreen}
onExit={() => setFullscreen(false)}
>
<div className="mt-1 grid grid-cols-1 place-content-center gap-1 lg:grid-cols-2 3xl:grid-cols-3">
{data.results.map((asset) => (
<div className="text-clip" key={asset.id}>
<LocationFeedTile asset={asset} />
</div>
))}
</div>
</Fullscreen>
<StillWatching>
<Fullscreen
fullscreenClassName="h-screen overflow-auto"
fullscreen={isFullscreen}
onExit={() => setFullscreen(false)}
>
<div className="mt-1 grid grid-cols-1 place-content-center gap-1 lg:grid-cols-2 3xl:grid-cols-3">
{data.results.map((asset) => (
<div className="text-clip" key={asset.id}>
<LocationFeedTile asset={asset} />
</div>
))}
</div>
</Fullscreen>
</StillWatching>
)}
</Page>
);
Expand Down
82 changes: 82 additions & 0 deletions src/Components/CameraFeed/StillWatching.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useEffect, useState } from "react";
import ConfirmDialog from "../Common/ConfirmDialog";
import ButtonV2 from "../Common/components/ButtonV2";
import CareIcon from "../../CAREUI/icons/CareIcon";
import { useTranslation } from "react-i18next";
import { useTimer } from "../../Utils/useTimer";
import careConfig from "@careConfig";

type State = "watching" | "prompted" | "timed-out";

const useStillWatching = () => {
const { idleTimeout, promptDuration } = careConfig.stillWatching;
const [state, setState] = useState<State>("watching");
const [sequence, setSequence] = useState(1);

const timer = useTimer(true);

const remainingTime = Math.ceil(
(idleTimeout + promptDuration) * Math.min(sequence, 3) - timer.seconds,
);

useEffect(() => {
if (remainingTime < 0) {
setState("timed-out");
timer.stop();
return;
}
if (remainingTime < promptDuration) {
setState("prompted");
return;
}
}, [promptDuration, remainingTime]);

return {
state,
remainingTime,
reset: (hardReset?: boolean) => {
if (hardReset) {
setSequence((seq) => seq + 1);
}
timer.reset();
setState("watching");
timer.start();
},
};
};

export default function StillWatching(props: { children: React.ReactNode }) {
const { t } = useTranslation();
const { state, remainingTime, reset } = useStillWatching();

return (
<div onClick={() => reset()}>
<ConfirmDialog
show={state === "prompted"}
title={t("are_you_still_watching")}
description={t("stream_stop_due_to_inativity")}
action={
<>
<CareIcon icon="l-play-circle" className="text-lg" />
{t("continue_watching")} ({remainingTime}s.)
</>
}
onConfirm={() => reset(true)}
onClose={() => reset(true)}
/>
{state === "timed-out" ? (
<div className="flex h-[50vh] w-full flex-col items-center justify-center gap-4 rounded-lg border-4 border-dashed border-secondary-400">
<span className="text-center text-xl font-bold text-secondary-700">
{t("stream_stopped_due_to_inativity")}
</span>
<ButtonV2 onClick={() => reset(true)}>
<CareIcon icon="l-play-circle" className="text-lg" />
{t("resume")}
</ButtonV2>
</div>
) : (
props.children
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import useBreakpoints from "../../../Common/hooks/useBreakpoints";
import { Warn } from "../../../Utils/Notifications";
import { useTranslation } from "react-i18next";
import { GetStatusResponse } from "../../CameraFeed/routes";
import StillWatching from "../../CameraFeed/StillWatching";

export const ConsultationFeedTab = (props: ConsultationTabProps) => {
const { t } = useTranslation();
Expand Down Expand Up @@ -148,7 +149,7 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => {
const cannotSaveToPreset = !hasMoved || !preset?.id;

return (
<>
<StillWatching>
<ConfirmDialog
title="Update Preset"
description="Are you sure you want to update this preset to the current location?"
Expand Down Expand Up @@ -257,7 +258,7 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => {
</div>
</CameraFeed>
</div>
</>
</StillWatching>
);
};

Expand Down
9 changes: 7 additions & 2 deletions src/Locale/en/Asset.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@
"update_asset_service_record": "Update Asset Service Record",
"eg_details_on_functionality_service_etc": "Eg. Details on functionality, service, etc.",
"updating": "Updating",
"update": "Update"
}
"update": "Update",
"are_you_still_watching": "Are you still watching?",
"stream_stop_due_to_inativity": "The live feed will stop streaming due to inactivity",
"stream_stopped_due_to_inativity": "The live feed has stopped streaming due to inactivity",
"continue_watching": "Continue watching",
"resume": "Resume"
}
7 changes: 4 additions & 3 deletions src/Utils/useTimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import { useEffect, useState } from "react";
* // To display the timer in your component:
* <div>{time}</div>
*/
export const useTimer = () => {
const [running, setRunning] = useState(false);
export const useTimer = (autoStart = false) => {
const [running, setRunning] = useState(autoStart);
const [time, setTime] = useState(0);

useEffect(() => {
Expand All @@ -39,7 +39,7 @@ export const useTimer = () => {
}, [running]);

return {
seconds: time,
seconds: time / 100,
time: (
<span>
{("0" + Math.floor((time / 6000) % 60)).slice(-2)}:
Expand All @@ -48,5 +48,6 @@ export const useTimer = () => {
),
start: () => setRunning(true),
stop: () => setRunning(false),
reset: () => setTime(0),
};
};
2 changes: 2 additions & 0 deletions src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ interface ImportMetaEnv {
readonly REACT_SAMPLE_FORMAT_ASSET_IMPORT?: string;
readonly REACT_SAMPLE_FORMAT_EXTERNAL_RESULT_IMPORT?: string;
readonly REACT_WARTIME_SHIFTING?: string;
readonly REACT_STILL_WATCHING_IDLE_TIMEOUT?: string;
readonly REACT_STILL_WATCHING_PROMPT_DURATION?: string;
readonly REACT_JWT_TOKEN_REFRESH_INTERVAL?: string;
readonly REACT_MIN_ENCOUNTER_DATE?: string;

Expand Down

0 comments on commit 4c61baa

Please sign in to comment.