From 08a06c6e8f001b073c5ed06db7a3a9e305b9c883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Theys?= Date: Mon, 5 Jun 2023 15:01:38 +0000 Subject: [PATCH] [REF] mail: make thread actions more generic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will remove the necessity to patch component, and remove duplciation between discuss and chat window. Part of task-3265211 closes odoo/odoo#123454 Signed-off-by: Alexandre Kühn (aku) --- .../embed/chat_window/chat_window_patch.js | 19 --- .../static/src/embed/core/thread_actions.js | 17 +++ .../static/src/chat_window/chat_window.js | 48 +------- .../static/src/chat_window/chat_window.xml | 27 +++-- addons/mail/static/src/core/thread_actions.js | 111 ++++++++++++++++++ .../static/src/core_ui/primary_variables.scss | 2 +- .../static/src/discuss/call/thread_actions.js | 42 +++++++ .../static/src/discuss/core/call_settings.xml | 2 +- .../src/discuss/core/channel_invitation.js | 8 +- .../src/discuss/core/channel_invitation.scss | 4 + .../src/discuss/core/channel_invitation.xml | 2 +- .../src/discuss/core/channel_member_list.js | 2 +- .../static/src/discuss/core/discuss_patch.js | 45 ------- .../src/discuss/core/discuss_patch.scss | 7 -- .../static/src/discuss/core/discuss_patch.xml | 20 ---- .../static/src/discuss/core/thread_actions.js | 60 ++++++++++ .../src/discuss/message_pin/discuss_patch.js | 32 ----- .../src/discuss/message_pin/discuss_patch.xml | 13 -- .../message_pin/pinned_messages_panel.js | 2 +- .../message_pin/pinned_messages_panel.xml | 3 +- .../src/discuss/message_pin/thread_actions.js | 34 ++++++ .../src/discuss/message_pin/thread_patch.js | 6 - .../message_pin/web/chat_window_patch.js | 46 -------- .../message_pin/web/chat_window_patch.xml | 8 -- .../src/discuss/web/chat_window_patch.js | 64 ---------- .../src/discuss/web/chat_window_patch.xml | 10 -- .../static/src/discuss/web/thread_actions.js | 29 +++++ addons/mail/static/src/discuss_app/discuss.js | 15 +-- .../mail/static/src/discuss_app/discuss.scss | 4 + .../mail/static/src/discuss_app/discuss.xml | 11 +- .../src/scss/variables/primary_variables.scss | 2 +- .../src/web/chat_window/chat_window_patch.js | 40 ------- addons/mail/static/src/web/thread_actions.js | 49 ++++++++ .../tests/discuss/crosstab/crosstab_tests.js | 1 + .../message_pin/pinned_messages_tests.js | 8 +- 35 files changed, 390 insertions(+), 403 deletions(-) create mode 100644 addons/im_livechat/static/src/embed/core/thread_actions.js create mode 100644 addons/mail/static/src/core/thread_actions.js create mode 100644 addons/mail/static/src/discuss/call/thread_actions.js delete mode 100644 addons/mail/static/src/discuss/core/discuss_patch.js delete mode 100644 addons/mail/static/src/discuss/core/discuss_patch.scss delete mode 100644 addons/mail/static/src/discuss/core/discuss_patch.xml create mode 100644 addons/mail/static/src/discuss/core/thread_actions.js delete mode 100644 addons/mail/static/src/discuss/message_pin/discuss_patch.js delete mode 100644 addons/mail/static/src/discuss/message_pin/discuss_patch.xml create mode 100644 addons/mail/static/src/discuss/message_pin/thread_actions.js delete mode 100644 addons/mail/static/src/discuss/message_pin/web/chat_window_patch.js delete mode 100644 addons/mail/static/src/discuss/message_pin/web/chat_window_patch.xml delete mode 100644 addons/mail/static/src/discuss/web/chat_window_patch.js delete mode 100644 addons/mail/static/src/discuss/web/chat_window_patch.xml create mode 100644 addons/mail/static/src/discuss/web/thread_actions.js create mode 100644 addons/mail/static/src/web/thread_actions.js diff --git a/addons/im_livechat/static/src/embed/chat_window/chat_window_patch.js b/addons/im_livechat/static/src/embed/chat_window/chat_window_patch.js index 1a6690ff984..e047249fe49 100644 --- a/addons/im_livechat/static/src/embed/chat_window/chat_window_patch.js +++ b/addons/im_livechat/static/src/embed/chat_window/chat_window_patch.js @@ -2,7 +2,6 @@ import { ChatWindow } from "@mail/chat_window/chat_window"; import { FeedbackPanel } from "../feedback_panel/feedback_panel"; -import { _t } from "@web/core/l10n/translation"; import { patch } from "@web/core/utils/patch"; import { useService } from "@web/core/utils/hooks"; import { SESSION_STATE } from "../core/livechat_service"; @@ -33,22 +32,4 @@ patch(ChatWindow.prototype, "im_livechat", { this.livechatService.leaveSession(); this.chatbotService.stop(); }, - - get actions() { - if (!this.chatbotService.canRestart) { - return this._super(); - } - return this._super().concat([ - { - id: "restart", - name: _t("Restart Conversation"), - icon: "fa fa-fw fa-refresh", - onSelect: () => { - this.chatbotService.restart(); - this.chatWindowService.show(this.props.chatWindow); - }, - sequence: 99, - }, - ]); - }, }); diff --git a/addons/im_livechat/static/src/embed/core/thread_actions.js b/addons/im_livechat/static/src/embed/core/thread_actions.js new file mode 100644 index 00000000000..5ce5d017594 --- /dev/null +++ b/addons/im_livechat/static/src/embed/core/thread_actions.js @@ -0,0 +1,17 @@ +/* @odoo-module */ + +import { _t } from "@web/core/l10n/translation"; +import { registry } from "@web/core/registry"; + +registry.category("mail.thread/actions").add("restart", { + condition(component) { + return component.chatbotService.canRestart; + }, + icon: "fa fa-fw fa-refresh", + name: _t("Restart Conversation"), + open(component) { + component.chatbotService.restart(); + component.chatWindowService.show(component.props.chatWindow); + }, + sequence: 99, +}); diff --git a/addons/mail/static/src/chat_window/chat_window.js b/addons/mail/static/src/chat_window/chat_window.js index 7810b9b9868..5a89682dee2 100644 --- a/addons/mail/static/src/chat_window/chat_window.js +++ b/addons/mail/static/src/chat_window/chat_window.js @@ -4,6 +4,7 @@ import { Call } from "@mail/rtc/call"; import { Thread } from "@mail/core_ui/thread"; import { Composer } from "@mail/composer/composer"; import { useStore } from "@mail/core/messaging_hook"; +import { useThreadActions } from "@mail/core/thread_actions"; import { useRtc } from "@mail/rtc/rtc_hook"; import { useMessageEdition, useMessageHighlight, useMessageToReplyTo } from "@mail/utils/hooks"; import { Component, useChildSubEnv, useRef, useState } from "@odoo/owl"; @@ -16,10 +17,6 @@ import { _t } from "@web/core/l10n/translation"; import { Dropdown } from "@web/core/dropdown/dropdown"; import { DropdownItem } from "@web/core/dropdown/dropdown_item"; -export const MODES = { - NONE: "", -}; - /** * @typedef {Object} Props * @property {import("@mail/chat_window/chat_window_model").ChatWindow} chatWindow @@ -40,7 +37,6 @@ export class ChatWindow extends Component { static template = "mail.ChatWindow"; setup() { - this.MODES = MODES; this.store = useStore(); /** @type {import("@mail/chat_window/chat_window_service").ChatWindowService} */ this.chatWindowService = useState(useService("mail.chat_window")); @@ -50,19 +46,10 @@ export class ChatWindow extends Component { this.messageEdition = useMessageEdition(); this.messageHighlight = useMessageHighlight(); this.messageToReplyTo = useMessageToReplyTo(); - this.state = useState({ - moreActionsExpanded: false, - /** - * activeMode: - * "member-list": channel member list is displayed - * "in-settings": settings is displayed - * "add-users": add users is displayed (small device) - * "": no action pannel - */ - activeMode: MODES.NONE, - }); + this.state = useState({ moreActionsExpanded: false }); this.ui = useState(useService("ui")); this.contentRef = useRef("content"); + this.threadActions = useThreadActions(); useChildSubEnv({ inChatWindow: true, messageHighlight: this.messageHighlight, @@ -125,35 +112,6 @@ export class ChatWindow extends Component { this.chatWindowService.notifyState(this.props.chatWindow); } - get orderedActions() { - return this.actions.sort((act1, act2) => act1.sequence - act2.sequence); - } - - get actions() { - const acts = []; - if ( - this.thread?.allowCalls && - this.thread !== this.rtc.state.channel && - !this.props.chatWindow.hidden - ) { - acts.push({ - id: "call", - name: _t("Start a Call"), - icon: "fa fa-fw fa-phone", - onSelect: () => this.rtc.toggleCall(this.props.chatWindow.thread), - sequence: 10, - }); - } - acts.push({ - id: "close", - name: _t("Close Chat Window"), - icon: "fa fa-fw fa-close", - onSelect: () => this.close(), - sequence: 100, - }); - return acts; - } - get moreMenuText() { return _t("More actions"); } diff --git a/addons/mail/static/src/chat_window/chat_window.xml b/addons/mail/static/src/chat_window/chat_window.xml index df2d5f42e4d..7cda19d5c1a 100644 --- a/addons/mail/static/src/chat_window/chat_window.xml +++ b/addons/mail/static/src/chat_window/chat_window.xml @@ -32,33 +32,34 @@ ()
- - - + + + - - - + + + - + - + - - - + + +
- + - + + diff --git a/addons/mail/static/src/core/thread_actions.js b/addons/mail/static/src/core/thread_actions.js new file mode 100644 index 00000000000..e7c87cc87d5 --- /dev/null +++ b/addons/mail/static/src/core/thread_actions.js @@ -0,0 +1,111 @@ +/* @odoo-module */ + +import { useComponent, useState } from "@odoo/owl"; +import { _t } from "@web/core/l10n/translation"; +import { registry } from "@web/core/registry"; + +export const threadActionsRegistry = registry.category("mail.thread/actions"); + +threadActionsRegistry.add("close", { + condition(component) { + return component.props.chatWindow; + }, + icon: "fa fa-fw fa-close", + name: _t("Close Chat Window"), + open(component) { + component.close(); + }, + sequence: 100, +}); + +function transformAction(component, id, action) { + return { + /** Closes this action. */ + close() { + if (this.toggle) { + component.threadActions.activeAction = null; + } + action.close?.(component, this); + }, + /** Optional component that should be displayed in the view when this action is active. */ + component: action.component, + /** Condition to display the component of this action. */ + get componentCondition() { + return this.isActive && this.component && this.condition && !this.popover; + }, + /** Props to pass to the component of this action. */ + componentProps: action.componentProps, + /** Condition to display this action. */ + get condition() { + return action.condition(component); + }, + /** Condition to disable the button of this action (but still display it). */ + get disabledCondition() { + return action.disabledCondition?.(component); + }, + /** Icon for the button this action. */ + icon: action.icon, + /** Large icon for the button this action. */ + iconLarge: action.iconLarge ?? action.icon, + /** Unique id of this action. */ + id, + /** States whether this action is currently active. */ + get isActive() { + return id === component.threadActions.activeAction?.id; + }, + /** Name of this action, displayed to the user. */ + get name() { + return this.isActive && action.nameActive ? action.nameActive : action.name; + }, + /** Action to execute when this action is selected (on or off). */ + onSelect() { + if (this.toggle && this.isActive) { + this.close(); + } else { + this.open(); + } + }, + /** Opens this action. */ + open() { + if (this.toggle) { + component.threadActions.activeAction = this; + } + action.open?.(component, this); + }, + /** Determines whether this is a popover linked to this action. */ + popover: null, + /** Determines the order of this action (smaller first). */ + get sequence() { + return typeof action.sequence === "function" + ? action.sequence(component) + : action.sequence; + }, + /** Component setup to execute when this action is registered. */ + setup: action.setup, + /** Text for the button of this action */ + text: action.text, + /** Determines whether this action is a one time effect or can be toggled (on or off). */ + toggle: action.toggle, + }; +} + +export function useThreadActions() { + const component = useComponent(); + const transformedActions = threadActionsRegistry + .getEntries() + .map(([id, action]) => transformAction(component, id, action)); + for (const action of transformedActions) { + if (action.setup) { + action.setup(action); + } + } + const state = useState({ + get actions() { + return transformedActions + .filter((action) => action.condition) + .sort((a1, a2) => a1.sequence - a2.sequence); + }, + activeAction: null, + }); + return state; +} diff --git a/addons/mail/static/src/core_ui/primary_variables.scss b/addons/mail/static/src/core_ui/primary_variables.scss index bac93d107f7..cfaa3aa79ef 100644 --- a/addons/mail/static/src/core_ui/primary_variables.scss +++ b/addons/mail/static/src/core_ui/primary_variables.scss @@ -12,7 +12,7 @@ $o-mail-LinkPreviewCard-height: 80px !default; $o-mail-Message-sidebarWidth: 48px !default; $o-mail-NavigableList-zIndex: 11; $o-mail-Chatter-minWidth: 530px !default; -$o-mail-Discuss-inspector: 250px !default; +$o-mail-Discuss-inspector: 300px !default; $o-mail-Discuss-headerHeight: 48px !default; @mixin o-FileViewer-arrow { diff --git a/addons/mail/static/src/discuss/call/thread_actions.js b/addons/mail/static/src/discuss/call/thread_actions.js new file mode 100644 index 00000000000..731b3dcfe7c --- /dev/null +++ b/addons/mail/static/src/discuss/call/thread_actions.js @@ -0,0 +1,42 @@ +/* @odoo-module */ + +import { threadActionsRegistry } from "@mail/core/thread_actions"; +import { CallSettings } from "@mail/discuss/core/call_settings"; +import { _t } from "@web/core/l10n/translation"; + +threadActionsRegistry + .add("call", { + condition(component) { + return ( + component.thread?.allowCalls && + component.thread !== component.rtc.state.channel && + !component.props.chatWindow?.hidden + ); + }, + icon: "fa fa-fw fa-phone", + iconLarge: "fa fa-fw fa-lg fa-phone", + name: _t("Start a Call"), + open(component) { + component.rtc.toggleCall(component.thread); + }, + sequence: 10, + }) + .add("settings", { + component: CallSettings, + condition(component) { + return ( + component.thread?.allowCalls && + (!component.props.chatWindow || component.props.chatWindow.isOpen) + ); + }, + icon: "fa fa-fw fa-gear", + iconLarge: "fa fa-fw fa-lg fa-gear", + name: _t("Show Call Settings"), + nameActive: _t("Hide Call Settings"), + sequence(component) { + return component.props.chatWindow && component.thread === component.rtc.state.channel + ? 6 + : 60; + }, + toggle: true, + }); diff --git a/addons/mail/static/src/discuss/core/call_settings.xml b/addons/mail/static/src/discuss/core/call_settings.xml index d31684e535c..f19e1e6f29a 100644 --- a/addons/mail/static/src/discuss/core/call_settings.xml +++ b/addons/mail/static/src/discuss/core/call_settings.xml @@ -2,7 +2,7 @@ -
+
Voice Settings
diff --git a/addons/mail/static/src/discuss/core/channel_invitation.js b/addons/mail/static/src/discuss/core/channel_invitation.js index 510fbda3597..6cfc7a985d9 100644 --- a/addons/mail/static/src/discuss/core/channel_invitation.js +++ b/addons/mail/static/src/discuss/core/channel_invitation.js @@ -10,7 +10,7 @@ import { useService } from "@web/core/utils/hooks"; export class ChannelInvitation extends Component { static components = { ImStatus }; static defaultProps = { hasSizeConstraints: false }; - static props = ["hasSizeConstraints?", "thread", "close?", "chatState?"]; + static props = ["hasSizeConstraints?", "thread", "close", "className?"]; static template = "discuss.ChannelInvitation"; setup() { @@ -114,11 +114,7 @@ export class ChannelInvitation extends Component { } ); } - if (this.ui.isSmall) { - this.props.chatState.activeMode = ""; - } else { - this.props.close(); - } + this.props.close(); } get invitationButtonText() { diff --git a/addons/mail/static/src/discuss/core/channel_invitation.scss b/addons/mail/static/src/discuss/core/channel_invitation.scss index 63137575cab..b8035f04ad0 100644 --- a/addons/mail/static/src/discuss/core/channel_invitation.scss +++ b/addons/mail/static/src/discuss/core/channel_invitation.scss @@ -1,3 +1,7 @@ +.o-discuss-ChannelInvitation { + min-height: 0; +} + .o-discuss-ChannelInvitation-has-size-constraints { width: Min(95vw, 400px); max-height: Min(calc(100vh - 140px), 530px); diff --git a/addons/mail/static/src/discuss/core/channel_invitation.xml b/addons/mail/static/src/discuss/core/channel_invitation.xml index 1cd433d70de..40dd51957aa 100644 --- a/addons/mail/static/src/discuss/core/channel_invitation.xml +++ b/addons/mail/static/src/discuss/core/channel_invitation.xml @@ -2,7 +2,7 @@ -
+

Invite people

diff --git a/addons/mail/static/src/discuss/core/channel_member_list.js b/addons/mail/static/src/discuss/core/channel_member_list.js index 0080468c2a6..c57aaf8f8a1 100644 --- a/addons/mail/static/src/discuss/core/channel_member_list.js +++ b/addons/mail/static/src/discuss/core/channel_member_list.js @@ -7,7 +7,7 @@ import { useService } from "@web/core/utils/hooks"; export class ChannelMemberList extends Component { static components = { ImStatus }; - static props = ["thread", "className"]; + static props = ["thread", "className?"]; static template = "discuss.ChannelMemberList"; setup() { diff --git a/addons/mail/static/src/discuss/core/discuss_patch.js b/addons/mail/static/src/discuss/core/discuss_patch.js deleted file mode 100644 index 9ee304550a2..00000000000 --- a/addons/mail/static/src/discuss/core/discuss_patch.js +++ /dev/null @@ -1,45 +0,0 @@ -/* @odoo-module */ - -import { CallSettings } from "@mail/discuss/core/call_settings"; -import { ChannelInvitation } from "@mail/discuss/core/channel_invitation"; -import { ChannelMemberList } from "@mail/discuss/core/channel_member_list"; -import { Discuss } from "@mail/discuss_app/discuss"; -import { useRef } from "@odoo/owl"; -import { usePopover } from "@web/core/popover/popover_hook"; -import { patch } from "@web/core/utils/patch"; - -patch(Discuss, "discuss", { - components: { ...Discuss.components, CallSettings, ChannelMemberList }, -}); - -patch(Discuss.prototype, "discuss", { - setup() { - this._super(); - this.addUsersRef = useRef("addUsers"); - this.popover = usePopover(ChannelInvitation, { - onClose: () => (this.state.isAddingUsers = false), - }); - this.state.isAddingUsers = false; - }, - toggleInviteForm() { - if (this.popover.isOpen) { - this.popover.close(); - } else { - this.state.isAddingUsers = true; - this.popover.open(this.addUsersRef.el, { - hasSizeConstraints: true, - thread: this.thread, - }); - } - }, - toggleMemberList() { - this.state.activeMode = - this.state.activeMode === this.MODES.MEMBER_LIST - ? this.MODES.NONE - : this.MODES.MEMBER_LIST; - }, - toggleSettings() { - this.state.activeMode = - this.state.activeMode === this.MODES.SETTINGS ? this.MODES.NONE : this.MODES.SETTINGS; - }, -}); diff --git a/addons/mail/static/src/discuss/core/discuss_patch.scss b/addons/mail/static/src/discuss/core/discuss_patch.scss deleted file mode 100644 index 798f91a5ef5..00000000000 --- a/addons/mail/static/src/discuss/core/discuss_patch.scss +++ /dev/null @@ -1,7 +0,0 @@ -.o-mail-Discuss-callSettings { - width: 300px; -} - -.o-mail-Discuss-channelMemberList { - width: $o-mail-Discuss-inspector; -} diff --git a/addons/mail/static/src/discuss/core/discuss_patch.xml b/addons/mail/static/src/discuss/core/discuss_patch.xml deleted file mode 100644 index b109875dd94..00000000000 --- a/addons/mail/static/src/discuss/core/discuss_patch.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - diff --git a/addons/mail/static/src/discuss/core/thread_actions.js b/addons/mail/static/src/discuss/core/thread_actions.js new file mode 100644 index 00000000000..399e8bee30b --- /dev/null +++ b/addons/mail/static/src/discuss/core/thread_actions.js @@ -0,0 +1,60 @@ +/* @odoo-module */ + +import { threadActionsRegistry } from "@mail/core/thread_actions"; +import { ChannelInvitation } from "@mail/discuss/core/channel_invitation"; +import { ChannelMemberList } from "@mail/discuss/core/channel_member_list"; +import { useComponent } from "@odoo/owl"; +import { _t } from "@web/core/l10n/translation"; +import { usePopover } from "@web/core/popover/popover_hook"; + +threadActionsRegistry + .add("add-users", { + close(component, action) { + action.popover?.close(); + }, + component: ChannelInvitation, + componentProps(action) { + return { close: () => action.close() }; + }, + condition(component) { + return ( + component.thread?.model === "discuss.channel" && + (!component.props.chatWindow || component.props.chatWindow.isOpen) + ); + }, + icon: "fa fa-fw fa-user-plus", + iconLarge: "fa fa-fw fa-lg fa-user-plus", + name: _t("Add Users"), + nameActive: _t("Stop Adding Users"), + open(component, action) { + action.popover?.open(component.root.el.querySelector(`[name="${action.id}"]`), { + hasSizeConstraints: true, + thread: component.thread, + }); + }, + sequence: 30, + setup(action) { + const component = useComponent(); + if (!component.props.chatWindow) { + action.popover = usePopover(ChannelInvitation, { + onClose: () => action.close(), + }); + } + }, + toggle: true, + }) + .add("member-list", { + component: ChannelMemberList, + condition(component) { + return ( + component.thread?.hasMemberList && + (!component.props.chatWindow || component.props.chatWindow.isOpen) + ); + }, + icon: "fa fa-fw fa-users", + iconLarge: "fa fa-fw fa-lg fa-users", + name: _t("Show Member List"), + nameActive: _t("Hide Member List"), + sequence: 40, + toggle: true, + }); diff --git a/addons/mail/static/src/discuss/message_pin/discuss_patch.js b/addons/mail/static/src/discuss/message_pin/discuss_patch.js deleted file mode 100644 index 6a1e6e445e3..00000000000 --- a/addons/mail/static/src/discuss/message_pin/discuss_patch.js +++ /dev/null @@ -1,32 +0,0 @@ -/* @odoo-module */ - -import { PinnedMessagesPanel } from "@mail/discuss/message_pin/pinned_messages_panel"; -import { Discuss, MODES } from "@mail/discuss_app/discuss"; -import { useChildSubEnv } from "@odoo/owl"; -import { patch } from "@web/core/utils/patch"; - -MODES.PINNED_MESSAGES = "pinned-messages"; - -patch(Discuss, "discuss/message_pin", { - components: { ...Discuss.components, PinnedMessagesPanel }, -}); - -patch(Discuss.prototype, "discuss/message_pin", { - setup() { - this._super(); - useChildSubEnv({ - pinMenu: { - open: () => (this.state.activeMode = "pinned-messages"), - close: () => { - if (this.state.activeMode === "pinned-messages") { - this.state.activeMode = ""; - } - }, - }, - }); - }, - togglePinMenu() { - this.state.activeMode = - this.state.activeMode === MODES.PINNED_MESSAGES ? MODES.NONE : MODES.PINNED_MESSAGES; - }, -}); diff --git a/addons/mail/static/src/discuss/message_pin/discuss_patch.xml b/addons/mail/static/src/discuss/message_pin/discuss_patch.xml deleted file mode 100644 index 9a963fe9a97..00000000000 --- a/addons/mail/static/src/discuss/message_pin/discuss_patch.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/addons/mail/static/src/discuss/message_pin/pinned_messages_panel.js b/addons/mail/static/src/discuss/message_pin/pinned_messages_panel.js index 697e3007743..6985f72d198 100644 --- a/addons/mail/static/src/discuss/message_pin/pinned_messages_panel.js +++ b/addons/mail/static/src/discuss/message_pin/pinned_messages_panel.js @@ -11,7 +11,7 @@ export class PinnedMessagesPanel extends Component { static components = { Message, }; - static props = ["thread"]; + static props = ["thread", "className?"]; static template = "discuss.PinnedMessagesPanel"; setup() { diff --git a/addons/mail/static/src/discuss/message_pin/pinned_messages_panel.xml b/addons/mail/static/src/discuss/message_pin/pinned_messages_panel.xml index 150ced64a29..16e7ce3705b 100644 --- a/addons/mail/static/src/discuss/message_pin/pinned_messages_panel.xml +++ b/addons/mail/static/src/discuss/message_pin/pinned_messages_panel.xml @@ -3,8 +3,7 @@
+ }" t-attf-class="{{ props.className }}">

Pinned messages

diff --git a/addons/mail/static/src/discuss/message_pin/thread_actions.js b/addons/mail/static/src/discuss/message_pin/thread_actions.js new file mode 100644 index 00000000000..b569c794bfd --- /dev/null +++ b/addons/mail/static/src/discuss/message_pin/thread_actions.js @@ -0,0 +1,34 @@ +/* @odoo-module */ + +import { threadActionsRegistry } from "@mail/core/thread_actions"; +import { PinnedMessagesPanel } from "@mail/discuss/message_pin/pinned_messages_panel"; +import { useChildSubEnv } from "@odoo/owl"; +import { _t } from "@web/core/l10n/translation"; + +threadActionsRegistry.add("pinned-messages", { + component: PinnedMessagesPanel, + condition(component) { + return ( + component.thread?.model === "discuss.channel" && + (!component.props.chatWindow || component.props.chatWindow.isOpen) + ); + }, + icon: "fa fa-fw fa-thumb-tack", + iconLarge: "fa fa-fw fa-lg fa-thumb-tack", + name: _t("Pinned Messages"), + nameActive: _t("Hide Pinned Messages"), + sequence: 20, + setup(action) { + useChildSubEnv({ + pinMenu: { + open: () => action.open(), + close: () => { + if (action.isActive) { + action.close(); + } + }, + }, + }); + }, + toggle: true, +}); diff --git a/addons/mail/static/src/discuss/message_pin/thread_patch.js b/addons/mail/static/src/discuss/message_pin/thread_patch.js index ff88052d070..f3ef9b06a59 100644 --- a/addons/mail/static/src/discuss/message_pin/thread_patch.js +++ b/addons/mail/static/src/discuss/message_pin/thread_patch.js @@ -1,15 +1,9 @@ /* @odoo-module */ import { Thread } from "@mail/core_ui/thread"; -import { useState } from "@odoo/owl"; -import { useService } from "@web/core/utils/hooks"; import { patch } from "@web/core/utils/patch"; patch(Thread.prototype, "discuss/message_pin", { - setup() { - this._super(); - this.ui = useState(useService("ui")); - }, /** * @override * @param {MouseEvent} ev diff --git a/addons/mail/static/src/discuss/message_pin/web/chat_window_patch.js b/addons/mail/static/src/discuss/message_pin/web/chat_window_patch.js deleted file mode 100644 index d364ee56b27..00000000000 --- a/addons/mail/static/src/discuss/message_pin/web/chat_window_patch.js +++ /dev/null @@ -1,46 +0,0 @@ -/* @odoo-module */ - -import { PinnedMessagesPanel } from "@mail/discuss/message_pin/pinned_messages_panel"; -import { ChatWindow, MODES } from "@mail/chat_window/chat_window"; -import { useChildSubEnv } from "@odoo/owl"; -import { _t } from "@web/core/l10n/translation"; -import { patch } from "@web/core/utils/patch"; - -MODES.PINNED_MESSAGES = "pinned-messages"; - -patch(ChatWindow, "discuss/message_pin", { - components: { ...ChatWindow.components, PinnedMessagesPanel }, -}); - -patch(ChatWindow.prototype, "discuss/message_pin", { - setup() { - this._super(); - useChildSubEnv({ - pinMenu: { - open: () => (this.state.activeMode = MODES.PINNED_MESSAGES), - close: () => { - if (this.state.activeMode === MODES.PINNED_MESSAGES) { - this.state.activeMode = MODES.NONE; - } - }, - }, - }); - }, - togglePinMenu() { - this.state.activeMode = - this.state.activeMode === MODES.PINNED_MESSAGES ? MODES.NONE : MODES.PINNED_MESSAGES; - }, - get actions() { - const acts = this._super(); - if (this.thread?.model === "discuss.channel" && this.props.chatWindow.isOpen) { - acts.push({ - id: "pinned", - name: _t("Pinned Messages"), - icon: "fa fa-fw fa-thumb-tack", - onSelect: () => this.togglePinMenu(), - sequence: 20, - }); - } - return acts; - }, -}); diff --git a/addons/mail/static/src/discuss/message_pin/web/chat_window_patch.xml b/addons/mail/static/src/discuss/message_pin/web/chat_window_patch.xml deleted file mode 100644 index b89f67bc2d3..00000000000 --- a/addons/mail/static/src/discuss/message_pin/web/chat_window_patch.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/mail/static/src/discuss/web/chat_window_patch.js b/addons/mail/static/src/discuss/web/chat_window_patch.js deleted file mode 100644 index 3615dccb555..00000000000 --- a/addons/mail/static/src/discuss/web/chat_window_patch.js +++ /dev/null @@ -1,64 +0,0 @@ -/* @odoo-module */ - -import { CallSettings } from "@mail/discuss/core/call_settings"; -import { ChannelInvitation } from "@mail/discuss/core/channel_invitation"; -import { ChannelMemberList } from "@mail/discuss/core/channel_member_list"; -import { ChatWindow } from "@mail/chat_window/chat_window"; -import { patch } from "@web/core/utils/patch"; -import { _t } from "@web/core/l10n/translation"; - -patch(ChatWindow, "discuss", { - components: { ...ChatWindow.components, CallSettings, ChannelInvitation, ChannelMemberList }, -}); - -patch(ChatWindow.prototype, "discuss", { - toggleAddUsers() { - this.state.activeMode = this.state.activeMode === "add-users" ? "" : "add-users"; - }, - toggleMemberList() { - this.state.activeMode = this.state.activeMode === "member-list" ? "" : "member-list"; - }, - toggleSettings() { - this.state.activeMode = this.state.activeMode === "in-settings" ? "" : "in-settings"; - }, - get actions() { - const acts = this._super(); - if (this.props.chatWindow.isOpen && this.thread?.model === "discuss.channel") { - acts.push({ - id: "invite", - name: - this.state.activeMode === "add-users" - ? _t("Stop Adding Users") - : _t("Add Users"), - icon: "fa fa-fw fa-user-plus", - onSelect: () => this.toggleAddUsers(), - sequence: 30, - }); - } - if (this.thread?.hasMemberList && this.props.chatWindow.isOpen) { - acts.push({ - id: "member", - name: - this.state.activeMode === "member-list" - ? _t("Hide Member List") - : _t("Show Member List"), - icon: "fa fa-fw fa-users", - onSelect: () => this.toggleMemberList(), - sequence: 40, - }); - } - if (this.thread?.allowCalls && this.props.chatWindow.isOpen) { - acts.push({ - id: "settings", - name: - this.state.activeMode === "in-settings" - ? _t("Hide Call Settings") - : _t("Show Call Settings"), - icon: "fa fa-fw fa-gear", - onSelect: () => this.toggleSettings(), - sequence: this.thread === this.rtc.state.channel ? 6 : 60, - }); - } - return acts; - }, -}); diff --git a/addons/mail/static/src/discuss/web/chat_window_patch.xml b/addons/mail/static/src/discuss/web/chat_window_patch.xml deleted file mode 100644 index aa7c514d971..00000000000 --- a/addons/mail/static/src/discuss/web/chat_window_patch.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/addons/mail/static/src/discuss/web/thread_actions.js b/addons/mail/static/src/discuss/web/thread_actions.js new file mode 100644 index 00000000000..66a62a8e125 --- /dev/null +++ b/addons/mail/static/src/discuss/web/thread_actions.js @@ -0,0 +1,29 @@ +/* @odoo-module */ + +import { threadActionsRegistry } from "@mail/core/thread_actions"; +import { _t } from "@web/core/l10n/translation"; + +threadActionsRegistry.add("expand-discuss", { + condition(component) { + return ( + component.thread && + component.props.chatWindow?.isOpen && + component.thread.model === "discuss.channel" && + !component.ui.isSmall + ); + }, + icon: "fa fa-fw fa-expand", + name: _t("Open in Discuss"), + open(component) { + component.threadService.setDiscussThread(component.thread); + component.actionService.doAction( + { + type: "ir.actions.client", + tag: "mail.action_discuss", + name: _t("Discuss"), + }, + { clearBreadcrumbs: true } + ); + }, + sequence: 51, +}); diff --git a/addons/mail/static/src/discuss_app/discuss.js b/addons/mail/static/src/discuss_app/discuss.js index d954208e920..1dc9938031c 100644 --- a/addons/mail/static/src/discuss_app/discuss.js +++ b/addons/mail/static/src/discuss_app/discuss.js @@ -2,6 +2,7 @@ import { ImStatus } from "./im_status"; import { AutoresizeInput } from "./autoresize_input"; +import { useThreadActions } from "@mail/core/thread_actions"; import { Thread } from "../core_ui/thread"; import { ThreadIcon } from "./thread_icon"; import { useMessaging, useStore } from "../core/messaging_hook"; @@ -24,12 +25,6 @@ import { useService } from "@web/core/utils/hooks"; import { _t } from "@web/core/l10n/translation"; import { url } from "@web/core/utils/urls"; -export const MODES = { - MEMBER_LIST: "member-list", - SETTINGS: "settings", - NONE: "", -}; - export class Discuss extends Component { static components = { AutoresizeInput, @@ -44,7 +39,6 @@ export class Discuss extends Component { static template = "mail.Discuss"; setup() { - this.MODES = MODES; this.messaging = useMessaging(); this.store = useStore(); this.threadService = useState(useService("mail.thread")); @@ -55,7 +49,7 @@ export class Discuss extends Component { this.messageEdition = useMessageEdition(); this.messageToReplyTo = useMessageToReplyTo(); this.contentRef = useRef("content"); - this.state = useState({ activeMode: MODES.NONE }); + this.root = useRef("root"); this.orm = useService("orm"); this.effect = useService("effect"); this.ui = useState(useService("ui")); @@ -65,6 +59,7 @@ export class Discuss extends Component { messageHighlight: this.messageHighlight, }); this.notification = useService("notification"); + this.threadActions = useThreadActions(); useEffect( () => { if ( @@ -87,10 +82,6 @@ export class Discuss extends Component { onWillUnmount(() => (this.store.discuss.isActive = false)); } - markAllAsRead() { - this.orm.silent.call("mail.message", "mark_all_as_read"); - } - get thread() { return this.store.threads[this.store.discuss.threadLocalId]; } diff --git a/addons/mail/static/src/discuss_app/discuss.scss b/addons/mail/static/src/discuss_app/discuss.scss index 1dd9d00d150..3fe3b4c4c90 100644 --- a/addons/mail/static/src/discuss_app/discuss.scss +++ b/addons/mail/static/src/discuss_app/discuss.scss @@ -4,6 +4,10 @@ } } +.o-mail-Discuss-inspector { + width: $o-mail-Discuss-inspector; +} + .o-mail-Discuss-selfAvatar { height: $o-mail-Avatar-sizeSmall; width: $o-mail-Avatar-sizeSmall; diff --git a/addons/mail/static/src/discuss_app/discuss.xml b/addons/mail/static/src/discuss_app/discuss.xml index 61207158bac..407d89d29df 100644 --- a/addons/mail/static/src/discuss_app/discuss.xml +++ b/addons/mail/static/src/discuss_app/discuss.xml @@ -40,11 +40,11 @@
- - - + + +
Avatar
@@ -66,6 +66,7 @@
+
diff --git a/addons/mail/static/src/scss/variables/primary_variables.scss b/addons/mail/static/src/scss/variables/primary_variables.scss index 6f92703606d..e8bbff64643 100644 --- a/addons/mail/static/src/scss/variables/primary_variables.scss +++ b/addons/mail/static/src/scss/variables/primary_variables.scss @@ -2,7 +2,7 @@ $o-mail-Avatar-size: 42px !default; $o-mail-ChatWindow-width: 360px !default; // same value as CHAT_WINDOW_WIDTH $o-mail-Chatter-mobile-gap: 2% !default; $o-mail-sidebar-icon-opacity: 0.7 !default; -$o-mail-Discuss-inspector: 250px !default; +$o-mail-Discuss-inspector: 300px !default; $o-mail-Avatar-sizeSmall: 24px !default; $o-mail-partner-status-icon-size: 1.2em !default; // Needed because $border-radius variations are all set to 0 in enterprise. diff --git a/addons/mail/static/src/web/chat_window/chat_window_patch.js b/addons/mail/static/src/web/chat_window/chat_window_patch.js index e682fdd08fc..887ce8978e3 100644 --- a/addons/mail/static/src/web/chat_window/chat_window_patch.js +++ b/addons/mail/static/src/web/chat_window/chat_window_patch.js @@ -3,7 +3,6 @@ import { ChatWindow } from "@mail/chat_window/chat_window"; import { ChannelSelector } from "@mail/discuss_app/channel_selector"; -import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; import { patch } from "@web/core/utils/patch"; @@ -14,43 +13,4 @@ patch(ChatWindow.prototype, "mail/chat_window", { this._super(...arguments); this.actionService = useService("action"); }, - - expand() { - if (this.thread.type === "chatter") { - this.actionService.doAction({ - type: "ir.actions.act_window", - res_id: this.thread.id, - res_model: this.thread.model, - views: [[false, "form"]], - }); - this.chatWindowService.close(this.props.chatWindow); - return; - } - this.threadService.setDiscussThread(this.thread); - this.actionService.doAction( - { - type: "ir.actions.client", - tag: "mail.action_discuss", - name: _t("Discuss"), - }, - { clearBreadcrumbs: true } - ); - }, - - get actions() { - const acts = this._super(); - if (this.thread && this.props.chatWindow.isOpen) { - acts.splice(acts.length - 1, 0, { - id: "expand", - name: - this.thread.model === "discuss.channel" - ? _t("Open in Discuss") - : _t("Open Form View"), - icon: "fa fa-fw fa-expand", - onSelect: () => this.expand(), - sequence: 50, - }); - } - return acts; - }, }); diff --git a/addons/mail/static/src/web/thread_actions.js b/addons/mail/static/src/web/thread_actions.js new file mode 100644 index 00000000000..991e35479f6 --- /dev/null +++ b/addons/mail/static/src/web/thread_actions.js @@ -0,0 +1,49 @@ +/* @odoo-module */ + +import { threadActionsRegistry } from "@mail/core/thread_actions"; +import { _t } from "@web/core/l10n/translation"; + +threadActionsRegistry + .add("mark-all-read", { + condition(component) { + return component.thread?.id === "inbox"; + }, + disabledCondition(component) { + return component.thread.isEmpty; + }, + open(component) { + component.orm.silent.call("mail.message", "mark_all_as_read"); + }, + sequence: 1, + text: _t("Mark all read"), + }) + .add("unstar-all", { + condition(component) { + return component.thread?.id === "starred"; + }, + disabledCondition(component) { + return component.thread.isEmpty; + }, + open(component) { + component.messageService.unstarAll(); + }, + sequence: 2, + text: _t("Unstar all"), + }) + .add("expand-form", { + condition(component) { + return component.thread?.type === "chatter" && component.props.chatWindow?.isOpen; + }, + icon: "fa fa-fw fa-expand", + name: _t("Open Form View"), + open(component) { + component.actionService.doAction({ + type: "ir.actions.act_window", + res_id: component.thread.id, + res_model: component.thread.model, + views: [[false, "form"]], + }); + component.chatWindowService.close(component.props.chatWindow); + }, + sequence: 50, + }); diff --git a/addons/mail/static/tests/discuss/crosstab/crosstab_tests.js b/addons/mail/static/tests/discuss/crosstab/crosstab_tests.js index 2341f616e1e..d05cc527804 100644 --- a/addons/mail/static/tests/discuss/crosstab/crosstab_tests.js +++ b/addons/mail/static/tests/discuss/crosstab/crosstab_tests.js @@ -17,6 +17,7 @@ QUnit.test("Add member to channel", async (assert) => { await click("[title='Add Users']"); await click(".o-discuss-ChannelInvitation-selectable:contains(Harry)"); await click("[title='Invite to Channel']"); + await click("[title='Show Member List']"); assert.containsOnce($, ".o-discuss-ChannelMember:contains(Harry)"); }); diff --git a/addons/mail/static/tests/discuss/message_pin/pinned_messages_tests.js b/addons/mail/static/tests/discuss/message_pin/pinned_messages_tests.js index 7e173376289..26ff03442e4 100644 --- a/addons/mail/static/tests/discuss/message_pin/pinned_messages_tests.js +++ b/addons/mail/static/tests/discuss/message_pin/pinned_messages_tests.js @@ -15,7 +15,7 @@ QUnit.test("Pin message", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - await click(".o-mail-Discuss-header button[title='Pinned messages']"); + await click(".o-mail-Discuss-header button[title='Pinned Messages']"); assert.containsOnce( $, ".o-discuss-PinnedMessagesPanel:contains(This channel doesn't have any pinned messages.)" @@ -37,7 +37,7 @@ QUnit.test("Unpin message", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - await click(".o-mail-Discuss-header button[title='Pinned messages']"); + await click(".o-mail-Discuss-header button[title='Pinned Messages']"); assert.containsOnce($, ".o-discuss-PinnedMessagesPanel .o-mail-Message"); await click(".o-mail-Message [title='Expand']"); await click(".dropdown-item:contains(Unpin)"); @@ -57,7 +57,7 @@ QUnit.test("Deleted messages are not pinned", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - await click(".o-mail-Discuss-header button[title='Pinned messages']"); + await click(".o-mail-Discuss-header button[title='Pinned Messages']"); assert.containsOnce($, ".o-discuss-PinnedMessagesPanel .o-mail-Message"); await click(".o-mail-Message [title='Expand']"); await click(".dropdown-item:contains(Delete)"); @@ -108,7 +108,7 @@ QUnit.test("Jump to message", async (assert) => { } const { openDiscuss } = await start(); await openDiscuss(channelId); - await click(".o-mail-Discuss-header button[title='Pinned messages']"); + await click(".o-mail-Discuss-header button[title='Pinned Messages']"); await click(".o-discuss-PinnedMessagesPanel button:contains(Jump)"); await nextTick(); assert.isVisible($(".o-mail-Message:contains(Hello world!)"));