components/Screensaver/Screensaver.jsxjavascript
import React from "react";
import { useScreensaver } from "../../services/screensaver";
import { useContent } from "../../services/content";
import { useConfig } from "../../services/config/config";
import { CacheableVideo } from "../CacheableVideo";
import "./Screensaver.scss";

/**
 * Full-screen overlay shown while the app is idle (see useScreensaver).
 * Stays mounted at all times so the opacity transition plays both when
 * fading in and when fading out; visibility is toggled via CSS class.
 * Dismissing it needs no handler here - any interaction is already picked
 * up by the global listeners in useIdle and flips isIdle back to false.
 *
 * The video shown depends on `defaultMode` (config-schema.js): each
 * presentation mode's own content file (see PRESENTATION_MODES in
 * presentationModes.js, loaded per-route by ContentProvider) defines its own
 * screensaver under "screensavers", keyed by that mode's key. In multi-app/
 * navbar mode (defaultMode "all") or for an unrecognized defaultMode value,
 * no entry matches and no video is shown.
 */
export const Screensaver = () => {
  const { isIdle, screensaverFadeDuration } = useScreensaver();
  const { content } = useContent();
  const { defaultMode } = useConfig();

  const screensaverSrc = content.screensavers?.[defaultMode];

  return (
    <div
      className={`screensaver${isIdle ? " screensaver--visible" : ""}`}
      style={{ transitionDuration: `${screensaverFadeDuration}ms` }}
      aria-hidden={!isIdle}>
      {screensaverSrc && (
        <CacheableVideo src={screensaverSrc} playing={isIdle} loop muted playsInline />
      )}
    </div>
  );
};