[FIX] mail: fix keyboard shortcut issue for small screen
Current behavior before PR: When having a small screen, the composer in a chat window has no actual shortcut to send the message. Desired behavior after PR is merged: 'CTRL-Enter' keyboard shortcut will work for small screen size. LINKS: PR https://github.com//pull/65241 Task-2446076 closes odoo/odoo#67724 X-original-commit: 2542f731c5b06c3b451ecde7992d3bb45adb2c60 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -1892,6 +1892,42 @@ QUnit.test('chat window should scroll to the newly posted message just after pos
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('chat window: post message on non-mailing channel with "CTRL-Enter" keyboard shortcut for small screen size', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
this.data['mail.channel'].records.push({
|
||||
id: 20,
|
||||
is_minimized: true,
|
||||
mass_mailing: false,
|
||||
});
|
||||
await this.start({
|
||||
env: {
|
||||
device: {
|
||||
isMobile: true, // here isMobile is used for the small screen size, not actually for the mobile devices
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_dropdownMenu .o_NotificationList_preview`).click()
|
||||
);
|
||||
// insert some HTML in editable
|
||||
await afterNextRender(() => {
|
||||
document.querySelector(`.o_ComposerTextInput_textarea`).focus();
|
||||
document.execCommand('insertText', false, "Test");
|
||||
});
|
||||
await afterNextRender(() => {
|
||||
const kevt = new window.KeyboardEvent('keydown', { ctrlKey: true, key: "Enter" });
|
||||
document.querySelector('.o_ComposerTextInput_textarea').dispatchEvent(kevt);
|
||||
});
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_Message',
|
||||
"should now have single message in channel after posting message from pressing 'CTRL-Enter' in text input of composer for small screen"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('[technical] chat window: composer state conservation on toggle home menu when folded', async function (assert) {
|
||||
// technical as show/hide home menu simulation are involved and home menu implementation
|
||||
// have side-effects on DOM that may make chat window components not work
|
||||
|
||||
@@ -289,8 +289,7 @@ class ComposerTextInput extends Component {
|
||||
!ev.altKey &&
|
||||
!ev.ctrlKey &&
|
||||
!ev.metaKey &&
|
||||
!ev.shiftKey &&
|
||||
!this.env.messaging.device.isMobile
|
||||
!ev.shiftKey
|
||||
) {
|
||||
this.trigger('o-composer-text-input-send-shortcut');
|
||||
ev.preventDefault();
|
||||
|
||||
@@ -20,7 +20,11 @@ class ThreadView extends Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
useShouldUpdateBasedOnProps();
|
||||
useStore((...args) => this._useStoreSelector(...args));
|
||||
useStore((...args) => this._useStoreSelector(...args), {
|
||||
compareDepth: {
|
||||
threadTextInputSendShortcuts: 1,
|
||||
},
|
||||
});
|
||||
useUpdate({ func: () => this._update() });
|
||||
/**
|
||||
* Reference of the composer. Useful to set focus on composer when
|
||||
@@ -134,6 +138,7 @@ class ThreadView extends Component {
|
||||
threadIsTemporary: thread && thread.isTemporary,
|
||||
threadMassMailing: thread && thread.mass_mailing,
|
||||
threadModel: thread && thread.model,
|
||||
threadTextInputSendShortcuts: thread && thread.textInputSendShortcuts || [],
|
||||
threadView,
|
||||
threadViewIsLoading: threadView && threadView.isLoading,
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
isDoFocus="props.isDoFocus"
|
||||
showAttachmentsExtensions="props.showComposerAttachmentsExtensions"
|
||||
showAttachmentsFilenames="props.showComposerAttachmentsFilenames"
|
||||
textInputSendShortcuts="(threadView.thread.model === 'mail.channel' and threadView.thread.mass_mailing) ? ['ctrl-enter', 'meta-enter'] : ['enter']"
|
||||
textInputSendShortcuts="threadView.textInputSendShortcuts"
|
||||
t-ref="composer"
|
||||
/>
|
||||
</t>
|
||||
|
||||
@@ -67,6 +67,33 @@ function factory(dependencies) {
|
||||
// Private
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @returns {mail.messaging}
|
||||
*/
|
||||
_computeMessaging() {
|
||||
return [['link', this.env.messaging]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @returns {string[]}
|
||||
*/
|
||||
_computeTextInputSendShortcuts() {
|
||||
if (!this.thread) {
|
||||
return;
|
||||
}
|
||||
const isMailingList = this.thread.model === 'mail.channel' && this.thread.mass_mailing;
|
||||
// Actually in mobile there is a send button, so we need there 'enter' to allow new line.
|
||||
// Hence, we want to use a different shortcut 'ctrl/meta enter' to send for small screen
|
||||
// size with a non-mailing channel.
|
||||
// here send will be done on clicking the button or using the 'ctrl/meta enter' shortcut.
|
||||
if (this.env.messaging.device.isMobile || isMailingList) {
|
||||
return ['ctrl-enter', 'meta-enter'];
|
||||
}
|
||||
return ['enter'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @returns {integer|undefined}
|
||||
@@ -199,6 +226,18 @@ function factory(dependencies) {
|
||||
composer: many2one('mail.composer', {
|
||||
related: 'thread.composer',
|
||||
}),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
device: one2one('mail.device', {
|
||||
related: 'messaging.device',
|
||||
}),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
deviceIsMobile: attr({
|
||||
related: 'device.isMobile',
|
||||
}),
|
||||
hasComposerFocus: attr({
|
||||
related: 'composer.hasFocus',
|
||||
}),
|
||||
@@ -255,6 +294,12 @@ function factory(dependencies) {
|
||||
messages: many2many('mail.message', {
|
||||
related: 'threadCache.messages',
|
||||
}),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
messaging: many2one('mail.messaging', {
|
||||
compute: '_computeMessaging',
|
||||
}),
|
||||
nonEmptyMessages: many2many('mail.message', {
|
||||
related: 'threadCache.nonEmptyMessages',
|
||||
}),
|
||||
@@ -287,6 +332,20 @@ function factory(dependencies) {
|
||||
stringifiedDomain: attr({
|
||||
related: 'threadViewer.stringifiedDomain',
|
||||
}),
|
||||
/**
|
||||
* Determines the keyboard shortcuts that are available to send a message
|
||||
* from the composer of this thread viewer.
|
||||
*/
|
||||
textInputSendShortcuts: attr({
|
||||
compute: '_computeTextInputSendShortcuts',
|
||||
dependencies: [
|
||||
'device',
|
||||
'deviceIsMobile',
|
||||
'thread',
|
||||
'threadMassMailing',
|
||||
'threadModel',
|
||||
],
|
||||
}),
|
||||
/**
|
||||
* Determines the `mail.thread` currently displayed by `this`.
|
||||
*/
|
||||
@@ -337,6 +396,18 @@ function factory(dependencies) {
|
||||
default: {},
|
||||
related: 'threadViewer.threadCacheInitialScrollPositions',
|
||||
}),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
threadMassMailing: attr({
|
||||
related: 'thread.mass_mailing',
|
||||
}),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
threadModel: attr({
|
||||
related: 'thread.model',
|
||||
}),
|
||||
/**
|
||||
* Not a real field, used to trigger `thread.markAsSeen` when one of
|
||||
* the dependencies changes.
|
||||
|
||||
Reference in New Issue
Block a user