Restrict editing of MUC messages...
to ones with the same XEP-0421 occupant ID
This commit is contained in:
parent
342c75775b
commit
7028286855
|
@ -10,6 +10,7 @@
|
||||||
- Move the `converse-oauth` plugin to the [community-plugins](https://github.com/conversejs/community-plugins)
|
- Move the `converse-oauth` plugin to the [community-plugins](https://github.com/conversejs/community-plugins)
|
||||||
- Don't apply message corrections when the MUC occupant-id doesn't match.
|
- Don't apply message corrections when the MUC occupant-id doesn't match.
|
||||||
- Update `nick` attribute on ChatRoom when user nickname changes
|
- Update `nick` attribute on ChatRoom when user nickname changes
|
||||||
|
- Restrict editing of MUC messages to ones with the same XEP-0421 occupant ID
|
||||||
- #2936: Fix documentation about enable_smacks option, which is true by default.
|
- #2936: Fix documentation about enable_smacks option, which is true by default.
|
||||||
|
|
||||||
## 9.1.1 (2022-05-05)
|
## 9.1.1 (2022-05-05)
|
||||||
|
|
|
@ -234,7 +234,7 @@ const ChatBox = ModelWithContact.extend({
|
||||||
this.notifications.set('chat_state', attrs.chat_state);
|
this.notifications.set('chat_state', attrs.chat_state);
|
||||||
}
|
}
|
||||||
if (u.shouldCreateMessage(attrs)) {
|
if (u.shouldCreateMessage(attrs)) {
|
||||||
const msg = handleCorrection(this, attrs) || await this.createMessage(attrs);
|
const msg = await handleCorrection(this, attrs) || await this.createMessage(attrs);
|
||||||
this.notifications.set({'chat_state': null});
|
this.notifications.set({'chat_state': null});
|
||||||
this.handleUnreadMessage(msg);
|
this.handleUnreadMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2280,7 +2280,7 @@ const ChatRoomMixin = {
|
||||||
this.updateNotifications(attrs.nick, attrs.chat_state);
|
this.updateNotifications(attrs.nick, attrs.chat_state);
|
||||||
}
|
}
|
||||||
if (u.shouldCreateGroupchatMessage(attrs)) {
|
if (u.shouldCreateGroupchatMessage(attrs)) {
|
||||||
const msg = handleCorrection(this, attrs) || (await this.createMessage(attrs));
|
const msg = await handleCorrection(this, attrs) || (await this.createMessage(attrs));
|
||||||
this.removeNotification(attrs.nick, ['composing', 'paused']);
|
this.removeNotification(attrs.nick, ['composing', 'paused']);
|
||||||
this.handleUnreadMessage(msg);
|
this.handleUnreadMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,12 +245,15 @@ export async function parseMUCMessage (stanza, chatbox) {
|
||||||
const from_real_jid = attrs.is_archived && getJIDFromMUCUserData(stanza, attrs) ||
|
const from_real_jid = attrs.is_archived && getJIDFromMUCUserData(stanza, attrs) ||
|
||||||
chatbox.occupants.findOccupant(attrs)?.get('jid');
|
chatbox.occupants.findOccupant(attrs)?.get('jid');
|
||||||
|
|
||||||
|
const own_occupant_id = chatbox.get('occupant_id');
|
||||||
|
const is_me = attrs.occupant_id && own_occupant_id ? own_occupant_id === attrs.occupant_id : attrs.nick === chatbox.get('nick');
|
||||||
|
|
||||||
attrs = Object.assign( {
|
attrs = Object.assign( {
|
||||||
from_real_jid,
|
from_real_jid,
|
||||||
'is_only_emojis': attrs.body ? u.isOnlyEmojis(attrs.body) : false,
|
'is_only_emojis': attrs.body ? u.isOnlyEmojis(attrs.body) : false,
|
||||||
'is_valid_receipt_request': isValidReceiptRequest(stanza, attrs),
|
'is_valid_receipt_request': isValidReceiptRequest(stanza, attrs),
|
||||||
'message': attrs.body || attrs.error, // TODO: Remove and use body and error attributes instead
|
'message': attrs.body || attrs.error, // TODO: Should only be used for error and info messages
|
||||||
'sender': attrs.nick === chatbox.get('nick') ? 'me' : 'them',
|
'sender': is_me ? 'me' : 'them',
|
||||||
}, attrs);
|
}, attrs);
|
||||||
|
|
||||||
if (attrs.is_archived && original_stanza.getAttribute('from') !== attrs.from_muc) {
|
if (attrs.is_archived && original_stanza.getAttribute('from') !== attrs.from_muc) {
|
||||||
|
|
|
@ -72,7 +72,7 @@ export function getMediaURLs (arr, text, offset=0) {
|
||||||
* @returns { _converse.Message|undefined } Returns the corrected
|
* @returns { _converse.Message|undefined } Returns the corrected
|
||||||
* message or `undefined` if not applicable.
|
* message or `undefined` if not applicable.
|
||||||
*/
|
*/
|
||||||
export function handleCorrection (model, attrs) {
|
export async function handleCorrection (model, attrs) {
|
||||||
if (!attrs.replace_id || !attrs.from) {
|
if (!attrs.replace_id || !attrs.from) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,8 @@ export function handleCorrection (model, attrs) {
|
||||||
|
|
||||||
const message = model.messages.models.find(query);
|
const message = model.messages.models.find(query);
|
||||||
if (!message) {
|
if (!message) {
|
||||||
return;
|
attrs['older_versions'] = [];
|
||||||
|
return await model.createMessage(attrs); // eslint-disable-line no-return-await
|
||||||
}
|
}
|
||||||
|
|
||||||
const older_versions = message.get('older_versions') || {};
|
const older_versions = message.get('older_versions') || {};
|
||||||
|
|
|
@ -296,10 +296,11 @@ describe('A Groupchat Message XEP-0308 correction ', function () {
|
||||||
from="lounge@montague.lit/newguy"
|
from="lounge@montague.lit/newguy"
|
||||||
to="_converse.connection.jid"
|
to="_converse.connection.jid"
|
||||||
type="groupchat"
|
type="groupchat"
|
||||||
id="${msg_id}">
|
id="${u.getUniqueId()}">
|
||||||
|
|
||||||
<body>But soft, what light through yonder chimney breaks?</body>
|
<body>But soft, what light through yonder chimney breaks?</body>
|
||||||
<occupant-id xmlns="urn:xmpp:occupant-id:0" id="2"></occupant-id>
|
<occupant-id xmlns="urn:xmpp:occupant-id:0" id="2"></occupant-id>
|
||||||
|
<replace id="${msg_id}" xmlns="urn:xmpp:message-correct:0"></replace>
|
||||||
</message>`
|
</message>`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -309,7 +310,7 @@ describe('A Groupchat Message XEP-0308 correction ', function () {
|
||||||
expect(model.messages.at(0).get('edited')).toBeFalsy();
|
expect(model.messages.at(0).get('edited')).toBeFalsy();
|
||||||
|
|
||||||
expect(model.messages.at(1).get('body')).toBe('But soft, what light through yonder chimney breaks?');
|
expect(model.messages.at(1).get('body')).toBe('But soft, what light through yonder chimney breaks?');
|
||||||
expect(model.messages.at(1).get('edited')).toBeFalsy();
|
expect(model.messages.at(1).get('edited')).toBeTruthy();
|
||||||
|
|
||||||
await model.handleMessageStanza(
|
await model.handleMessageStanza(
|
||||||
stx`
|
stx`
|
||||||
|
@ -317,9 +318,10 @@ describe('A Groupchat Message XEP-0308 correction ', function () {
|
||||||
from="lounge@montague.lit/newguy"
|
from="lounge@montague.lit/newguy"
|
||||||
to="_converse.connection.jid"
|
to="_converse.connection.jid"
|
||||||
type="groupchat"
|
type="groupchat"
|
||||||
id="${msg_id}">
|
id="${u.getUniqueId()}">
|
||||||
|
|
||||||
<body>But soft, what light through yonder hatch breaks?</body>
|
<body>But soft, what light through yonder hatch breaks?</body>
|
||||||
|
<replace id="${msg_id}" xmlns="urn:xmpp:message-correct:0"></replace>
|
||||||
</message>`
|
</message>`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -329,13 +331,91 @@ describe('A Groupchat Message XEP-0308 correction ', function () {
|
||||||
expect(model.messages.at(0).get('edited')).toBeFalsy();
|
expect(model.messages.at(0).get('edited')).toBeFalsy();
|
||||||
|
|
||||||
expect(model.messages.at(1).get('body')).toBe('But soft, what light through yonder chimney breaks?');
|
expect(model.messages.at(1).get('body')).toBe('But soft, what light through yonder chimney breaks?');
|
||||||
expect(model.messages.at(1).get('edited')).toBeFalsy();
|
expect(model.messages.at(1).get('edited')).toBeTruthy();
|
||||||
|
|
||||||
expect(model.messages.at(2).get('body')).toBe('But soft, what light through yonder hatch breaks?');
|
expect(model.messages.at(2).get('body')).toBe('But soft, what light through yonder hatch breaks?');
|
||||||
expect(model.messages.at(2).get('edited')).toBeFalsy();
|
expect(model.messages.at(2).get('edited')).toBeTruthy();
|
||||||
|
|
||||||
const message_els = Array.from(view.querySelectorAll('.chat-msg'));
|
const message_els = Array.from(view.querySelectorAll('.chat-msg'));
|
||||||
expect(message_els.reduce((acc, m) => acc && u.hasClass('chat-msg--followup', m), true)).toBe(false);
|
expect(message_els.reduce((acc, m) => acc && u.hasClass('chat-msg--followup', m), true)).toBe(false);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it(
|
||||||
|
"cannot be edited if it's from a different occupant id",
|
||||||
|
mock.initConverse([], {}, async function (_converse) {
|
||||||
|
const nick = 'romeo';
|
||||||
|
const muc_jid = 'lounge@montague.lit';
|
||||||
|
const features = [...mock.default_muc_features, Strophe.NS.OCCUPANTID];
|
||||||
|
const model = await mock.openAndEnterChatRoom(_converse, muc_jid, nick, features);
|
||||||
|
|
||||||
|
expect(model.get('occupant_id')).toBe(model.occupants.at(0).get('occupant_id'));
|
||||||
|
|
||||||
|
const msg_id = u.getUniqueId();
|
||||||
|
await model.handleMessageStanza(
|
||||||
|
stx`
|
||||||
|
<message
|
||||||
|
from="lounge@montague.lit/${nick}"
|
||||||
|
to="_converse.connection.jid"
|
||||||
|
type="groupchat"
|
||||||
|
id="${msg_id}">
|
||||||
|
|
||||||
|
<body>But soft, what light through yonder airlock breaks?</body>
|
||||||
|
<occupant-id xmlns="urn:xmpp:occupant-id:0" id="${model.get('occupant_id')}"></occupant-id>
|
||||||
|
</message>`
|
||||||
|
);
|
||||||
|
|
||||||
|
const view = _converse.chatboxviews.get(muc_jid);
|
||||||
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length);
|
||||||
|
expect(model.messages.at(0).get('body')).toBe('But soft, what light through yonder airlock breaks?');
|
||||||
|
|
||||||
|
await model.handleMessageStanza(
|
||||||
|
stx`
|
||||||
|
<message
|
||||||
|
from="lounge@montague.lit/${nick}"
|
||||||
|
to="_converse.connection.jid"
|
||||||
|
type="groupchat"
|
||||||
|
id="${u.getUniqueId()}">
|
||||||
|
|
||||||
|
<body>But soft, what light through yonder chimney breaks?</body>
|
||||||
|
<occupant-id xmlns="urn:xmpp:occupant-id:0" id="${model.get('occupant_id')}"></occupant-id>
|
||||||
|
<replace id="${msg_id}" xmlns="urn:xmpp:message-correct:0"></replace>
|
||||||
|
</message>`
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(model.messages.at(0).get('body')).toBe('But soft, what light through yonder chimney breaks?');
|
||||||
|
expect(model.messages.at(0).get('edited')).toBeTruthy();
|
||||||
|
|
||||||
|
await model.handleMessageStanza(
|
||||||
|
stx`
|
||||||
|
<message
|
||||||
|
from="lounge@montague.lit/${nick}"
|
||||||
|
to="_converse.connection.jid"
|
||||||
|
type="groupchat"
|
||||||
|
id="${u.getUniqueId()}">
|
||||||
|
|
||||||
|
<body>But soft, what light through yonder hatch breaks?</body>
|
||||||
|
<occupant-id xmlns="urn:xmpp:occupant-id:0" id="${u.getUniqueId()}"></occupant-id>
|
||||||
|
<replace id="${msg_id}" xmlns="urn:xmpp:message-correct:0"></replace>
|
||||||
|
</message>`
|
||||||
|
);
|
||||||
|
|
||||||
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 2);
|
||||||
|
expect(model.messages.length).toBe(2);
|
||||||
|
expect(model.messages.at(0).get('body')).toBe('But soft, what light through yonder chimney breaks?');
|
||||||
|
expect(model.messages.at(0).get('edited')).toBeTruthy();
|
||||||
|
expect(model.messages.at(0).get('editable')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(model.messages.at(1).get('body')).toBe('But soft, what light through yonder hatch breaks?');
|
||||||
|
expect(model.messages.at(1).get('edited')).toBeTruthy();
|
||||||
|
expect(model.messages.at(1).get('editable')).toBeFalsy();
|
||||||
|
|
||||||
|
const message_els = Array.from(view.querySelectorAll('.chat-msg'));
|
||||||
|
expect(message_els.reduce((acc, m) => acc && u.hasClass('chat-msg--followup', m), true)).toBe(false);
|
||||||
|
|
||||||
|
// We can edit our own message, but not the other
|
||||||
|
expect(message_els[0].querySelector('converse-dropdown .chat-msg__action-edit')).toBeDefined();
|
||||||
|
expect(message_els[1].querySelector('converse-dropdown .chat-msg__action-edit')).toBe(null);
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import { CustomElement } from './element.js';
|
import { CustomElement } from './element.js';
|
||||||
import { api, converse } from '@converse/headless/core';
|
import { api, converse } from '@converse/headless/core';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
import './styles/message-versions.scss';
|
||||||
|
|
||||||
const { dayjs } = converse.env;
|
const { dayjs } = converse.env;
|
||||||
|
|
||||||
|
const tpl_older_version = (k, older_versions) => html`<p class="older-msg"><time>${dayjs(k).format('MMM D, YYYY, HH:mm:ss')}</time>: ${older_versions[k]}</p>`;
|
||||||
|
|
||||||
|
|
||||||
export class MessageVersions extends CustomElement {
|
export class MessageVersions extends CustomElement {
|
||||||
|
|
||||||
|
@ -15,13 +19,15 @@ export class MessageVersions extends CustomElement {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const older_versions = this.model.get('older_versions');
|
const older_versions = this.model.get('older_versions');
|
||||||
|
const keys = Object.keys(older_versions);
|
||||||
return html`
|
return html`
|
||||||
<h4>Older versions</h4>
|
${ keys.length ?
|
||||||
${ Object.keys(older_versions).map(
|
html`<h4>${__('Older versions')}</h4> ${keys.map(k => tpl_older_version(k, older_versions))}` :
|
||||||
k => html`<p class="older-msg"><time>${dayjs(k).format('MMM D, YYYY, HH:mm:ss')}</time>: ${older_versions[k]}</p>`) }
|
html`<h4>${__('No older versions found')}</h4>`
|
||||||
|
}
|
||||||
<hr/>
|
<hr/>
|
||||||
<h4>Current version</h4>
|
<h4>${__('Current version')}</h4>
|
||||||
<p>${this.model.getMessageText()}</p>`;
|
<p><time>${dayjs(this.model.get('time')).format('MMM D, YYYY, HH:mm:ss')}</time>: ${this.model.getMessageText()}</p>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
src/shared/components/styles/message-versions.scss
Normal file
7
src/shared/components/styles/message-versions.scss
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.conversejs {
|
||||||
|
converse-message-versions {
|
||||||
|
time {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,12 +3,6 @@
|
||||||
color: var(--subdued-color);
|
color: var(--subdued-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.older-msg {
|
|
||||||
time {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
.show-msg-author-modal {
|
.show-msg-author-modal {
|
||||||
align-self: flex-start; // Don't expand height to that of largest sibling
|
align-self: flex-start; // Don't expand height to that of largest sibling
|
||||||
|
|
Loading…
Reference in New Issue
Block a user