From 3751379f1e9a4c215fb6eb898b4ccc67659b9ace Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Tue, 10 May 2022 21:51:50 +0700 Subject: initial commit 2 --- .../src/components/thread_icon/thread_icon.js | 64 +++++++++++ .../src/components/thread_icon/thread_icon.scss | 26 +++++ .../src/components/thread_icon/thread_icon.xml | 58 ++++++++++ .../components/thread_icon/thread_icon_tests.js | 118 +++++++++++++++++++++ 4 files changed, 266 insertions(+) create mode 100644 addons/mail/static/src/components/thread_icon/thread_icon.js create mode 100644 addons/mail/static/src/components/thread_icon/thread_icon.scss create mode 100644 addons/mail/static/src/components/thread_icon/thread_icon.xml create mode 100644 addons/mail/static/src/components/thread_icon/thread_icon_tests.js (limited to 'addons/mail/static/src/components/thread_icon') diff --git a/addons/mail/static/src/components/thread_icon/thread_icon.js b/addons/mail/static/src/components/thread_icon/thread_icon.js new file mode 100644 index 00000000..71017ec0 --- /dev/null +++ b/addons/mail/static/src/components/thread_icon/thread_icon.js @@ -0,0 +1,64 @@ +odoo.define('mail/static/src/components/thread_icon/thread_icon.js', function (require) { +'use strict'; + +const components = { + ThreadTypingIcon: require('mail/static/src/components/thread_typing_icon/thread_typing_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 { Component } = owl; + +class ThreadIcon extends Component { + + /** + * @override + */ + constructor(...args) { + super(...args); + useShouldUpdateBasedOnProps(); + useStore(props => { + const thread = this.env.models['mail.thread'].get(props.threadLocalId); + const correspondent = thread ? thread.correspondent : undefined; + return { + correspondent, + correspondentImStatus: correspondent && correspondent.im_status, + history: this.env.messaging.history, + inbox: this.env.messaging.inbox, + moderation: this.env.messaging.moderation, + partnerRoot: this.env.messaging.partnerRoot, + starred: this.env.messaging.starred, + thread, + threadChannelType: thread && thread.channel_type, + threadModel: thread && thread.model, + threadOrderedOtherTypingMembersLength: thread && thread.orderedOtherTypingMembers.length, + threadPublic: thread && thread.public, + threadTypingStatusText: thread && thread.typingStatusText, + }; + }); + } + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @returns {mail.thread} + */ + get thread() { + return this.env.models['mail.thread'].get(this.props.threadLocalId); + } + +} + +Object.assign(ThreadIcon, { + components, + props: { + threadLocalId: String, + }, + template: 'mail.ThreadIcon', +}); + +return ThreadIcon; + +}); diff --git a/addons/mail/static/src/components/thread_icon/thread_icon.scss b/addons/mail/static/src/components/thread_icon/thread_icon.scss new file mode 100644 index 00000000..3824ec44 --- /dev/null +++ b/addons/mail/static/src/components/thread_icon/thread_icon.scss @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------ +// Layout +// ------------------------------------------------------------------ + +.o_ThreadIcon { + display: flex; + width: 13px; + justify-content: center; + flex: 0 0 auto; +} + +.o_ThreadIcon_typing { + flex: 1 1 auto; +} + +// ------------------------------------------------------------------ +// Style +// ------------------------------------------------------------------ + +.o_ThreadIcon_away { + color: theme-color('warning'); +} + +.o_ThreadIcon_online { + color: $o-enterprise-primary-color; +} diff --git a/addons/mail/static/src/components/thread_icon/thread_icon.xml b/addons/mail/static/src/components/thread_icon/thread_icon.xml new file mode 100644 index 00000000..95c4694a --- /dev/null +++ b/addons/mail/static/src/components/thread_icon/thread_icon.xml @@ -0,0 +1,58 @@ + + + + +
+ + + + +
+ + +
+ + + + + + + +
+ + +
+ + +
+ + +
+ + +
+ + + + +
+ + +
+ + +
+ + +
+ + + +
+
+ + diff --git a/addons/mail/static/src/components/thread_icon/thread_icon_tests.js b/addons/mail/static/src/components/thread_icon/thread_icon_tests.js new file mode 100644 index 00000000..d233d6f8 --- /dev/null +++ b/addons/mail/static/src/components/thread_icon/thread_icon_tests.js @@ -0,0 +1,118 @@ +odoo.define('mail/static/src/components/thread_icon/thread_icon_tests.js', function (require) { +'use strict'; + +const components = { + ThreadIcon: require('mail/static/src/components/thread_icon/thread_icon.js'), +}; +const { + afterEach, + afterNextRender, + beforeEach, + createRootComponent, + start, +} = require('mail/static/src/utils/test_utils.js'); + +QUnit.module('mail', {}, function () { +QUnit.module('components', {}, function () { +QUnit.module('thread_icon', {}, function () { +QUnit.module('thread_icon_tests.js', { + beforeEach() { + beforeEach(this); + + this.createThreadIcon = async thread => { + await createRootComponent(this, components.ThreadIcon, { + props: { threadLocalId: thread.localId }, + 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('chat: correspondent is typing', async function (assert) { + assert.expect(5); + + this.data['res.partner'].records.push({ + id: 17, + im_status: 'online', + name: 'Demo', + }); + this.data['mail.channel'].records.push({ + channel_type: 'chat', + id: 20, + members: [this.data.currentPartnerId, 17], + }); + await this.start(); + const thread = this.env.models['mail.thread'].findFromIdentifyingData({ + id: 20, + model: 'mail.channel', + }); + await this.createThreadIcon(thread); + + assert.containsOnce( + document.body, + '.o_ThreadIcon', + "should have thread icon" + ); + assert.containsOnce( + document.body, + '.o_ThreadIcon_online', + "should have thread icon with partner im status icon 'online'" + ); + + // simulate receive typing notification from demo "is typing" + await afterNextRender(() => { + const typingData = { + info: 'typing_status', + is_typing: true, + partner_id: 17, + partner_name: "Demo", + }; + const notification = [[false, 'mail.channel', 20], typingData]; + this.widget.call('bus_service', 'trigger', 'notification', [notification]); + }); + assert.containsOnce( + document.body, + '.o_ThreadIcon_typing', + "should have thread icon with partner currently typing" + ); + assert.strictEqual( + document.querySelector('.o_ThreadIcon_typing').title, + "Demo is typing...", + "title of icon should tell demo is currently typing" + ); + + // simulate receive typing notification from demo "no longer is typing" + await afterNextRender(() => { + const typingData = { + info: 'typing_status', + is_typing: false, + partner_id: 17, + partner_name: "Demo", + }; + const notification = [[false, 'mail.channel', 20], typingData]; + this.widget.call('bus_service', 'trigger', 'notification', [notification]); + }); + assert.containsOnce( + document.body, + '.o_ThreadIcon_online', + "should have thread icon with partner im status icon 'online' (no longer typing)" + ); +}); + +}); +}); +}); + +}); -- cgit v1.2.3