[FIX] web_editor, *: prevent blocked UI when original image not found

*: website

When the user uploads an image and remove the original image, some
image options continue to work thanks to a backend fallback on another
image. Unfortunately, this fallback is not available for images coming
from unsplash. This commit fixes this issue by preventing the UI to be
blocked when the original image is not found.

Steps to reproduce the bug:
- Activate the developer mode
- Configure Unsplash in the settings
- Add a Cover block on a page
- Replace the background image with an unsplash image
- Save
- Edit the page and via the media dialog, remove the original image

=> The options on the image cannot start anymore.

opw-3636499
opw-3474103

closes odoo/odoo#148197

X-original-commit: 3b0ac160a800f70ae965b60110e83e41655bca5f
Signed-off-by: Romain Derie (rde) <rde@odoo.com>
Signed-off-by: Guillaume Dieleman (gdi) <gdi@odoo.com>
This commit is contained in:
Guillaume-gdi
2024-01-05 15:14:34 +00:00
parent c821460f93
commit 3859bbe99b
2 changed files with 45 additions and 4 deletions
@@ -349,12 +349,29 @@ export async function applyModifications(img, dataOptions = {}) {
* @param {String} src URL of the image to load
* @param {HTMLImageElement} [img] img element in which to load the image
* @returns {Promise<HTMLImageElement>} Promise that resolves to the loaded img
* or a placeholder image if the src is not found.
*/
export function loadImage(src, img = new Image()) {
const handleImage = (source, resolve, reject) => {
img.addEventListener("load", () => resolve(img), {once: true});
img.addEventListener("error", reject, {once: true});
img.src = source;
};
// The server will return a placeholder image with the following src.
const placeholderHref = "/web/image/__odoo__unknown__src__/";
return new Promise((resolve, reject) => {
img.addEventListener('load', () => resolve(img), {once: true});
img.addEventListener('error', reject, {once: true});
img.src = src;
fetch(src)
.then(response => {
if (!response.ok) {
src = placeholderHref;
}
handleImage(src, resolve, reject);
})
.catch(error => {
src = placeholderHref;
handleImage(src, resolve, reject);
});
});
}
@@ -2,6 +2,24 @@
import wTourUtils from "@website/js/tours/tour_utils";
const wallRaceConditionClass = "image_wall_race_condition";
const preventRaceConditionSteps = [{
content: "Wait a few ms to avoid race condition",
// Ensure the class is remove from previous call of those steps
trigger: `body:not(.${wallRaceConditionClass})`,
run() {
setTimeout(() => {
document.body.classList.add(wallRaceConditionClass);
}, 500);
}
}, {
content: "Check the race condition class is added after a few ms",
trigger: `body.${wallRaceConditionClass}`,
run() {
document.body.classList.remove(wallRaceConditionClass);
}
}];
const selectSignImageStep = {
content: "Click on image 14",
extra_trigger: ".o_we_customize_panel:not(:has(.snippet-option-GalleryElement))",
@@ -9,7 +27,13 @@ const selectSignImageStep = {
};
// Without reselecting the image, the tour manages to click on the
// move button before the active image is updated.
const reselectSignImageSteps = [{
// We need to wait a few ms before clicking on the footer because after
// clicking on reposition option, there may be a delay during the click on
// another block would be ignored.
const reselectSignImageSteps = [
...preventRaceConditionSteps,
{
content: "Select footer",
trigger: "iframe footer",
}, selectSignImageStep];