fix(transcription): use navigator.sendBeacon for beforeunload save
Replace async executeSave in beforeunload handler with navigator.sendBeacon — synchronous and reliable for page unload. Sends pending text as JSON blob to the block update endpoint. Fixes @Sara: "beforeunload handlers cannot reliably await async" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,13 +7,14 @@ import type { TranscriptionBlockData } from '$lib/types';
|
|||||||
type SaveState = 'idle' | 'saving' | 'saved' | 'fading' | 'error';
|
type SaveState = 'idle' | 'saving' | 'saved' | 'fading' | 'error';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
documentId: string;
|
||||||
blocks: TranscriptionBlockData[];
|
blocks: TranscriptionBlockData[];
|
||||||
onBlockFocus: (blockId: string) => void;
|
onBlockFocus: (blockId: string) => void;
|
||||||
onSaveBlock: (blockId: string, text: string) => Promise<void>;
|
onSaveBlock: (blockId: string, text: string) => Promise<void>;
|
||||||
onDeleteBlock: (blockId: string) => Promise<void>;
|
onDeleteBlock: (blockId: string) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
let { blocks, onBlockFocus, onSaveBlock, onDeleteBlock }: Props = $props();
|
let { documentId, blocks, onBlockFocus, onSaveBlock, onDeleteBlock }: Props = $props();
|
||||||
|
|
||||||
let activeBlockId: string | null = $state(null);
|
let activeBlockId: string | null = $state(null);
|
||||||
let saveStates = new SvelteMap<string, SaveState>();
|
let saveStates = new SvelteMap<string, SaveState>();
|
||||||
@@ -115,9 +116,19 @@ function handleDelete(blockId: string) {
|
|||||||
onDeleteBlock(blockId);
|
onDeleteBlock(blockId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function flushViaBeacon() {
|
||||||
|
for (const [blockId, text] of pendingTexts) {
|
||||||
|
clearDebounce(blockId);
|
||||||
|
const url = `/api/documents/${documentId}/transcription-blocks/${blockId}`;
|
||||||
|
const body = JSON.stringify({ text });
|
||||||
|
navigator.sendBeacon(url, new Blob([body], { type: 'application/json' }));
|
||||||
|
pendingTexts.delete(blockId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
function onBeforeUnload() {
|
function onBeforeUnload() {
|
||||||
flushAllPending();
|
flushViaBeacon();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('beforeunload', onBeforeUnload);
|
window.addEventListener('beforeunload', onBeforeUnload);
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ onMount(() => {
|
|||||||
{#if transcribeMode}
|
{#if transcribeMode}
|
||||||
<div class="w-[400px] shrink-0 border-l border-line lg:w-[480px]">
|
<div class="w-[400px] shrink-0 border-l border-line lg:w-[480px]">
|
||||||
<TranscriptionEditView
|
<TranscriptionEditView
|
||||||
|
documentId={doc.id}
|
||||||
blocks={transcriptionBlocks}
|
blocks={transcriptionBlocks}
|
||||||
onBlockFocus={handleBlockFocus}
|
onBlockFocus={handleBlockFocus}
|
||||||
onSaveBlock={saveBlock}
|
onSaveBlock={saveBlock}
|
||||||
|
|||||||
Reference in New Issue
Block a user