Преглед на файлове

Create a more minimalist retraction message

JC Brand преди 5 месеца
родител
ревизия
147be37037

+ 8 - 0
src/headless/plugins/chat/message.js

@@ -140,6 +140,13 @@ class Message extends ModelWithContact(ColorAwareModel(Model)) {
         return text.startsWith('/me ');
     }
 
+    /**
+     * @returns {boolean}
+     */
+    isRetracted () {
+        return this.get('retracted') || this.get('moderated') === 'retracted';
+    }
+
     /**
      * Returns a boolean indicating whether this message is considered a followup
      * message from the previous one. Followup messages are shown grouped together
@@ -161,6 +168,7 @@ class Message extends ModelWithContact(ColorAwareModel(Model)) {
         }
         const date = dayjs(this.get('time'));
         return this.get('from') === prev_model.get('from') &&
+            !this.isRetracted() && !prev_model.isRetracted() &&
             !this.isMeCommand() && !prev_model.isMeCommand() &&
             !!this.get('is_encrypted') === !!prev_model.get('is_encrypted') &&
             this.get('type') === prev_model.get('type') && this.get('type') !== 'info' &&

+ 4 - 0
src/headless/types/plugins/chat/message.d.ts

@@ -188,6 +188,10 @@ declare class Message extends Message_base {
      * @returns {boolean}
      */
     isMeCommand(): boolean;
+    /**
+     * @returns {boolean}
+     */
+    isRetracted(): boolean;
     /**
      * Returns a boolean indicating whether this message is considered a followup
      * message from the previous one. Followup messages are shown grouped together

+ 2 - 2
src/plugins/muc-views/templates/mep-message.js

@@ -14,7 +14,7 @@ export default (el) => {
             <div class="chat-msg__content">
                 <div class="chat-msg__body chat-msg__body--${el.model.get('type')} ${el.model.get('is_delayed') ? 'chat-msg__body--delayed' : '' }">
                     <div class="chat-info__message">
-                        ${ el.isRetracted() ? el.renderRetraction() : html`
+                        ${ el.model.isRetracted() ? el.renderRetraction() : html`
                             <converse-texture
                                 .mentions=${el.model.get('references')}
                                 render_styling
@@ -25,7 +25,7 @@ export default (el) => {
                         `}
                     </div>
                     <converse-message-actions
-                        ?is_retracted=${el.isRetracted()}
+                        ?is_retracted=${el.model.isRetracted()}
                         .model=${el.model}></converse-message-actions>
                 </div>
             </div>

+ 2 - 2
src/plugins/muc-views/tests/mep.js

@@ -244,7 +244,7 @@ describe("A XEP-0316 MEP notification", function () {
         expect(view.model.messages.at(0).get('moderation_reason')).toBeUndefined;
         expect(view.model.messages.at(0).get('is_ephemeral')).toBe(false);
         expect(view.model.messages.at(0).get('editable')).toBe(false);
-        const msg_el = view.querySelector('.chat-msg--retracted .chat-info__message div');
-        expect(msg_el.textContent).toBe(`${nick} has removed this message`);
+        const msg_el = view.querySelector('.chat-msg--retracted .chat-info__message .retraction');
+        expect(msg_el.firstElementChild.textContent).toBe(`${nick} has removed a message`);
     }));
 });

+ 19 - 19
src/plugins/muc-views/tests/retractions.js

@@ -242,8 +242,8 @@ describe("Message Retractions", function () {
             expect(view.model.messages.at(0).get('retracted')).toBeTruthy();
             expect(view.model.messages.at(0).get('editable')).toBe(false);
             expect(view.querySelectorAll('.chat-msg--retracted').length).toBe(1);
-            const msg_el = view.querySelector('.chat-msg--retracted .chat-msg__message');
-            expect(msg_el.textContent.trim()).toBe('eve has removed this message');
+            const msg_el = view.querySelector('.chat-msg--retracted .chat-msg__message .retraction');
+            expect(msg_el?.textContent.trim()).toBe('eve has removed a message');
             expect(msg_el.querySelector('.chat-msg--retracted q')).toBe(null);
         }));
 
@@ -314,10 +314,10 @@ describe("Message Retractions", function () {
             expect(view.model.messages.at(0).get('editable')).toBe(false);
             expect(view.querySelectorAll('.chat-msg--retracted').length).toBe(1);
 
-            const msg_el = view.querySelector('.chat-msg--retracted .chat-msg__message');
-            expect(msg_el.firstElementChild.textContent.trim()).toBe('romeo has removed this message');
+            const ret_el = view.querySelector('.chat-msg--retracted .chat-msg__message .retraction');
+            expect(ret_el.firstElementChild.textContent.trim()).toBe('romeo has removed a message');
 
-            const qel = msg_el.querySelector('q');
+            const qel = ret_el.querySelector('q');
             expect(qel.textContent.trim()).toBe('This content is inappropriate for this forum!');
 
             // The server responds with a retraction message
@@ -421,8 +421,8 @@ describe("Message Retractions", function () {
             expect(view.model.messages.length).toBe(1);
             expect(view.model.messages.at(0).get('moderated')).toBe('retracted');
             expect(view.querySelectorAll('.chat-msg--retracted').length).toBe(1);
-            const msg_el = view.querySelector('.chat-msg--retracted .chat-msg__message div');
-            expect(msg_el.textContent).toBe('romeo has removed this message');
+            const msg_el = view.querySelector('.chat-msg--retracted .chat-msg__message .retraction');
+            expect(msg_el.firstElementChild.textContent.trim()).toBe('romeo has removed a message');
             const qel = view.querySelector('.chat-msg--retracted .chat-msg__message q');
             expect(qel.textContent).toBe('This content is inappropriate for this forum!');
 
@@ -495,8 +495,8 @@ describe("Message Retractions", function () {
             expect(view.model.messages.last().get('editable')).toBe(false);
             expect(message.get(`stanza_id ${muc_jid}`)).toBe(stanza_id);
             expect(view.querySelectorAll('.chat-msg--retracted').length).toBe(1);
-            const el = view.querySelector('.chat-msg--retracted .chat-msg__message div');
-            expect(el.textContent).toBe('You have removed this message');
+            const el = view.querySelector('.chat-msg--retracted .chat-msg__message .retraction');
+            expect(el?.textContent.trim()).toBe('You have removed a message');
         }));
 
         it("can be retracted by its author, causing an error message in response",
@@ -516,8 +516,8 @@ describe("Message Retractions", function () {
 
             expect(view.model.messages.length).toBe(1);
             await u.waitUntil(() => view.model.messages.last().get('retracted'), 1000);
-            const el = view.querySelector('.chat-msg--retracted .chat-msg__message div');
-            expect(el.textContent.trim()).toBe('You have removed this message');
+            const el = view.querySelector('.chat-msg--retracted .chat-msg__message .retraction');
+            expect(el?.textContent.trim()).toBe('You have removed a message');
 
             const message = view.model.messages.last();
             const stanza_id = message.get(`stanza_id ${view.model.get('jid')}`);
@@ -562,8 +562,8 @@ describe("Message Retractions", function () {
             expect(view.model.messages.length).toBe(1);
             expect(view.model.messages.last().get('retracted')).toBeTruthy();
             await u.waitUntil(() => view.querySelectorAll('.chat-msg--retracted').length === 1);
-            const el = view.querySelector('.chat-msg--retracted .chat-msg__message div');
-            expect(el.textContent.trim()).toBe('You have removed this message');
+            const el = view.querySelector('.chat-msg--retracted .chat-msg__message .retraction');
+            expect(el?.textContent.trim()).toBe('You have removed a message');
 
             await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
 
@@ -641,7 +641,7 @@ describe("Message Retractions", function () {
             const occupant = view.model.getOwnOccupant();
             expect(occupant.get('role')).toBe('moderator');
 
-            view.model.sendMessage({'body': 'Visit this site to get free bitcoin'});
+            view.model.sendMessage({body: 'Visit this site to get free bitcoin'});
             await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
 
             // Check that you can only edit a message before it's been
@@ -702,7 +702,7 @@ describe("Message Retractions", function () {
             expect(view.querySelectorAll('.chat-msg--retracted').length).toBe(1);
 
             const msg_el = view.querySelector('.chat-msg--retracted .chat-msg__message');
-            expect(msg_el.firstElementChild.textContent.trim()).toBe('romeo has removed this message');
+            expect(msg_el.querySelector('.retraction')?.textContent.trim()).toBe('romeo has removed a message');
             expect(msg_el.querySelector('q')).toBe(null);
 
             // The server responds with a retraction message
@@ -801,8 +801,8 @@ describe("Message Retractions", function () {
             await u.waitUntil(() => view.querySelectorAll('.chat-msg').length);
             expect(view.querySelectorAll('.chat-msg').length).toBe(1);
             expect(view.querySelectorAll('.chat-msg--retracted').length).toBe(1);
-            const el = view.querySelector('.chat-msg--retracted .chat-msg__message div');
-            expect(el.textContent.trim()).toBe('eve has removed this message');
+            const el = view.querySelector('.chat-msg--retracted .chat-msg__message .retraction');
+            expect(el?.textContent.trim()).toBe('eve has removed a message');
         }));
 
         it("may be returned as a tombstone moderated groupchat message",
@@ -888,8 +888,8 @@ describe("Message Retractions", function () {
             expect(view.querySelectorAll('.chat-msg').length).toBe(1);
 
             expect(view.querySelectorAll('.chat-msg--retracted').length).toBe(1);
-            const el = view.querySelector('.chat-msg--retracted .chat-msg__message div');
-            expect(el.textContent.trim()).toBe('A moderator has removed this message');
+            const el = view.querySelector('.chat-msg--retracted .chat-msg__message .retraction');
+            expect(el.firstElementChild.textContent.trim()).toBe('A moderator has removed a message');
             const qel = view.querySelector('.chat-msg--retracted .chat-msg__message q');
             expect(qel.textContent.trim()).toBe('This message contains inappropriate content');
         }));

+ 6 - 9
src/shared/chat/message.js

@@ -123,10 +123,6 @@ export default class Message extends CustomElement {
         this.parentElement.removeChild(this);
     }
 
-    isRetracted () {
-        return this.model.get('retracted') || this.model.get('moderated') === 'retracted';
-    }
-
     hasMentions () {
         const is_groupchat = this.model.get('type') === 'groupchat';
         return is_groupchat && this.model.get('sender') === 'them' && this.model_with_messages.isUserMentioned(this.model);
@@ -141,11 +137,12 @@ export default class Message extends CustomElement {
     }
 
     getExtraMessageClasses () {
+        const is_action = this.model.isMeCommand() || this.model.isRetracted();
         const extra_classes = [
             this.model.isFollowup() ? 'chat-msg--followup' : null,
             this.model.get('is_delayed') ? 'delayed' : null,
-            this.model.isMeCommand() ? 'chat-msg--action' : null,
-            this.isRetracted() ? 'chat-msg--retracted' : null,
+            is_action ? 'chat-msg--action' : null,
+            this.model.isRetracted() ? 'chat-msg--retracted' : null,
             this.model.get('type'),
             this.shouldShowAvatar() ? 'chat-msg--with-avatar' : null,
         ].map(c => c);
@@ -171,11 +168,11 @@ export default class Message extends CustomElement {
                     occupants.findOccupant({'nick': Strophe.getResourceFromJid(retracted_by_mod)});
             }
             const modname = this.model.mod ? this.model.mod.getDisplayName() : __('A moderator');
-            return __('%1$s has removed this message', modname);
+            return __('%1$s has removed a message', modname);
         } else {
             return this.model.get('sender') === 'me' ?
-                __('You have removed this message') :
-                __('%1$s has removed this message', this.model.getDisplayName());
+                __('You have removed a message') :
+                __('%1$s has removed a message', this.model.getDisplayName());
         }
     }
 

+ 16 - 15
src/shared/chat/templates/message.js

@@ -19,7 +19,7 @@ export default (el) => {
     const is_first_unread = el.model_with_messages.get('first_unread_id') === el.model.get('id');
     const is_followup = el.model.isFollowup();
     const is_me_message = el.model.isMeCommand();
-    const is_retracted = el.isRetracted();
+    const is_retracted = el.model.isRetracted();
     const msgid = el.model.get('msgid');
     const sender = el.model.get('sender');
     const time = el.model.get('time');
@@ -30,7 +30,11 @@ export default (el) => {
     const pretty_time = dayjs(edited || time).format(format);
     const hats = getHats(el.model);
     const username = el.model.getDisplayName();
-    const should_show_avatar = el.shouldShowAvatar();
+
+    const is_action = is_me_message || is_retracted;
+    debugger;
+    const should_show_header = !is_action && !is_followup;
+    const should_show_avatar = el.shouldShowAvatar() && should_show_header;
 
     // The model to use for the avatar.
     // Note: it can happen that the contact has not the vcard attribute but the message has.
@@ -52,7 +56,7 @@ export default (el) => {
             <!-- Anchor to allow us to scroll the message into view -->
             <a id="${msgid}"></a>
 
-            ${should_show_avatar && !is_followup
+            ${should_show_avatar
                 ? html`<a class="show-msg-author-modal" @click=${el.showUserModal}>
                       <converse-avatar
                           .model=${avatar_model}
@@ -65,12 +69,8 @@ export default (el) => {
                   </a>`
                 : ''}
 
-            <div
-                class="chat-msg__content chat-msg__content--${sender} ${is_me_message
-                    ? 'chat-msg__content--action'
-                    : ''}"
-            >
-                ${!is_me_message && !is_followup
+            <div class="chat-msg__content chat-msg__content--${sender} ${is_action ? 'chat-msg__content--action' : ''}">
+                ${should_show_header
                     ? html` <span class="chat-msg__heading">
                           <span class="chat-msg__author">
                               <a class="show-msg-author-modal" @click=${el.showUserModal} style="${author_style}"
@@ -91,12 +91,13 @@ export default (el) => {
                         : ''} ${el.model.get('is_delayed') ? 'chat-msg__body--delayed' : ''}"
                 >
                     <div class="chat-msg__message">
-                        ${is_me_message
-                            ? html` <time timestamp="${edited || time}" class="chat-msg__time">${pretty_time}</time
-                                  >&nbsp;
-                                  <span class="chat-msg__author" style="${author_style}"
-                                      >${is_me_message ? '**' : ''}${username}</span
-                                  >&nbsp;`
+                        ${is_action
+                            ? html`<time timestamp="${edited || time}" class="chat-msg__time">${pretty_time}</time>
+                                  ${is_me_message
+                                      ? html`<span class="chat-msg__author" style="${author_style}"
+                                                >${is_me_message ? '**' : ''}${username}</span
+                                            >&nbsp;`
+                                      : ''}`
                             : ''}
                         ${is_retracted ? el.renderRetraction() : el.renderMessageText()}
                     </div>

+ 13 - 6
src/shared/chat/templates/retraction.js

@@ -2,10 +2,17 @@ import { html } from 'lit';
 
 import '../styles/retraction.scss';
 
+/**
+ * @param {import('shared/chat/message').default} el
+ */
 export default (el) => {
-    const retraction_text = el.isRetracted() ? el.getRetractionText() : null;
-    return html`
-        <div class="retraction">${retraction_text}</div>
-        ${ el.model.get('moderation_reason') ?
-                html`<q class="chat-msg--retracted__reason">${el.model.get('moderation_reason')}</q>` : '' }`;
-}
+    const retraction_text = el.model.isRetracted() ? el.getRetractionText() : null;
+    return html`<span class="retraction">
+        <span>${retraction_text}</span>
+        ${el.model.get('moderation_reason')
+            ? html`<q class="chat-msg--retracted__reason"
+                  >${el.model.get('moderation_reason')}</q
+              >`
+            : ''}
+    </span>`;
+};

+ 6 - 0
src/shared/styles/messages.scss

@@ -280,8 +280,14 @@
                     font-size: var(--message-font-size);
                 }
                 .chat-msg__time {
+                    margin-inline-end: 0.5em;
                     margin-inline-start: 0;
                 }
+
+                .retraction {
+                    display: flex;
+                    flex-direction: column;
+                }
             }
 
             .chat-msg__content {

+ 0 - 1
src/types/shared/chat/message.d.ts

@@ -21,7 +21,6 @@ export default class Message extends CustomElement {
     onUnfurlAnimationEnd(): void;
     onRetryClicked(): Promise<void>;
     show_spinner: boolean;
-    isRetracted(): any;
     hasMentions(): any;
     getOccupantAffiliation(): any;
     getOccupantRole(): any;

+ 1 - 1
src/types/shared/chat/templates/retraction.d.ts

@@ -1,3 +1,3 @@
-declare function _default(el: any): import("lit").TemplateResult<1>;
+declare function _default(el: import("shared/chat/message").default): import("lit").TemplateResult<1>;
 export default _default;
 //# sourceMappingURL=retraction.d.ts.map