Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 6s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 7s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 11s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 11s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m29s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 9m44s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Fixes #344 Co-Authored-By: OpenAI Codex <codex@openai.com>
45 lines
2.0 KiB
TypeScript
45 lines
2.0 KiB
TypeScript
// Shared dirty-guard wiring for the media-source editor screens (design §D.2). Mirrors the
|
|
// SchedulesScreen convention (registerNavigationGuard on the current dirty state + a `beforeunload`
|
|
// listener while dirty) so an editor with unsaved changes vetoes both in-app navigation (App's
|
|
// popstate owner consults canLeaveCurrentScreen) and full-page unloads (reload / close tab).
|
|
//
|
|
// The guard predicate reads the LATEST dirty value via a ref updated on every render, so App's
|
|
// synchronous popstate handler sees the current state without the effect having to re-register on
|
|
// each keystroke. A single hook keeps the three editors (connection / libraries / path-replacements)
|
|
// consistent instead of copy-pasting the guard block into each.
|
|
|
|
import { useCallback, useEffect, useRef } from 'react';
|
|
import { registerNavigationGuard } from '../navigationGuard';
|
|
|
|
export function useDirtyGuard(dirty: boolean, prompt: string): () => void {
|
|
const dirtyRef = useRef(dirty);
|
|
// A successful save may navigate in the same promise callback that queues the clean baseline.
|
|
// React has not rendered that baseline yet, so let the caller transfer the guard synchronously
|
|
// before dispatching the synthetic popstate. The normal render still owns the durable state.
|
|
const markClean = useCallback(() => {
|
|
dirtyRef.current = false;
|
|
}, []);
|
|
|
|
// Keep the ref current for the synchronous guard predicate. Updated in an effect (committed before
|
|
// any user pop/nav gesture can fire) rather than during render.
|
|
useEffect(() => {
|
|
dirtyRef.current = dirty;
|
|
}, [dirty]);
|
|
|
|
useEffect(() => registerNavigationGuard(() => !dirtyRef.current || window.confirm(prompt)), [prompt]);
|
|
|
|
useEffect(() => {
|
|
if (!dirty) {
|
|
return undefined;
|
|
}
|
|
const handler = (event: BeforeUnloadEvent) => {
|
|
event.preventDefault();
|
|
event.returnValue = '';
|
|
};
|
|
window.addEventListener('beforeunload', handler);
|
|
return () => window.removeEventListener('beforeunload', handler);
|
|
}, [dirty]);
|
|
|
|
return markClean;
|
|
}
|