services/orientation.jsjavascript
/*
Resolves which orientation ("landscape"/"portrait") the *current route*
expects the kiosk to be held in, so RotateOverlay
(src/components/RotateOverlay/RotateOverlay.jsx) can compare it against
the physical orientation from useOrientation (src/hooks/orientation.jsx)
and tell the visitor to rotate the display when they don't match.
Two sources, in priority order:
1. `deviceType` (config-schema.js, URL param) - when explicitly set to
"landscape" or "portrait" it forces that orientation for every route
on this kiosk, e.g. for a device that's physically bolted into a
fixed orientation regardless of which app is open. Its default,
"default", means "no override, defer to the route".
2. The route's own `orientation` - "/" (AppSelection) is landscape,
every other route's is declared in PRESENTATION_MODES
(services/presentationModes.js).
*/
import { useLocation } from "react-router";
import { useConfig } from "./config/config";
import { PRESENTATION_MODES } from "./presentationModes";
// AppSelection ("/") isn't part of PRESENTATION_MODES (it's the hub, not a
// presentation mode itself), so its expected orientation is declared here.
const ROOT_ORIENTATION = "landscape";
const DEVICE_TYPE_ORIENTATIONS = ["landscape", "portrait"];
/**
* @returns {"landscape"|"portrait"|null} The orientation the current route
* (optionally overridden by `deviceType`) expects, or `null` if the route
* has no orientation requirement.
*/
export const useRequiredOrientation = () => {
const { pathname } = useLocation();
const { deviceType } = useConfig();
// deviceType overrides whatever the route would normally require - the
// kiosk's physical mounting takes precedence over the app's own preference.
if (DEVICE_TYPE_ORIENTATIONS.includes(deviceType)) return deviceType;
if (pathname === "/") return ROOT_ORIENTATION;
const mode = PRESENTATION_MODES.find(
({ path }) => pathname === path || pathname.startsWith(`${path}/`)
);
return mode?.orientation ?? null;
};