TRACES: | DR-204 484 ungated `console.*` calls across 63 non-test frontend files shipped to end users with no way to turn them off. Mechanical substitution, no control flow, error handling or message semantics changed: console.log / console.debug -> log.debug console.info -> log.info console.warn -> log.warn console.error -> log.error Hand-written `"[Scope] …"` prefixes are dropped where the logger's scope now carries them; scope names that already existed are preserved verbatim (`[Auth]`, `[VideoPlayer]`, `[PiP]`, …) and inferred from the filename where a file had none. `src/routes/player/[id]/+page.svelte` keeps its `NextEpisode` and `AutoPlay` sub-scopes as separate loggers rather than flattening them into the page scope. `grep -rn 'console\.' src/` now matches nothing outside the tests and the facade itself.
73 lines
2.1 KiB
TypeScript
73 lines
2.1 KiB
TypeScript
/**
|
|
* Immersive (system-bar-free) full-screen video, Android only.
|
|
*
|
|
* TRACES: UR-066 | DR-157
|
|
*
|
|
* `requestFullscreen()` is the only fullscreen control the web layer has, and in
|
|
* an Android WebView it does not touch the Activity window — it expands the
|
|
* element inside a viewport that already spans the whole screen (MainActivity
|
|
* calls `enableEdgeToEdge()`, and SDK 36 makes that mandatory). So the status and
|
|
* navigation bars stayed painted over full-screen video, and "fullscreen"
|
|
* changed nothing visible.
|
|
*
|
|
* Hiding them needs `WindowInsetsControllerCompat` on the Activity, so it goes
|
|
* through the `AndroidImmersive` @JavascriptInterface installed by MainActivity.
|
|
* Elsewhere (desktop, the Linux WebKitGTK webview) the real `requestFullscreen()`
|
|
* already does the right thing and these calls are no-ops.
|
|
*/
|
|
|
|
import { createLogger } from "$lib/utils/logger";
|
|
|
|
const log = createLogger("Immersive");
|
|
|
|
interface AndroidImmersiveBridge {
|
|
enter(): void;
|
|
exit(): void;
|
|
isSupported(): boolean;
|
|
}
|
|
|
|
declare global {
|
|
interface Window {
|
|
AndroidImmersive?: AndroidImmersiveBridge;
|
|
}
|
|
}
|
|
|
|
function bridge(): AndroidImmersiveBridge | undefined {
|
|
if (typeof window === "undefined") return undefined;
|
|
return window.AndroidImmersive;
|
|
}
|
|
|
|
/** Whether native immersive mode exists on this platform. */
|
|
export function isImmersiveSupported(): boolean {
|
|
try {
|
|
return bridge()?.isSupported() ?? false;
|
|
} catch (err) {
|
|
log.warn("isSupported check failed:", err);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** Hide the system bars. No-op where unsupported. */
|
|
export function enterImmersive(): void {
|
|
try {
|
|
bridge()?.enter();
|
|
} catch (err) {
|
|
log.error("Failed to hide the system bars:", err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Restore the system bars. No-op where unsupported.
|
|
*
|
|
* Call this on leaving fullscreen *and* on player teardown — the bars belong to
|
|
* the Activity, not the player, so a player destroyed while immersive would
|
|
* leave every screen behind it without a status or navigation bar.
|
|
*/
|
|
export function exitImmersive(): void {
|
|
try {
|
|
bridge()?.exit();
|
|
} catch (err) {
|
|
log.error("Failed to restore the system bars:", err);
|
|
}
|
|
}
|