Public Access
directory card photos: load from the image endpoint, not inline base64
The webhook no longer sends `Image`, so the card photo comes from its own endpoint, addressed by the record id — one <img> per card, one request. - static/expert-image.js: the route AND its query-parameter name in ONE constant (id, confirmed by measurement; unverified in the brief), plus an optional version token used only when the payload carries one. - Directory card: <img src="…odoo_experts_image?id=<id>">, width/height reserve the 72x72 box, decoding=async, eager for the first row and lazy below it; onerror swaps in the SAME monogram a card with no photo shows, so a failing image looks exactly like today. - The static build-time card loses its 185 KB inline base64 photo and shows a monogram: it is the no-JavaScript fallback, where there is no onerror to fall back with. Page 341,345 -> 158,428 bytes. - /freelancer-edit/: the photo block is a look, not a field (no file input), and the whole-record save sends NO `Image` key at all.
This commit is contained in:
1 parent
9d14652a57
commit
2b0e89191a
3 files changed
+134
-71
No files matched your search
+30
-46
@@ -149,6 +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>
|
||||
|
||||
<!-- 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
|
||||
@@ -574,53 +575,36 @@
|
||||
form.appendChild(linkField("edFiverr", "fiverr", "Fiverr URL",
|
||||
readField(record, "fiverr"), "https://www.fiverr.com/…"));
|
||||
|
||||
// The photo is editable here with the SAME rules as the application form
|
||||
// (static/photo-upload.js). A picked file is validated and downscaled on the spot;
|
||||
// leaving it untouched keeps the record's own Image, which rides back byte-identical
|
||||
// because the save sends the whole record (see the submit handler below).
|
||||
var newImage = null;
|
||||
// The photo block is a LOOK, not a field. The photo does not live in this record
|
||||
// any more — the payload carries no `Image` — it comes from its own endpoint,
|
||||
// addressed by the record id (window.O4PExpertImage). So nothing here edits it,
|
||||
// and the save below sends no `Image` key at all: not the value, not null, not "".
|
||||
var photoWrap = el("div", "field");
|
||||
var photoLabel = el("label", "", "Photo");
|
||||
photoLabel.setAttribute("for", "edImage");
|
||||
photoWrap.appendChild(photoLabel);
|
||||
photoWrap.appendChild(el("label", "", "Photo"));
|
||||
var photoRow = el("div", "photo-readonly");
|
||||
var img = readField(record, "Image");
|
||||
var currentImage = (typeof img === "string" && (img.indexOf("https://") === 0 || img.indexOf("data:image/") === 0)) ? img : null;
|
||||
var previewImg = el("img");
|
||||
previewImg.id = "edImagePreview";
|
||||
previewImg.alt = "";
|
||||
if (currentImage) {
|
||||
previewImg.src = currentImage;
|
||||
photoRow.appendChild(previewImg);
|
||||
} else {
|
||||
previewImg.hidden = true;
|
||||
photoRow.appendChild(previewImg);
|
||||
photoRow.appendChild(el("span", "mono", initials(who)));
|
||||
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);
|
||||
}
|
||||
photoWrap.appendChild(photoRow);
|
||||
var photoInput = el("input");
|
||||
photoInput.type = "file";
|
||||
photoInput.id = "edImage";
|
||||
photoInput.name = "Image";
|
||||
photoInput.accept = "image/jpeg,image/png,image/webp";
|
||||
photoWrap.appendChild(photoInput);
|
||||
var photoResult = el("p", "hint", "");
|
||||
photoResult.id = "edImageResult";
|
||||
photoResult.setAttribute("role", "status");
|
||||
photoResult.setAttribute("aria-live", "polite");
|
||||
photoWrap.appendChild(photoResult);
|
||||
photoWrap.appendChild(el("p", "hint",
|
||||
"JPEG, PNG or WebP, up to 8 MB — we resize it to a square of at most 384 px first. " +
|
||||
"Leave it to keep your current photo."));
|
||||
O4PPhoto.wire(photoInput, previewImg, photoResult, function (dataUrl) {
|
||||
newImage = dataUrl;
|
||||
var fb = photoRow.querySelector(".mono");
|
||||
if (fb) fb.hidden = !!dataUrl;
|
||||
if (dataUrl) return; // wire already shows the new image
|
||||
// reverted to "no new photo": show the record's own photo again
|
||||
if (currentImage) { previewImg.src = currentImage; previewImg.hidden = false; }
|
||||
else previewImg.hidden = true;
|
||||
});
|
||||
"Your photo comes from your listing and is not editable here — reply to the email " +
|
||||
"that brought you here if it needs to change."));
|
||||
form.appendChild(photoWrap);
|
||||
|
||||
var save = el("button", "btn", "Save my changes");
|
||||
@@ -686,10 +670,10 @@
|
||||
else if (had) payload[pair[1]] = "";
|
||||
});
|
||||
|
||||
// An untouched photo rides back exactly as loaded — it came in with the record
|
||||
// above and was never touched, so it stays byte-identical. Only a NEWLY picked
|
||||
// photo (already validated and downscaled) replaces it.
|
||||
if (newImage !== null) payload.Image = newImage;
|
||||
// The photo does NOT ride in this record any more: the payload carries no
|
||||
// `Image` key and this form sends none — not the value, not null, not "". The
|
||||
// delete is the guarantee, whatever a reply may one day carry.
|
||||
delete payload.Image;
|
||||
|
||||
fetch(C.webhook, {
|
||||
method: C.saveMethod,
|
||||
|
||||
Reference in new issue
Block a user