Files
threadcount-community/lib/wakelock.ts
T
ThreadCount c89010a4f1 ThreadCount Community edition
Uniform stock management for healthcare linen rooms: the coordinator app, the phone counter and the staff app, for your own server. Built from f976bd5 on 2026-09-15. Licensed under the Functional Source License (FSL-1.1-ALv2).
2026-09-15 23:18:05 +10:00

46 lines
2.1 KiB
TypeScript

"use client";
/* Keep the screen on while a count is open.
*
* A shelf count is minutes of handling garments with the phone held low, and Android's display
* timeout is often fifteen seconds. Waking the phone, unlocking it and finding your place again
* every few garments is the difference between counting a bay and giving up on it.
*
* This is the Screen Wake Lock API rather than a Capacitor plugin, which means it works the same
* in the Android shell and in mobile Chrome, and needs no permission — only a secure context,
* which /m always has. Android drops the lock whenever the page is hidden, so it is re-taken on
* the way back from a phone call or the app switcher. */
import { useEffect } from "react";
type Sentinel = { released: boolean; release: () => Promise<void>; addEventListener: (t: string, f: () => void) => void };
type WakeLockNav = Navigator & { wakeLock?: { request: (type: "screen") => Promise<Sentinel> } };
/** Holds a screen wake lock for as long as `active` is true. A no-op where it isn't supported. */
export function useKeepAwake(active: boolean) {
useEffect(() => {
if (!active) return;
const nav = navigator as WakeLockNav;
if (!nav.wakeLock) return; // older WebView, or an insecure origin
let sentinel: Sentinel | null = null;
let dropped = false;
const take = async () => {
if (dropped || sentinel || document.visibilityState !== "visible") return;
try {
sentinel = await nav.wakeLock!.request("screen");
// The system releases it on its own terms too; forget the handle when it does.
sentinel.addEventListener("release", () => { sentinel = null; });
} catch { /* battery saver refuses it — the screen just times out as usual */ }
};
const onVisible = () => { if (document.visibilityState === "visible") void take(); };
void take();
document.addEventListener("visibilitychange", onVisible);
return () => {
dropped = true;
document.removeEventListener("visibilitychange", onVisible);
if (sentinel && !sentinel.released) sentinel.release().catch(() => {});
sentinel = null;
};
}, [active]);
}