From 9dd1bcd80e18f70440b2d1d1db83a673c6000e3f Mon Sep 17 00:00:00 2001 From: tsm-odoo Date: Wed, 21 Jun 2023 09:36:37 +0000 Subject: [PATCH] [IMP] im_livechat: improve session history tree view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR improves the livechat session history list view: - Clicking on a session opens the channel in discuss if the user is a member of it. Opens the form view otherwise. - Display duration of the session (last message datetime - first message datetime) - Display country of the visitor part of task-3332872 closes odoo/odoo#125899 Signed-off-by: Alexandre Kühn (aku) --- addons/im_livechat/__manifest__.py | 1 + addons/im_livechat/models/discuss_channel.py | 8 +++++ .../discuss_channel_list_view.js | 13 ++++++++ .../discuss_channel_list_view_controller.js | 30 +++++++++++++++++++ .../views/discuss_channel_views.xml | 5 +++- .../static/src/core/common/thread_service.js | 17 +++++++++++ .../fields/float_time/float_time_field.js | 6 +++- 7 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 addons/im_livechat/static/src/views/discuss_channel_list/discuss_channel_list_view.js create mode 100644 addons/im_livechat/static/src/views/discuss_channel_list/discuss_channel_list_view_controller.js diff --git a/addons/im_livechat/__manifest__.py b/addons/im_livechat/__manifest__.py index e72519d3ee0..27c0768cfca 100644 --- a/addons/im_livechat/__manifest__.py +++ b/addons/im_livechat/__manifest__.py @@ -71,6 +71,7 @@ Help your customers with this chat, and analyse their feedback. 'im_livechat/static/src/js/colors_reset_button/*', 'im_livechat/static/src/js/im_livechat_chatbot_steps_one2many.js', 'im_livechat/static/src/js/im_livechat_chatbot_script_answers_m2m.js', + 'im_livechat/static/src/views/**/*', 'im_livechat/static/src/chat_window/**/*', 'im_livechat/static/src/composer/**/*', 'im_livechat/static/src/core/**/*', diff --git a/addons/im_livechat/models/discuss_channel.py b/addons/im_livechat/models/discuss_channel.py index 54576acf155..2bca787f580 100644 --- a/addons/im_livechat/models/discuss_channel.py +++ b/addons/im_livechat/models/discuss_channel.py @@ -18,6 +18,7 @@ class DiscussChannel(models.Model): anonymous_name = fields.Char('Anonymous Name') channel_type = fields.Selection(selection_add=[('livechat', 'Livechat Conversation')], ondelete={'livechat': 'cascade'}) + duration = fields.Float('Duration', compute='_compute_duration', help='Duration of the session in hours') livechat_active = fields.Boolean('Is livechat ongoing?', help='Livechat session is active until visitor leaves the conversation.') livechat_channel_id = fields.Many2one('im_livechat.channel', 'Channel') livechat_operator_id = fields.Many2one('res.partner', string='Operator') @@ -28,6 +29,13 @@ class DiscussChannel(models.Model): _sql_constraints = [('livechat_operator_id', "CHECK((channel_type = 'livechat' and livechat_operator_id is not null) or (channel_type != 'livechat'))", 'Livechat Operator ID is required for a channel of type livechat.')] + @api.depends('message_ids') + def _compute_duration(self): + for record in self: + start = record.message_ids[-1].date if record.message_ids else record.create_date + end = record.message_ids[0].date if record.message_ids else fields.Datetime.now() + record.duration = (end - start).total_seconds() / 3600 + def _compute_is_chat(self): super()._compute_is_chat() for record in self: diff --git a/addons/im_livechat/static/src/views/discuss_channel_list/discuss_channel_list_view.js b/addons/im_livechat/static/src/views/discuss_channel_list/discuss_channel_list_view.js new file mode 100644 index 00000000000..a3fb96515e8 --- /dev/null +++ b/addons/im_livechat/static/src/views/discuss_channel_list/discuss_channel_list_view.js @@ -0,0 +1,13 @@ +/* @odoo-module */ + +import { DiscussChannelListController } from "@im_livechat/views/discuss_channel_list/discuss_channel_list_view_controller"; + +import { listView } from "@web/views/list/list_view"; +import { registry } from "@web/core/registry"; + +const discussChannelListView = { + ...listView, + Controller: DiscussChannelListController, +}; + +registry.category("views").add("im_livechat.discuss_channel_list", discussChannelListView); diff --git a/addons/im_livechat/static/src/views/discuss_channel_list/discuss_channel_list_view_controller.js b/addons/im_livechat/static/src/views/discuss_channel_list/discuss_channel_list_view_controller.js new file mode 100644 index 00000000000..260410c8f21 --- /dev/null +++ b/addons/im_livechat/static/src/views/discuss_channel_list/discuss_channel_list_view_controller.js @@ -0,0 +1,30 @@ +/* @odoo-module */ + +import { createLocalId } from "@mail/utils/common/misc"; +import { _t } from "@web/core/l10n/translation"; +import { useService } from "@web/core/utils/hooks"; +import { ListController } from "@web/views/list/list_controller"; + +export class DiscussChannelListController extends ListController { + setup() { + super.setup(...arguments); + this.threadService = useService("mail.thread"); + this.store = useService("mail.store"); + } + + async openRecord(record) { + if (!record.data.is_member) { + return super.openRecord(record); + } + const channel = + this.store.threads[createLocalId(record.resModel, record.resId)] ?? + (await this.threadService.fetchChannel(record.resId)); + if (!channel.is_pinned) { + this.threadService.pin(channel); + } + this.threadService.setDiscussThread(channel); + this.actionService.doAction("mail.action_discuss", { + name: _t("Discuss"), + }); + } +} diff --git a/addons/im_livechat/views/discuss_channel_views.xml b/addons/im_livechat/views/discuss_channel_views.xml index bd20b5bb117..3f25e5aa8ad 100644 --- a/addons/im_livechat/views/discuss_channel_views.xml +++ b/addons/im_livechat/views/discuss_channel_views.xml @@ -28,9 +28,12 @@ discuss.channel.tree discuss.channel - + + + + diff --git a/addons/mail/static/src/core/common/thread_service.js b/addons/mail/static/src/core/common/thread_service.js index d1718fcd460..6c7e733fd14 100644 --- a/addons/mail/static/src/core/common/thread_service.js +++ b/addons/mail/static/src/core/common/thread_service.js @@ -72,6 +72,20 @@ export class ThreadService { return thread; } + /** + * @param {import("@mail/core/common/thread_model").Thread} thread + * @param {number} id + * @returns {Promise} + */ + async fetchChannel(id) { + const [channelData] = await this.orm.call("discuss.channel", "channel_info", [id]); + return this.insert({ + ...channelData, + model: "discuss.channel", + type: channelData.channel.channel_type, + }); + } + async fetchChannelMembers(thread) { const known_member_ids = thread.channelMembers.map((channelMember) => channelMember.id); const results = await this.rpc("/discuss/channel/members", { @@ -439,6 +453,9 @@ export class ThreadService { } unpin(thread) { + if (this.store.discuss.threadLocalId === thread.localId) { + this.router.replaceState({ active_id: undefined }); + } if (thread.model !== "discuss.channel") { return; } diff --git a/addons/web/static/src/views/fields/float_time/float_time_field.js b/addons/web/static/src/views/fields/float_time/float_time_field.js index ac0170babe4..60b1a5ad1f6 100644 --- a/addons/web/static/src/views/fields/float_time/float_time_field.js +++ b/addons/web/static/src/views/fields/float_time/float_time_field.js @@ -16,6 +16,7 @@ export class FloatTimeField extends Component { ...standardFieldProps, inputType: { type: String, optional: true }, placeholder: { type: String, optional: true }, + displaySeconds: { type: Boolean, optional: true }, }; static defaultProps = { inputType: "text", @@ -31,7 +32,9 @@ export class FloatTimeField extends Component { } get formattedValue() { - return formatFloatTime(this.props.record.data[this.props.name]); + return formatFloatTime(this.props.record.data[this.props.name], { + displaySeconds: this.props.displaySeconds, + }); } } @@ -48,6 +51,7 @@ export const floatTimeField = { supportedTypes: ["float"], isEmpty: () => false, extractProps: ({ attrs, options }) => ({ + displaySeconds: options.displaySeconds, inputType: options.type, placeholder: attrs.placeholder, }),