diff --git a/addons/mail/static/src/components/message/message.js b/addons/mail/static/src/components/message/message.js index e63d35332ea..0f410b78673 100644 --- a/addons/mail/static/src/components/message/message.js +++ b/addons/mail/static/src/components/message/message.js @@ -140,7 +140,7 @@ class Message extends Component { // TODO FIXME for public user this might not be accessible. task-2223236 // we should probably use the correspondig attachment id + access token // or create a dedicated route to get message image, checking the access right of the message - return `/web/image/res.partner/${this.message.author.id}/image_128`; + return this.message.author.avatarUrl; } else if (this.message.message_type === 'email') { return '/mail/static/src/img/email_icon.png'; } diff --git a/addons/mail/static/src/components/message/message.scss b/addons/mail/static/src/components/message/message.scss index bb2fea6e47d..0b03a8ba835 100644 --- a/addons/mail/static/src/components/message/message.scss +++ b/addons/mail/static/src/components/message/message.scss @@ -131,8 +131,8 @@ } .o_Message_sidebar { - flex: 0 0 50px; - max-width: 50px; + flex: 0 0 $o-mail-message-sidebar-width; + max-width: $o-mail-message-sidebar-width; display: flex; margin-inline-end: map-get($spacers, 2); justify-content: center; 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 index 2b6bede0909..5caefd7f341 100644 --- 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 @@ -64,7 +64,7 @@ class ThreadNeedactionPreview extends Component { return this.thread.moduleIcon; } if (this.thread.correspondent) { - return `/web/image/res.partner/${this.thread.correspondent.id}/image_128`; + return this.thread.correspondent.avatarUrl; } if (this.thread.model === 'mail.channel') { return `/web/image/mail.channel/${this.thread.id}/image_128`; diff --git a/addons/mail/static/src/components/thread_preview/thread_preview.js b/addons/mail/static/src/components/thread_preview/thread_preview.js index 0e5a226a5f2..b10e8ee2586 100644 --- a/addons/mail/static/src/components/thread_preview/thread_preview.js +++ b/addons/mail/static/src/components/thread_preview/thread_preview.js @@ -59,7 +59,7 @@ class ThreadPreview extends Component { */ image() { if (this.thread.correspondent) { - return `/web/image/res.partner/${this.thread.correspondent.id}/image_128`; + return this.thread.correspondent.avatarUrl; } return `/web/image/mail.channel/${this.thread.id}/image_128`; } diff --git a/addons/mail/static/src/models/country/country.js b/addons/mail/static/src/models/country/country.js index 2e44f54712d..fb3617cf783 100644 --- a/addons/mail/static/src/models/country/country.js +++ b/addons/mail/static/src/models/country/country.js @@ -3,6 +3,7 @@ odoo.define('mail/static/src/models/country/country.js', function (require) { const { registerNewModel } = require('mail/static/src/model/model_core.js'); const { attr } = require('mail/static/src/model/model_field.js'); +const { clear } = require('mail/static/src/model/model_field_command.js'); function factory(dependencies) { @@ -19,9 +20,27 @@ function factory(dependencies) { return `${this.modelName}_${data.id}`; } + /** + * @private + * @returns {string|undefined} + */ + _computeFlagUrl() { + if (!this.code) { + return clear(); + } + return `/base/static/img/country_flags/${this.code}.png`; + } + } Country.fields = { + code: attr(), + flagUrl: attr({ + compute: '_computeFlagUrl', + dependencies: [ + 'code', + ], + }), id: attr(), name: attr(), }; diff --git a/addons/mail/static/src/models/partner/partner.js b/addons/mail/static/src/models/partner/partner.js index 4a8a1c54f0d..e52e148c9e1 100644 --- a/addons/mail/static/src/models/partner/partner.js +++ b/addons/mail/static/src/models/partner/partner.js @@ -190,6 +190,14 @@ function factory(dependencies) { // Private //---------------------------------------------------------------------- + /** + * @private + * @returns {string} + */ + _computeAvatarUrl() { + return `/web/image/res.partner/${this.id}/image_128`; + } + /** * @override */ @@ -266,6 +274,12 @@ function factory(dependencies) { active: attr({ default: true, }), + avatarUrl: attr({ + compute: '_computeAvatarUrl', + dependencies: [ + 'id', + ], + }), correspondentThreads: one2many('mail.thread', { inverse: 'correspondent', }), diff --git a/addons/mail/static/src/variables.scss b/addons/mail/static/src/variables.scss index 8d8caab19d6..e9bae9ab8cc 100644 --- a/addons/mail/static/src/variables.scss +++ b/addons/mail/static/src/variables.scss @@ -10,5 +10,7 @@ $o-mail-discuss-sidebar-active-indicator-margin-right: 10px !default; $o-mail-discuss-sidebar-active-indicator-width: 3px !default; $o-mail-discuss-sidebar-scrollbar-width: 15px !default; +$o-mail-message-sidebar-width: 50px; + $o-mail-moderation-accept-color: theme-color('success') !default; $o-mail-moderation-reject-color: theme-color('danger') !default; diff --git a/addons/website_livechat/__manifest__.py b/addons/website_livechat/__manifest__.py index 2ffb375b6eb..f30748fec11 100644 --- a/addons/website_livechat/__manifest__.py +++ b/addons/website_livechat/__manifest__.py @@ -24,5 +24,7 @@ Allow website visitors to chat with the collaborators. This module also brings a 'qweb': [ 'static/src/bugfix/bugfix.xml', 'static/src/bugfix/public_bugfix.xml', + 'static/src/components/discuss/discuss.xml', + 'static/src/components/visitor_banner/visitor_banner.xml', ], } diff --git a/addons/website_livechat/models/mail_channel.py b/addons/website_livechat/models/mail_channel.py index f82559491ab..8d6f91969ac 100644 --- a/addons/website_livechat/models/mail_channel.py +++ b/addons/website_livechat/models/mail_channel.py @@ -35,6 +35,7 @@ class MailChannel(models.Model): channel_infos_dict[channel.id]['visitor'] = { 'name': visitor.display_name, 'country_code': visitor.country_id.code.lower() if visitor.country_id else False, + 'country_id': visitor.country_id.id, 'is_connected': visitor.is_connected, 'history': self.sudo()._get_visitor_history(visitor), 'website': visitor.website_id.name, diff --git a/addons/website_livechat/static/src/components/discuss/discuss.js b/addons/website_livechat/static/src/components/discuss/discuss.js new file mode 100644 index 00000000000..18b41cea432 --- /dev/null +++ b/addons/website_livechat/static/src/components/discuss/discuss.js @@ -0,0 +1,13 @@ +odoo.define('website_livechat/static/src/components/discuss/discuss.js', function (require) { +'use strict'; + +const components = { + Discuss: require('mail/static/src/components/discuss/discuss.js'), + VisitorBanner: require('website_livechat/static/src/components/visitor_banner/visitor_banner.js'), +}; + +Object.assign(components.Discuss.components, { + VisitorBanner: components.VisitorBanner, +}); + +}); diff --git a/addons/website_livechat/static/src/components/discuss/discuss.xml b/addons/website_livechat/static/src/components/discuss/discuss.xml new file mode 100644 index 00000000000..af5a14696f0 --- /dev/null +++ b/addons/website_livechat/static/src/components/discuss/discuss.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/addons/website_livechat/static/src/components/discuss/discuss_tests.js b/addons/website_livechat/static/src/components/discuss/discuss_tests.js new file mode 100644 index 00000000000..153e55e4533 --- /dev/null +++ b/addons/website_livechat/static/src/components/discuss/discuss_tests.js @@ -0,0 +1,269 @@ +odoo.define('website_livechat/static/src/components/discuss/discuss_tests.js', function (require) { +'use strict'; + +const { + afterEach, + beforeEach, + start, +} = require('mail/static/src/utils/test_utils.js'); + +QUnit.module('website_livechat', {}, function () { +QUnit.module('components', {}, function () { +QUnit.module('discuss', {}, function () { +QUnit.module('discuss_tests.js', { + beforeEach() { + beforeEach(this); + + this.start = async params => { + const { env, widget } = await start(Object.assign({}, params, { + autoOpenDiscuss: true, + data: this.data, + hasDiscuss: true, + })); + this.env = env; + this.widget = widget; + }; + }, + afterEach() { + afterEach(this); + }, +}); + +QUnit.test('rendering of visitor banner', async function (assert) { + assert.expect(13); + + this.data['res.country'].records.push({ + id: 11, + code: 'FAKE', + }); + this.data['website.visitor'].records.push({ + id: 11, + country_id: 11, + display_name: 'Visitor #11', + history: 'Home → Contact', + is_connected: true, + lang: "English", + website: "General website", + }); + this.data['mail.channel'].records.push({ + channel_type: 'livechat', + id: 11, + livechat_operator_id: this.data.currentPartnerId, + livechat_visitor_id: 11, + members: [this.data.currentPartnerId, this.data.publicPartnerId], + }); + await this.start({ + discuss: { + context: { + active_id: 'mail.channel_11', + }, + }, + }); + assert.containsOnce( + document.body, + '.o_VisitorBanner', + "should have a visitor banner", + ); + assert.containsOnce( + document.body, + '.o_VisitorBanner_avatar', + "should show the visitor avatar in the banner", + ); + assert.strictEqual( + document.querySelector('.o_VisitorBanner_avatar').dataset.src, + "/mail/static/src/img/smiley/avatar.jpg", + "should show the default avatar", + ); + assert.containsOnce( + document.body, + '.o_VisitorBanner_onlineStatusIcon', + "should show the visitor online status icon on the avatar", + ); + assert.strictEqual( + document.querySelector('.o_VisitorBanner_country').dataset.src, + "/base/static/img/country_flags/FAKE.png", + "should show the flag of the country of the visitor", + ); + assert.containsOnce( + document.body, + '.o_VisitorBanner_visitor', + "should show the visitor name in the banner", + ); + assert.strictEqual( + document.querySelector('.o_VisitorBanner_visitor').textContent, + "Visitor #11", + "should have 'Visitor #11' as visitor name", + ); + assert.containsOnce( + document.body, + '.o_VisitorBanner_language', + "should show the visitor language in the banner", + ); + assert.strictEqual( + document.querySelector('.o_VisitorBanner_language').textContent, + "English", + "should have 'English' as language of the visitor", + ); + assert.containsOnce( + document.body, + '.o_VisitorBanner_website', + "should show the visitor website in the banner", + ); + assert.strictEqual( + document.querySelector('.o_VisitorBanner_website').textContent, + "General website", + "should have 'General website' as website of the visitor", + ); + assert.containsOnce( + document.body, + '.o_VisitorBanner_history', + "should show the visitor history in the banner", + ); + assert.strictEqual( + document.querySelector('.o_VisitorBanner_history').textContent, + "Home → Contact", + "should have 'Home → Contact' as history of the visitor", + ); +}); + +QUnit.test('livechat with non-logged visitor should show visitor banner', async function (assert) { + assert.expect(1); + + this.data['res.country'].records.push({ + id: 11, + code: 'FAKE', + }); + this.data['website.visitor'].records.push({ + id: 11, + country_id: 11, + display_name: 'Visitor #11', + history: 'Home → Contact', + is_connected: true, + lang: "English", + website: "General website", + }); + this.data['mail.channel'].records.push({ + channel_type: 'livechat', + id: 11, + livechat_operator_id: this.data.currentPartnerId, + livechat_visitor_id: 11, + members: [this.data.currentPartnerId, this.data.publicPartnerId], + }); + await this.start({ + discuss: { + context: { + active_id: 'mail.channel_11', + }, + }, + }); + assert.containsOnce( + document.body, + '.o_VisitorBanner', + "should have a visitor banner", + ); +}); + +QUnit.test('livechat with logged visitor should show visitor banner', async function (assert) { + assert.expect(2); + + this.data['res.country'].records.push({ + id: 11, + code: 'FAKE', + }); + this.data['res.partner'].records.push({ + id: 12, + name: 'Partner Visitor', + }); + this.data['website.visitor'].records.push({ + id: 11, + country_id: 11, + display_name: 'Visitor #11', + history: 'Home → Contact', + is_connected: true, + lang: "English", + partner_id: 12, + website: "General website", + }); + this.data['mail.channel'].records.push({ + channel_type: 'livechat', + id: 11, + livechat_operator_id: this.data.currentPartnerId, + livechat_visitor_id: 11, + members: [this.data.currentPartnerId, 12], + }); + await this.start({ + discuss: { + context: { + active_id: 'mail.channel_11', + }, + }, + }); + assert.containsOnce( + document.body, + '.o_VisitorBanner', + "should have a visitor banner", + ); + assert.strictEqual( + document.querySelector('.o_VisitorBanner_visitor').textContent, + "Partner Visitor", + "should have partner name as display name of logged visitor on the visitor banner" + ); +}); + +QUnit.test('livechat without visitor should not show visitor banner', async function (assert) { + assert.expect(2); + + this.data['res.partner'].records.push({ id: 11 }); + this.data['mail.channel'].records.push({ + channel_type: 'livechat', + id: 11, + livechat_operator_id: this.data.currentPartnerId, + members: [this.data.currentPartnerId, 11], + }); + await this.start({ + discuss: { + context: { + active_id: 'mail.channel_11', + }, + }, + }); + assert.containsOnce( + document.body, + '.o_MessageList', + "should have a message list", + ); + assert.containsNone( + document.body, + '.o_VisitorBanner', + "should not have any visitor banner", + ); +}); + +QUnit.test('non-livechat channel should not show visitor banner', async function (assert) { + assert.expect(2); + + this.data['mail.channel'].records.push({ id: 11, name: "General" }); + await this.start({ + discuss: { + context: { + active_id: 'mail.channel_11', + }, + }, + }); + assert.containsOnce( + document.body, + '.o_MessageList', + "should have a message list", + ); + assert.containsNone( + document.body, + '.o_VisitorBanner', + "should not have any visitor banner", + ); +}); + +}); +}); +}); + +}); diff --git a/addons/website_livechat/static/src/components/visitor_banner/visitor_banner.js b/addons/website_livechat/static/src/components/visitor_banner/visitor_banner.js new file mode 100644 index 00000000000..0b39134dcfc --- /dev/null +++ b/addons/website_livechat/static/src/components/visitor_banner/visitor_banner.js @@ -0,0 +1,45 @@ +odoo.define('website_livechat/static/src/components/visitor_banner/visitor_banner.js', function (require) { +'use strict'; + +const useStore = require('mail/static/src/component_hooks/use_store/use_store.js'); + +const { Component } = owl; + +class VisitorBanner extends Component { + + /** + * @override + */ + constructor(...args) { + super(...args); + useStore(props => { + const visitor = this.env.models['website_livechat.visitor'].get(props.visitorLocalId); + return { + visitor: visitor ? visitor.__state : undefined, + }; + }); + } + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @returns {website_livechat.visitor} + */ + get visitor() { + return this.env.models['website_livechat.visitor'].get(this.props.visitorLocalId); + } + +} + +Object.assign(VisitorBanner, { + props: { + visitorLocalId: String, + }, + template: 'website_livechat.VisitorBanner', +}); + +return VisitorBanner; + +}); diff --git a/addons/website_livechat/static/src/components/visitor_banner/visitor_banner.scss b/addons/website_livechat/static/src/components/visitor_banner/visitor_banner.scss new file mode 100644 index 00000000000..9f042d2a16e --- /dev/null +++ b/addons/website_livechat/static/src/components/visitor_banner/visitor_banner.scss @@ -0,0 +1,90 @@ +// ----------------------------------------------------------------------------- +// Layout +// ----------------------------------------------------------------------------- + +.o_VisitorBanner { + border-bottom-width: $border-width; + border-bottom-style: solid; + display: flex; + flex: 0 0 auto; + padding: map-get($spacers, 4) map-get($spacers, 2); +} + +.o_VisitorBanner_avatar { + height: map-get($sizes, 100); + width: map-get($sizes, 100); + object-fit: cover; +} + +.o_VisitorBanner_avatarContainer { + height: $o-mail-thread-avatar-size; + width: $o-mail-thread-avatar-size; + margin-left: map-get($spacers, 1); + margin-right: map-get($spacers, 1); + position: relative; +} + +.o_VisitorBanner_country { + margin-inline-end: map-get($spacers, 1); +} + +.o_VisitorBanner_history { + margin-top: map-get($spacers, 1); +} + +.o_VisitorBanner_historyIcon { + margin-inline-end: map-get($spacers, 1); +} + +.o_VisitorBanner_language { + margin-inline-end: map-get($spacers, 3); +} + +.o_VisitorBanner_languageIcon { + margin-inline-end: map-get($spacers, 1); +} + +.o_VisitorBanner_onlineStatusIcon { + @include o-position-absolute($bottom: 0, $right: 0); + display: flex; + align-items: center; + justify-content: center; + flex-flow: column; + width: 1.2em; + height: 1.2em; + line-height: 1.3em; + font-size: x-small; +} + +.o_VisitorBanner_sidebar { + display: flex; + flex: 0 0 $o-mail-message-sidebar-width; + justify-content: center; + margin-inline-end: map-get($spacers, 2); + max-width: $o-mail-message-sidebar-width; +} + +.o_VisitorBanner_visitor { + margin-inline-end: map-get($spacers, 3); +} + +.o_VisitorBanner_websiteIcon { + margin-inline-end: map-get($spacers, 1); +} + +// ------------------------------------------------------------------ +// Style +// ------------------------------------------------------------------ + +.o_VisitorBanner { + background: $white; + border-bottom-color: gray('400'); +} + +.o_VisitorBanner_onlineStatusIcon { + color: $o-enterprise-primary-color; +} + +.o_VisitorBanner_visitor { + font-weight: $font-weight-bold; +} diff --git a/addons/website_livechat/static/src/components/visitor_banner/visitor_banner.xml b/addons/website_livechat/static/src/components/visitor_banner/visitor_banner.xml new file mode 100644 index 00000000000..b1dc4bd3283 --- /dev/null +++ b/addons/website_livechat/static/src/components/visitor_banner/visitor_banner.xml @@ -0,0 +1,37 @@ + + + + +
+
+
+ Avatar + + + +
+
+
+ + + + + + + + + + + + + + +
+ + +
+
+
+
+ +
diff --git a/addons/website_livechat/static/src/models/thread/thread.js b/addons/website_livechat/static/src/models/thread/thread.js new file mode 100644 index 00000000000..46d80ca794f --- /dev/null +++ b/addons/website_livechat/static/src/models/thread/thread.js @@ -0,0 +1,45 @@ +odoo.define('website_livechat/static/src/models/thread/thread.js', function (require) { +'use strict'; + +const { + registerClassPatchModel, + registerFieldPatchModel, +} = require('mail/static/src/model/model_core.js'); +const { many2one } = require('mail/static/src/model/model_field.js'); + +registerClassPatchModel('mail.thread', 'website_livechat/static/src/models/thread/thread.js', { + + //---------------------------------------------------------------------- + // Public + //---------------------------------------------------------------------- + + /** + * @override + */ + convertData(data) { + const data2 = this._super(data); + if ('visitor' in data) { + if (data.visitor) { + data2.visitor = [[ + 'insert', + this.env.models['website_livechat.visitor'].convertData(data.visitor) + ]]; + } else { + data2.visitor = [['unlink']]; + } + } + return data2; + }, + +}); + +registerFieldPatchModel('mail.thread', 'website_livechat/static/src/models/thread/thread.js', { + /** + * Visitor connected to the livechat. + */ + visitor: many2one('website_livechat.visitor', { + inverse: 'threads', + }), +}); + +}); diff --git a/addons/website_livechat/static/src/models/visitor/visitor.js b/addons/website_livechat/static/src/models/visitor/visitor.js new file mode 100644 index 00000000000..8c1b417e0d0 --- /dev/null +++ b/addons/website_livechat/static/src/models/visitor/visitor.js @@ -0,0 +1,169 @@ +odoo.define('website_livechat/static/src/models/partner/partner.js', function (require) { +'use strict'; + +const { registerNewModel } = require('mail/static/src/model/model_core.js'); +const { attr, many2one, one2many } = require('mail/static/src/model/model_field.js'); + +function factory(dependencies) { + + class Visitor extends dependencies['mail.model'] { + //---------------------------------------------------------------------- + // Public + //---------------------------------------------------------------------- + + /** + * @override + */ + static convertData(data) { + const data2 = {}; + if ('country_id' in data) { + if (data.country_id) { + data2.country = [['insert', { + id: data.country_id, + code: data.country_code, + }]]; + } else { + data2.country = [['unlink']]; + } + } + if ('history' in data) { + data2.history = data.history; + } + if ('is_connected' in data) { + data2.is_connected = data.is_connected; + } + if ('lang' in data) { + data2.lang = data.lang; + } + if ('name' in data) { + data2.name = data.name; + } + if ('partner_id' in data) { + if (data.partner_id) { + data2.partner = [['insert', { id: data.partner_id }]]; + } else { + data2.partner = [['unlink']]; + } + } + if ('website' in data) { + data2.website = data.website; + } + return data2; + } + + //---------------------------------------------------------------------- + // Private + //---------------------------------------------------------------------- + + /** + * @private + * @returns {string} + */ + _computeAvatarUrl() { + if (!this.partner) { + return '/mail/static/src/img/smiley/avatar.jpg'; + } + return this.partner.avatarUrl; + } + + /** + * @private + * @returns {mail.country} + */ + _computeCountry() { + if (this.partner && this.partner.country) { + return [['link', this.partner.country]]; + } + if (this.country) { + return [['link', this.country]]; + } + return [['unlink']]; + } + + /** + * @private + * @returns {string} + */ + _computeNameOrDisplayName() { + if (this.partner) { + return this.partner.nameOrDisplayName; + } + return this.name; + } + } + + Visitor.fields = { + /** + * Url to the avatar of the visitor. + */ + avatarUrl: attr({ + compute: '_computeAvatarUrl', + dependencies: [ + 'partner', + 'partnerAvatarUrl', + ], + }), + /** + * Country of the visitor. + */ + country: many2one('mail.country', { + compute: '_computeCountry', + dependencies: [ + 'country', + 'partnerCountry', + ], + }), + /** + * Browsing history of the visitor as a string. + */ + history: attr(), + /** + * Determine whether the visitor is connected or not. + */ + is_connected: attr(), + /** + * Name of the language of the visitor. (Ex: "English") + */ + lang: attr(), + /** + * Name of the visitor. + */ + name: attr(), + nameOrDisplayName: attr({ + compute: '_computeNameOrDisplayName', + dependencies: [ + 'name', + 'partnerNameOrDisplayName', + ], + }), + /** + * Partner linked to this visitor, if any. + */ + partner: many2one('mail.partner'), + partnerAvatarUrl: attr({ + related: 'partner.avatarUrl', + }), + partnerCountry: many2one('mail.country',{ + related: 'partner.country', + }), + partnerNameOrDisplayName: attr({related: 'partner.nameOrDisplayName'}), + /** + * Threads with this visitor as member + */ + threads: one2many('mail.thread', { + inverse: 'visitor', + }), + /** + * Name of the website on which the visitor is connected. (Ex: "Website 1") + */ + website: attr(), + }; + + Visitor.modelName = 'website_livechat.visitor'; + + return Visitor; +} + +registerNewModel('website_livechat.visitor', factory); + +}); diff --git a/addons/website_livechat/static/tests/helpers/mock_models.js b/addons/website_livechat/static/tests/helpers/mock_models.js index 93ae70331a8..bc4079596f8 100644 --- a/addons/website_livechat/static/tests/helpers/mock_models.js +++ b/addons/website_livechat/static/tests/helpers/mock_models.js @@ -17,7 +17,19 @@ MockModels.patch('website_livechat/static/tests/helpers/mock_models.js', T => const data = super.generateData(...arguments); Object.assign(data, { 'website.visitor': { - fields: {}, + fields: { + country_id: { string: "Country", type: 'many2one', relation: 'res.country' }, + display_name: { string: "Display name", type: 'string' }, + // Represent the browsing history of the visitor as a string. + // To ease testing this allows tests to set it directly instead + // of implementing the computation made on server. + // This should normally not be a field. + history: { string: "History", type: 'string'}, + is_connected: { string: "Is connected", type: 'boolean' }, + lang: { string: "Language", type: 'string'}, + partner_id: {string: "partner", type: "many2one", relation: 'res.partner'}, + website: { string: "Website", type: 'string' }, + }, records: [], }, }); diff --git a/addons/website_livechat/static/tests/helpers/mock_server.js b/addons/website_livechat/static/tests/helpers/mock_server.js index 8a75f07abfe..1383e521fe9 100644 --- a/addons/website_livechat/static/tests/helpers/mock_server.js +++ b/addons/website_livechat/static/tests/helpers/mock_server.js @@ -17,6 +17,32 @@ MockServer.include({ } return this._super(...arguments); }, + /** + * Overrides to add visitor information to livechat channels. + * + * @override + */ + _mockMailChannelChannelInfo(ids, extra_info) { + const channelInfos = this._super(...arguments); + for (const channelInfo of channelInfos) { + const channel = this._getRecords('mail.channel', [['id', '=', channelInfo.id]])[0]; + if (channel.channel_type === 'livechat' && channelInfo.livechat_visitor_id) { + const visitor = this._getRecords('website.visitor', [['id', '=', channelInfo.livechat_visitor_id]])[0]; + const country = this._getRecords('res.country', [['id', '=', visitor.country_id]])[0]; + channelInfo.visitor = { + name: visitor.display_name, + country_code: country && country.code, + country_id: country && country.id, + is_connected: visitor.is_connected, + history: visitor.history, // TODO should be computed + website: visitor.website, + lang: visitor.lang, + partner_id: visitor.partner_id, + } + } + } + return channelInfos; + }, /** * @private * @param {integer[]} ids diff --git a/addons/website_livechat/views/assets.xml b/addons/website_livechat/views/assets.xml index 92f52b64f75..7fe3cb2c102 100644 --- a/addons/website_livechat/views/assets.xml +++ b/addons/website_livechat/views/assets.xml @@ -4,11 +4,16 @@