Files
bizmatch-qc-desktop/web/pdf_viewer.js
2026-07-15 19:34:13 -05:00

115 lines
3.4 KiB
JavaScript

// Native Chromium PDF viewer using a single persistent iframe.
// The iframe is created once in index.html and reused for all documents.
class NativePdfViewer {
constructor(frame, container) {
this.frame = frame;
this.container = container;
this.generation = 0;
this.currentKey = null;
this.lastUrl = null;
this.loadTimer = null;
this._onLoad = null;
this._createDOM();
}
_createDOM() {
this.loadingDiv = document.createElement("div");
this.loadingDiv.className = "native-pdf-loading";
this.loadingDiv.textContent = "Loading PDF\u2026";
this.loadingDiv.hidden = true;
this.container.appendChild(this.loadingDiv);
this.errorDiv = document.createElement("div");
this.errorDiv.className = "native-pdf-error";
this.errorDiv.hidden = true;
this.errorMsg = document.createElement("div");
this.errorMsg.className = "native-pdf-error-msg";
this.errorDiv.appendChild(this.errorMsg);
this.retryBtn = document.createElement("button");
this.retryBtn.className = "native-pdf-retry";
this.retryBtn.textContent = "Retry";
this.retryBtn.addEventListener("click", () => {
if (this.lastUrl && this.currentKey) {
this._navigate(this.lastUrl, this.currentKey, true);
}
});
this.errorDiv.appendChild(this.retryBtn);
this.container.appendChild(this.errorDiv);
// Log iframe sizing once
console.log(
"[BizMatch QC] iframe size:",
this.frame.clientWidth + "x" + this.frame.clientHeight,
"container:",
this.container.clientWidth + "x" + this.container.clientHeight,
);
}
_showState(state) {
this.loadingDiv.hidden = state !== "loading";
this.errorDiv.hidden = state !== "error";
}
load(url, _byteSize, key) {
this._navigate(url, key, false);
}
_navigate(url, key, force) {
// Deduplicate only AFTER the key is accepted
if (!force && key && key === this.currentKey) {
return;
}
const generation = ++this.generation;
this.currentKey = key;
this.lastUrl = url;
this._showState("loading");
console.log(`[BizMatch QC] Native PDF src assigned: ${key}`);
console.log(
`[BizMatch QC] Native PDF iframe size: ${this.frame.clientWidth}x${this.frame.clientHeight}`,
);
// Register per-navigation load handler
if (this._onLoad) {
this.frame.removeEventListener("load", this._onLoad);
}
this._onLoad = () => {
if (generation !== this.generation) return;
if (this.loadTimer) {
clearTimeout(this.loadTimer);
this.loadTimer = null;
}
this._showState("loaded");
console.log(`[BizMatch QC] Native PDF load event: ${key}`);
};
this.frame.addEventListener("load", this._onLoad, { once: true });
// Safety timeout
if (this.loadTimer) clearTimeout(this.loadTimer);
this.loadTimer = setTimeout(() => {
if (generation === this.generation) {
console.warn(`[BizMatch QC] Native PDF load timeout: ${key}`);
this._showState("error");
this.errorMsg.textContent =
"PDF load timed out. The file may be very large.";
}
}, 15000);
this.frame.src = url;
}
destroy() {
if (this.loadTimer) clearTimeout(this.loadTimer);
if (this._onLoad) this.frame.removeEventListener("load", this._onLoad);
this.generation = 0;
this.frame.src = "";
this._showState("loading");
}
}
globalThis.NativePdfViewer = NativePdfViewer;