[FIX] mail: filter commands used in composer based on channel type

closes odoo/odoo#60644

Task-id: 2343850
X-original-commit: caaf436bdf1f72634bf8a83672c6f683e0013713
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Julien Giannone
2020-10-23 15:04:52 +00:00
parent fbd24823e7
commit 461ae4fc5e
5 changed files with 120 additions and 25 deletions
@@ -673,9 +673,20 @@ QUnit.test('add an emoji after a channel mention', async function (assert) {
QUnit.test('display command suggestions on typing "/"', async function (assert) {
assert.expect(2);
this.data['mail.channel'].records.push({ channel_type: 'channel', id: 20 });
this.data['mail.channel_command'].records.push(
{
channel_types: ['channel'],
help: "List users in the current channel",
name: "who",
},
);
await this.start();
const composer = this.env.models['mail.composer'].create();
await this.createComposerComponent(composer);
const thread = this.env.models['mail.thread'].findFromIdentifyingData({
id: 20,
model: 'mail.channel',
});
await this.createComposerComponent(thread.composer);
assert.containsNone(
document.body,
@@ -697,12 +708,23 @@ QUnit.test('display command suggestions on typing "/"', async function (assert)
);
});
QUnit.test('use a command', async function (assert) {
QUnit.test('use a command for a specific channel type', async function (assert) {
assert.expect(4);
this.data['mail.channel'].records.push({ channel_type: 'channel', id: 20 });
this.data['mail.channel_command'].records.push(
{
channel_types: ['channel'],
help: "List users in the current channel",
name: "who",
},
);
await this.start();
const composer = this.env.models['mail.composer'].create();
await this.createComposerComponent(composer);
const thread = this.env.models['mail.thread'].findFromIdentifyingData({
id: 20,
model: 'mail.channel',
});
await this.createComposerComponent(thread.composer);
assert.containsNone(
document.body,
@@ -739,13 +761,56 @@ QUnit.test('use a command', async function (assert) {
);
});
QUnit.test("channel with no commands should not prompt any command suggestions on typing /", async function (assert) {
assert.expect(1);
this.data['mail.channel'].records.push({ channel_type: 'chat', id: 20 });
this.data['mail.channel_command'].records.push(
{
channel_types: ['channel'],
help: "bla bla bla",
name: "who",
},
);
await this.start();
const thread = this.env.models['mail.thread'].findFromIdentifyingData({
id: 20,
model: 'mail.channel',
});
await this.createComposerComponent(thread.composer);
await afterNextRender(() => {
document.querySelector('.o_ComposerTextInput_textarea').focus();
document.execCommand('insertText', false, "/");
});
await afterNextRender(() => {
const composer_text_input = document.querySelector('.o_ComposerTextInput_textarea');
composer_text_input.dispatchEvent(new window.KeyboardEvent('keydown'));
composer_text_input.dispatchEvent(new window.KeyboardEvent('keyup'));
});
assert.containsNone(
document.body,
'.o_ComposerSuggestion',
"should not prompt (command) suggestion after typing / (reason: no channel commands in chat channels)"
);
});
QUnit.test('use a command after some text', async function (assert) {
assert.expect(5);
this.data['mail.channel'].records.push({ channel_type: 'channel', id: 20 });
this.data['mail.channel_command'].records.push(
{
channel_types: ['channel'],
help: "List users in the current channel",
name: "who",
},
);
await this.start();
const composer = this.env.models['mail.composer'].create();
await this.createComposerComponent(composer);
const thread = this.env.models['mail.thread'].findFromIdentifyingData({
id: 20,
model: 'mail.channel',
});
await this.createComposerComponent(thread.composer);
assert.containsNone(
document.body,
'.o_ComposerSuggestion',
@@ -792,9 +857,20 @@ QUnit.test('use a command after some text', async function (assert) {
QUnit.test('add an emoji after a command', async function (assert) {
assert.expect(5);
this.data['mail.channel'].records.push({ channel_type: 'channel', id: 20 });
this.data['mail.channel_command'].records.push(
{
channel_types: ['channel'],
help: "List users in the current channel",
name: "who",
},
);
await this.start();
const composer = this.env.models['mail.composer'].create();
await this.createComposerComponent(composer);
const thread = this.env.models['mail.thread'].findFromIdentifyingData({
id: 20,
model: 'mail.channel',
});
await this.createComposerComponent(thread.composer);
assert.containsNone(
document.body,
@@ -10,7 +10,10 @@ function factory(dependencies) {
ChannelCommand.fields = {
/**
* FIXME use this value task-2343850
* Determines on which channel types `this` is available.
* Type of the channel (e.g. 'chat', 'channel' or 'groups')
* This field should contain an array when filtering is desired.
* Otherwise, it should be undefined when all types are allowed.
*/
channel_types: attr(),
/**
@@ -602,7 +602,15 @@ function factory(dependencies) {
_getCommandFromText(content) {
if (content.startsWith('/')) {
const firstWord = content.substring(1).split(/\s/)[0];
return this.env.messaging.commands.find(command => command.name === firstWord);
return this.env.messaging.commands.find(command => {
if (command.name !== firstWord) {
return false;
}
if (command.channel_types) {
return command.channel_types.includes(this.thread.channel_type);
}
return true;
});
}
return undefined;
}
@@ -685,15 +693,16 @@ function factory(dependencies) {
* @param {string} mentionKeyword
*/
_updateSuggestedChannelCommands(mentionKeyword) {
this.update({
suggestedChannelCommands: [[
'replace',
this.env.messaging.commands.filter(
command => command.name.includes(mentionKeyword)
)
]],
const commands = this.env.messaging.commands.filter(command => {
if (!command.name.includes(mentionKeyword)) {
return false;
}
if (command.channel_types) {
return command.channel_types.includes(this.thread.channel_type);
}
return true;
});
this.update({ suggestedChannelCommands: [['replace', commands]] });
if (this.suggestedChannelCommands[0]) {
this.update({
activeSuggestedChannelCommand: [['link', this.suggestedChannelCommands[0]]],
@@ -95,6 +95,15 @@ class MockModels {
},
records: [],
},
// Fake model to simulate "hardcoded" commands from python
'mail.channel_command': {
fields: {
channel_types: { type: 'binary' }, // array is expected
help: { type: 'char' },
name: { type: 'char' },
},
records: [],
},
'mail.followers': {
fields: {
channel_id: { type: 'integer' },
@@ -398,6 +398,8 @@ MockServer.include({
const shortcodes = this._getRecords('mail.shortcode', []);
const commands = this._getRecords('mail.channel_command', []);
const starredCounter = this._getRecords('mail.message', [
['starred_partner_ids', 'in', this.currentPartnerId],
]).length;
@@ -408,11 +410,7 @@ MockServer.include({
channel_direct_message: directMessageInfos,
channel_private_group: privateGroupInfos,
},
commands: [{
channel_types: ["channel", "chat"],
help: "List users in the current channel",
name: "who",
}],
commands,
current_partner: currentPartnerFormat,
current_user_id: this.currentUserId,
mail_failures: mailFailures,