feat: closing the tab warns about a document that has no file
The unload warning followed the changes a file had not got yet; a document that never was a file got none, on the grounds that the autosave keeps it. It now warns as soon as such a document has content — the same emptiness test the confirm before a new document uses.
This commit is contained in:
1 parent
52d9ab5720
commit
15608896a6
3 files changed
+19
-5
No files matched your search
+6
-3
@@ -166,8 +166,8 @@
|
||||
let dirty = $state(false);
|
||||
let cleanSum: number | null = null;
|
||||
let sumTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
// Whether there is a file to lose those changes from. A document that only ever
|
||||
// lived in the browser is kept by the autosave, so leaving is not worth a warning.
|
||||
// Whether there is a file to lose those changes from — a saved file makes the dot,
|
||||
// and the unload warning, follow the changes rather than the document's existence.
|
||||
let documentHasFile = $state(false);
|
||||
|
||||
// The page count rides in with the zones but is a layout result, not an edit: a
|
||||
@@ -202,7 +202,10 @@
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!dirty || !documentHasFile) return;
|
||||
// A document with no file behind it lives in this browser alone, so closing the tab
|
||||
// is worth the browser's warning as much as changes a file has not got yet. The
|
||||
// same emptiness test the confirm before a new document uses.
|
||||
if (documentHasFile ? !dirty : tick < 0 || !isDocNonEmpty()) return;
|
||||
const warn = (e: BeforeUnloadEvent) => e.preventDefault();
|
||||
addEventListener('beforeunload', warn);
|
||||
return () => removeEventListener('beforeunload', warn);
|
||||
|
||||
@@ -228,8 +228,9 @@ settle pass or the spell checker keeps the document moving), and the baseline is
|
||||
**synchronously** on the effect's first run, not in that timer: under a repagination the
|
||||
timer can be pushed out past the reader's first edit, which would make that edit the
|
||||
baseline. A save, an open or a new document takes a fresh baseline (`markSaved`).
|
||||
Leaving the page warns only when the document actually has a file — a browser-only document
|
||||
is kept by the autosave, so there would be nothing to lose.
|
||||
Leaving the page warns while a document with a file differs from it, and for a document
|
||||
with no file at all as soon as it has content (the emptiness test the confirm before a new
|
||||
document uses). The wording is the browser's own — `beforeunload` carries no message.
|
||||
|
||||
## Built-in templates (`TemplateGalleryDialog.svelte`, `lib/templates/`)
|
||||
|
||||
|
||||
@@ -30,11 +30,20 @@ try {
|
||||
await page.reload({ waitUntil: 'load' });
|
||||
await page.waitForSelector('.tiptap', { timeout: 15_000 });
|
||||
|
||||
// What the browser asks before closing the tab: a beforeunload nobody cancels lets it go.
|
||||
const holdsOn = () => page.evaluate(() => {
|
||||
const e = new Event('beforeunload', { cancelable: true });
|
||||
window.dispatchEvent(e);
|
||||
return e.defaultPrevented;
|
||||
});
|
||||
check(!(await holdsOn()), 'an empty document lets the tab close');
|
||||
|
||||
// Save on a document with no file behind it settles the format first: the download
|
||||
// route cannot read one back from the browser's own dialog.
|
||||
await page.evaluate(() => { window.showSaveFilePicker = undefined; });
|
||||
await page.click('.tiptap');
|
||||
await page.keyboard.type('Format first');
|
||||
check(await holdsOn(), 'a document that was never saved warns before the tab closes');
|
||||
await page.keyboard.press(`${MOD}+s`);
|
||||
const [firstSave] = await Promise.all([
|
||||
page.waitForEvent('download', { timeout: 30_000 }),
|
||||
@@ -42,6 +51,7 @@ try {
|
||||
]);
|
||||
check(firstSave.suggestedFilename().endsWith('.docx'),
|
||||
`Ctrl+S on an unsaved document asks for the format (${firstSave.suggestedFilename()})`);
|
||||
check(!(await holdsOn()), 'the saved document lets the tab close again');
|
||||
|
||||
// A multi-page corpus document, through the file input (no picker in headless).
|
||||
await page.setInputFiles('input.file-input[accept*=".odt"]', join(ROOT, 'tests/corpus/05-breaks.odt'));
|
||||
|
||||
Reference in new issue
Block a user