Переглянути джерело

Re-add support for a new messages indicator

Fixes #2040
JC Brand 5 роки тому
батько
коміт
ac36adddfe

+ 2 - 2
spec/chatbox.js

@@ -1353,7 +1353,7 @@ describe("Chatboxes", function () {
             done();
         }));
 
-        it("is incremeted when message is received, chatbox is scrolled down and the window is not focused",
+        it("is incremented when message is received, chatbox is scrolled down and the window is not focused",
             mock.initConverse(['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
@@ -1375,7 +1375,7 @@ describe("Chatboxes", function () {
             done();
         }));
 
-        it("is incremeted when message is received, chatbox is scrolled up and the window is not focused",
+        it("is incremented when message is received, chatbox is scrolled up and the window is not focused",
             mock.initConverse(
                 ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {

+ 24 - 0
spec/messages.js

@@ -6,6 +6,30 @@ const u = converse.env.utils;
 
 describe("A Chat Message", function () {
 
+    it("will be demarcated if it's the first newly received message",
+        mock.initConverse(['rosterGroupsFetched', 'chatBoxesFetched'], {},
+            async function (done, _converse) {
+
+        await mock.waitForRoster(_converse, 'current', 1);
+        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);
+        await _converse.handleMessageStanza(mock.createChatMessage(_converse, contact_jid, 'This message will be read'));
+
+        _converse.windowState = 'hidden';
+        await _converse.handleMessageStanza(mock.createChatMessage(_converse, contact_jid, 'This message will be new'));
+
+        await u.waitUntil(() => view.model.messages.length);
+        expect(view.model.get('num_unread')).toBe(1);
+        expect(view.model.get('first_unread_id')).toBe(view.model.messages.last().get('id'));
+
+        await u.waitUntil(() => view.el.querySelectorAll('converse-chat-message').length === 2);
+        const last_msg_el = view.el.querySelector('converse-chat-message:last-child');
+        expect(last_msg_el.firstElementChild?.textContent).toBe('New messages');
+        done();
+    }));
+
+
     it("is rejected if it's an unencapsulated forwarded message",
         mock.initConverse(
             ['rosterGroupsFetched', 'chatBoxesFetched'], {},

+ 1 - 1
spec/mock.js

@@ -4,7 +4,7 @@ let _converse, initConverse;
 
 const converseLoaded = new Promise(resolve => window.addEventListener('converse-loaded', resolve));
 
-jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 7000;
 
 mock.initConverse = function (promise_names=[], settings=null, func) {
     if (typeof promise_names === "function") {

+ 2 - 1
spec/presence.js

@@ -74,7 +74,8 @@ describe("A sent presence stanza", function () {
         spyOn(_converse.connection, 'send').and.callThrough();
 
         const cbview = _converse.chatboxviews.get('controlbox');
-        cbview.el.querySelector('.change-status').click()
+        const change_status_el = await u.waitUntil(() => cbview.el.querySelector('.change-status'));
+        change_status_el.click()
         const modal = _converse.xmppstatusview.status_modal;
         await u.waitUntil(() => u.isVisible(modal.el), 1000);
         const msg = 'My custom status';

+ 17 - 16
src/components/message-history.js

@@ -20,6 +20,7 @@ const tpl_message = (o) => html`
         ?has_mentions=${o.has_mentions}
         ?is_delayed=${o.is_delayed}
         ?is_encrypted=${o.is_encrypted}
+        ?is_first_unread=${o.is_first_unread}
         ?is_me_message=${o.is_me_message}
         ?is_only_emojis=${o.is_only_emojis}
         ?is_retracted=${o.is_retracted}
@@ -67,6 +68,18 @@ function getDayIndicator (model) {
     }
 }
 
+function getHats (model) {
+    if (model.get('type') === 'groupchat') {
+        if (api.settings.get('muc_hats_from_vcard')) {
+            const role = model.vcard ? model.vcard.get('role') : null;
+            return role ? role.split(',') : [];
+        } else {
+            return model.occupant?.get('hats') || [];
+        }
+    }
+    return [];
+}
+
 
 class MessageHistory extends CustomElement {
 
@@ -91,30 +104,18 @@ class MessageHistory extends CustomElement {
         }
         const day = getDayIndicator(model);
         const templates = day ? [day] : [];
-        const is_retracted = model.get('retracted') || model.get('moderated') === 'retracted';
         const is_groupchat = model.get('type') === 'groupchat';
-
-        let hats = [];
-        if (is_groupchat) {
-            if (api.settings.get('muc_hats_from_vcard')) {
-                const role = model.vcard ? model.vcard.get('role') : null;
-                hats = role ? role.split(',') : [];
-            } else {
-                hats = model.occupant?.get('hats') || [];
-            }
-        }
-
         const chatbox = this.chatview.model;
-        const has_mentions = is_groupchat && model.get('sender') === 'them' && chatbox.isUserMentioned(model);
         const message = tpl_message(
             Object.assign(model.toJSON(), {
                 'chatview': this.chatview,
+                'has_mentions': is_groupchat && model.get('sender') === 'them' && chatbox.isUserMentioned(model),
+                'hats': getHats(model),
+                'is_first_unread': chatbox.get('first_unread_id') === model.get('id'),
                 'is_me_message': model.isMeCommand(),
+                'is_retracted': model.get('retracted') || model.get('moderated') === 'retracted',
                 'occupant': model.occupant,
                 'username': model.getDisplayName(),
-                has_mentions,
-                hats,
-                is_retracted,
                 model,
             }));
         return [...templates, message];

+ 5 - 4
src/components/message.js

@@ -17,7 +17,8 @@ const i18n_edit_message = __('Edit this message');
 const i18n_edited = __('This message has been edited');
 const i18n_show = __('Show more');
 const i18n_show_less = __('Show less');
-const i18n_uploading = __('Uploading file:')
+const i18n_uploading = __('Uploading file:');
+const i18n_new_messages = __('New messages');
 
 
 class Message extends CustomElement {
@@ -30,19 +31,19 @@ class Message extends CustomElement {
             editable: { type: Boolean },
             error: { type: String },
             error_text: { type: String },
-            first_unread: { type: Boolean },
             from: { type: String },
             has_mentions: { type: Boolean },
             hats: { type: Array },
+            edited: { type: String },
             is_delayed: { type: Boolean },
             is_encrypted: { type: Boolean },
+            is_first_unread: { type: Boolean },
             is_me_message: { type: Boolean },
             is_only_emojis: { type: Boolean },
             is_retracted: { type: Boolean },
             is_spoiler: { type: Boolean },
             is_spoiler_visible: { type: Boolean },
             message_type: { type: String },
-            edited: { type: String },
             model: { type: Object },
             moderated_by: { type: String },
             moderation_reason: { type: String },
@@ -125,6 +126,7 @@ class Message extends CustomElement {
     renderChatMessage () {
         const is_groupchat_message = (this.message_type === 'groupchat');
         return html`
+            ${ this.is_first_unread ? html`<div class="message date-separator"><hr class="separator"><span class="separator-text">${ i18n_new_messages }</span></div>` : '' }
             <div class="message chat-msg ${this.message_type} ${this.getExtraMessageClasses()}
                     ${ this.is_me_message ? 'chat-msg--action' : '' }
                     ${this.isFollowup() ? 'chat-msg--followup' : ''}"
@@ -132,7 +134,6 @@ class Message extends CustomElement {
 
                 ${ renderAvatar(this) }
                 <div class="chat-msg__content chat-msg__content--${this.sender} ${this.is_me_message ? 'chat-msg__content--action' : ''}">
-                    ${this.first_unread ? html`<div class="message date-separator"><hr class="separator"><span class="separator-text">{{{this.__('unread messages')}}}</span></div>` : '' }
                     <span class="chat-msg__heading">
                         ${ (this.is_me_message) ? html`
                             <time timestamp="${this.time}" class="chat-msg__time">${this.pretty_time}</time>

+ 7 - 19
src/headless/converse-chat.js

@@ -1145,26 +1145,14 @@ converse.plugins.add('converse-chat', {
                     return;
                 }
                 if (utils.isNewMessage(message) && this.isHidden()) {
-                    this.setFirstUnreadMsgId(message);
-                    this.save({'num_unread': this.get('num_unread') + 1});
-                    _converse.incrementMsgCounter();
-                }
-            },
-
-            /**
-             * Sets the msgid of the first unread realtime message in a ChatBox.
-             * @param {_converse.Message} message
-             */
-            setFirstUnreadMsgId (message) {
-                if (this.get('num_unread') == 0) {
-                    const first_unread_id = this.get('first_unread_id');
-
-                    if (first_unread_id) {
-                      const msg = this.messages.get(first_unread_id);
-                      if (msg) msg.save("first_unread", false);
+                    const settings = {
+                        'num_unread': this.get('num_unread') + 1
+                    };
+                    if (this.get('num_unread') === 0) {
+                        settings['first_unread_id'] = message.get('id');
                     }
-                    message.save("first_unread", true);
-                    this.save({'first_unread_id': message.get('id')});
+                    this.save(settings);
+                    _converse.incrementMsgCounter();
                 }
             },
 

+ 6 - 2
src/headless/converse-muc.js

@@ -2408,8 +2408,12 @@ converse.plugins.add('converse-muc', {
                 const body = message.get('message');
                 if (!body) { return; }
                 if (u.isNewMessage(message) && this.isHidden()) {
-                    this.setFirstUnreadMsgId(message);
-                    const settings = {'num_unread_general': this.get('num_unread_general') + 1};
+                    const settings = {
+                        'num_unread_general': this.get('num_unread_general') + 1
+                    };
+                    if (this.get('num_unread') === 0) {
+                        settings['first_unread_id'] = message.get('id');
+                    }
                     if (this.isUserMentioned(message)) {
                         settings.num_unread = this.get('num_unread') + 1;
                         _converse.incrementMsgCounter();