فهرست منبع

Some UI improvements

- Render images as thumbnails
- Use the image.html template when rendering images from pasted URLs
- Update message and spoiler markup to render avatars
- Use the default avatar as fallback when user doesn't have one
- Instead of 'me' render own name or JID
JC Brand 7 سال پیش
والد
کامیت
3d42425083

+ 0 - 1
docs/source/developer_guidelines.rst

@@ -101,7 +101,6 @@ Brief description of converse.js's dependencies
 
 Converse.js relies on the following dependencies:
 
-* `JQuery <http://jquery.com/>`_ for DOM manipulation and `promises <http://api.jquery.com/promise/>`_.
 * `moment.js <http://momentjs.com/>`_ provides a better API for handling dates and times.
 * `Strophe.js <http://strophe.im/>`_ maintains the XMPP session, is used to
   build XMPP stanzas, to send them, and to register handlers for received stanzas.

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 4
mockup/chatbox.html


+ 22 - 3
mockup/chatroom.html

@@ -97,7 +97,6 @@
                                     </div>
 								</div>
 
-
 								<div class="message chat-msg">
                                     <canvas height="36" width="36" class="avatar"></canvas>
                                     <div class="chat-msg-content">
@@ -111,6 +110,24 @@
                                             Or, if thou wilt not, be but sworn my love,
                                             And I'll no longer be a Capulet.
                                         </p>
+                                        <div class="chat-msg-media"></div>
+                                    </div>
+								</div>
+
+								<div class="message chat-msg">
+                                    <canvas height="36" width="36" class="avatar"></canvas>
+                                    <div class="chat-msg-content">
+                                        <span class="chat-msg-heading">
+                                            <span class="chat-msg-author">Juliet Capulet</span>
+                                            <span class="chat-msg-time text-muted">19:45</span>
+                                        </span>
+                                        <p class="chat-msg-text"></p>
+                                        <div class="chat-msg-media">
+                                            <a href="https://images.unsplash.com/photo-1496660067708-010ebdd7ce72?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=ea3514e6e00d8ce25c24d992b97929d9&dpr=1&auto=format&fit=crop&w=1000&q=80&cs=tinysrgb"
+                                               target="_blank" rel="noopener">
+                                                <img class="chat-image img-thumbnail" src="https://images.unsplash.com/photo-1496660067708-010ebdd7ce72?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=ea3514e6e00d8ce25c24d992b97929d9&dpr=1&auto=format&fit=crop&w=1000&q=80&cs=tinysrgb"> 
+                                            </a>
+                                        </div>
                                     </div>
 								</div>
 
@@ -121,13 +138,15 @@
                                             <span class="chat-msg-author">Romeo Montague</span>
                                             <span class="chat-msg-time text-muted">19:36</span>
                                         </span>
-                                        <div class="spoiler-hint">By a name
-                                            <a class="badge badge-info toggle-spoiler" data-toggle-state="closed" href="#"><i class="fa fa-eye"></i>Show more</a>
+                                        <div>
+                                            <span class="spoiler-hint">By a name</span>
+                                            <a class="badge badge-info spoiler-toggle" data-toggle-state="closed" href="#"><i class="fa fa-eye"></i>Show more</a>
                                         </div>
                                         <div class="chat-msg-text spoiler collapsed">
                                             I know not how to tell thee who I am: My name, dear saint, is hateful to
                                             myself, Because it is an enemy to thee. Had I it written, I would tear the word. 
                                         </div>
+                                        <div class="chat-msg-media"></div>
                                     </div>
                                 </div>
 

+ 1 - 1
mockup/utils.js

@@ -52,7 +52,7 @@ function toggleSpoilerMessage (ev) {
 }
 
 window.initSpoilers = function () {
-    const spoilers = document.querySelectorAll('.toggle-spoiler');
+    const spoilers = document.querySelectorAll('.spoiler-toggle');
     _.each(spoilers, (spoiler_el) => {
         spoiler_el.addEventListener('click', toggleSpoilerMessage);
     });

+ 13 - 13
spec/spoilers.js

@@ -44,9 +44,9 @@
             _converse.chatboxes.onMessage(msg);
 
             var view = _converse.chatboxviews.get(sender_jid);
-            expect(_.includes(view.el.querySelector('.chat-msg-author').textContent, 'Max Frankfurter')).toBeTruthy();
+            expect(view.el.querySelector('.chat-msg-author').textContent).toBe('Max Frankfurter');
 
-            var message_content = view.el.querySelector('.chat-msg-content');
+            var message_content = view.el.querySelector('.chat-msg-text');
             expect(message_content.textContent).toBe(spoiler);
 
             var spoiler_hint_el = view.el.querySelector('.spoiler-hint');
@@ -82,7 +82,7 @@
             var view = _converse.chatboxviews.get(sender_jid);
             expect(_.includes(view.el.querySelector('.chat-msg-author').textContent, 'Max Frankfurter')).toBeTruthy();
 
-            var message_content = view.el.querySelector('.chat-msg-content');
+            var message_content = view.el.querySelector('.chat-msg-text');
             expect(message_content.textContent).toBe(spoiler);
 
             var spoiler_hint_el = view.el.querySelector('.spoiler-hint');
@@ -148,17 +148,17 @@
                 expect(body_el.textContent).toBe('This is the spoiler');
 
                 /* Test the HTML spoiler message */
-                expect(view.el.querySelector('.chat-msg-author').textContent.split(':')[1].trim().split(' ')[1]).toBe('me');
+                expect(view.el.querySelector('.chat-msg-author').textContent).toBe('dummy@localhost');
 
-                var spoiler_msg_el = view.el.querySelector('.chat-msg-content.spoiler');
+                var spoiler_msg_el = view.el.querySelector('.chat-msg-text.spoiler');
                 expect(spoiler_msg_el.textContent).toBe('This is the spoiler');
                 expect(_.includes(spoiler_msg_el.classList, 'collapsed')).toBeTruthy();
 
-                spoiler_toggle = view.el.querySelector('.toggle-spoiler');
-                expect(spoiler_toggle.textContent).toBe('Show hidden message');
+                spoiler_toggle = view.el.querySelector('.spoiler-toggle');
+                expect(spoiler_toggle.textContent).toBe('Show more');
                 spoiler_toggle.click();
                 expect(_.includes(spoiler_msg_el.classList, 'collapsed')).toBeFalsy();
-                expect(spoiler_toggle.textContent).toBe('Hide hidden message');
+                expect(spoiler_toggle.textContent).toBe('Show less');
                 spoiler_toggle.click();
                 expect(_.includes(spoiler_msg_el.classList, 'collapsed')).toBeTruthy();
                 done();
@@ -227,17 +227,17 @@
                 expect(body_el.textContent).toBe('This is the spoiler');
 
                 /* Test the HTML spoiler message */
-                expect(view.el.querySelector('.chat-msg-author').textContent.split(':')[1].trim().split(' ')[1]).toBe('me');
+                expect(view.el.querySelector('.chat-msg-author').textContent).toBe('dummy@localhost');
 
-                var spoiler_msg_el = view.el.querySelector('.chat-msg-content.spoiler');
+                var spoiler_msg_el = view.el.querySelector('.chat-msg-text.spoiler');
                 expect(spoiler_msg_el.textContent).toBe('This is the spoiler');
                 expect(_.includes(spoiler_msg_el.classList, 'collapsed')).toBeTruthy();
 
-                spoiler_toggle = view.el.querySelector('.toggle-spoiler');
-                expect(spoiler_toggle.textContent).toBe('Show hidden message');
+                spoiler_toggle = view.el.querySelector('.spoiler-toggle');
+                expect(spoiler_toggle.textContent).toBe('Show more');
                 spoiler_toggle.click();
                 expect(_.includes(spoiler_msg_el.classList, 'collapsed')).toBeFalsy();
-                expect(spoiler_toggle.textContent).toBe('Hide hidden message');
+                expect(spoiler_toggle.textContent).toBe('Show less');
                 spoiler_toggle.click();
                 expect(_.includes(spoiler_msg_el.classList, 'collapsed')).toBeTruthy();
                 done();

+ 0 - 1
src/converse-chatboxes.js

@@ -193,7 +193,6 @@
                     'image': _converse.DEFAULT_IMAGE,
                     'image_type': _converse.DEFAULT_IMAGE_TYPE,
                     'num_unread': 0,
-                    'show_avatar': true,
                     'type': 'chatbox',
                     'message_type': 'chat',
                     'url': ''

+ 6 - 4
src/converse-chatview.js

@@ -249,7 +249,7 @@
                     'click .toggle-compose-spoiler': 'toggleComposeSpoilerMessage',
                     'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
                     'click .toggle-smiley': 'toggleEmojiMenu',
-                    'click .toggle-spoiler': 'toggleSpoilerMessage',
+                    'click .spoiler-toggle': 'toggleSpoilerMessage',
                     'click .upload-file': 'toggleFileUpload',
                     'keypress .chat-textarea': 'keyPressed',
                     'input .chat-textarea': 'inputChanged'
@@ -373,7 +373,7 @@
                                 this.el.querySelector('.chat-toolbar').insertAdjacentHTML('afterBegin', html);
                             }
                         }
-                    });
+                    }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
                 },
 
                 insertHeading () {
@@ -907,14 +907,16 @@
                         toggle_el.parentElement.parentElement.querySelector('.spoiler')
                     );
                     if (toggle_el.getAttribute("data-toggle-state") == "closed") {
-                        toggle_el.textContent = __('Show less');
+                        toggle_el.textContent = 'Show less';
                         icon_el.classList.remove("fa-eye");
                         icon_el.classList.add("fa-eye-slash");
+                        toggle_el.insertAdjacentElement('afterBegin', icon_el);
                         toggle_el.setAttribute("data-toggle-state", "open");
                     } else {
-                        toggle_el.textContent = __('Show more');
+                        toggle_el.textContent = 'Show more';
                         icon_el.classList.remove("fa-eye-slash");
                         icon_el.classList.add("fa-eye");
+                        toggle_el.insertAdjacentElement('afterBegin', icon_el);
                         toggle_el.setAttribute("data-toggle-state", "closed");
                     }
                 },

+ 4 - 2
src/converse-core.js

@@ -1509,10 +1509,12 @@
 
             defaults () {
                 return {
-                    "status":  _converse.default_state,
                     "jid": _converse.bare_jid,
                     "nickname": _converse.nickname,
-                    "vcard_updated": null
+                    "status":  _converse.default_state,
+                    "vcard_updated": null,
+                    'image': _converse.DEFAULT_IMAGE,
+                    'image_type': _converse.DEFAULT_IMAGE_TYPE
                 }
             },
 

+ 0 - 1
src/converse-headline.js

@@ -74,7 +74,6 @@
             _converse.HeadlinesBox = _converse.ChatBox.extend({
                 defaults: {
                     'type': 'headline',
-                    'show_avatar': false,
                     'bookmarked': false,
                     'chat_state': undefined,
                     'num_unread': 0,

+ 17 - 8
src/converse-message-view.js

@@ -44,8 +44,8 @@
             _converse.MessageView = Backbone.NativeView.extend({
 
                 initialize () {
-                    const chatbox = this.model.collection.chatbox;
-                    chatbox.on('change:fullname', (chatbox) => this.model.save('fullname', chatbox.get('fullname')));
+                    this.chatbox = this.model.collection.chatbox;
+                    this.chatbox.on('change:fullname', (chatbox) => this.model.save('fullname', chatbox.get('fullname')));
 
                     this.model.on('change:fullname', this.render, this);
                     this.model.on('change:progress', this.renderFileUploadProgresBar, this);
@@ -61,7 +61,7 @@
                         return this.renderErrorMessage();
                     }
 
-                    let template, username,
+                    let template, username, image, image_type,
                         text = this.model.get('message');
 
                     // TODO: store proper username on the message itself
@@ -71,9 +71,16 @@
                         username = arr[1];
                         text = arr[2];
                     } else {
-                        const fullname = _converse.xmppstatus.get('fullname') || this.model.get('fullname');
-                        username = this.model.get('sender') === 'me' && __('me') || fullname || this.model.get('from');
+                        username = this.model.get('fullname') || this.model.get('from');
                         template = this.model.get('is_spoiler') ? tpl_spoiler_message : tpl_message;
+
+                        if (this.model.get('sender') === 'me') {
+                            image_type = _converse.xmppstatus.get('image_type');
+                            image = _converse.xmppstatus.get('image');
+                        } else {
+                            image_type = this.chatbox.get('image_type');
+                            image = this.chatbox.get('image');
+                        }
                     }
                     const moment_time = moment(this.model.get('time'));
                     const msg = u.stringToElement(template(
@@ -82,7 +89,9 @@
                             'time': moment_time.format(),
                             'username': username,
                             'extra_classes': this.getExtraMessageClasses(),
-                            'label_show': __('Show more')
+                            'label_show': __('Show more'),
+                            'image_type': image_type,
+                            'image': image
                         })
                     ));
 
@@ -97,7 +106,7 @@
                         )(url);
                     }
 
-                    const msg_content = msg.querySelector('.chat-msg-content');
+                    const msg_content = msg.querySelector('.chat-msg-text');
                     if (text !== url) {
                         text = xss.filterXSS(text, {'whiteList': {}});
                         msg_content.innerHTML = _.flow(
@@ -106,7 +115,7 @@
                             _.partial(u.addEmoji, _converse, emojione, _)
                         )(text);
                     }
-                    u.renderImageURLs(msg_content).then(() => {
+                    u.renderImageURLs(_converse, msg_content).then(() => {
                         this.model.collection.trigger('rendered');
                     });
                     return this.replaceElement(msg);

+ 1 - 1
src/converse-muc-views.js

@@ -1485,7 +1485,7 @@
                  * Chat rooms can be listed, joined and new rooms can be created.
                  */
                 tagName: 'div',
-                className: 'controlbox-pane',
+                className: 'controlbox-section',
                 id: 'chatrooms',
                 events: {
                     'click a.chatbox-btn.fa-users': 'showAddRoomModal',

+ 0 - 4
src/converse-muc.js

@@ -304,15 +304,11 @@
                 getOutgoingMessageAttributes (text, spoiler_hint) {
                     const is_spoiler = this.get('composing_spoiler');
                     return {
-                        'from': _converse.connection.jid,
                         'fullname': this.get('nick'),
                         'is_spoiler': is_spoiler,
                         'message': text ? u.httpToGeoUri(emojione.shortnameToUnicode(text), _converse) : undefined,
-                        'msgid': _converse.connection.getUniqueId(),
                         'sender': 'me',
                         'spoiler_hint': is_spoiler ? spoiler_hint : undefined,
-                        'time': moment().format(),
-                        'to': this.get('jid'),
                         'type': 'groupchat',
                     };
                 },

+ 1 - 0
src/converse-rosterview.js

@@ -719,6 +719,7 @@
             _converse.RosterView = Backbone.OrderedListView.extend({
                 tagName: 'div',
                 id: 'converse-roster',
+                className: 'controlbox-section',
 
                 ItemView: _converse.RosterGroupView,
                 listItems: 'model',

+ 1 - 1
src/templates/bookmark.html

@@ -1,4 +1,4 @@
-<div class="room-item">
+<div class="list-item room-item">
 <div class="available-chatroom d-flex flex-row {[ if (o.hidden) { ]} hidden {[ } ]}" data-room-jid="{{{o.jid}}}">
     <a class="open-room w-100" data-room-jid="{{{o.jid}}}" title="{{{o.open_title}}}" href="#">{{{o.name}}}</a>
     <a class="remove-bookmark fa fa-bookmark align-self-center {[ if (o.bookmarked) { ]} button-on {[ } ]}"

+ 0 - 8
src/templates/chatbox_head.html

@@ -1,14 +1,6 @@
 <div class="chat-head chat-head-chatbox row no-gutters">
     <div class="col">
         <div class="row no-gutters">
-            {[ if (o.show_avatar) { ]}
-            <div class="col-auto">
-                <img alt="User Avatar"
-                    class="avatar"
-                    height="{{{o.avatar_height}}}px" width="{{{o.avatar_width}}}px"
-                    src="data:{{{o.image_type || o._converse.DEFAULT_IMAGE_TYPE}}};base64,{{{o.image || o._converse.DEFAULT_IMAGE}}}"/>
-            </div>
-            {[ } ]}
             <div class="col chat-title" title="{{{o.jid}}}">
                 {[ if (o.url) { ]}
                     <a href="{{{o.url}}}" target="_blank" rel="noopener" class="user">

+ 4 - 1
src/templates/image.html

@@ -1 +1,4 @@
-<img class="chat-image" src="{{{o.url}}}"/>
+<a href="{{{o.url}}}"
+    target="_blank" rel="noopener">
+    <img class="chat-image img-thumbnail" src="{{{o.url}}}"> 
+</a>

+ 9 - 3
src/templates/message.html

@@ -1,5 +1,11 @@
 <div class="message chat-msg {{{o.extra_classes}}}" data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}">
-    <span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.pretty_time}}} {{{o.username}}}:&nbsp;</span>
-    <span class="chat-msg-content"></span>
-    <div class="chat-msg-media"></div>
+    <img alt="User Avatar" class="avatar" height="36px" width="36px" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
+    <div class="chat-msg-content">
+        <span class="chat-msg-heading">
+            <span class="chat-msg-author">{{{o.username}}}</span>
+            <span class="chat-msg-time tex{{{o.username}}}t-muted">{{{o.pretty_time}}}</span>
+        </span>
+        <p class="chat-msg-text">
+        <div class="chat-msg-media"></div>
+    </div>
 </div>

+ 1 - 1
src/templates/room_panel.html

@@ -1,6 +1,6 @@
 <!-- <div id="chatrooms"> -->
 <div class="d-flex">
-    <span class="w-100">{{{o.heading_chatrooms}}}</span>
+    <span class="w-100 controlbox-heading">{{{o.heading_chatrooms}}}</span>
     <a class="chatbox-btn trigger-list-chatrooms-modal fa fa-list-ul" title="{{{o.title_list_rooms}}}" data-toggle="modal" data-target="#list-chatrooms-modal"></a>
     <a class="chatbox-btn trigger-add-chatrooms-modal fa fa-users" title="{{{o.title_new_room}}}" data-toggle="modal" data-target="#add-chatrooms-modal"></a>
 </div>

+ 1 - 1
src/templates/rooms_list_item.html

@@ -1,4 +1,4 @@
-<div class="room-item">
+<div class="list-item room-item">
 <div class="available-chatroom d-flex flex-row {[ if (o.num_unread_general) { ]} unread-msgs {[ } ]}" data-room-jid="{{{o.jid}}}">
 {[ if (o.num_unread) { ]}
     <span class="msgs-indicator badge badge-info">{{{ o.num_unread }}}</span>

+ 1 - 1
src/templates/roster.html

@@ -1,5 +1,5 @@
 <div class="d-flex">
-    <span class="w-100">{{{o.heading_contacts}}}</span>
+    <span class="w-100 controlbox-heading">{{{o.heading_contacts}}}</span>
     <a class="chatbox-btn add-contact fa fa-user-plus" title="{{{o.title_add_contact}}}"
        data-toggle="modal" data-target="#add-contact-modal"></a>
 </div>

+ 12 - 4
src/templates/spoiler_message.html

@@ -1,6 +1,14 @@
 <div class="message chat-msg {{{o.extra_classes}}}" data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}">
-    <span class="chat-msg-author chat-msg-{{{o.sender}}}">{{{o.pretty_time}}} {{{o.username}}}:&nbsp;</span>
-    <div class="spoiler-hint">{{{o.spoiler_hint}}}</div>
-    <a class="toggle-spoiler" data-toggle-state="closed" href="#"><i class="fa fa-eye"></li>{{{o.label_show}}}</a>
-    <div class="chat-msg-content spoiler collapsed"><!-- message gets added here via renderMessage --></div>
+    <img alt="User Avatar" class="avatar" height="36px" width="36px" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
+    <div class="chat-msg-content">
+        <span class="chat-msg-heading">
+            <span class="chat-msg-author">{{{o.username}}}</span>
+            <span class="chat-msg-time text-muted">{{{o.pretty_time}}}</span>
+        </span>
+        <div>
+            <span class="spoiler-hint">{{{o.spoiler_hint}}}</span>
+            <a class="badge badge-info spoiler-toggle" data-toggle-state="closed" href="#"><i class="fa fa-eye"></i>{{{o.label_show}}}</a>
+        </div>
+        <div class="chat-msg-text spoiler collapsed"><!-- message gets added here via renderMessage --></div>
+    </div>
 </div>

+ 10 - 8
src/utils/core.js

@@ -218,30 +218,32 @@
         return text;
     };
 
-    u.renderImageURLs = function (obj) {
+    u.renderImageURLs = function (_converse, obj) {
         /* Returns a Promise which resolves once all images have been loaded.
          */
+        const { __ } = _converse;
         const list = obj.textContent.match(URL_REGEX) || [];
         return Promise.all(
             _.map(list, (url) =>
                 new Promise((resolve, reject) =>
                     isImage(url).then(function (img) {
-                        // XXX: need to create a new image, otherwise the event
-                        // listener doesn't fire
                         const i = new Image();
-                        i.className = 'chat-image';
                         i.src = img.src;
                         i.addEventListener('load', resolve);
                         // We also resolve for non-images, otherwise the
                         // Promise.all resolves prematurely.
                         i.addEventListener('error', resolve);
-                        var anchors = sizzle(`a[href="${url}"]`, obj);
-                        _.each(anchors, (a) => {
-                            a.replaceChild(i, a.firstChild);
+
+                        _.each(sizzle(`a[href="${url}"]`, obj), (a) => {
+                            a.innerHTML = tpl_image({
+                                'url': url,
+                                'label_download': __('Download image file')
+                            })
                         });
                     }).catch(resolve)
                 )
-            ))
+            )
+        )
     };
 
     u.renderFileURL = function (_converse, url) {

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است