refactor(client/pdf): handle blob request on client side

This commit is contained in:
Elian Doran 2026-01-03 20:54:28 +02:00
parent 21cf5e1df7
commit 69511134e5
No known key found for this signature in database
4 changed files with 45 additions and 22 deletions

View File

@ -45,6 +45,10 @@ interface WithContext {
interface PdfDocumentModifiedMessage extends WithContext {
type: "pdfjs-viewer-document-modified";
}
interface PdfDocumentBlobResultMessage extends WithContext {
type: "pdfjs-viewer-blob";
data: Uint8Array<ArrayBufferLike>;
}
@ -113,4 +117,5 @@ type PdfMessageEvent = MessageEvent<
| PdfViewerThumbnailMessage
| PdfViewerAttachmentsMessage
| PdfViewerLayersMessage
| PdfDocumentBlobResultMessage
>;

View File

@ -174,7 +174,7 @@ export function useBlobEditorSpacedUpdate({ note, noteType, noteContext, getData
noteType: NoteType;
note: FNote,
noteContext: NoteContext | null | undefined,
getData: () => Promise<Blob> | Blob | undefined,
getData: () => Promise<Blob | undefined> | Blob | undefined,
onContentChange: (newBlob: FBlob) => void,
dataSaved?: (savedData: Blob) => void,
updateInterval?: number;

View File

@ -16,14 +16,28 @@ export default function PdfPreview({ note, blob, componentId, noteContext }: {
}) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const historyConfig = useViewModeConfig<HistoryData>(note, "pdfHistory");
const dataRef = useRef<Blob>(new Blob());
const spacedUpdate = useBlobEditorSpacedUpdate({
note,
noteType: "file",
noteContext,
getData() {
return dataRef.current;
if (!iframeRef.current?.contentWindow) return undefined;
return new Promise<Blob>((resolve) => {
const onMessageReceived = (event: PdfMessageEvent) => {
if (event.data.type !== "pdfjs-viewer-blob") return;
if (event.data.noteId !== note.noteId || event.data.ntxId !== noteContext.ntxId) return;
const blob = new Blob([event.data.data as Uint8Array<ArrayBuffer>], { type: note.mime });
window.removeEventListener("message", onMessageReceived);
resolve(blob);
};
window.addEventListener("message", onMessageReceived);
iframeRef.current?.contentWindow?.postMessage({
type: "trilium-request-blob",
});
});
},
onContentChange() {
if (iframeRef.current?.contentWindow) {
@ -35,9 +49,7 @@ export default function PdfPreview({ note, blob, componentId, noteContext }: {
useEffect(() => {
function handleMessage(event: PdfMessageEvent) {
if (event.data?.type === "pdfjs-viewer-document-modified") {
const blob = new Blob([event.data.data as Uint8Array<ArrayBuffer>], { type: note.mime });
if (event.data.noteId === note.noteId && event.data.ntxId === noteContext.ntxId) {
dataRef.current = blob;
spacedUpdate.resetUpdateTimer();
spacedUpdate.scheduleUpdate();
}

View File

@ -55,35 +55,41 @@ function getCustomAppOptions(urlParams: URLSearchParams) {
function manageSave() {
const app = window.PDFViewerApplication;
const storage = app.pdfDocument.annotationStorage;
let timeout = null;
function debouncedSave() {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(async () => {
if (!storage) return;
function onChange() {
if (!storage) return;
window.parent.postMessage({
type: "pdfjs-viewer-document-modified",
ntxId: window.TRILIUM_NTX_ID,
noteId: window.TRILIUM_NOTE_ID
} satisfies PdfDocumentModifiedMessage, window.location.origin);
storage.resetModified();
}
window.addEventListener("message", async (event) => {
if (event.origin !== window.location.origin) return;
if (event.data?.type === "trilium-request-blob") {
const app = window.PDFViewerApplication;
const data = await app.pdfDocument.saveDocument();
window.parent.postMessage({
type: "pdfjs-viewer-document-modified",
type: "pdfjs-viewer-blob",
data,
ntxId: window.TRILIUM_NTX_ID,
noteId: window.TRILIUM_NOTE_ID
} satisfies PdfDocumentModifiedMessage, window.location.origin);
storage.resetModified();
timeout = null;
}, 2_000);
}
} satisfies PdfDocumentBlobResultMessage, window.location.origin)
}
});
app.pdfDocument.annotationStorage.onSetModified = debouncedSave; // works great for most cases, including forms.
app.eventBus.on("annotationeditorcommit", debouncedSave);
app.eventBus.on("annotationeditorparamschanged", debouncedSave);
app.pdfDocument.annotationStorage.onSetModified = onChange; // works great for most cases, including forms.
app.eventBus.on("annotationeditorcommit", onChange);
app.eventBus.on("annotationeditorparamschanged", onChange);
app.eventBus.on("annotationeditorstateschanged", evt => { // needed for detecting when annotations are moved around.
const { activeEditorId } = evt;
// When activeEditorId becomes null, an editor was just committed
if (activeEditorId === null) {
debouncedSave();
onChange();
}
});
}