From 5bd9361c3c66bc67e69753608fb4ca481d93ddfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Thu, 28 Apr 2022 08:45:41 +0000 Subject: [PATCH] [IMP] mail: introduce view models (step 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces models that define records being 1:1 map with components, as a step to move further to having essentially all business code in models. Having code in models is desirable to have very maintainable code, thanks to robust and declarative code with an ORM-like architecture. Task-2831082 closes odoo/odoo#90057 Signed-off-by: Alexandre Kühn (aku) --- .../attachment_box/attachment_box.js | 15 --- .../attachment_box/attachment_box.xml | 1 - .../static/src/components/chatter/chatter.xml | 1 - .../src/components/composer/composer.js | 17 --- .../src/components/composer/composer.xml | 1 - .../discuss_mobile_mailbox_selection.xml | 8 +- .../discuss_mobile_mailbox_selection_item.js | 37 ++++++ .../discuss_mobile_mailbox_selection_item.xml | 14 +++ .../src/components/drop_zone/drop_zone.js | 109 +----------------- .../src/components/drop_zone/drop_zone.xml | 2 +- .../message_action_list.js | 1 - .../message_action_list.xml | 2 +- .../components/message_list/message_list.js | 21 +--- .../messaging_menu/messaging_menu.js | 2 + .../src/components/thread_view/thread_view.js | 8 +- .../components/thread_view/thread_view.xml | 1 - .../mail/static/src/models/drop_zone_view.js | 100 +++++++++++++++- .../static/src/models/message_action_list.js | 10 ++ .../static/src/models/message_list_view.js | 15 +++ .../mail/static/src/models/messaging_menu.js | 1 + .../src/models/use_drag_visible_drop_zone.js | 4 +- .../discuss_mobile_mailbox_selection_tests.js | 6 +- 22 files changed, 192 insertions(+), 184 deletions(-) create mode 100644 addons/mail/static/src/components/discuss_mobile_mailbox_selection_item/discuss_mobile_mailbox_selection_item.js create mode 100644 addons/mail/static/src/components/discuss_mobile_mailbox_selection_item/discuss_mobile_mailbox_selection_item.xml diff --git a/addons/mail/static/src/components/attachment_box/attachment_box.js b/addons/mail/static/src/components/attachment_box/attachment_box.js index cca9928ad72..6e3803b85a9 100644 --- a/addons/mail/static/src/components/attachment_box/attachment_box.js +++ b/addons/mail/static/src/components/attachment_box/attachment_box.js @@ -13,7 +13,6 @@ export class AttachmentBox extends Component { setup() { super.setup(); useComponentToModel({ fieldName: 'component', modelName: 'AttachmentBoxView' }); - this._onDropZoneFilesDropped = this._onDropZoneFilesDropped.bind(this); } //-------------------------------------------------------------------------- @@ -27,20 +26,6 @@ export class AttachmentBox extends Component { return this.messaging && this.messaging.models['AttachmentBoxView'].get(this.props.localId); } - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * @private - * @param {Object} detail - * @param {FileList} detail.files - */ - async _onDropZoneFilesDropped(detail) { - await this.attachmentBoxView.fileUploader.uploadFiles(detail.files); - this.attachmentBoxView.useDragVisibleDropZone.update({ isVisible: false }); - } - } Object.assign(AttachmentBox, { diff --git a/addons/mail/static/src/components/attachment_box/attachment_box.xml b/addons/mail/static/src/components/attachment_box/attachment_box.xml index d6a6cbb85b9..8bc18a0083e 100644 --- a/addons/mail/static/src/components/attachment_box/attachment_box.xml +++ b/addons/mail/static/src/components/attachment_box/attachment_box.xml @@ -16,7 +16,6 @@ diff --git a/addons/mail/static/src/components/chatter/chatter.xml b/addons/mail/static/src/components/chatter/chatter.xml index ae498380aec..f5e1a87ef41 100644 --- a/addons/mail/static/src/components/chatter/chatter.xml +++ b/addons/mail/static/src/components/chatter/chatter.xml @@ -33,7 +33,6 @@ diff --git a/addons/mail/static/src/components/composer/composer.js b/addons/mail/static/src/components/composer/composer.js index a45039cb10c..d04c91db924 100644 --- a/addons/mail/static/src/components/composer/composer.js +++ b/addons/mail/static/src/components/composer/composer.js @@ -15,7 +15,6 @@ export class Composer extends Component { super.setup(); useComponentToModel({ fieldName: 'component', modelName: 'ComposerView' }); useRefToModel({ fieldName: 'buttonEmojisRef', modelName: 'ComposerView', refName: 'buttonEmojis' }); - this._onDropZoneFilesDropped = this._onDropZoneFilesDropped.bind(this); } //-------------------------------------------------------------------------- @@ -29,22 +28,6 @@ export class Composer extends Component { return this.messaging && this.messaging.models['ComposerView'].get(this.props.localId); } - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * Called when some files have been dropped in the dropzone. - * - * @private - * @param {Object} detail - * @param {FileList} detail.files - */ - async _onDropZoneFilesDropped(detail) { - await this.composerView.fileUploader.uploadFiles(detail.files); - this.composerView.useDragVisibleDropZone.update({ isVisible: false }); - } - } Object.assign(Composer, { diff --git a/addons/mail/static/src/components/composer/composer.xml b/addons/mail/static/src/components/composer/composer.xml index 9d8aaf462bc..da1e2815c09 100644 --- a/addons/mail/static/src/components/composer/composer.xml +++ b/addons/mail/static/src/components/composer/composer.xml @@ -21,7 +21,6 @@ diff --git a/addons/mail/static/src/components/discuss_mobile_mailbox_selection/discuss_mobile_mailbox_selection.xml b/addons/mail/static/src/components/discuss_mobile_mailbox_selection/discuss_mobile_mailbox_selection.xml index 10687a46660..6f1e0f40674 100644 --- a/addons/mail/static/src/components/discuss_mobile_mailbox_selection/discuss_mobile_mailbox_selection.xml +++ b/addons/mail/static/src/components/discuss_mobile_mailbox_selection/discuss_mobile_mailbox_selection.xml @@ -4,13 +4,7 @@
- +
diff --git a/addons/mail/static/src/components/discuss_mobile_mailbox_selection_item/discuss_mobile_mailbox_selection_item.js b/addons/mail/static/src/components/discuss_mobile_mailbox_selection_item/discuss_mobile_mailbox_selection_item.js new file mode 100644 index 00000000000..79bcf7640d6 --- /dev/null +++ b/addons/mail/static/src/components/discuss_mobile_mailbox_selection_item/discuss_mobile_mailbox_selection_item.js @@ -0,0 +1,37 @@ +/** @odoo-module **/ + +import { registerMessagingComponent } from '@mail/utils/messaging_component'; + +const { Component } = owl; + +export class DiscussMobileMailboxSelectionItem extends Component { + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @returns {DiscussView} + */ + get discussView() { + return this.messaging && this.messaging.models['DiscussView'].get(this.props.discussViewLocalId); + } + + /** + * @returns {Thread} + */ + get mailbox() { + return this.messaging && this.messaging.models['Thread'].get(this.props.mailboxLocalId); + } + +} + +Object.assign(DiscussMobileMailboxSelectionItem, { + props: { + discussViewLocalId: String, + mailboxLocalId: String, + }, + template: 'mail.DiscussMobileMailboxSelectionItem', +}); + +registerMessagingComponent(DiscussMobileMailboxSelectionItem); diff --git a/addons/mail/static/src/components/discuss_mobile_mailbox_selection_item/discuss_mobile_mailbox_selection_item.xml b/addons/mail/static/src/components/discuss_mobile_mailbox_selection_item/discuss_mobile_mailbox_selection_item.xml new file mode 100644 index 00000000000..4824d58bd50 --- /dev/null +++ b/addons/mail/static/src/components/discuss_mobile_mailbox_selection_item/discuss_mobile_mailbox_selection_item.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/addons/mail/static/src/components/drop_zone/drop_zone.js b/addons/mail/static/src/components/drop_zone/drop_zone.js index 306afe11f28..d88fb486076 100644 --- a/addons/mail/static/src/components/drop_zone/drop_zone.js +++ b/addons/mail/static/src/components/drop_zone/drop_zone.js @@ -2,29 +2,10 @@ import { registerMessagingComponent } from '@mail/utils/messaging_component'; -const { Component, useState } = owl; +const { Component } = owl; export class DropZone extends Component { - /** - * @override - */ - setup() { - super.setup(); - this.state = useState({ - /** - * Determine whether the user is dragging files over the dropzone. - * Useful to provide visual feedback in that case. - */ - isDraggingInside: false, - }); - /** - * Counts how many drag enter/leave happened on self and children. This - * ensures the drop effect stays active when dragging over a child. - */ - this._dragCount = 0; - } - //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- @@ -46,94 +27,6 @@ export class DropZone extends Component { return Boolean(this.root.el && this.root.el.contains(node)); } - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * Making sure that dragging content is external files. - * Ignoring other content dragging like text. - * - * @private - * @param {DataTransfer} dataTransfer - * @returns {boolean} - */ - _isDragSourceExternalFile(dataTransfer) { - const dragDataType = dataTransfer.types; - if (dragDataType.constructor === window.DOMStringList) { - return dragDataType.contains('Files'); - } - if (dragDataType.constructor === Array) { - return dragDataType.includes('Files'); - } - return false; - } - - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * Shows a visual drop effect when dragging inside the dropzone. - * - * @private - * @param {DragEvent} ev - */ - _onDragenter(ev) { - ev.preventDefault(); - if (this._dragCount === 0) { - this.state.isDraggingInside = true; - } - this._dragCount++; - } - - /** - * Hides the visual drop effect when dragging outside the dropzone. - * - * @private - * @param {DragEvent} ev - */ - _onDragleave(ev) { - this._dragCount--; - if (this._dragCount === 0) { - this.state.isDraggingInside = false; - } - } - - /** - * Prevents default (from the template) in order to receive the drop event. - * The drop effect cursor works only when set on dragover. - * - * @private - * @param {DragEvent} ev - */ - _onDragover(ev) { - ev.preventDefault(); - ev.dataTransfer.dropEffect = 'copy'; - } - - /** - * Trigger callback 'props.onDropzoneFilesDropped' with event when new files are dropped - * on the dropzone, and then removes the visual drop effect. - * - * The parents should handle this event to process the files as they wish, - * such as uploading them. - * - * @private - * @param {DragEvent} ev - */ - _onDrop(ev) { - ev.preventDefault(); - if (this._isDragSourceExternalFile(ev.dataTransfer)) { - if (this.props.onDropzoneFilesDropped) { - this.props.onDropzoneFilesDropped({ - files: ev.dataTransfer.files, - }); - } - } - this.state.isDraggingInside = false; - } - } Object.assign(DropZone, { diff --git a/addons/mail/static/src/components/drop_zone/drop_zone.xml b/addons/mail/static/src/components/drop_zone/drop_zone.xml index d1c4f7dd3a8..c4a246391fc 100644 --- a/addons/mail/static/src/components/drop_zone/drop_zone.xml +++ b/addons/mail/static/src/components/drop_zone/drop_zone.xml @@ -2,7 +2,7 @@ -
+

Drag Files Here

diff --git a/addons/mail/static/src/components/message_action_list/message_action_list.js b/addons/mail/static/src/components/message_action_list/message_action_list.js index f02e11768c8..be8c6c34603 100644 --- a/addons/mail/static/src/components/message_action_list/message_action_list.js +++ b/addons/mail/static/src/components/message_action_list/message_action_list.js @@ -13,7 +13,6 @@ export class MessageActionList extends Component { setup() { super.setup(); useRefToModel({ fieldName: 'actionReactionRef', modelName: 'MessageActionList', refName: 'actionReaction' }); - this.ADD_A_REACTION = this.env._t("Add a Reaction"); } /** diff --git a/addons/mail/static/src/components/message_action_list/message_action_list.xml b/addons/mail/static/src/components/message_action_list/message_action_list.xml index 5432e69fbaa..96dfb5722b5 100644 --- a/addons/mail/static/src/components/message_action_list/message_action_list.xml +++ b/addons/mail/static/src/components/message_action_list/message_action_list.xml @@ -3,7 +3,7 @@
- + diff --git a/addons/mail/static/src/models/drop_zone_view.js b/addons/mail/static/src/models/drop_zone_view.js index e36171be3a7..8f4d2e2da01 100644 --- a/addons/mail/static/src/models/drop_zone_view.js +++ b/addons/mail/static/src/models/drop_zone_view.js @@ -1,11 +1,95 @@ /** @odoo-module **/ import { registerModel } from '@mail/model/model_core'; -import { one } from '@mail/model/model_field'; +import { attr, one } from '@mail/model/model_field'; +import { decrement, increment } from '@mail/model/model_field_command'; registerModel({ name: 'DropZoneView', identifyingFields: [['attachmentBoxViewOwner', 'composerViewOwner']], + recordMethods: { + /** + * Shows a visual drop effect when dragging inside the dropzone. + * + * @param {DragEvent} ev + */ + onDragenter(ev) { + if (!this.exists()) { + return; + } + ev.preventDefault(); + if (this.dragCount === 0) { + this.update({ isDraggingInside: true }); + } + this.update({ dragCount: increment() }); + }, + /** + * Hides the visual drop effect when dragging outside the dropzone. + * + * @param {DragEvent} ev + */ + onDragleave(ev) { + if (!this.exists()) { + return; + } + this.update({ dragCount: decrement() }); + if (this.dragCount === 0) { + this.update({ isDraggingInside: false }); + } + }, + /** + * Prevents default (from the template) in order to receive the drop event. + * The drop effect cursor works only when set on dragover. + * + * @param {DragEvent} ev + */ + onDragover(ev) { + ev.preventDefault(); + ev.dataTransfer.dropEffect = 'copy'; + }, + /** + * Trigger callback 'props.onDropzoneFilesDropped' with event when new files are dropped + * on the dropzone, and then removes the visual drop effect. + * + * The parents should handle this event to process the files as they wish, + * such as uploading them. + * + * @param {DragEvent} ev + */ + async onDrop(ev) { + if (!this.exists()) { + return; + } + ev.preventDefault(); + this.update({ isDraggingInside: false }); + if (this._isDragSourceExternalFile(ev.dataTransfer)) { + if (this.attachmentBoxViewOwner) { + await this.attachmentBoxViewOwner.fileUploader.uploadFiles(ev.dataTransfer.files); + } + if (this.composerViewOwner) { + await this.composerViewOwner.fileUploader.uploadFiles(ev.dataTransfer.files); + } + } + }, + /** + * Making sure that dragging content is external files. + * Ignoring other content dragging like text. + * + * @private + * @param {DataTransfer} dataTransfer + * @returns {boolean} + */ + _isDragSourceExternalFile(dataTransfer) { + const dragDataType = dataTransfer.types; + if (dragDataType.constructor === window.DOMStringList) { + return dragDataType.contains('Files'); + } + if (dragDataType.constructor === Array) { + return dragDataType.includes('Files'); + } + return false; + }, + }, fields: { attachmentBoxViewOwner: one('AttachmentBoxView', { inverse: 'dropZoneView', @@ -15,5 +99,19 @@ registerModel({ inverse: 'dropZoneView', readonly: true, }), + /** + * Counts how many drag enter/leave happened on self and children. This + * ensures the drop effect stays active when dragging over a child. + */ + dragCount: attr({ + default: 0, + }), + /** + * Determines whether the user is dragging files over the dropzone. + * Useful to provide visual feedback in that case. + */ + isDraggingInside: attr({ + default: false, + }), }, }); diff --git a/addons/mail/static/src/models/message_action_list.js b/addons/mail/static/src/models/message_action_list.js index 61abef25228..63123dfb729 100644 --- a/addons/mail/static/src/models/message_action_list.js +++ b/addons/mail/static/src/models/message_action_list.js @@ -72,6 +72,13 @@ registerModel({ onClickToggleStar(ev) { this.message.toggleStar(); }, + /** + * @private + * @returns {string} + */ + _computeAddReactionText() { + return this.env._t("Add a Reaction"); + }, /** * @private * @returns {boolean} @@ -103,6 +110,9 @@ registerModel({ * States the reference to the reaction action in the component. */ actionReactionRef: attr(), + addReactionText: attr({ + compute: '_computeAddReactionText', + }), deleteConfirmDialog: one('Dialog', { inverse: 'messageActionListOwnerAsDeleteConfirm', isCausal: true, diff --git a/addons/mail/static/src/models/message_list_view.js b/addons/mail/static/src/models/message_list_view.js index 7505ec19c2e..c6fc1e566bd 100644 --- a/addons/mail/static/src/models/message_list_view.js +++ b/addons/mail/static/src/models/message_list_view.js @@ -2,6 +2,7 @@ import { registerModel } from '@mail/model/model_core'; import { attr, one } from '@mail/model/model_field'; +import { clear } from '@mail/model/model_field_command'; registerModel({ name: 'MessageListView', @@ -16,6 +17,16 @@ registerModel({ } return this.component.root.el; }, + /** + * @private + * @returns {boolean|FieldCommand} + */ + _computeHasScrollAdjust() { + if (this.threadViewOwner.threadViewer.chatter) { + return this.threadViewOwner.threadViewer.chatter.hasMessageListScrollAdjust; + } + return clear(); + }, /*** * @private * @returns {boolean} @@ -38,6 +49,10 @@ registerModel({ * States the OWL component of this message list view */ component: attr(), + hasScrollAdjust: attr({ + compute: '_computeHasScrollAdjust', + default: true, + }), /** * States whether the message list scroll position is at the end of * the message list. Depending of the message list order, this could be diff --git a/addons/mail/static/src/models/messaging_menu.js b/addons/mail/static/src/models/messaging_menu.js index 5f9c0751fd7..a838d4d755d 100644 --- a/addons/mail/static/src/models/messaging_menu.js +++ b/addons/mail/static/src/models/messaging_menu.js @@ -122,6 +122,7 @@ registerModel({ activeTabId: attr({ default: 'all', }), + component: attr(), /** * States the counter of this messaging menu. The counter is an integer * value to give to the current user an estimate of how many things diff --git a/addons/mail/static/src/models/use_drag_visible_drop_zone.js b/addons/mail/static/src/models/use_drag_visible_drop_zone.js index b89d6ef0a7c..7b48ad29118 100644 --- a/addons/mail/static/src/models/use_drag_visible_drop_zone.js +++ b/addons/mail/static/src/models/use_drag_visible_drop_zone.js @@ -11,7 +11,7 @@ registerModel({ _created() { document.addEventListener('dragenter', this._onDragenterListener, true); document.addEventListener('dragleave', this._onDragleaveListener, true); - document.addEventListener('drop', this._onDropListener, true); + document.addEventListener('drop', this._onDropListener); // Thoses Events prevent the browser to open or download the file if // it's dropped outside of the dropzone @@ -21,7 +21,7 @@ registerModel({ _willDelete() { document.removeEventListener('dragenter', this._onDragenterListener, true); document.removeEventListener('dragleave', this._onDragleaveListener, true); - document.removeEventListener('drop', this._onDropListener, true); + document.removeEventListener('drop', this._onDropListener); window.removeEventListener('dragover', ev => ev.preventDefault()); window.removeEventListener('drop', ev => ev.preventDefault()); diff --git a/addons/mail/static/tests/qunit_mobile_suite_tests/components/discuss_mobile_mailbox_selection_tests.js b/addons/mail/static/tests/qunit_mobile_suite_tests/components/discuss_mobile_mailbox_selection_tests.js index 9197595eed3..d1dcdb860e2 100644 --- a/addons/mail/static/tests/qunit_mobile_suite_tests/components/discuss_mobile_mailbox_selection_tests.js +++ b/addons/mail/static/tests/qunit_mobile_suite_tests/components/discuss_mobile_mailbox_selection_tests.js @@ -48,13 +48,13 @@ QUnit.test('select another mailbox', async function (assert) { ); assert.containsOnce( document.body, - `.o_DiscussMobileMailboxSelection_button[ + `.o_DiscussMobileMailboxSelectionItem[ data-mailbox-local-id="${messaging.starred.localId}" ]`, "should have a button to open starred mailbox" ); - await click(`.o_DiscussMobileMailboxSelection_button[ + await click(`.o_DiscussMobileMailboxSelectionItem[ data-mailbox-local-id="${messaging.starred.localId}"] `); assert.containsOnce( @@ -106,7 +106,7 @@ QUnit.test('auto-select "Inbox" when discuss had channel as active thread', asyn "'mailbox' tab should be selected after click on mailbox tab" ); assert.hasClass( - document.querySelector(`.o_DiscussMobileMailboxSelection_button[data-mailbox-local-id="${ + document.querySelector(`.o_DiscussMobileMailboxSelectionItem[data-mailbox-local-id="${ messaging.inbox.localId }"]`), 'o-active',