Public Access
directory card photo: fetch the JSON envelope, decode the data URI, and bound every request
The endpoint answers 200 application/json with {"Image":"data:image/jpeg;base64,…"} —
a JSON envelope, NOT image bytes. An <img src="…?id=N"> pointed at it never settles:
measured on the live page, complete=false · naturalWidth=0 for 20 s, so the fallback
never fired and the card showed an empty slot. The card now FETCHES the route, reads
`Image` and puts the data URI on an <img> only when it passes the isImageSrc allowlist.
- static/expert-image.js: `load(id)` replaces `url(id, rec)`. It fetches the route,
reads `Image`, accepts the array shape n8n sometimes returns, and resolves to a usable
image source or `null` on ANY failure. EVERY request is bounded by `timeoutMs` (8000):
an AbortController is aborted by a setTimeout, the abort rejects the fetch and lands in
the same catch that returns null — so a hang becomes the monogram, not an empty box.
The timer is cleared on every completion. The decoded URI is cached by id (failures
too), so a re-render or a shared id never re-fetches. Every non-success path — timeout,
abort, non-2xx, {}, a blank/missing Image, a value that fails isImageSrc — ends in the
monogram: never an empty box, never a broken-image icon, never alt text in the layout.
- Directory card (odoo-community-developers/index.html): the monogram reserves the 72x72
box and stands until a real photo has DECODED; the photo is requested lazily, only when
a card nears the viewport (IntersectionObserver, rootMargin 200px), so a 14-card page
does not fire 1.4 MB on load. The monogram and the photo are the same 72x72 box, so the
swap moves nothing (measured: head box 274.81x72 and ask-row tops byte-identical across
a loaded photo and a monogram).
- /freelancer-edit/: the photo preview makes the same fetch, so the form no longer points
an <img> at the JSON route either.
Proved in a real browser against the repo served with the two webhook URLs stubbed:
jpeg data URI -> photo (src prefix data:image/jpeg;base64,/, naturalWidth 800); {} / 404 /
500 / a bad value -> monogram, 0 broken-image icons, 0 cards with neither; a stub that
never answers -> monogram after 8000 ms with the request ABORTED (client abort event at
8000.x ms, all 6 sockets disconnected server-side). No record created, no probe write.
This commit is contained in:
1 parent
2b0e89191a
commit
29ff175e89
3 files changed
+191
-86
No files matched your search
+19
-16
@@ -149,7 +149,7 @@
|
||||
<!-- The 15-point rule, in ONE copy for both forms (this one and the application form). -->
|
||||
<script src="/static/focus-rule.js?v=22ac72a8f0"></script>
|
||||
<script src="/static/photo-upload.js?v=c17638457b"></script>
|
||||
<script src="/static/expert-image.js?v=e496d4a4e3"></script>
|
||||
<script src="/static/expert-image.js?v=bad5d8c733"></script>
|
||||
|
||||
<!-- 1) Read the credential, then take it OUT of the address bar.
|
||||
The hash arrives as ?has=... or ?hash=... (both spellings accepted, 'has' first — the
|
||||
@@ -585,21 +585,24 @@
|
||||
var photoMono = el("span", "mono", initials(who));
|
||||
photoRow.appendChild(photoMono);
|
||||
if (rec && rec.id !== undefined && rec.id !== null && rec.id !== "") {
|
||||
var photoImg = el("img");
|
||||
photoImg.id = "edImagePreview";
|
||||
photoImg.alt = "";
|
||||
photoImg.decoding = "async";
|
||||
photoImg.hidden = true; // the monogram stands in until the photo is there
|
||||
photoImg.addEventListener("load", function () {
|
||||
if (photoMono.parentNode) photoMono.parentNode.removeChild(photoMono);
|
||||
photoImg.hidden = false;
|
||||
}, { once: true });
|
||||
photoImg.addEventListener("error", function () {
|
||||
// A failing photo is the monogram: no broken-image icon, no empty box.
|
||||
if (photoImg.parentNode) photoImg.parentNode.removeChild(photoImg);
|
||||
}, { once: true });
|
||||
photoImg.src = O4PExpertImage.url(String(rec.id), rec);
|
||||
photoRow.appendChild(photoImg);
|
||||
// Same fetch the directory card makes: the route answers JSON, not image bytes,
|
||||
// so the value is read from `Image` and only then put on an <img>. Any failure
|
||||
// (or a value that fails isImageSrc) leaves the monogram standing.
|
||||
O4PExpertImage.load(String(rec.id)).then(function (src) {
|
||||
if (!src) return;
|
||||
var photoImg = el("img");
|
||||
photoImg.id = "edImagePreview";
|
||||
photoImg.alt = "";
|
||||
photoImg.decoding = "async";
|
||||
photoImg.addEventListener("load", function () {
|
||||
if (photoMono.parentNode) photoMono.parentNode.replaceChild(photoImg, photoMono);
|
||||
}, { once: true });
|
||||
photoImg.addEventListener("error", function () {
|
||||
// A failing photo is the monogram: no broken-image icon, no empty box.
|
||||
if (photoImg.parentNode) photoImg.parentNode.removeChild(photoImg);
|
||||
}, { once: true });
|
||||
photoImg.src = src;
|
||||
});
|
||||
}
|
||||
photoWrap.appendChild(photoRow);
|
||||
photoWrap.appendChild(el("p", "hint",
|
||||
|
||||
Reference in new issue
Block a user