Jelajahi Sumber

refactor: update MUC message retraction to use stx template and latest XEP-0424 spec

JC Brand 6 bulan lalu
induk
melakukan
886f8da81a

+ 23 - 26
src/headless/plugins/muc/muc.js

@@ -808,30 +808,27 @@ class MUC extends ModelWithMessages(ColorAwareModel(ChatBoxBase)) {
      */
     async retractOwnMessage(message) {
         const __ = _converse.__;
-        const origin_id = message.get('origin_id');
-        if (!origin_id) {
-            throw new Error("Can't retract message without a XEP-0359 Origin ID");
-        }
         const editable = message.get('editable');
-        const stanza = $msg({
-            'id': getUniqueId(),
-            'to': this.get('jid'),
-            'type': 'groupchat',
-        })
-            .c('store', { xmlns: Strophe.NS.HINTS })
-            .up()
-            .c('apply-to', {
-                'id': origin_id,
-                'xmlns': Strophe.NS.FASTEN,
-            })
-            .c('retract', { xmlns: Strophe.NS.RETRACT });
+        const retraction_id = getUniqueId();
+        const id = message.get('id');
+
+        const stanza = stx`
+            <message id="${retraction_id}"
+                     to="${this.get('jid')}"
+                     type="groupchat"
+                     xmlns="jabber:client">
+                <retract id="${id}" xmlns="${Strophe.NS.RETRACT}"/>
+                <body>/me retracted a message</body>
+                <store xmlns="${Strophe.NS.HINTS}"/>
+                <fallback xmlns="${Strophe.NS.FALLBACK}" for="${Strophe.NS.RETRACT}" />
+            </message>`;
 
         // Optimistic save
         message.set({
-            'retracted': new Date().toISOString(),
-            'retracted_id': origin_id,
-            'retraction_id': stanza.tree().getAttribute('id'),
-            'editable': false
+            retracted: new Date().toISOString(),
+            retracted_id: id,
+            retraction_id: retraction_id,
+            editable: false
         });
         const result = await this.sendTimedMessage(stanza);
 
@@ -841,11 +838,11 @@ class MUC extends ModelWithMessages(ColorAwareModel(ChatBoxBase)) {
             log.error(result);
             message.save({
                 editable,
-                'error_type': 'timeout',
-                'error': __('A timeout happened while while trying to retract your message.'),
-                'retracted': undefined,
-                'retracted_id': undefined,
-                'retraction_id': undefined
+                error_type: 'timeout',
+                error: __('A timeout happened while trying to retract your message.'),
+                retracted: undefined,
+                retracted_id: undefined,
+                retraction_id: undefined
             });
         }
     }
@@ -896,7 +893,7 @@ class MUC extends ModelWithMessages(ColorAwareModel(ChatBoxBase)) {
                 'xmlns': Strophe.NS.FASTEN,
             })
             .c('moderate', { xmlns: Strophe.NS.MODERATE })
-            .c('retract', { xmlns: Strophe.NS.RETRACT })
+            .c('retract', { xmlns: Strophe.NS.RETRACT0 })
             .up()
             .c('reason')
             .t(reason || '');

+ 3 - 3
src/headless/shared/model-with-messages.js

@@ -855,9 +855,9 @@ export default function ModelWithMessages(BaseModel) {
                 }
                 message.save(pick(attrs, RETRACTION_ATTRIBUTES));
                 return true;
-            } else if (attrs.retract_id) {
+            } else if (attrs.retracted_id) {
                 // Handle direct retract element
-                const message = this.messages.findWhere({ 'origin_id': attrs.retract_id, 'from': attrs.from });
+                const message = this.messages.findWhere({ 'origin_id': attrs.retracted_id, 'from': attrs.from });
                 if (!message) {
                     attrs['dangling_retraction'] = true;
                     await this.createMessage(attrs);
@@ -865,7 +865,7 @@ export default function ModelWithMessages(BaseModel) {
                 }
                 message.save({
                     'retracted': new Date().toISOString(),
-                    'retracted_id': attrs.retract_id,
+                    'retracted_id': attrs.retracted_id,
                     'editable': false
                 });
                 return true;

+ 2 - 1
src/headless/types/shared/actions.d.ts

@@ -30,6 +30,7 @@ export function sendChatState(jid: string, chat_state: string): void;
  * Sends a message stanza to retract a message in this chat
  * @param {string} jid
  * @param {import('../plugins/chat/message').default} message - The message which we're retracting.
+ * @param {string} retraction_id - Unique ID for the retraction message
  */
-export function sendRetractionMessage(jid: string, message: import("../plugins/chat/message").default): any;
+export function sendRetractionMessage(jid: string, message: import("../plugins/chat/message").default, retraction_id: string): any;
 //# sourceMappingURL=actions.d.ts.map

+ 9 - 2
src/headless/types/shared/parsers.d.ts

@@ -24,9 +24,16 @@ export function getEncryptionAttributes(stanza: Element): import("./types").Encr
  * @param {Element} stanza - The message stanza
  * @param {Element} original_stanza - The original stanza, that contains the
  *  message stanza, if it was contained, otherwise it's the message stanza itself.
- * @returns {Object}
+ * @returns {import('./types').RetractionAttrs | {}}
+ */
+export function getDeprecatedRetractionAttributes(stanza: Element, original_stanza: Element): import("./types").RetractionAttrs | {};
+/**
+ * @param {Element} stanza - The message stanza
+ * @param {Element} original_stanza - The original stanza, that contains the
+ *  message stanza, if it was contained, otherwise it's the message stanza itself.
+ * @returns {import('./types').RetractionAttrs | {}}
  */
-export function getRetractionAttributes(stanza: Element, original_stanza: Element): any;
+export function getRetractionAttributes(stanza: Element, original_stanza: Element): import("./types").RetractionAttrs | {};
 /**
  * @param {Element} stanza
  * @param {Element} original_stanza

+ 7 - 0
src/headless/types/shared/types.d.ts

@@ -5,6 +5,13 @@ type EncryptionPayloadAttrs = {
     prekey?: boolean;
     device_id: string;
 };
+export type RetractionAttrs = {
+    editable: boolean;
+    is_tombstone?: boolean;
+    retracted: string;
+    retracted_id?: string;
+    retraction_id?: string;
+};
 export type EncryptionAttrs = {
     encrypted?: EncryptionPayloadAttrs;
     is_encrypted: boolean;

+ 13 - 12
src/plugins/muc-views/tests/retractions.js

@@ -26,7 +26,7 @@ async function sendAndThenRetractMessage (_converse, view) {
     const submit_button = document.querySelector('#converse-modals .modal button[type="submit"]');
     submit_button.click();
     const sent_stanzas = _converse.api.connection.get().sent_stanzas;
-    return u.waitUntil(() => sent_stanzas.filter(s => s.querySelector('message apply-to[xmlns="urn:xmpp:fasten:0"]')).pop());
+    return u.waitUntil(() => sent_stanzas.filter(s => s.querySelector('message retract')).pop());
 }
 
 
@@ -632,27 +632,29 @@ describe("Message Retractions", function () {
 
             expect(retraction_stanza).toEqualStanza(stx`
                 <message id="${retraction_stanza.getAttribute('id')}" to="${muc_jid}" type="groupchat" xmlns="jabber:client">
-                    <retract id="${msg_obj.get('origin_id')}" xmlns="urn:xmpp:message-retract:0"/>
+                    <retract id="${msg_obj.get('origin_id')}" xmlns="urn:xmpp:message-retract:1"/>
+                    <body>/me retracted a message</body>
                     <store xmlns="urn:xmpp:hints"/>
-                    <apply-to  xmlns="urn:xmpp:fasten:0">
-                    </apply-to>
+                    <fallback xmlns="urn:xmpp:fallback:0" for="urn:xmpp:message-retract:1"/>
                 </message>`);
 
             const message = view.model.messages.last();
             expect(message.get('is_ephemeral')).toBe(false);
             expect(message.get('editable')).toBeFalsy();
 
-            const stanza_id = message.get(`stanza_id ${muc_jid}`);
             // The server responds with a retraction message
+            const stanza_id = '5f3dbc5e-e1d3-4077-a492-693f3769c7ad';
             const reflection = stx`
                 <message type="groupchat"
                         id="${retraction_stanza.getAttribute('id')}"
                         from="${muc_jid}"
                         to="${muc_jid}/romeo"
                         xmlns="jabber:client">
-                    <apply-to id="${stanza_id}" xmlns="urn:xmpp:fasten:0">
-                        <retract xmlns='urn:xmpp:message-retract:0' />
-                    </apply-to>
+                    <stanza-id xmlns="urn:xmpp:sid:0" id="${stanza_id}" by="room@muc.example.com"/>
+                    <retract id="${msg_obj.get('origin_id')}" xmlns="urn:xmpp:message-retract:1"/>
+                    <body>/me retracted a message</body>
+                    <store xmlns="urn:xmpp:hints"/>
+                    <fallback xmlns="urn:xmpp:fallback:0" for="urn:xmpp:message-retract:1"/>
                 </message>`;
 
             spyOn(view.model, 'handleRetraction').and.callThrough();
@@ -663,6 +665,7 @@ describe("Message Retractions", function () {
             expect(view.model.messages.last().get('retracted')).toBeTruthy();
             expect(view.model.messages.last().get('is_ephemeral')).toBe(false);
             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');
@@ -700,9 +703,7 @@ describe("Message Retractions", function () {
                     <error by='${muc_jid}' type='auth'>
                         <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
                     </error>
-                    <apply-to id="${stanza_id}" xmlns="urn:xmpp:fasten:0">
-                        <retract xmlns='urn:xmpp:message-retract:0' />
-                    </apply-to>
+                    <retract id="${stanza_id}" xmlns="urn:xmpp:message-retract:1"/>
                 </message>`;
 
             _converse.api.connection.get()._dataRecv(mock.createRequest(error));
@@ -747,7 +748,7 @@ describe("Message Retractions", function () {
             const error_messages = view.querySelectorAll('.chat-msg__error');
             expect(error_messages.length).toBe(1);
             expect(error_messages[0].textContent.trim()).toBe(
-                'Message delivery failed.\nA timeout happened while while trying to retract your message.');
+                'Message delivery failed.\nA timeout happened while trying to retract your message.');
         }));