Commit ab5dd4a1 authored by Emmanuel Gil Peyrot's avatar Emmanuel Gil Peyrot Committed by JC Brand

Switch avatar rendering from canvas to SVG.

This delegates the calculation of the aspect ratio to the browser, and
generally simplifies the code.

Fixes #1156.
parent 352c0797
......@@ -9631,7 +9631,7 @@ body.reset {
#conversejs div, #conversejs span, #conversejs h1, #conversejs h2, #conversejs h3, #conversejs h4, #conversejs h5, #conversejs h6, #conversejs p, #conversejs blockquote,
#conversejs pre, #conversejs a, #conversejs em, #conversejs img, #conversejs strong, #conversejs dl, #conversejs dt, #conversejs dd, #conversejs ol, #conversejs ul, #conversejs li,
#conversejs fieldset, #conversejs form, #conversejs legend, #conversejs table, #conversejs caption, #conversejs tbody,
#conversejs tfoot, #conversejs thead, #conversejs tr, #conversejs th, #conversejs td, #conversejs article, #conversejs aside, #conversejs canvas, #conversejs details,
#conversejs tfoot, #conversejs thead, #conversejs tr, #conversejs th, #conversejs td, #conversejs article, #conversejs aside, #conversejs details,
#conversejs embed, #conversejs figure, #conversejs figcaption, #conversejs footer, #conversejs header, #conversejs hgroup, #conversejs menu,
#conversejs nav, #conversejs output, #conversejs ruby, #conversejs section, #conversejs summary, #conversejs time, #conversejs mark, #conversejs audio, #conversejs video {
margin: 0;
......@@ -9669,7 +9669,7 @@ body.reset {
color: var(--subdued-color); }
#conversejs a.fa:hover, #conversejs a.far:hover, #conversejs a.fas:hover, #conversejs a:visited.fa:hover, #conversejs a:visited.far:hover, #conversejs a:visited.fas:hover, #conversejs a:not([href]):not([tabindex]).fa:hover, #conversejs a:not([href]):not([tabindex]).far:hover, #conversejs a:not([href]):not([tabindex]).fas:hover {
color: var(--gray-color); }
#conversejs canvas {
#conversejs svg {
border-radius: var(--chatbox-border-radius); }
#conversejs .fa, #conversejs .far, #conversejs .fas {
color: var(--subdued-color); }
......
......@@ -58492,8 +58492,10 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var backbone_overview__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! backbone.overview */ "./node_modules/backbone.overview/backbone.overview.js");
/* harmony import */ var backbone_overview__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(backbone_overview__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! templates/chatboxes.html */ "./src/templates/chatboxes.html");
/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! templates/avatar.svg */ "./src/templates/avatar.svg");
/* harmony import */ var templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/chatboxes.html */ "./src/templates/chatboxes.html");
/* harmony import */ var templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5__);
// Converse.js
// http://conversejs.org
//
......@@ -58504,6 +58506,7 @@ __webpack_require__.r(__webpack_exports__);
const _converse$env = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].env,
Backbone = _converse$env.Backbone,
_ = _converse$env._,
......@@ -58519,27 +58522,12 @@ const AvatarMixin = {
}
const image_type = this.model.vcard.get('image_type'),
image = this.model.vcard.get('image'),
img_src = "data:" + image_type + ";base64," + image,
img = new Image();
return new Promise((resolve, reject) => {
img.onload = () => {
const ctx = canvas_el.getContext('2d'),
ratio = img.width / img.height;
ctx.clearRect(0, 0, canvas_el.width, canvas_el.height);
if (ratio < 1) {
const scaled_img_with = canvas_el.width * ratio,
x = Math.floor((canvas_el.width - scaled_img_with) / 2);
ctx.drawImage(img, x, 0, scaled_img_with, canvas_el.height);
} else {
ctx.drawImage(img, 0, 0, canvas_el.width, canvas_el.height * ratio);
}
resolve();
};
img.src = img_src;
image = this.model.vcard.get('image');
canvas_el.outerHTML = templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4___default()({
'classes': canvas_el.getAttribute('class'),
'width': canvas_el.width,
'height': canvas_el.height,
'image': "data:" + image_type + ";base64," + image
});
}
......@@ -58618,11 +58606,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
render() {
try {
this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4___default()();
this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5___default()();
} catch (e) {
this._ensureElement();
this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_4___default()();
this.el.innerHTML = templates_chatboxes_html__WEBPACK_IMPORTED_MODULE_5___default()();
}
this.row_el = this.el.querySelector('.row');
......@@ -61766,14 +61754,13 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
msg_content.innerHTML = _.flow(_.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].geoUriToHttp, _, _converse.geouri_replacement), _.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addMentionsMarkup, _, this.model.get('references'), this.model.collection.chatbox), utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addHyperlinks, utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderNewLines, _.partial(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].addEmoji, _converse, _))(text);
}
const promises = [];
promises.push(utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderImageURLs(_converse, msg_content));
const promise = utils_emoji__WEBPACK_IMPORTED_MODULE_8__["default"].renderImageURLs(_converse, msg_content);
if (this.model.get('type') !== 'headline') {
promises.push(this.renderAvatar(msg));
this.renderAvatar(msg);
}
await Promise.all(promises);
await promise;
this.replaceElement(msg);
this.model.collection.trigger('rendered', this);
},
......@@ -100974,6 +100961,34 @@ return __p
/***/ }),
/***/ "./src/templates/avatar.svg":
/*!**********************************!*\
!*** ./src/templates/avatar.svg ***!
\**********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./node_modules/lodash/escape.js")};
module.exports = function(o) {
var __t, __p = '', __e = _.escape;
__p += '<!-- src/templates/avatar.svg -->\n<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="' +
__e(o.classes) +
'" width="' +
__e(o.width) +
'" height="' +
__e(o.height) +
'">\n <image width="' +
__e(o.width) +
'" height="' +
__e(o.height) +
'" preserveAspectRatio="xMidYMid meet" xlink:href="' +
__e(o.image) +
'"/>\n</svg>\n';
return __p
};
/***/ }),
/***/ "./src/templates/bookmark.html":
/*!*************************************!*\
!*** ./src/templates/bookmark.html ***!
......@@ -102892,7 +102907,7 @@ __e(o.image) +
} ;
__p += '\n ';
if (!o.image) { ;
__p += '\n <canvas class="avatar" height="100px" width="100px"/>\n ';
__p += '\n <canvas class="avatar" height="100px" width="100px"></canvas>\n ';
} ;
__p += '\n </a>\n <input class="hidden" name="image" type="file">\n </div>\n <div class="col">\n <div class="form-group">\n <label class="col-form-label">' +
__e(o.label_jid) +
......@@ -102989,7 +103004,7 @@ var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./no
module.exports = function(o) {
var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
function print() { __p += __j.call(arguments, '') }
__p += '<!-- src/templates/profile_view.html -->\n<div class="userinfo controlbox-padded">\n<div class="controlbox-section profile d-flex">\n <a class="show-profile" href="#">\n <canvas alt="o.__(\'Your avatar\')" class="avatar align-self-center" height="40" width="40"></canvas>\n </a>\n <span class="username w-100 align-self-center">' +
__p += '<!-- src/templates/profile_view.html -->\n<div class="userinfo controlbox-padded">\n<div class="controlbox-section profile d-flex">\n <a class="show-profile" href="#">\n <canvas class="avatar align-self-center" height="40" width="40"></canvas>\n </a>\n <span class="username w-100 align-self-center">' +
__e(o.fullname) +
'</span>\n <a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="' +
__e(o.info_details) +
......@@ -196,7 +196,7 @@ body.reset {
div, span, h1, h2, h3, h4, h5, h6, p, blockquote,
pre, a, em, img, strong, dl, dt, dd, ol, ul, li,
fieldset, form, legend, table, caption, tbody,
tfoot, thead, tr, th, td, article, aside, canvas, details,
tfoot, thead, tr, th, td, article, aside, details,
embed, figure, figcaption, footer, header, hgroup, menu,
nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
......@@ -254,7 +254,7 @@ body.reset {
}
}
canvas {
svg {
border-radius: var(--chatbox-border-radius);
}
......
......@@ -8,6 +8,7 @@ import "@converse/headless/converse-chatboxes";
import "backbone.nativeview";
import "backbone.overview";
import converse from "@converse/headless/converse-core";
import tpl_avatar from "templates/avatar.svg";
import tpl_chatboxes from "templates/chatboxes.html";
const { Backbone, _, utils } = converse.env;
......@@ -22,25 +23,13 @@ const AvatarMixin = {
return;
}
const image_type = this.model.vcard.get('image_type'),
image = this.model.vcard.get('image'),
img_src = "data:" + image_type + ";base64," + image,
img = new Image();
return new Promise((resolve, reject) => {
img.onload = () => {
const ctx = canvas_el.getContext('2d'),
ratio = img.width / img.height;
ctx.clearRect(0, 0, canvas_el.width, canvas_el.height);
if (ratio < 1) {
const scaled_img_with = canvas_el.width*ratio,
x = Math.floor((canvas_el.width-scaled_img_with)/2);
ctx.drawImage(img, x, 0, scaled_img_with, canvas_el.height);
} else {
ctx.drawImage(img, 0, 0, canvas_el.width, canvas_el.height*ratio);
}
resolve();
};
img.src = img_src;
image = this.model.vcard.get('image');
canvas_el.outerHTML = tpl_avatar({
'classes': canvas_el.getAttribute('class'),
'width': canvas_el.width,
'height': canvas_el.height,
'image': "data:" + image_type + ";base64," + image,
});
},
};
......
......@@ -155,12 +155,11 @@ converse.plugins.add('converse-message-view', {
_.partial(u.addEmoji, _converse, _)
)(text);
}
const promises = [];
promises.push(u.renderImageURLs(_converse, msg_content));
const promise = u.renderImageURLs(_converse, msg_content);
if (this.model.get('type') !== 'headline') {
promises.push(this.renderAvatar(msg));
this.renderAvatar(msg);
}
await Promise.all(promises);
await promise;
this.replaceElement(msg);
this.model.collection.trigger('rendered', this);
},
......
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="{{{o.classes}}}" width="{{{o.width}}}" height="{{{o.height}}}">
<image width="{{{o.width}}}" height="{{{o.height}}}" preserveAspectRatio="xMidYMid meet" xlink:href="{{{o.image}}}"/>
</svg>
......@@ -26,7 +26,7 @@
<img alt="{{{o.alt_avatar}}}" class="img-thumbnail avatar align-self-center" height="100px" width="100px" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
{[ } ]}
{[ if (!o.image) { ]}
<canvas class="avatar" height="100px" width="100px"/>
<canvas class="avatar" height="100px" width="100px"></canvas>
{[ } ]}
</a>
<input class="hidden" name="image" type="file">
......
<div class="userinfo controlbox-padded">
<div class="controlbox-section profile d-flex">
<a class="show-profile" href="#">
<canvas alt="o.__('Your avatar')" class="avatar align-self-center" height="40" width="40"></canvas>
<canvas class="avatar align-self-center" height="40" width="40"></canvas>
</a>
<span class="username w-100 align-self-center">{{{o.fullname}}}</span>
<a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="{{{o.info_details}}}"></a>
......
......@@ -36,7 +36,7 @@ const config = {
use: "exports-loader?filterXSS,filterCSS"
},
{
test: /\.html$/,
test: /\.(html|svg)$/,
exclude: /node_modules/,
use: [{
loader: 'lodash-template-webpack-loader',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment