Selaa lähdekoodia

Use converse-avatar for setting avatar

JC Brand 3 vuotta sitten
vanhempi
commit
664f290001

+ 10 - 3
src/headless/plugins/vcard.js

@@ -209,11 +209,13 @@ converse.plugins.add('converse-vcard', {
                 return;
                 return;
             } else {
             } else {
                 message.vcard = getVCardForChatroomOccupant(message);
                 message.vcard = getVCardForChatroomOccupant(message);
+                message.vcard.on('change', () => message.trigger('vcard:change'));
+                message.trigger('vcard:add');
             }
             }
         }
         }
 
 
 
 
-        _converse.initVCardCollection = async function () {
+        async function initVCardCollection () {
             _converse.vcards = new _converse.VCards();
             _converse.vcards = new _converse.VCards();
             const id = `${_converse.bare_jid}-converse.vcards`;
             const id = `${_converse.bare_jid}-converse.vcards`;
             initStorage(_converse.vcards, id);
             initStorage(_converse.vcards, id);
@@ -226,7 +228,12 @@ converse.plugins.add('converse-vcard', {
             const vcards = _converse.vcards;
             const vcards = _converse.vcards;
             if (_converse.session) {
             if (_converse.session) {
                 const jid = _converse.session.get('bare_jid');
                 const jid = _converse.session.get('bare_jid');
-                _converse.xmppstatus.vcard = vcards.findWhere({'jid': jid}) || vcards.create({'jid': jid});
+                const status = _converse.xmppstatus;
+                status.vcard = vcards.findWhere({'jid': jid}) || vcards.create({'jid': jid});
+                if (status.vcard) {
+                    status.vcard.on('change', () => status.trigger('vcard:change'));
+                    status.trigger('vcard:add');
+                }
             }
             }
             /**
             /**
              * Triggered as soon as the `_converse.vcards` collection has been initialized and populated from cache.
              * Triggered as soon as the `_converse.vcards` collection has been initialized and populated from cache.
@@ -256,7 +263,7 @@ converse.plugins.add('converse-vcard', {
         api.listen.on('clearSession', () => clearVCardsSession());
         api.listen.on('clearSession', () => clearVCardsSession());
         api.listen.on('messageInitialized', m => setVCardOnModel(m));
         api.listen.on('messageInitialized', m => setVCardOnModel(m));
         api.listen.on('rosterContactInitialized', m => setVCardOnModel(m));
         api.listen.on('rosterContactInitialized', m => setVCardOnModel(m));
-        api.listen.on('statusInitialized', _converse.initVCardCollection);
+        api.listen.on('statusInitialized', initVCardCollection);
 
 
 
 
         /************************ BEGIN API ************************/
         /************************ BEGIN API ************************/

+ 5 - 1
src/modals/templates/occupant.js

@@ -15,7 +15,11 @@ export default (o) => {
                 <div class="modal-body" class="d-flex">
                 <div class="modal-body" class="d-flex">
                     <div class="row">
                     <div class="row">
                         <div class="col-auto">
                         <div class="col-auto">
-                            <converse-avatar class="avatar chat-msg__avatar" .model=${o.vcard} height="120" width="120"></converse-avatar>
+                            <converse-avatar
+                                class="avatar chat-msg__avatar"
+                                .data=${o.vcard?.attributes}
+                                nonce=${o.vcard?.get('vcard_updated')}
+                                height="120" width="120"></converse-avatar>
                         </div>
                         </div>
                         <div class="col">
                         <div class="col">
                             <ul class="occupant-details">
                             <ul class="occupant-details">

+ 6 - 1
src/plugins/chatview/templates/chat-head.js

@@ -19,7 +19,12 @@ async function getDropdownButtons (promise) {
 
 
 export default (o) => {
 export default (o) => {
     const i18n_profile = __("The User's Profile Image");
     const i18n_profile = __("The User's Profile Image");
-    const avatar = html`<span title="${i18n_profile}"><converse-avatar class="avatar chat-msg__avatar" .model=${o.model.vcard} height="40" width="40"></converse-avatar></span>`;
+    const avatar = html`<span title="${i18n_profile}">
+        <converse-avatar
+            class="avatar chat-msg__avatar"
+            .data=${o.model.vcard?.attributes}
+            nonce=${o.model.vcard?.get('vcard_updated')}
+            height="40" width="40"></converse-avatar></span>`;
     const display_name = o.model.getDisplayName();
     const display_name = o.model.getDisplayName();
 
 
     const tpl_dropdown_btns = () => getDropdownButtons(o.heading_buttons_promise)
     const tpl_dropdown_btns = () => getDropdownButtons(o.heading_buttons_promise)

+ 0 - 12
src/plugins/profile/modals/profile.js

@@ -30,22 +30,10 @@ const ProfileModal = BootstrapModal.extend({
         return tpl_profile_modal(Object.assign(
         return tpl_profile_modal(Object.assign(
             this.model.toJSON(),
             this.model.toJSON(),
             this.model.vcard.toJSON(),
             this.model.vcard.toJSON(),
-            this.getAvatarData(),
             { 'view': this }
             { 'view': this }
         ));
         ));
     },
     },
 
 
-    getAvatarData () {
-        const image_type = this.model.vcard.get('image_type');
-        const image_data = this.model.vcard.get('image');
-        const image = "data:" + image_type + ";base64," + image_data;
-        return {
-            'height': 128,
-            'width': 128,
-            image,
-        };
-    },
-
     afterRender () {
     afterRender () {
         this.tabs = sizzle('.nav-item .nav-link', this.el).map(e => new bootstrap.Tab(e));
         this.tabs = sizzle('.nav-item .nav-link', this.el).map(e => new bootstrap.Tab(e));
     },
     },

+ 3 - 4
src/plugins/profile/statusview.js

@@ -6,12 +6,11 @@ import { _converse, api } from '@converse/headless/core';
 
 
 class ProfileView extends CustomElement {
 class ProfileView extends CustomElement {
 
 
-    async initialize () {
+    initialize () {
         this.model = _converse.xmppstatus;
         this.model = _converse.xmppstatus;
+        this.listenTo(this.model, "vcard:add", this.requestUpdate);
         this.listenTo(this.model, "change", this.requestUpdate);
         this.listenTo(this.model, "change", this.requestUpdate);
-        await api.waitUntil('VCardsInitialized');
-        this.listenTo(this.model.vcard, "change", this.requestUpdate);
-        this.requestUpdate();
+        this.listenTo(this.model, "vcard:change", this.requestUpdate);
     }
     }
 
 
     render () {
     render () {

+ 4 - 1
src/plugins/profile/templates/profile.js

@@ -39,7 +39,10 @@ export default (el) => {
         <div class="userinfo controlbox-padded">
         <div class="userinfo controlbox-padded">
             <div class="controlbox-section profile d-flex">
             <div class="controlbox-section profile d-flex">
                 <a class="show-profile" href="#" @click=${el.showProfileModal}>
                 <a class="show-profile" href="#" @click=${el.showProfileModal}>
-                    <converse-avatar class="avatar align-self-center" .model=${el.model.vcard} height="40" width="40"></converse-avatar>
+                    <converse-avatar class="avatar align-self-center"
+                        .data=${el.model.vcard?.attributes}
+                        nonce=${el.model.vcard?.get('vcard_updated')}
+                        height="40" width="40"></converse-avatar>
                 </a>
                 </a>
                 <span class="username w-100 align-self-center">${fullname}</span>
                 <span class="username w-100 align-self-center">${fullname}</span>
                 ${show_settings_button  ? tpl_user_settings_button(el) : ''}
                 ${show_settings_button  ? tpl_user_settings_button(el) : ''}

+ 1 - 1
src/plugins/profile/templates/profile_modal.js

@@ -125,7 +125,7 @@ export default (o) => {
                             <form class="converse-form converse-form--modal profile-form" action="#">
                             <form class="converse-form converse-form--modal profile-form" action="#">
                                 <div class="row">
                                 <div class="row">
                                     <div class="col-auto">
                                     <div class="col-auto">
-                                        <converse-image-picker image="${o.image}" width="${o.width}" height="${o.height}"></converse-image-picker>
+                                        <converse-image-picker .data="${{image: o.image, image_type: o.image_type}}" width="128" height="128"></converse-image-picker>
                                     </div>
                                     </div>
                                     <div class="col">
                                     <div class="col">
                                         <div class="form-group">
                                         <div class="form-group">

+ 5 - 1
src/plugins/rosterview/templates/roster_item.js

@@ -25,7 +25,11 @@ export default  (el, item) => {
    const i18n_remove = __('Click to remove %1$s as a contact', display_name);
    const i18n_remove = __('Click to remove %1$s as a contact', display_name);
    return html`
    return html`
    <a class="list-item-link cbox-list-item open-chat ${ num_unread ? 'unread-msgs' : '' }" title="${i18n_chat}" href="#" @click=${el.openChat}>
    <a class="list-item-link cbox-list-item open-chat ${ num_unread ? 'unread-msgs' : '' }" title="${i18n_chat}" href="#" @click=${el.openChat}>
-      <converse-avatar class="avatar" .model=${el.model.vcard} height="30" width="30"></converse-avatar>
+      <converse-avatar
+         class="avatar"
+         .data=${el.model.vcard?.attributes}
+         nonce=${el.model.vcard?.get('vcard_updated')}
+         height="30" width="30"></converse-avatar>
       <span class="${status_icon}" title="${desc_status}"></span>
       <span class="${status_icon}" title="${desc_status}"></span>
       ${ num_unread ? html`<span class="msgs-indicator">${ num_unread }</span>` : '' }
       ${ num_unread ? html`<span class="msgs-indicator">${ num_unread }</span>` : '' }
       <span class="contact-name contact-name--${el.show} ${ num_unread ? 'unread-msgs' : ''}">${display_name}</span>
       <span class="contact-name contact-name--${el.show} ${ num_unread ? 'unread-msgs' : ''}">${display_name}</span>

+ 10 - 4
src/shared/avatar/avatar.js

@@ -9,9 +9,10 @@ export default class Avatar extends CustomElement {
 
 
     static get properties () {
     static get properties () {
         return {
         return {
-            model: { type: Object },
+            data: { type: Object },
             width: { type: String },
             width: { type: String },
             height: { type: String },
             height: { type: String },
+            nonce: { type: String }, // Used to trigger rerenders
         }
         }
     }
     }
 
 
@@ -22,9 +23,14 @@ export default class Avatar extends CustomElement {
     }
     }
 
 
     render  () {
     render  () {
-        const image_type = this.model?.get('image_type') || _converse.DEFAULT_IMAGE_TYPE;
-        const image_data = this.model?.get('image') || _converse.DEFAULT_IMAGE;
-        const image = "data:" + image_type + ";base64," + image_data;
+        const image_type = this.data?.image_type || _converse.DEFAULT_IMAGE_TYPE;
+        let image;
+        if (this.data?.data_uri) {
+            image = this.data?.data_uri;
+        } else {
+            const image_data = this.data?.image || _converse.DEFAULT_IMAGE;
+            image = "data:" + image_type + ";base64," + image_data;
+        }
         return tpl_avatar({
         return tpl_avatar({
             'classes': this.getAttribute('class'),
             'classes': this.getAttribute('class'),
             'height': this.height,
             'height': this.height,

+ 0 - 38
src/shared/avatar/view.js

@@ -1,38 +0,0 @@
-import tpl_avatar from 'shared/templates/avatar.js';
-import { ElementView } from '@converse/skeletor/src/element';
-import { View } from '@converse/skeletor/src/view';
-import { converse } from '@converse/headless/core';
-
-const u = converse.env.utils;
-
-const AvatarMixin = {
-
-    renderAvatar (el) {
-        el = el || (this?.el ?? this);
-        const avatar_el = el.querySelector('canvas.avatar, svg.avatar');
-        if (avatar_el === null) {
-            return;
-        }
-        if (this.model.vcard) {
-            const data = {
-                'classes': avatar_el.getAttribute('class'),
-                'width': avatar_el.getAttribute('width'),
-                'height': avatar_el.getAttribute('height'),
-                'image_type': this.model.vcard.get('image_type'),
-                'image': this.model.vcard.get('image')
-            };
-            avatar_el.outerHTML = u.getElementFromTemplateResult(tpl_avatar(data)).outerHTML;
-        }
-    }
-}
-
-
-export const ViewWithAvatar = View.extend(AvatarMixin);
-
-
-export class ElementViewWithAvatar extends ElementView {
-
-    renderAvatar (el) {
-        AvatarMixin.renderAvatar.call(this, el);
-    }
-}

+ 0 - 12
src/shared/chat/message.js

@@ -127,18 +127,6 @@ export default class Message extends CustomElement {
             ['chat', 'groupchat'].includes(this.model.get('type'));
             ['chat', 'groupchat'].includes(this.model.get('type'));
     }
     }
 
 
-    getAvatarData () {
-        const image_type = this.model.vcard?.get('image_type') || _converse.DEFAULT_IMAGE_TYPE;
-        const image_data = this.model.vcard?.get('image') || _converse.DEFAULT_IMAGE;
-        const image = "data:" + image_type + ";base64," + image_data;
-        return {
-            'classes': 'chat-msg__avatar',
-            'height': 36,
-            'width': 36,
-            image,
-        };
-    }
-
     onUnfurlAnimationEnd () {
     onUnfurlAnimationEnd () {
         if (this.model.get('url_preview_transition') === 'fade-out') {
         if (this.model.get('url_preview_transition') === 'fade-out') {
             this.model.save({
             this.model.save({

+ 4 - 4
src/shared/chat/templates/file-progress.js

@@ -9,13 +9,13 @@ export default (el) => {
     const size = filesize(el.model.file.size);
     const size = filesize(el.model.file.size);
     return html`
     return html`
         <div class="message chat-msg">
         <div class="message chat-msg">
-            <converse-avatar class="avatar align-self-center" .model=${el.model.vcard} height="40" width="40"></converse-avatar>
-
             ${ el.shouldShowAvatar() ?
             ${ el.shouldShowAvatar() ?
                 html`<a class="show-msg-author-modal" @click=${el.showUserModal}>
                 html`<a class="show-msg-author-modal" @click=${el.showUserModal}>
-                    <converse-avatar class="avatar align-self-center" .model=${el.model.vcard} height="40" width="40"></converse-avatar>
+                    <converse-avatar class="avatar align-self-center"
+                        .data=${el.model.vcard?.attributes}
+                        nonce=${el.model.vcard?.get('vcard_updated')}
+                        height="40" width="40"></converse-avatar>
                 </a>` : '' }
                 </a>` : '' }
-
             <div class="chat-msg__content">
             <div class="chat-msg__content">
                 <span class="chat-msg__text">${i18n_uploading} <strong>${filename}</strong>, ${size}</span>
                 <span class="chat-msg__text">${i18n_uploading} <strong>${filename}</strong>, ${size}</span>
                 <progress value="${el.model.get('progress')}"/>
                 <progress value="${el.model.get('progress')}"/>

+ 5 - 1
src/shared/chat/templates/message.js

@@ -20,7 +20,11 @@ export default (el, o) => {
 
 
             ${ o.should_show_avatar ?
             ${ o.should_show_avatar ?
                 html`<a class="show-msg-author-modal" @click=${el.showUserModal}>
                 html`<a class="show-msg-author-modal" @click=${el.showUserModal}>
-                    <converse-avatar class="avatar align-self-center" .model=${el.model.vcard} height="40" width="40"></converse-avatar>
+                    <converse-avatar
+                        class="avatar align-self-center"
+                        .data=${el.model.vcard?.attributes}
+                        nonce=${el.model.vcard?.get('vcard_updated')}
+                        height="40" width="40"></converse-avatar>
                 </a>` : '' }
                 </a>` : '' }
 
 
             <div class="chat-msg__content chat-msg__content--${o.sender} ${o.is_me_message ? 'chat-msg__content--action' : ''}">
             <div class="chat-msg__content chat-msg__content--${o.sender} ${o.is_me_message ? 'chat-msg__content--action' : ''}">

+ 8 - 9
src/shared/components/image-picker.js

@@ -2,7 +2,6 @@ import { CustomElement } from './element.js';
 import { __ } from 'i18n';
 import { __ } from 'i18n';
 import { api } from "@converse/headless/core";
 import { api } from "@converse/headless/core";
 import { html } from 'lit';
 import { html } from 'lit';
-import { renderAvatar } from "shared/directives/avatar.js";
 
 
 const i18n_profile_picture = __('Your profile picture');
 const i18n_profile_picture = __('Your profile picture');
 
 
@@ -12,20 +11,15 @@ export default class ImagePicker extends CustomElement {
     static get properties () {
     static get properties () {
         return {
         return {
             'height': { type: Number },
             'height': { type: Number },
-            'image': { type: String },
+            'data': { type: Object},
             'width': { type: Number },
             'width': { type: Number },
         }
         }
     }
     }
 
 
     render () {
     render () {
-        const avatar_data = {
-            'height': this.height,
-            'image': this.image,
-            'width': this.width,
-        };
         return html`
         return html`
             <a class="change-avatar" @click=${this.openFileSelection} title="${i18n_profile_picture}">
             <a class="change-avatar" @click=${this.openFileSelection} title="${i18n_profile_picture}">
-                ${ renderAvatar(avatar_data) }
+                <converse-avatar class="avatar" .data=${this.data} height="${this.height}" width="${this.width}"></converse-avatar>
             </a>
             </a>
             <input @change=${this.updateFilePreview} class="hidden" name="image" type="file"/>
             <input @change=${this.updateFilePreview} class="hidden" name="image" type="file"/>
         `;
         `;
@@ -39,7 +33,12 @@ export default class ImagePicker extends CustomElement {
     updateFilePreview (ev) {
     updateFilePreview (ev) {
         const file = ev.target.files[0];
         const file = ev.target.files[0];
         const reader = new FileReader();
         const reader = new FileReader();
-        reader.onloadend = () => (this.image = reader.result);
+        reader.onloadend = () => {
+            this.data = {
+                'data_uri': reader.result,
+                'image_type': file.type
+            }
+        }
         reader.readAsDataURL(file);
         reader.readAsDataURL(file);
     }
     }
 }
 }

+ 0 - 19
src/shared/directives/avatar.js

@@ -1,19 +0,0 @@
-import tpl_avatar from 'shared/avatar/templates/avatar.js';
-import { Directive, directive } from "lit/directive.js";
-
-
-class AvatarDirective extends Directive {
-
-    render (o) { // eslint-disable-line class-methods-use-this
-        const data = {
-            'classes': o.classes ? `${o.classes} avatar` : 'avatar',
-            'height': o.width || 36,
-            'image': o.image,
-            'image_type': o.image_type,
-            'width': o.height || 36,
-        }
-        return tpl_avatar(data);
-    }
-}
-
-export const renderAvatar = directive(AvatarDirective);