diff options
| author | Sébastien Theys <seb@odoo.com> | 2020-08-28 10:16:37 +0000 |
|---|---|---|
| committer | Sébastien Theys <seb@odoo.com> | 2020-08-28 13:43:54 +0000 |
| commit | f9ee1cf5f904116e4d1d70a8eaf4ee1d58b18a3a (patch) | |
| tree | 89425d0677083bb9489e42d47521c4bcf1e4ff44 /addons/mail/static/src/components/thread_viewer | |
| parent | 98e12901c97a67dc87f21647473754cd6f384d4a (diff) | |
[FIX] mail, *: rename thread viewer to thread view
The "viewer" should be the "parent" not the view itself. This distinction will
become important in a future commit to allow viewers to not display the view in
some situations.
Rename done as a standalone commit to ease diff readability.
Part of task-2310623
closes odoo/odoo#56737
X-original-commit: 1b811e6a972d72de02965adda67c5b039bee023f
Related: odoo/enterprise#12756
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
Diffstat (limited to 'addons/mail/static/src/components/thread_viewer')
4 files changed, 0 insertions, 694 deletions
diff --git a/addons/mail/static/src/components/thread_viewer/thread_viewer.js b/addons/mail/static/src/components/thread_viewer/thread_viewer.js deleted file mode 100644 index 8e8f8f14dbb..00000000000 --- a/addons/mail/static/src/components/thread_viewer/thread_viewer.js +++ /dev/null @@ -1,191 +0,0 @@ -odoo.define('mail/static/src/components/thread_viewer/thread_viewer.js', function (require) { -'use strict'; - -const components = { - Composer: require('mail/static/src/components/composer/composer.js'), - MessageList: require('mail/static/src/components/message_list/message_list.js'), -}; -const useStore = require('mail/static/src/component_hooks/use_store/use_store.js'); - -const { Component } = owl; -const { useRef } = owl.hooks; - -class ThreadViewer extends Component { - - /** - * @param {...any} args - */ - constructor(...args) { - super(...args); - useStore((...args) => this._useStoreSelector(...args)); - /** - * Reference of the composer. Useful to set focus on composer when - * thread has the focus. - */ - this._composerRef = useRef('composer'); - /** - * Reference of the message list. Useful to determine scroll positions. - */ - this._messageListRef = useRef('messageList'); - } - - mounted() { - this._update(); - } - - patched() { - this._update(); - } - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - /** - * Focus the thread. If it has a composer, focus it. - */ - focus() { - if (!this._composerRef.comp) { - return; - } - this._composerRef.comp.focus(); - } - - /** - * Focusout the thread. - */ - focusout() { - if (!this._composerRef.comp) { - return; - } - this._composerRef.comp.focusout(); - } - - /** - * Get the scroll position in the message list. - * - * @returns {integer|undefined} - */ - getScrollTop() { - if (!this._messageListRef.comp) { - return undefined; - } - return this._messageListRef.comp.getScrollTop(); - } - - /** - * @returns {mail.thread_viewer} - */ - get threadViewer() { - return this.env.models['mail.thread_viewer'].get(this.props.threadViewerLocalId); - } - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * Called when thread component is mounted or patched. - * - * @private - */ - _update() { - const messageList = this._messageListRef.comp; - this.trigger('o-rendered'); - /** - * Control panel may offset scrolling position of message list due to - * height of buttons. To prevent this, control panel re-render is - * triggered before message list. Correct way should be to adjust - * scroll positions after everything has been rendered, but OWL doesn't - * have such an API for the moment. - */ - if (messageList) { - messageList.adjustFromComponentHints(); - } - } - - /** - * Returns data selected from the store. - * - * @private - * @param {Object} props - * @returns {Object} - */ - _useStoreSelector(props) { - const threadViewer = this.env.models['mail.thread_viewer'].get(props.threadViewerLocalId); - const thread = threadViewer ? threadViewer.thread : undefined; - const threadCache = threadViewer ? threadViewer.threadCache : undefined; - return { - isDeviceMobile: this.env.messaging.device.isMobile, - thread: thread ? thread.__state : undefined, - threadCache: threadCache ? threadCache.__state : undefined, - threadViewer: threadViewer ? threadViewer.__state : undefined, - }; - } - -} - -Object.assign(ThreadViewer, { - components, - defaultProps: { - composerAttachmentsDetailsMode: 'auto', - hasComposer: false, - hasMessageCheckbox: false, - hasSquashCloseMessages: false, - haveMessagesAuthorRedirect: false, - haveMessagesMarkAsReadIcon: false, - haveMessagesReplyIcon: false, - order: 'asc', - showComposerAttachmentsExtensions: true, - showComposerAttachmentsFilenames: true, - }, - props: { - composerAttachmentsDetailsMode: { - type: String, - validate: prop => ['auto', 'card', 'hover', 'none'].includes(prop), - }, - hasComposer: Boolean, - hasComposerCurrentPartnerAvatar: { - type: Boolean, - optional: true, - }, - hasComposerSendButton: { - type: Boolean, - optional: true, - }, - /** - * If set, determines whether the composer should display status of - * members typing on related thread. When this prop is not provided, - * it defaults to composer component default value. - */ - hasComposerThreadTyping: { - type: Boolean, - optional: true, - }, - hasMessageCheckbox: Boolean, - hasScrollAdjust: { - type: Boolean, - optional: true, - }, - hasSquashCloseMessages: Boolean, - haveMessagesAuthorRedirect: Boolean, - haveMessagesMarkAsReadIcon: Boolean, - haveMessagesReplyIcon: Boolean, - order: { - type: String, - validate: prop => ['asc', 'desc'].includes(prop), - }, - selectedMessageLocalId: { - type: String, - optional: true, - }, - showComposerAttachmentsExtensions: Boolean, - showComposerAttachmentsFilenames: Boolean, - threadViewerLocalId: String, - }, - template: 'mail.ThreadViewer', -}); - -return ThreadViewer; - -}); diff --git a/addons/mail/static/src/components/thread_viewer/thread_viewer.scss b/addons/mail/static/src/components/thread_viewer/thread_viewer.scss deleted file mode 100644 index 9b39a493979..00000000000 --- a/addons/mail/static/src/components/thread_viewer/thread_viewer.scss +++ /dev/null @@ -1,38 +0,0 @@ -// ------------------------------------------------------------------ -// Layout -// ------------------------------------------------------------------ - -.o_ThreadViewer { - display: flex; - position: relative; - flex-flow: column; - overflow: auto; -} - -.o_ThreadViewer_composer { - flex: 0 0 auto; -} - -.o_ThreadViewer_loading { - display: flex; - align-self: center; - flex: 1 1 auto; - align-items: center; -} - -.o_ThreadViewer_messageList { - flex: 1 1 auto; -} - -// ------------------------------------------------------------------ -// Style -// ------------------------------------------------------------------ - -.o_ThreadViewer { - background-color: gray('100'); - -} - -.o_ThreadViewer_loadingIcon { - margin-right: 3px; -} diff --git a/addons/mail/static/src/components/thread_viewer/thread_viewer.xml b/addons/mail/static/src/components/thread_viewer/thread_viewer.xml deleted file mode 100644 index 93202f6e423..00000000000 --- a/addons/mail/static/src/components/thread_viewer/thread_viewer.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<templates xml:space="preserve"> - - <t t-name="mail.ThreadViewer" owl="1"> - <div class="o_ThreadViewer" t-att-data-thread-local-id="threadViewer and threadViewer.thread and threadViewer.thread.localId"> - <t t-if="threadViewer"> - <t t-if="threadViewer.isShowingLoading" name="loadingCondition"> - <div class="o_ThreadViewer_loading"> - <span><i class="o_ThreadViewer_loadingIcon fa fa-spinner fa-spin" title="Loading..." role="img"/>Loading...</span> - </div> - </t> - <t t-elif="threadViewer.threadCache.isLoaded"> - <MessageList - class="o_ThreadViewer_messageList" - hasMessageCheckbox="props.hasMessageCheckbox" - hasScrollAdjust="props.hasScrollAdjust" - hasSquashCloseMessages="props.hasSquashCloseMessages" - haveMessagesAuthorRedirect="props.haveMessagesAuthorRedirect" - haveMessagesMarkAsReadIcon="props.haveMessagesMarkAsReadIcon" - haveMessagesReplyIcon="props.haveMessagesReplyIcon" - order="props.order" - selectedMessageLocalId="props.selectedMessageLocalId" - threadViewerLocalId="threadViewer.localId" - t-ref="messageList" - /> - </t> - <t t-if="props.hasComposer"> - <Composer - class="o_ThreadViewer_composer" - attachmentsDetailsMode="props.composerAttachmentsDetailsMode" - composerLocalId="threadViewer.thread.composer.localId" - hasCurrentPartnerAvatar="props.hasComposerCurrentPartnerAvatar" - hasSendButton="props.hasComposerSendButton" - hasTextInputSendOnEnterEnabled="(threadViewer.thread.model === 'mail.channel' and threadViewer.thread.mass_mailing) ? false : undefined" - hasThreadTyping="props.hasComposerThreadTyping" - isCompact="(threadViewer.thread.model === 'mail.channel' and threadViewer.thread.mass_mailing) ? false : undefined" - showAttachmentsExtensions="props.showComposerAttachmentsExtensions" - showAttachmentsFilenames="props.showComposerAttachmentsFilenames" - t-ref="composer" - /> - </t> - </t> - </div> - </t> - -</templates> diff --git a/addons/mail/static/src/components/thread_viewer/thread_viewer_tests.js b/addons/mail/static/src/components/thread_viewer/thread_viewer_tests.js deleted file mode 100644 index 31a56bfb1f1..00000000000 --- a/addons/mail/static/src/components/thread_viewer/thread_viewer_tests.js +++ /dev/null @@ -1,419 +0,0 @@ -odoo.define('mail/static/src/components/thread_viewer/thread_viewer_tests.js', function (require) { -'use strict'; - -const components = { - ThreadViewer: require('mail/static/src/components/thread_viewer/thread_viewer.js'), -}; -const { - afterEach, - afterNextRender, - beforeEach, - createRootComponent, - dragenterFiles, - start, -} = require('mail/static/src/utils/test_utils.js'); - -QUnit.module('mail', {}, function () { -QUnit.module('components', {}, function () { -QUnit.module('thread_viewer', {}, function () { -QUnit.module('thread_viewer_tests.js', { - beforeEach() { - beforeEach(this); - - /** - * @param {mail.thread_viewer} threadViewer - * @param {Object} [otherProps={}] - * @param {Object} [param2={}] - * @param {boolean} [param2.isFixedSize=false] - */ - this.createThreadViewerComponent = async (threadViewer, otherProps = {}, { isFixedSize = false } = {}) => { - let target; - if (isFixedSize) { - // needed to allow scrolling in some tests - const div = document.createElement('div'); - Object.assign(div.style, { - display: 'flex', - 'flex-flow': 'column', - height: '300px', - }); - this.widget.el.append(div); - target = div; - } else { - target = this.widget.el; - } - const props = Object.assign({ threadViewerLocalId: threadViewer.localId }, otherProps); - await createRootComponent(this, components.ThreadViewer, { props, target }); - }; - - this.start = async params => { - const { env, widget } = await start(Object.assign({}, params, { - data: this.data, - })); - this.env = env; - this.widget = widget; - }; - }, - afterEach() { - afterEach(this); - }, -}); - -QUnit.test('dragover files on thread with composer', async function (assert) { - assert.expect(1); - - await this.start(); - const thread = this.env.models['mail.thread'].create({ - channel_type: 'channel', - id: 100, - members: [['insert', [ - { - email: "john@example.com", - id: 9, - name: "John", - }, - { - email: "fred@example.com", - id: 10, - name: "Fred", - }, - ]]], - model: 'mail.channel', - name: "General", - public: 'public', - }); - const threadViewer = this.env.models['mail.thread_viewer'].create({ thread: [['link', thread]] }); - await this.createThreadViewerComponent(threadViewer, { hasComposer: true }); - await afterNextRender(() => - dragenterFiles(document.querySelector('.o_ThreadViewer')) - ); - assert.ok( - document.querySelector('.o_Composer_dropZone'), - "should have dropzone when dragging file over the thread" - ); -}); - -QUnit.test('message list desc order', async function (assert) { - assert.expect(5); - - for (let i = 0; i <= 60; i++) { - this.data['mail.message'].records.push({ - channel_ids: [100], - model: 'mail.channel', - res_id: 100, - }); - } - await this.start(); - const thread = this.env.models['mail.thread'].create({ - channel_type: 'channel', - id: 100, - members: [['insert', [ - { - email: "john@example.com", - id: 9, - name: "John", - }, - { - email: "fred@example.com", - id: 10, - name: "Fred", - }, - ]]], - model: 'mail.channel', - name: "General", - public: 'public', - }); - const threadViewer = this.env.models['mail.thread_viewer'].create({ thread: [['link', thread]] }); - await this.createThreadViewerComponent(threadViewer, { order: 'desc' }, { isFixedSize: true }); - const messageItems = document.querySelectorAll(`.o_MessageList_item`); - assert.notOk( - messageItems[0].classList.contains("o_MessageList_loadMore"), - "load more link should NOT be before messages" - ); - assert.ok( - messageItems[messageItems.length - 1].classList.contains("o_MessageList_loadMore"), - "load more link should be after messages" - ); - assert.strictEqual( - document.querySelectorAll(`.o_Message`).length, - 30, - "should have 30 messages at the beginning" - ); - - // scroll to bottom - await afterNextRender(() => { - document.querySelector(`.o_ThreadViewer_messageList`).scrollTop = - document.querySelector(`.o_ThreadViewer_messageList`).scrollHeight; - }); - assert.strictEqual( - document.querySelectorAll(`.o_Message`).length, - 60, - "should have 60 messages after scrolled to bottom" - ); - - await afterNextRender(() => { - document.querySelector(`.o_ThreadViewer_messageList`).scrollTop = 0; - }); - assert.strictEqual( - document.querySelectorAll(`.o_Message`).length, - 60, - "scrolling to top should not trigger any message fetching" - ); -}); - -QUnit.test('message list asc order', async function (assert) { - assert.expect(5); - - for (let i = 0; i <= 60; i++) { - this.data['mail.message'].records.push({ - channel_ids: [100], - model: 'mail.channel', - res_id: 100, - }); - } - await this.start(); - const thread = this.env.models['mail.thread'].create({ - channel_type: 'channel', - id: 100, - members: [['insert', [ - { - email: "john@example.com", - id: 9, - name: "John", - }, - { - email: "fred@example.com", - id: 10, - name: "Fred", - }, - ]]], - model: 'mail.channel', - name: "General", - public: 'public', - }); - const threadViewer = this.env.models['mail.thread_viewer'].create({ thread: [['link', thread]] }); - await this.createThreadViewerComponent(threadViewer, { order: 'asc' }, { isFixedSize: true }); - const messageItems = document.querySelectorAll(`.o_MessageList_item`); - assert.notOk( - messageItems[messageItems.length - 1].classList.contains("o_MessageList_loadMore"), - "load more link should be before messages" - ); - assert.ok( - messageItems[0].classList.contains("o_MessageList_loadMore"), - "load more link should NOT be after messages" - ); - assert.strictEqual( - document.querySelectorAll(`.o_Message`).length, - 30, - "should have 30 messages at the beginning" - ); - - // scroll to top - await afterNextRender(() => { - document.querySelector(`.o_ThreadViewer_messageList`).scrollTop = 0; - }); - assert.strictEqual( - document.querySelectorAll(`.o_Message`).length, - 60, - "should have 60 messages after scrolled to top" - ); - - // scroll to bottom - await afterNextRender(() => { - document.querySelector(`.o_ThreadViewer_messageList`).scrollTop = - document.querySelector(`.o_ThreadViewer_messageList`).scrollHeight; - }); - assert.strictEqual( - document.querySelectorAll(`.o_Message`).length, - 60, - "scrolling to bottom should not trigger any message fetching" - ); -}); - -QUnit.test('mark channel as fetched when a new message is loaded and as seen when focusing composer [REQUIRE FOCUS]', async function (assert) { - assert.expect(8); - - await this.start({ - mockRPC(route, args) { - if (args.method === 'channel_fetched') { - assert.strictEqual( - args.args[0][0], - 100, - 'channel_fetched is called on the right channel id' - ); - assert.strictEqual( - args.model, - 'mail.channel', - 'channel_fetched is called on the right channel model' - ); - assert.step('rpc:channel_fetch'); - } else if (args.method === 'channel_seen') { - assert.strictEqual( - args.args[0][0], - 100, - 'channel_seen is called on the right channel id' - ); - assert.strictEqual( - args.model, - 'mail.channel', - 'channel_seeb is called on the right channel model' - ); - assert.step('rpc:channel_seen'); - } - return this._super(...arguments); - } - }); - const thread = this.env.models['mail.thread'].create({ - // FIXME should not be needed, see task-2277537 - composer: [['create']], // avoid initial focus - id: 100, - isServerPinned: true, // just to avoid joinChannel to be called - members: [['insert', [ - { - email: "john@example.com", - id: this.env.messaging.currentPartner.id, - name: "John", - }, - { - email: "fred@example.com", - id: 10, - name: "Fred", - }, - ]]], - message_unread_counter: 1, // seen would not be called if not > 0 - model: 'mail.channel', - }); - - const threadViewer = this.env.models['mail.thread_viewer'].create({ thread: [['link', thread]] }); - await this.createThreadViewerComponent(threadViewer, { hasComposer: true }); - const notifications = [ - [['myDB', 'mail.channel', 100], { - channelId: 100, - id: 1, - body: "<p>fdsfsd</p>", - author_id: [10, "Fred"], - model: "mail.channel", - channel_ids: [100], - }] - ]; - - await afterNextRender(() => - this.widget.call('bus_service', 'trigger', 'notification', notifications) - ); - assert.verifySteps( - ['rpc:channel_fetch'], - "Channel should have been fetched but not seen yet" - ); - - await afterNextRender(() => thread.composer.focus()); - assert.verifySteps( - ['rpc:channel_seen'], - "Channel should have been marked as seen after threadViewer got the focus" - ); -}); - -QUnit.test('mark channel as fetched and seen when a new message is loaded if composer is focused [REQUIRE FOCUS]', async function (assert) { - assert.expect(4); - - await this.start({ - mockRPC(route, args) { - if (args.method === 'channel_fetched' && args.args[0] === 100) { - throw new Error("'channel_fetched' RPC must not be called for created channel as message is directly seen"); - } else if (args.method === 'channel_seen') { - assert.strictEqual( - args.args[0][0], - 100, - 'channel_seen is called on the right channel id' - ); - assert.strictEqual( - args.model, - 'mail.channel', - 'channel_seen is called on the right channel model' - ); - assert.step('rpc:channel_seen'); - } - return this._super(...arguments); - } - }); - const thread = this.env.models['mail.thread'].create({ - id: 100, - isServerPinned: true, // just to avoid joinChannel to be called - members: [['insert', [ - { - email: "john@example.com", - id: this.env.messaging.currentPartner.id, - name: "John", - }, - { - email: "fred@example.com", - id: 10, - name: "Fred", - }, - ]]], - message_unread_counter: 1, // seen would not be called if not > 0 - model: 'mail.channel', - }); - - const threadViewer = this.env.models['mail.thread_viewer'].create({ thread: [['link', thread]] }); - await this.createThreadViewerComponent(threadViewer, { hasComposer: true }); - const notifications = [ - [['myDB', 'mail.channel', 100], { - channelId: 100, - id: 1, - body: "<p>fdsfsd</p>", - author_id: [10, "Fred"], - model: "mail.channel", - channel_ids: [100], - }] - ]; - await afterNextRender(() => - this.widget.call('bus_service', 'trigger', 'notification', notifications) - ); - assert.verifySteps( - ['rpc:channel_seen'], - "Channel should have been mark as seen directly" - ); -}); - -QUnit.test('show message subject if thread is mailing channel', async function (assert) { - assert.expect(3); - - this.data['mail.message'].records.push({ - channel_ids: [100], - model: 'mail.channel', - res_id: 100, - subject: "Salutations, voyageur", - }); - await this.start(); - const thread = this.env.models['mail.thread'].create({ - channel_type: 'channel', - id: 100, - mass_mailing: true, - model: 'mail.channel', - name: "General", - public: 'public', - }); - const threadViewer = this.env.models['mail.thread_viewer'].create({ thread: [['link', thread]] }); - await this.createThreadViewerComponent(threadViewer); - - assert.containsOnce( - document.body, - '.o_Message', - "should display a single message" - ); - assert.containsOnce( - document.body, - '.o_Message_subject', - "should display subject of the message" - ); - assert.strictEqual( - document.querySelector('.o_Message_subject').textContent, - "Subject: Salutations, voyageur", - "Subject of the message should be 'Salutations, voyageur'" - ); -}); - -}); -}); -}); - -}); |
