refactor: improve image field lazy loading mechanism with fallbacks and enhance its loading state styling.

This commit is contained in:
Suherdy Yacob 2026-03-16 15:09:24 +07:00
parent 65056cb9ea
commit 947543d54d
2 changed files with 34 additions and 19 deletions

View File

@ -52,10 +52,13 @@ body div.o_sign_sign_item.o_sign_image_item span.o_placeholder {
} }
.o_sign_image_item.o_sign_image_loading_container { .o_sign_image_item.o_sign_image_loading_container {
background: #f6f7f8; background: #f6f7f8 !important;
background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%); background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%) !important;
background-size: 800px 104px; background-size: 800px 104px !important;
animation: o_sign_image_shimmer 1.2s forwards infinite linear; animation: o_sign_image_shimmer 1.2s forwards infinite linear !important;
min-width: 50px !important;
min-height: 50px !important;
z-index: 10 !important;
} }
@keyframes o_sign_image_shimmer { @keyframes o_sign_image_shimmer {

View File

@ -27,43 +27,55 @@ const triggerLazyLoad = (el) => {
* Setup IntersectionObserver for an element. * Setup IntersectionObserver for an element.
*/ */
const setupLazyObserver = (iframeInstance, el) => { const setupLazyObserver = (iframeInstance, el) => {
if (!el || !window.IntersectionObserver) {
// Fallback for no observer support
triggerLazyLoad(el);
return;
}
const lazyImg = el.querySelector('.o_sign_image_lazy'); const lazyImg = el.querySelector('.o_sign_image_lazy');
if (!lazyImg || !lazyImg.dataset.src) return; if (!lazyImg || !lazyImg.dataset.src) return;
// Use the viewer container as root if possible // Use a safety timeout to ensure it loads even if observer fails
const root = iframeInstance.root ? iframeInstance.root.querySelector('#viewerContainer') : null; const safetyTimeout = setTimeout(() => triggerLazyLoad(el), 5000);
const observer = new IntersectionObserver((entries) => { const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => { entries.forEach(entry => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
clearTimeout(safetyTimeout);
triggerLazyLoad(el); triggerLazyLoad(el);
observer.disconnect(); observer.disconnect();
} }
}); });
}, { }, {
root: root,
rootMargin: '200px' rootMargin: '200px'
}); });
observer.observe(el); observer.observe(el);
}; };
// 1. Patch PDFIframe for read-only views // 1. Patch PDFIframe (Base class)
// We patch only what is strictly necessary and avoid super calls if possible for base classes
patch(PDFIframe.prototype, { patch(PDFIframe.prototype, {
enableCustom(signItem) { enableCustom(signItem) {
// Base PDFIframe.enableCustom is empty, no need for super // PDFIframe.enableCustom is normally empty
if (signItem.data.type === 'image') { if (typeof super.enableCustom === 'function') {
super.enableCustom(signItem);
}
if (signItem && signItem.data && signItem.data.type === 'image') {
setupLazyObserver(this, signItem.el); setupLazyObserver(this, signItem.el);
} }
} }
}); });
// 2. Patch SignablePDFIframe for active signer views // 2. Patch SignablePDFIframe (Subclass)
patch(SignablePDFIframe.prototype, { patch(SignablePDFIframe.prototype, {
enableCustom(signItem) { enableCustom(signItem) {
super.enableCustom(signItem); // Safe because SignablePDFIframe extends PDFIframe // Ensure we call the base implementation (which now includes our lazy loading)
if (signItem.data.type === 'image') { super.enableCustom(signItem);
if (signItem && signItem.data && signItem.data.type === 'image') {
const el = signItem.el; const el = signItem.el;
const data = signItem.data; const data = signItem.data;
if (!el) return;
el.setAttribute('data-type', 'image'); el.setAttribute('data-type', 'image');
@ -143,9 +155,6 @@ patch(SignablePDFIframe.prototype, {
reader.readAsDataURL(file); reader.readAsDataURL(file);
}); });
} }
// Also setup lazy observer for signer view
setupLazyObserver(this, el);
} }
}, },
@ -153,6 +162,9 @@ patch(SignablePDFIframe.prototype, {
if (item.data.type === 'image') { if (item.data.type === 'image') {
return item.el.dataset.value || item.data.value || false; return item.el.dataset.value || item.data.value || false;
} }
return super.getSignatureValueFromElement(item); if (typeof super.getSignatureValueFromElement === 'function') {
return super.getSignatureValueFromElement(item);
}
return false;
} }
}); });