Browse Source

Add a `renderImage` directive which handles errors

JC Brand 5 years ago
parent
commit
a014eca9e7

+ 6 - 6
spec/http-file-upload.js

@@ -318,9 +318,9 @@ describe("XEP-0363: HTTP File Upload", function () {
                         `</message>`);
                     await u.waitUntil(() => view.el.querySelector('.chat-image'), 1000);
                     // Check that the image renders
-                    expect(view.el.querySelector('.chat-msg .chat-msg__media').innerHTML.trim()).toEqual(
-                        `<!----><a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
-                        `<img class="chat-image img-thumbnail" src="${base_url}/logo/conversejs-filled.svg"></a><!---->`);
+                    expect(view.el.querySelector('.chat-msg .chat-msg__media').innerHTML.replace(/<!---->/g, '').trim()).toEqual(
+                        `<a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
+                        `<img class="chat-image img-thumbnail" src="${base_url}/logo/conversejs-filled.svg"></a>`);
                     XMLHttpRequest.prototype.send = send_backup;
                     done();
                 }));
@@ -422,9 +422,9 @@ describe("XEP-0363: HTTP File Upload", function () {
                         `</message>`);
                     await u.waitUntil(() => view.el.querySelector('.chat-image'), 1000);
                     // Check that the image renders
-                    expect(view.el.querySelector('.chat-msg .chat-msg__media').innerHTML.trim()).toEqual(
-                        `<!----><a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
-                        `<img class="chat-image img-thumbnail" src="${base_url}/logo/conversejs-filled.svg"></a><!---->`);
+                    expect(view.el.querySelector('.chat-msg .chat-msg__media').innerHTML.replace(/<!---->/g, '').trim()).toEqual(
+                        `<a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
+                        `<img class="chat-image img-thumbnail" src="${base_url}/logo/conversejs-filled.svg"></a>`);
 
                     XMLHttpRequest.prototype.send = send_backup;
                     done();

+ 25 - 4
spec/messages.js

@@ -952,7 +952,7 @@ describe("A Chat Message", function () {
         let msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
         expect(msg.innerHTML.replace(/<!---->/g, '').trim()).toEqual(
             `<a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
-                `<img class="chat-image img-thumbnail" src="${base_url}/logo/conversejs-filled.svg">`+
+                `<img class="chat-image img-thumbnail" src="https://conversejs.org/logo/conversejs-filled.svg">`+
             `</a>`);
 
         message += "?param1=val1&param2=val2";
@@ -982,6 +982,27 @@ describe("A Chat Message", function () {
         done();
     }));
 
+    it("will fall back to rendering images as URLs",
+        mock.initConverse(
+            ['rosterGroupsFetched', 'chatBoxesFetched'], {},
+            async function (done, _converse) {
+
+        await mock.waitForRoster(_converse, 'current');
+        const base_url = 'https://conversejs.org';
+        const message = base_url+"/logo/non-existing.svg";
+        const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+        await mock.openChatBoxFor(_converse, contact_jid);
+        const view = _converse.api.chatviews.get(contact_jid);
+        spyOn(view.model, 'sendMessage').and.callThrough();
+        mock.sendMessage(view, message);
+        await u.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length, 1000)
+        expect(view.model.sendMessage).toHaveBeenCalled();
+        const msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
+        await u.waitUntil(() => msg.innerHTML.replace(/<!---->/g, '').trim() ==
+            `<a target="_blank" rel="noopener" href="https://conversejs.org/logo/non-existing.svg">https://conversejs.org/logo/non-existing.svg</a>`);
+        done();
+    }));
+
     it("will render the message time as configured",
             mock.initConverse(
                 ['rosterGroupsFetched', 'chatBoxesFetched'], {},
@@ -1971,9 +1992,9 @@ describe("A Chat Message", function () {
             expect(u.hasClass('chat-msg__text', msg)).toBe(true);
             expect(msg.textContent).toEqual('Have you seen this funny image?');
             const media = view.el.querySelector('.chat-msg .chat-msg__media');
-            expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "")).toEqual(
-                `<!----><a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
-                `<img class="chat-image img-thumbnail" src="${base_url}/logo/conversejs-filled.svg"></a><!---->`);
+            expect(media.innerHTML.replace(/<!---->/g, '').replace(/(\r\n|\n|\r)/gm, "")).toEqual(
+                `<a class="chat-image__link" target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
+                `<img class="chat-image img-thumbnail" src="${base_url}/logo/conversejs-filled.svg"></a>`);
             done();
         }));
     });

+ 1 - 2
src/templates/directives/body.js

@@ -206,9 +206,8 @@ class MessageBodyRenderer {
 
 
 export const renderBodyText = directive(component => part => {
-    const model = component.model;
     const renderer = new MessageBodyRenderer(component);
     part.setValue(renderer.render());
-    part.commit();
+    const model = component.model;
     model.collection?.trigger('rendered', model);
 });

+ 16 - 0
src/templates/directives/image.js

@@ -0,0 +1,16 @@
+import { directive, html } from "lit-html";
+
+
+export const renderImage = directive(url => part => {
+    function onError () {
+        part.setValue(converse.env.utils.convertUrlToHyperlink(url));
+        part.commit();
+    }
+    part.setValue(
+        html`<a href="${url}"
+                class="chat-image__link"
+                target="_blank"
+                rel="noopener"
+            ><img class="chat-image img-thumbnail" src="${url}" @error=${onError}/></a>`
+    );
+});

+ 2 - 1
src/templates/image.js

@@ -1,3 +1,4 @@
 import { html } from "lit-html";
+import { renderImage } from "./directives/image.js";
 
-export default (o) => html`<a href="${o.url}" class="chat-image__link" target="_blank" rel="noopener"><img class="chat-image img-thumbnail" src="${o.url}"/></a>`;
+export default (o) => html`${renderImage(o.url)}`;

+ 5 - 34
src/utils/html.js

@@ -99,11 +99,7 @@ function renderImageURL (_converse, uri) {
     if (!_converse.api.settings.get('show_images_inline')) {
         return u.convertURIoHyperlink(uri);
     }
-    const { __ } = _converse;
-    return tpl_image({
-        'url': uri.toString(),
-        'label_download': __('Download image "%1$s"', getFileName(uri))
-    })
+    return tpl_image({'url': uri.toString()});
 }
 
 function renderFileURL (_converse, uri) {
@@ -162,24 +158,6 @@ u.applyDragResistance = function (value, default_value) {
 };
 
 
-function loadImage (url) {
-    return new Promise((resolve, reject) => {
-        const err_msg = `Could not determine whether it's an image: ${url}`;
-        const img = new Image();
-        const timer = window.setTimeout(() => reject(new Error(err_msg)), 20000);
-        img.onerror = img.onabort = function () {
-            clearTimeout(timer);
-            reject(new Error(err_msg));
-        };
-        img.onload = function () {
-            clearTimeout(timer);
-            resolve(img);
-        };
-        img.src = url;
-    });
-}
-
-
 u.calculateElementHeight = function (el) {
     /* Return the height of the passed in DOM element,
      * based on the heights of its children.
@@ -311,24 +289,17 @@ u.escapeHTML = function (string) {
         .replace(/"/g, "&quot;");
 };
 
-u.convertToImageTag = async function (url) {
+
+u.convertToImageTag = function (url) {
     const uri = getURI(url);
     const img_url_without_ext = ['imgur.com', 'pbs.twimg.com'].includes(uri.hostname());
-    let src;
     if (u.isImageURL(url) || img_url_without_ext) {
         if (img_url_without_ext) {
             const format = (uri.hostname() === 'pbs.twimg.com') ? uri.search(true).format : 'png';
-            src = uri.removeSearch(/.*/).toString() + `.${format}`;
+            return tpl_image({'url': uri.removeSearch(/.*/).toString() + `.${format}`});
         } else {
-            src = url;
-        }
-        try {
-            await loadImage(src);
-        } catch (e) {
-            log.error(e);
-            return u.convertUrlToHyperlink(url);
+            return tpl_image({url});
         }
-        return tpl_image({url, src});
     }
 }