summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/components/thread_needaction_preview
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/src/components/thread_needaction_preview
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mail/static/src/components/thread_needaction_preview')
-rw-r--r--addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js151
-rw-r--r--addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.scss108
-rw-r--r--addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.xml58
-rw-r--r--addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js457
4 files changed, 774 insertions, 0 deletions
diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js
new file mode 100644
index 00000000..b70c8f6b
--- /dev/null
+++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js
@@ -0,0 +1,151 @@
+odoo.define('mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js', function (require) {
+'use strict';
+
+const components = {
+ MessageAuthorPrefix: require('mail/static/src/components/message_author_prefix/message_author_prefix.js'),
+ PartnerImStatusIcon: require('mail/static/src/components/partner_im_status_icon/partner_im_status_icon.js'),
+};
+const useShouldUpdateBasedOnProps = require('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js');
+const useStore = require('mail/static/src/component_hooks/use_store/use_store.js');
+const mailUtils = require('mail.utils');
+
+const { Component } = owl;
+const { useRef } = owl.hooks;
+
+class ThreadNeedactionPreview extends Component {
+
+ /**
+ * @override
+ */
+ constructor(...args) {
+ super(...args);
+ useShouldUpdateBasedOnProps();
+ useStore(props => {
+ const thread = this.env.models['mail.thread'].get(props.threadLocalId);
+ const mainThreadCache = thread ? thread.mainCache : undefined;
+ let lastNeedactionMessageAsOriginThreadAuthor;
+ let lastNeedactionMessageAsOriginThread;
+ let threadCorrespondent;
+ if (thread) {
+ lastNeedactionMessageAsOriginThread = mainThreadCache.lastNeedactionMessageAsOriginThread;
+ threadCorrespondent = thread.correspondent;
+ }
+ if (lastNeedactionMessageAsOriginThread) {
+ lastNeedactionMessageAsOriginThreadAuthor = lastNeedactionMessageAsOriginThread.author;
+ }
+ return {
+ isDeviceMobile: this.env.messaging.device.isMobile,
+ lastNeedactionMessageAsOriginThread: lastNeedactionMessageAsOriginThread ? lastNeedactionMessageAsOriginThread.__state : undefined,
+ lastNeedactionMessageAsOriginThreadAuthor: lastNeedactionMessageAsOriginThreadAuthor
+ ? lastNeedactionMessageAsOriginThreadAuthor.__state
+ : undefined,
+ thread: thread ? thread.__state : undefined,
+ threadCorrespondent: threadCorrespondent
+ ? threadCorrespondent.__state
+ : undefined,
+ };
+ });
+ /**
+ * Reference of the "mark as read" button. Useful to disable the
+ * top-level click handler when clicking on this specific button.
+ */
+ this._markAsReadRef = useRef('markAsRead');
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Get the image route of the thread.
+ *
+ * @returns {string}
+ */
+ image() {
+ if (this.thread.moduleIcon) {
+ return this.thread.moduleIcon;
+ }
+ if (this.thread.correspondent) {
+ return this.thread.correspondent.avatarUrl;
+ }
+ if (this.thread.model === 'mail.channel') {
+ return `/web/image/mail.channel/${this.thread.id}/image_128`;
+ }
+ return '/mail/static/src/img/smiley/avatar.jpg';
+ }
+
+ /**
+ * Get inline content of the last message of this conversation.
+ *
+ * @returns {string}
+ */
+ get inlineLastNeedactionMessageBody() {
+ if (!this.thread.lastNeedactionMessage) {
+ return '';
+ }
+ return mailUtils.htmlToTextContentInline(this.thread.lastNeedactionMessage.prettyBody);
+ }
+
+ /**
+ * Get inline content of the last message of this conversation.
+ *
+ * @returns {string}
+ */
+ get inlineLastNeedactionMessageAsOriginThreadBody() {
+ if (!this.thread.lastNeedactionMessageAsOriginThread) {
+ return '';
+ }
+ return mailUtils.htmlToTextContentInline(this.thread.lastNeedactionMessageAsOriginThread.prettyBody);
+ }
+
+ /**
+ * @returns {mail.thread}
+ */
+ get thread() {
+ return this.env.models['mail.thread'].get(this.props.threadLocalId);
+ }
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onClick(ev) {
+ const markAsRead = this._markAsReadRef.el;
+ if (markAsRead && markAsRead.contains(ev.target)) {
+ // handled in `_onClickMarkAsRead`
+ return;
+ }
+ this.thread.open();
+ if (!this.env.messaging.device.isMobile) {
+ this.env.messaging.messagingMenu.close();
+ }
+ }
+
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onClickMarkAsRead(ev) {
+ this.env.models['mail.message'].markAllAsRead([
+ ['model', '=', this.thread.model],
+ ['res_id', '=', this.thread.id],
+ ]);
+ }
+
+}
+
+Object.assign(ThreadNeedactionPreview, {
+ components,
+ props: {
+ threadLocalId: String,
+ },
+ template: 'mail.ThreadNeedactionPreview',
+});
+
+return ThreadNeedactionPreview;
+
+});
diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.scss b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.scss
new file mode 100644
index 00000000..5de87f8b
--- /dev/null
+++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.scss
@@ -0,0 +1,108 @@
+// ------------------------------------------------------------------
+// Layout
+// ------------------------------------------------------------------
+
+.o_ThreadNeedactionPreview {
+ @include o-mail-notification-list-item-layout();
+
+ &:hover .o_ThreadNeedactionPreview_markAsRead {
+ opacity: 1;
+ }
+}
+
+.o_ThreadNeedactionPreview_content {
+ @include o-mail-notification-list-item-content-layout();
+}
+
+.o_ThreadNeedactionPreview_core {
+ @include o-mail-notification-list-item-core-layout();
+}
+
+.o_ThreadNeedactionPreview_coreItem {
+ @include o-mail-notification-list-item-core-item-layout();
+}
+
+.o_ThreadNeedactionPreview_counter {
+ @include o-mail-notification-list-item-counter-layout();
+}
+
+.o_ThreadNeedactionPreview_date {
+ @include o-mail-notification-list-item-date-layout();
+}
+
+.o_ThreadNeedactionPreview_header {
+ @include o-mail-notification-list-item-header-layout();
+}
+
+.o_ThreadNeedactionPreview_image {
+ @include o-mail-notification-list-item-image-layout();
+}
+
+.o_ThreadNeedactionPreview_imageContainer {
+ @include o-mail-notification-list-item-image-container-layout();
+}
+
+.o_ThreadNeedactionPreview_inlineText {
+ @include o-mail-notification-list-item-inline-text-layout();
+}
+
+.o_ThreadNeedactionPreview_markAsRead {
+ @include o-mail-notification-list-item-mark-as-read-layout();
+}
+
+.o_ThreadNeedactionPreview_name {
+ @include o-mail-notification-list-item-name-layout();
+}
+
+.o_ThreadNeedactionPreview_partnerImStatusIcon {
+ @include o-mail-notification-list-item-partner-im-status-icon-layout();
+}
+
+.o_ThreadNeedactionPreview_sidebar {
+ @include o-mail-notification-list-item-sidebar-layout();
+}
+
+// ------------------------------------------------------------------
+// Style
+// ------------------------------------------------------------------
+
+.o_ThreadNeedactionPreview {
+ @include o-mail-notification-list-item-style();
+ background-color: rgba($o-brand-primary, 0.1);
+
+ &:hover {
+ background-color: rgba($o-brand-primary, 0.2);
+
+ .o_ThreadNeedactionPreview_partnerImStatusIcon {
+ @include o-mail-notification-list-item-hover-partner-im-status-icon-style();
+ }
+ }
+}
+
+.o_ThreadNeedactionPreview_core {
+ @include o-mail-notification-list-item-core-style();
+}
+
+.o_ThreadNeedactionPreview_counter {
+ @include o-mail-notification-list-item-bold-style();
+}
+
+.o_ThreadNeedactionPreview_date {
+ @include o-mail-notification-list-item-date-style();
+}
+
+.o_ThreadNeedactionPreview_image {
+ @include o-mail-notification-list-item-image-style();
+}
+
+.o_ThreadNeedactionPreview_markAsRead {
+ @include o-mail-notification-list-item-mark-as-read-style();
+}
+
+.o_ThreadNeedactionPreview_name {
+ @include o-mail-notification-list-item-bold-style();
+}
+
+.o_ThreadNeedactionPreview_partnerImStatusIcon {
+ @include o-mail-notification-list-item-partner-im-status-icon-style();
+}
diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.xml b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.xml
new file mode 100644
index 00000000..3fd33224
--- /dev/null
+++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates xml:space="preserve">
+
+ <t t-name="mail.ThreadNeedactionPreview" owl="1">
+ <!--
+ The preview template is used by the discuss in mobile, and by the systray
+ menu in order to show preview of threads.
+ -->
+ <div class="o_ThreadNeedactionPreview" t-on-click="_onClick" t-att-data-thread-local-id="thread ? thread.localId : undefined">
+ <t t-if="thread">
+ <div class="o_ThreadNeedactionPreview_sidebar">
+ <div class="o_ThreadNeedactionPreview_imageContainer o_ThreadNeedactionPreview_sidebarItem">
+ <img class="o_ThreadNeedactionPreview_image" t-att-src="image()" alt="Thread Image"/>
+ <t t-if="thread.correspondent and thread.correspondent.im_status">
+ <PartnerImStatusIcon
+ class="o_ThreadNeedactionPreview_partnerImStatusIcon"
+ t-att-class="{
+ 'o-mobile': env.messaging.device.isMobile,
+ }"
+ partnerLocalId="thread.correspondent.localId"
+ />
+ </t>
+ </div>
+ </div>
+ <div class="o_ThreadNeedactionPreview_content">
+ <div class="o_ThreadNeedactionPreview_header">
+ <span class="o_ThreadNeedactionPreview_name" t-att-class="{ 'o-mobile': env.messaging.device.isMobile }">
+ <t t-esc="thread.displayName"/>
+ </span>
+ <span class="o_ThreadNeedactionPreview_counter">
+ (<t t-esc="thread.needactionMessagesAsOriginThread.length"/>)
+ </span>
+ <span class="o-autogrow"/>
+ <t t-if="thread.lastNeedactionMessageAsOriginThread">
+ <span class="o_ThreadNeedactionPreview_date">
+ <t t-esc="thread.lastNeedactionMessageAsOriginThread.date.fromNow()"/>
+ </span>
+ </t>
+ </div>
+ <div class="o_ThreadNeedactionPreview_core">
+ <span class="o_ThreadNeedactionPreview_coreItem o_ThreadNeedactionPreview_inlineText" t-att-class="{ 'o-empty': inlineLastNeedactionMessageAsOriginThreadBody.length === 0 }">
+ <t t-if="thread.lastNeedactionMessageAsOriginThread and thread.lastNeedactionMessageAsOriginThread.author">
+ <MessageAuthorPrefix
+ messageLocalId="thread.lastNeedactionMessageAsOriginThread.localId"
+ threadLocalId="thread.localId"
+ />
+ </t>
+ <t t-esc="inlineLastNeedactionMessageAsOriginThreadBody"/>
+ </span>
+ <span class="o-autogrow"/>
+ <span class="o_ThreadNeedactionPreview_coreItem o_ThreadNeedactionPreview_markAsRead fa fa-check" title="Mark as Read" t-on-click="_onClickMarkAsRead" t-ref="markAsRead"/>
+ </div>
+ </div>
+ </t>
+ </div>
+ </t>
+
+</templates>
diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js
new file mode 100644
index 00000000..ca1fe22c
--- /dev/null
+++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js
@@ -0,0 +1,457 @@
+odoo.define('mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js', function (require) {
+'use strict';
+
+const components = {
+ ThreadNeedactionPreview: require('mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js'),
+};
+
+const {
+ afterEach,
+ afterNextRender,
+ beforeEach,
+ createRootComponent,
+ start,
+} = require('mail/static/src/utils/test_utils.js');
+
+const Bus = require('web.Bus');
+
+QUnit.module('mail', {}, function () {
+QUnit.module('components', {}, function () {
+QUnit.module('thread_needaction_preview', {}, function () {
+QUnit.module('thread_needaction_preview_tests.js', {
+ beforeEach() {
+ beforeEach(this);
+
+ this.createThreadNeedactionPreviewComponent = async props => {
+ await createRootComponent(this, components.ThreadNeedactionPreview, {
+ props,
+ target: this.widget.el
+ });
+ };
+
+ this.start = async params => {
+ const { afterEvent, env, widget } = await start(Object.assign({}, params, {
+ data: this.data,
+ }));
+ this.afterEvent = afterEvent;
+ this.env = env;
+ this.widget = widget;
+ };
+ },
+ afterEach() {
+ afterEach(this);
+ },
+});
+
+QUnit.test('mark as read', async function (assert) {
+ assert.expect(5);
+
+ this.data['mail.message'].records.push({
+ id: 21,
+ model: 'res.partner',
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ res_id: 11,
+ });
+ this.data['mail.notification'].records.push({
+ mail_message_id: 21,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: this.data.currentPartnerId,
+ });
+ await this.start({
+ hasChatWindow: true,
+ hasMessagingMenu: true,
+ async mockRPC(route, args) {
+ if (route.includes('mark_all_as_read')) {
+ assert.step('mark_all_as_read');
+ assert.deepEqual(
+ args.kwargs.domain,
+ [
+ ['model', '=', 'res.partner'],
+ ['res_id', '=', 11],
+ ],
+ "should mark all as read the correct thread"
+ );
+ }
+ return this._super(...arguments);
+ },
+ });
+ await afterNextRender(() => this.afterEvent({
+ eventName: 'o-thread-cache-loaded-messages',
+ func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
+ message: "should wait until inbox loaded initial needaction messages",
+ predicate: ({ threadCache }) => {
+ return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox';
+ },
+ }));
+ assert.containsOnce(
+ document.body,
+ '.o_ThreadNeedactionPreview_markAsRead',
+ "should have 1 mark as read button"
+ );
+
+ await afterNextRender(() =>
+ document.querySelector('.o_ThreadNeedactionPreview_markAsRead').click()
+ );
+ assert.verifySteps(
+ ['mark_all_as_read'],
+ "should have marked the thread as read"
+ );
+ assert.containsNone(
+ document.body,
+ '.o_ChatWindow',
+ "should not have opened the thread"
+ );
+});
+
+QUnit.test('click on preview should mark as read and open the thread', async function (assert) {
+ assert.expect(6);
+
+ this.data['mail.message'].records.push({
+ id: 21,
+ model: 'res.partner',
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ res_id: 11,
+ });
+ this.data['mail.notification'].records.push({
+ mail_message_id: 21,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: this.data.currentPartnerId,
+ });
+ await this.start({
+ hasChatWindow: true,
+ hasMessagingMenu: true,
+ async mockRPC(route, args) {
+ if (route.includes('mark_all_as_read')) {
+ assert.step('mark_all_as_read');
+ assert.deepEqual(
+ args.kwargs.domain,
+ [
+ ['model', '=', 'res.partner'],
+ ['res_id', '=', 11],
+ ],
+ "should mark all as read the correct thread"
+ );
+ }
+ return this._super(...arguments);
+ },
+ });
+ await afterNextRender(() => this.afterEvent({
+ eventName: 'o-thread-cache-loaded-messages',
+ func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
+ message: "should wait until inbox loaded initial needaction messages",
+ predicate: ({ threadCache }) => {
+ return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox';
+ },
+ }));
+ assert.containsOnce(
+ document.body,
+ '.o_ThreadNeedactionPreview',
+ "should have a preview initially"
+ );
+ assert.containsNone(
+ document.body,
+ '.o_ChatWindow',
+ "should have no chat window initially"
+ );
+
+ await afterNextRender(() =>
+ document.querySelector('.o_ThreadNeedactionPreview').click()
+ );
+ assert.verifySteps(
+ ['mark_all_as_read'],
+ "should have marked the message as read on clicking on the preview"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_ChatWindow',
+ "should have opened the thread on clicking on the preview"
+ );
+});
+
+QUnit.test('click on expand from chat window should close the chat window and open the form view', async function (assert) {
+ assert.expect(8);
+
+ const bus = new Bus();
+ bus.on('do-action', null, payload => {
+ assert.step('do_action');
+ assert.strictEqual(
+ payload.action.res_id,
+ 11,
+ "should redirect to the id of the thread"
+ );
+ assert.strictEqual(
+ payload.action.res_model,
+ 'res.partner',
+ "should redirect to the model of the thread"
+ );
+ });
+ this.data['mail.message'].records.push({
+ id: 21,
+ model: 'res.partner',
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ res_id: 11,
+ });
+ this.data['mail.notification'].records.push({
+ mail_message_id: 21,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: this.data.currentPartnerId,
+ });
+ await this.start({
+ env: { bus },
+ hasChatWindow: true,
+ hasMessagingMenu: true,
+ });
+ await afterNextRender(() => this.afterEvent({
+ eventName: 'o-thread-cache-loaded-messages',
+ func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
+ message: "should wait until inbox loaded initial needaction messages",
+ predicate: ({ threadCache }) => {
+ return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox';
+ },
+ }));
+ assert.containsOnce(
+ document.body,
+ '.o_ThreadNeedactionPreview',
+ "should have a preview initially"
+ );
+ await afterNextRender(() =>
+ document.querySelector('.o_ThreadNeedactionPreview').click()
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_ChatWindow',
+ "should have opened the thread on clicking on the preview"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_ChatWindowHeader_commandExpand',
+ "should have an expand button"
+ );
+
+ await afterNextRender(() =>
+ document.querySelector('.o_ChatWindowHeader_commandExpand').click()
+ );
+ assert.containsNone(
+ document.body,
+ '.o_ChatWindow',
+ "should have closed the chat window on clicking expand"
+ );
+ assert.verifySteps(
+ ['do_action'],
+ "should have done an action to open the form view"
+ );
+});
+
+QUnit.test('[technical] opening a non-channel chat window should not call channel_fold', async function (assert) {
+ // channel_fold should not be called when opening non-channels in chat
+ // window, because there is no server sync of fold state for them.
+ assert.expect(3);
+
+ this.data['mail.message'].records.push({
+ id: 21,
+ model: 'res.partner',
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ res_id: 11,
+ });
+ this.data['mail.notification'].records.push({
+ mail_message_id: 21,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: this.data.currentPartnerId,
+ });
+ await this.start({
+ hasChatWindow: true,
+ hasMessagingMenu: true,
+ async mockRPC(route, args) {
+ if (route.includes('channel_fold')) {
+ const message = "should not call channel_fold when opening a non-channel chat window";
+ assert.step(message);
+ console.error(message);
+ throw Error(message);
+ }
+ return this._super(...arguments);
+ },
+ });
+ await afterNextRender(() => this.afterEvent({
+ eventName: 'o-thread-cache-loaded-messages',
+ func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
+ message: "should wait until inbox loaded initial needaction messages",
+ predicate: ({ threadCache }) => {
+ return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox';
+ },
+ }));
+ assert.containsOnce(
+ document.body,
+ '.o_ThreadNeedactionPreview',
+ "should have a preview initially"
+ );
+ assert.containsNone(
+ document.body,
+ '.o_ChatWindow',
+ "should have no chat window initially"
+ );
+
+ await afterNextRender(() =>
+ document.querySelector('.o_ThreadNeedactionPreview').click()
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_ChatWindow',
+ "should have opened the chat window on clicking on the preview"
+ );
+});
+
+QUnit.test('preview should display last needaction message preview even if there is a more recent message that is not needaction in the thread', async function (assert) {
+ assert.expect(2);
+
+ this.data['res.partner'].records.push({
+ id: 11,
+ name: "Stranger",
+ });
+ this.data['mail.message'].records.push({
+ author_id: 11,
+ body: "I am the oldest but needaction",
+ id: 21,
+ model: 'res.partner',
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ res_id: 11,
+ });
+ this.data['mail.message'].records.push({
+ author_id: this.data.currentPartnerId,
+ body: "I am more recent",
+ id: 22,
+ model: 'res.partner',
+ res_id: 11,
+ });
+ this.data['mail.notification'].records.push({
+ mail_message_id: 21,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: this.data.currentPartnerId,
+ });
+ await this.start({
+ hasChatWindow: true,
+ hasMessagingMenu: true,
+ });
+ await afterNextRender(() => this.afterEvent({
+ eventName: 'o-thread-cache-loaded-messages',
+ func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
+ message: "should wait until inbox loaded initial needaction messages",
+ predicate: ({ threadCache }) => {
+ return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox';
+ },
+ }));
+ assert.containsOnce(
+ document.body,
+ '.o_ThreadNeedactionPreview_inlineText',
+ "should have a preview from the last message"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_ThreadNeedactionPreview_inlineText').textContent,
+ 'Stranger: I am the oldest but needaction',
+ "the displayed message should be the one that needs action even if there is a more recent message that is not needaction on the thread"
+ );
+});
+
+QUnit.test('needaction preview should only show on its origin thread', async function (assert) {
+ assert.expect(2);
+
+ this.data['mail.channel'].records.push({ id: 12 });
+ this.data['mail.message'].records.push({
+ channel_ids: [12],
+ id: 21,
+ model: 'res.partner',
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ res_id: 11,
+ });
+ this.data['mail.notification'].records.push({
+ mail_message_id: 21,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: this.data.currentPartnerId,
+ });
+ await this.start({ hasMessagingMenu: true });
+ await afterNextRender(() => this.afterEvent({
+ eventName: 'o-thread-cache-loaded-messages',
+ func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
+ message: "should wait until inbox loaded initial needaction messages",
+ predicate: ({ threadCache }) => {
+ return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox';
+ },
+ }));
+ assert.containsOnce(
+ document.body,
+ '.o_ThreadNeedactionPreview',
+ "should have only one preview"
+ );
+ const thread = this.env.models['mail.thread'].findFromIdentifyingData({
+ id: 11,
+ model: 'res.partner',
+ });
+ assert.containsOnce(
+ document.body,
+ `.o_ThreadNeedactionPreview[data-thread-local-id="${thread.localId}"]`,
+ "preview should be on the origin thread"
+ );
+});
+
+QUnit.test('chat window header should not have unread counter for non-channel thread', async function (assert) {
+ assert.expect(2);
+
+ this.data['res.partner'].records.push({ id: 11 });
+ this.data['mail.message'].records.push({
+ author_id: 11,
+ body: 'not empty',
+ id: 21,
+ model: 'res.partner',
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ res_id: 11,
+ });
+ this.data['mail.notification'].records.push({
+ mail_message_id: 21,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: this.data.currentPartnerId,
+ });
+ await this.start({
+ hasChatWindow: true,
+ hasMessagingMenu: true,
+ });
+ await afterNextRender(() => this.afterEvent({
+ eventName: 'o-thread-cache-loaded-messages',
+ func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
+ message: "should wait until inbox loaded initial needaction messages",
+ predicate: ({ threadCache }) => {
+ return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox';
+ },
+ }));
+ await afterNextRender(() =>
+ document.querySelector('.o_ThreadNeedactionPreview').click()
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_ChatWindow',
+ "should have opened the chat window on clicking on the preview"
+ );
+ assert.containsNone(
+ document.body,
+ '.o_ChatWindowHeader_counter',
+ "chat window header should not have unread counter for non-channel thread"
+ );
+});
+
+});
+});
+});
+
+});