chat: New `chatSendMessage` client-side hook

pull/5256/head
Richard Hansen 2021-10-26 02:08:14 -04:00
parent 4c2f7f9a11
commit 9fbd2e5c3d
4 changed files with 71 additions and 3 deletions

View File

@ -56,6 +56,9 @@
see the original unprocessed message text and any added metadata.
* `rendered`: Allows plugins to completely override how the message is
rendered in the UI.
* New `chatSendMessage` client-side hook that enables plugins to process the
text before sending it to the server or augment the message object with
custom metadata.
# 1.8.14

View File

@ -315,6 +315,18 @@ Context properties:
* `duration`: How long (in milliseconds) to display the gritter notification (0
to disable).
## `chatSendMessage`
Called from: `src/static/js/chat.js`
This hook runs on the client side whenever the user sends a new chat message.
Plugins can mutate the message object to change the message text or add metadata
to control how the message will be rendered by the `chatNewMessage` hook.
Context properties:
* `message`: The message object that will be sent to the Etherpad server.
## collectContentPre
Called from: src/static/js/contentcollector.js

View File

@ -100,10 +100,12 @@ exports.chat = (() => {
}
}
},
send() {
async send() {
const text = $('#chatinput').val();
if (text.replace(/\s+/, '').length === 0) return;
this._pad.collabClient.sendMessage({type: 'CHAT_MESSAGE', message: new ChatMessage(text)});
const message = new ChatMessage(text);
await hooks.aCallAll('chatSendMessage', Object.freeze({message}));
this._pad.collabClient.sendMessage({type: 'CHAT_MESSAGE', message});
$('#chatinput').val('');
},
async addMessage(msg, increment, isHistoryAdd) {

View File

@ -4,9 +4,10 @@ describe('chat hooks', function () {
let ChatMessage;
let hooks;
const hooksBackup = {};
let padId;
const loadPad = async (opts = {}) => {
await helper.aNewPad(opts);
padId = await helper.aNewPad(opts);
ChatMessage = helper.padChrome$.window.require('ep_etherpad-lite/static/js/ChatMessage');
({hooks} = helper.padChrome$.window.require('ep_etherpad-lite/static/js/pluginfw/plugin_defs'));
for (const [name, defs] of Object.entries(hooks)) {
@ -95,4 +96,54 @@ describe('chat hooks', function () {
expect(helper.chatTextParagraphs().last()[0]).to.be(rendered);
});
});
describe('chatSendMessage', function () {
it('message is a ChatMessage object', async function () {
await Promise.all([
checkHook('chatSendMessage', ({message}) => {
expect(message).to.be.a(ChatMessage);
}),
helper.sendChatMessage(`${this.test.title}{enter}`),
]);
});
it('message metadata propagates end-to-end', async function () {
const metadata = {foo: this.test.title};
await Promise.all([
checkHook('chatSendMessage', ({message}) => {
message.customMetadata = metadata;
}),
checkHook('chatNewMessage', ({message: {customMetadata}}) => {
expect(JSON.stringify(customMetadata)).to.equal(JSON.stringify(metadata));
}),
helper.sendChatMessage(`${this.test.title}{enter}`),
]);
});
it('message metadata is saved in the database', async function () {
const msg = this.test.title;
const metadata = {foo: this.test.title};
await Promise.all([
checkHook('chatSendMessage', ({message}) => {
message.customMetadata = metadata;
}),
helper.sendChatMessage(`${msg}{enter}`),
]);
let gotMessage;
const messageP = new Promise((resolve) => gotMessage = resolve);
await loadPad({
id: padId,
hookFns: {
chatNewMessage: [
(hookName, {message}) => {
if (message.text === `${msg}\n`) gotMessage(message);
},
],
},
});
const message = await messageP;
expect(message).to.be.a(ChatMessage);
expect(JSON.stringify(message.customMetadata)).to.equal(JSON.stringify(metadata));
});
});
});