From 3751379f1e9a4c215fb6eb898b4ccc67659b9ace Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Tue, 10 May 2022 21:51:50 +0700 Subject: initial commit 2 --- .../message_seen_indicator.js | 136 ++++++++++ .../message_seen_indicator.scss | 39 +++ .../message_seen_indicator.xml | 16 ++ .../message_seen_indicator_tests.js | 294 +++++++++++++++++++++ 4 files changed, 485 insertions(+) create mode 100644 addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.js create mode 100644 addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.scss create mode 100644 addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.xml create mode 100644 addons/mail/static/src/components/message_seen_indicator/message_seen_indicator_tests.js (limited to 'addons/mail/static/src/components/message_seen_indicator') diff --git a/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.js b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.js new file mode 100644 index 00000000..ed555b0c --- /dev/null +++ b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.js @@ -0,0 +1,136 @@ +odoo.define('mail/static/src/components/message_seen_indicator/message_seen_indicator.js', function (require) { +'use strict'; + +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 { Component } = owl; + +class MessageSeenIndicator extends Component { + + /** + * @override + */ + constructor(...args) { + super(...args); + useShouldUpdateBasedOnProps(); + useStore(props => { + const message = this.env.models['mail.message'].get(props.messageLocalId); + const thread = this.env.models['mail.thread'].get(props.threadLocalId); + const messageSeenIndicator = thread && thread.model === 'mail.channel' + ? this.env.models['mail.message_seen_indicator'].findFromIdentifyingData({ + channelId: thread.id, + messageId: message.id, + }) + : undefined; + return { + messageSeenIndicator: messageSeenIndicator ? messageSeenIndicator.__state : undefined, + }; + }); + } + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @returns {string} + */ + get indicatorTitle() { + if (!this.messageSeenIndicator) { + return ''; + } + if (this.messageSeenIndicator.hasEveryoneSeen) { + return this.env._t("Seen by Everyone"); + } + if (this.messageSeenIndicator.hasSomeoneSeen) { + const partnersThatHaveSeen = this.messageSeenIndicator.partnersThatHaveSeen.map( + partner => partner.name + ); + if (partnersThatHaveSeen.length === 1) { + return _.str.sprintf( + this.env._t("Seen by %s"), + partnersThatHaveSeen[0] + ); + } + if (partnersThatHaveSeen.length === 2) { + return _.str.sprintf( + this.env._t("Seen by %s and %s"), + partnersThatHaveSeen[0], + partnersThatHaveSeen[1] + ); + } + return _.str.sprintf( + this.env._t("Seen by %s, %s and more"), + partnersThatHaveSeen[0], + partnersThatHaveSeen[1] + ); + } + if (this.messageSeenIndicator.hasEveryoneFetched) { + return this.env._t("Received by Everyone"); + } + if (this.messageSeenIndicator.hasSomeoneFetched) { + const partnersThatHaveFetched = this.messageSeenIndicator.partnersThatHaveFetched.map( + partner => partner.name + ); + if (partnersThatHaveFetched.length === 1) { + return _.str.sprintf( + this.env._t("Received by %s"), + partnersThatHaveFetched[0] + ); + } + if (partnersThatHaveFetched.length === 2) { + return _.str.sprintf( + this.env._t("Received by %s and %s"), + partnersThatHaveFetched[0], + partnersThatHaveFetched[1] + ); + } + return _.str.sprintf( + this.env._t("Received by %s, %s and more"), + partnersThatHaveFetched[0], + partnersThatHaveFetched[1] + ); + } + return ''; + } + + /** + * @returns {mail.message} + */ + get message() { + return this.env.models['mail.message'].get(this.props.messageLocalId); + } + + /** + * @returns {mail.message_seen_indicator} + */ + get messageSeenIndicator() { + if (!this.thread || this.thread.model !== 'mail.channel') { + return undefined; + } + return this.env.models['mail.message_seen_indicator'].findFromIdentifyingData({ + channelId: this.thread.id, + messageId: this.message.id, + }); + } + + /** + * @returns {mail.Thread} + */ + get thread() { + return this.env.models['mail.thread'].get(this.props.threadLocalId); + } +} + +Object.assign(MessageSeenIndicator, { + props: { + messageLocalId: String, + threadLocalId: String, + }, + template: 'mail.MessageSeenIndicator', +}); + +return MessageSeenIndicator; + +}); diff --git a/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.scss b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.scss new file mode 100644 index 00000000..3a9d566e --- /dev/null +++ b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.scss @@ -0,0 +1,39 @@ +// ------------------------------------------------------------------ +// Layout +// ------------------------------------------------------------------ + +.o_MessageSeenIndicator { + display: flex; + position: relative; + flex-wrap: nowrap; +} + +.o_MessageSeenIndicator_icon { + + &.o-first { + padding-left: map-get($spacers, 1); + } + + &.o-second { + position: absolute; + top: -1px; + left: -1px; + } +} + +// ------------------------------------------------------------------ +// Style +// ------------------------------------------------------------------ + +.o_MessageSeenIndicator { + opacity: 0.6; + + &.o-all-seen { + color: $o-brand-odoo; + } + + &:hover { + cursor: pointer; + opacity: 0.8; + } +} diff --git a/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.xml b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.xml new file mode 100644 index 00000000..e905afaa --- /dev/null +++ b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator_tests.js b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator_tests.js new file mode 100644 index 00000000..fb9c6b8b --- /dev/null +++ b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator_tests.js @@ -0,0 +1,294 @@ +odoo.define('mail/static/src/components/message_seen_indicator/message_seen_indicator_tests', function (require) { +'use strict'; + +const components = { + MessageSendIndicator: require('mail/static/src/components/message_seen_indicator/message_seen_indicator.js'), +}; +const { + afterEach, + beforeEach, + createRootComponent, + start, +} = require('mail/static/src/utils/test_utils.js'); + +QUnit.module('mail', {}, function () { +QUnit.module('components', {}, function () { +QUnit.module('message_seen_indicator', {}, function () { +QUnit.module('message_seen_indicator_tests.js', { + beforeEach() { + beforeEach(this); + + this.createMessageSeenIndicatorComponent = async ({ message, thread }, otherProps) => { + const props = Object.assign( + { messageLocalId: message.localId, threadLocalId: thread.localId }, + otherProps + ); + await createRootComponent(this, components.MessageSendIndicator, { + props, + target: this.widget.el, + }); + }; + + 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('rendering when just one has received the message', async function (assert) { + assert.expect(3); + + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 1000, + model: 'mail.channel', + partnerSeenInfos: [['create', [ + { + channelId: 1000, + lastFetchedMessage: [['insert', { id: 100 }]], + partnerId: 10, + }, + { + channelId: 1000, + partnerId: 100, + }, + ]]], + messageSeenIndicators: [['insert', { + channelId: 1000, + messageId: 100, + }]], + }); + const message = this.env.models['mail.message'].insert({ + author: [['insert', { id: this.env.messaging.currentPartner.id, display_name: "Demo User" }]], + body: "

Test

", + id: 100, + originThread: [['link', thread]], + }); + await this.createMessageSeenIndicatorComponent({ message, thread }); + assert.containsOnce( + document.body, + '.o_MessageSeenIndicator', + "should display a message seen indicator component" + ); + assert.doesNotHaveClass( + document.querySelector('.o_MessageSeenIndicator'), + 'o-all-seen', + "indicator component should not be considered as all seen" + ); + assert.containsOnce( + document.body, + '.o_MessageSeenIndicator_icon', + "should display only one seen indicator icon" + ); +}); + +QUnit.test('rendering when everyone have received the message', async function (assert) { + assert.expect(3); + + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 1000, + model: 'mail.channel', + partnerSeenInfos: [['create', [ + { + channelId: 1000, + lastFetchedMessage: [['insert', { id: 100 }]], + partnerId: 10, + }, + { + channelId: 1000, + lastFetchedMessage: [['insert', { id: 99 }]], + partnerId: 100, + }, + ]]], + messageSeenIndicators: [['insert', { + channelId: 1000, + messageId: 100, + }]], + }); + const message = this.env.models['mail.message'].insert({ + author: [['insert', { id: this.env.messaging.currentPartner.id, display_name: "Demo User" }]], + body: "

Test

", + id: 100, + originThread: [['link', thread]], + }); + await this.createMessageSeenIndicatorComponent({ message, thread }); + assert.containsOnce( + document.body, + '.o_MessageSeenIndicator', + "should display a message seen indicator component" + ); + assert.doesNotHaveClass( + document.querySelector('.o_MessageSeenIndicator'), + 'o-all-seen', + "indicator component should not be considered as all seen" + ); + assert.containsOnce( + document.body, + '.o_MessageSeenIndicator_icon', + "should display only one seen indicator icon" + ); +}); + +QUnit.test('rendering when just one has seen the message', async function (assert) { + assert.expect(3); + + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 1000, + model: 'mail.channel', + partnerSeenInfos: [['create', [ + { + channelId: 1000, + lastFetchedMessage: [['insert', { id: 100 }]], + lastSeenMessage: [['insert', { id: 100 }]], + partnerId: 10, + }, + { + channelId: 1000, + lastFetchedMessage: [['insert', { id: 99 }]], + partnerId: 100, + }, + ]]], + messageSeenIndicators: [['insert', { + channelId: 1000, + messageId: 100, + }]], + }); + const message = this.env.models['mail.message'].insert({ + author: [['insert', { id: this.env.messaging.currentPartner.id, display_name: "Demo User" }]], + body: "

Test

", + id: 100, + originThread: [['link', thread]], + }); + await this.createMessageSeenIndicatorComponent({ message, thread }); + assert.containsOnce( + document.body, + '.o_MessageSeenIndicator', + "should display a message seen indicator component" + ); + assert.doesNotHaveClass( + document.querySelector('.o_MessageSeenIndicator'), + 'o-all-seen', + "indicator component should not be considered as all seen" + ); + assert.containsN( + document.body, + '.o_MessageSeenIndicator_icon', + 2, + "should display two seen indicator icon" + ); +}); + +QUnit.test('rendering when just one has seen & received the message', async function (assert) { + assert.expect(3); + + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 1000, + model: 'mail.channel', + partnerSeenInfos: [['create', [ + { + channelId: 1000, + lastFetchedMessage: [['insert', { id: 100 }]], + lastSeenMessage: [['insert', { id: 100 }]], + partnerId: 10, + }, + { + channelId: 1000, + partnerId: 100, + }, + ]]], + messageSeenIndicators: [['insert', { + channelId: 1000, + messageId: 100, + }]], + }); + const message = this.env.models['mail.message'].insert({ + author: [['insert', { id: this.env.messaging.currentPartner.id, display_name: "Demo User" }]], + body: "

Test

", + id: 100, + originThread: [['link', thread]], + }); + await this.createMessageSeenIndicatorComponent({ message, thread }); + assert.containsOnce( + document.body, + '.o_MessageSeenIndicator', + "should display a message seen indicator component" + ); + assert.doesNotHaveClass( + document.querySelector('.o_MessageSeenIndicator'), + 'o-all-seen', + "indicator component should not be considered as all seen" + ); + assert.containsN( + document.body, + '.o_MessageSeenIndicator_icon', + 2, + "should display two seen indicator icon" + ); +}); + +QUnit.test('rendering when just everyone has seen the message', async function (assert) { + assert.expect(3); + + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 1000, + model: 'mail.channel', + partnerSeenInfos: [['create', [ + { + channelId: 1000, + lastFetchedMessage: [['insert', { id: 100 }]], + lastSeenMessage: [['insert', { id: 100 }]], + partnerId: 10, + }, + { + channelId: 1000, + lastFetchedMessage: [['insert', { id: 100 }]], + lastSeenMessage: [['insert', { id: 100 }]], + partnerId: 100, + }, + ]]], + messageSeenIndicators: [['insert', { + channelId: 1000, + messageId: 100, + }]], + }); + const message = this.env.models['mail.message'].insert({ + author: [['insert', { id: this.env.messaging.currentPartner.id, display_name: "Demo User" }]], + body: "

Test

", + id: 100, + originThread: [['link', thread]], + }); + await this.createMessageSeenIndicatorComponent({ message, thread }); + assert.containsOnce( + document.body, + '.o_MessageSeenIndicator', + "should display a message seen indicator component" + ); + assert.hasClass( + document.querySelector('.o_MessageSeenIndicator'), + 'o-all-seen', + "indicator component should not considered as all seen" + ); + assert.containsN( + document.body, + '.o_MessageSeenIndicator_icon', + 2, + "should display two seen indicator icon" + ); +}); + +}); +}); +}); + +}); -- cgit v1.2.3