[IMP] mail: introduce PopoverManager, and some code cleaning
1. Remove useless props category on component `ChannelMemberListCategory` 2. Make fields of model `DiscussSidebarCategory` more declarative, i.e. by defining values from compute of field rather than imperative code somewhere in the code of discuss. 3. Introduce a new model `PopoverManager`, which eases use of `PopoverView`s as they are automatically added in the DOM in a similar way than `Dialog`s with `DialogManager` or `ChatWindow`s with `ChatWindowManager`. Doing so also make these popover view no longer deep child in component tree: this fixes an issue in which popover views could be cut due to DOM tree. Task-2871688 closes odoo/odoo#92896 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { addFields, patchIdentifyingFields } from '@mail/model/model_core';
|
||||
import { addFields, patchIdentifyingFields, patchRecordMethods } from '@mail/model/model_core';
|
||||
import { one } from '@mail/model/model_field';
|
||||
// ensure that the model definition is loaded before the patch
|
||||
import '@mail/models/discuss_sidebar_category';
|
||||
@@ -15,3 +15,33 @@ addFields('DiscussSidebarCategory', {
|
||||
patchIdentifyingFields('DiscussSidebarCategory', identifyingFields => {
|
||||
identifyingFields[0].push('discussAsLivechat');
|
||||
});
|
||||
|
||||
patchRecordMethods('DiscussSidebarCategory', {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeName() {
|
||||
if (this.discussAsLivechat) {
|
||||
return this.env._t("Livechat");
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeSortComputeMethod() {
|
||||
if (this.discussAsLivechat) {
|
||||
return 'last_action';
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeSupportedChannelTypes() {
|
||||
if (this.discussAsLivechat) {
|
||||
return ['livechat'];
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -15,10 +15,7 @@ patchRecordMethods('MessagingInitializer', {
|
||||
this.messaging.discuss.update({
|
||||
categoryLivechat: insertAndReplace({
|
||||
isServerOpen: is_discuss_sidebar_category_livechat_open,
|
||||
name: this.env._t("Livechat"),
|
||||
serverStateKey: 'is_discuss_sidebar_category_livechat_open',
|
||||
sortComputeMethod: 'last_action',
|
||||
supportedChannelTypes: ['livechat'],
|
||||
}),
|
||||
});
|
||||
this._super(...arguments);
|
||||
|
||||
@@ -122,7 +122,6 @@
|
||||
<button class="o_Activity_toolButton o_Activity_markDoneButton btn btn-link btn-primary pt-0 pl-0" t-att-title="activityView.markDoneText" t-ref="markDoneButton" t-on-click="activityView.onClickMarkDoneButton">
|
||||
<i class="fa fa-check"/> Mark Done
|
||||
</button>
|
||||
<PopoverView t-if="activityView.markDonePopoverView" record="activityView.markDonePopoverView"/>
|
||||
<t t-if="activityView.fileUploader">
|
||||
<button class="o_Activity_toolButton o_Activity_uploadButton btn btn-link btn-primary pt-0 pl-0" t-on-click="activityView.onClickUploadDocument">
|
||||
<i class="fa fa-upload"/> Upload Document
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<t t-if="channelMemberListView">
|
||||
<div class="o_ChannelMemberList d-flex flex-column overflow-auto bg-light" t-attf-class="{{ className }}" t-ref="root">
|
||||
<t t-if="channelMemberListView.onlineCategoryView">
|
||||
<ChannelMemberListCategory record="channelMemberListView.onlineCategoryView" category="'online'" channelMemberListView="channelMemberListView"/>
|
||||
<ChannelMemberListCategory record="channelMemberListView.onlineCategoryView" channelMemberListView="channelMemberListView"/>
|
||||
</t>
|
||||
<t t-if="channelMemberListView.offlineCategoryView">
|
||||
<ChannelMemberListCategory record="channelMemberListView.offlineCategoryView" category="'offline'" channelMemberListView="channelMemberListView"/>
|
||||
<ChannelMemberListCategory record="channelMemberListView.offlineCategoryView" channelMemberListView="channelMemberListView"/>
|
||||
</t>
|
||||
<t t-if="channelMemberListView.channel.unknownMemberCount === 1">
|
||||
<span class="mx-2 mt-2">And 1 other member.</span>
|
||||
|
||||
+4
-5
@@ -29,10 +29,10 @@ export class ChannelMemberListCategory extends Component {
|
||||
if (!this.channelMemberListView.exists()) {
|
||||
return [];
|
||||
}
|
||||
if (this.props.category === 'online') {
|
||||
if (this.record.channelMemberListViewOwnerAsOnline) {
|
||||
return this.channelMemberListView.channel.orderedOnlineMembers;
|
||||
}
|
||||
if (this.props.category === 'offline') {
|
||||
if (this.record.channelMemberListViewOwnerAsOffline) {
|
||||
return this.channelMemberListView.channel.orderedOfflineMembers;
|
||||
}
|
||||
return [];
|
||||
@@ -43,10 +43,10 @@ export class ChannelMemberListCategory extends Component {
|
||||
*/
|
||||
get title() {
|
||||
let categoryText = "";
|
||||
if (this.props.category === 'online') {
|
||||
if (this.record.channelMemberListViewOwnerAsOnline) {
|
||||
categoryText = this.env._t("Online");
|
||||
}
|
||||
if (this.props.category === 'offline') {
|
||||
if (this.record.channelMemberListViewOwnerAsOffline) {
|
||||
categoryText = this.env._t("Offline");
|
||||
}
|
||||
return sprintf(
|
||||
@@ -62,7 +62,6 @@ export class ChannelMemberListCategory extends Component {
|
||||
|
||||
Object.assign(ChannelMemberListCategory, {
|
||||
props: {
|
||||
category: String,
|
||||
channelMemberListView: Object,
|
||||
record: Object,
|
||||
},
|
||||
|
||||
@@ -107,7 +107,6 @@
|
||||
>
|
||||
<i class="fa fa-smile-o"/>
|
||||
</button>
|
||||
<PopoverView t-if="composerView.emojisPopoverView" record="composerView.emojisPopoverView"/>
|
||||
<button class="o_Composer_button o_Composer_buttonAttachment o_Composer_toolButton btn btn-light fa fa-paperclip mx-1 border-0 rounded-pill bg-white" t-att-class="{ 'o-isDeviceSmall': messaging.device.isSmall }" title="Add attachment" type="button" t-on-click="composerView.onClickAddAttachment"/>
|
||||
</div>
|
||||
<t t-if="composerView.isExpandable">
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<t t-if="messageActionList">
|
||||
<div class="o_MessageActionList d-flex border rounded-sm bg-white" t-att-class="{ 'o-isDeviceSmall': messaging.device.isSmall }" t-attf-class="{{ className }}" t-on-click="messageActionList.onClick" t-ref="root">
|
||||
<i t-if="messageActionList.message.hasReactionIcon" class="o_MessageActionList_action o_MessageActionList_actionReaction fa fa-lg fa-smile-o o_cursor_pointer" t-attf-class="{{ messaging.device.isSmall ? 'pl-3 py-3 pr-2' : 'pl-2 py-2 pr-1' }}" t-att-title="messageActionList.addReactionText" t-on-click="messageActionList.onClickActionReaction" t-ref="actionReaction"/>
|
||||
<PopoverView t-if="messageActionList.reactionPopoverView" record="messageActionList.reactionPopoverView"/>
|
||||
<span t-if="messageActionList.message.canStarBeToggled" class="o_MessageActionList_action o_MessageActionList_actionStar o_cursor_pointer" t-attf-class="{{ messaging.device.isSmall ? 'px-2 py-3' : 'px-1 py-2' }}" t-att-class="{
|
||||
'o_MessageActionList_actionStar_active': messageActionList.message.isStarred,
|
||||
'fa fa-lg fa-star': messageActionList.message.isStarred,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerMessagingComponent } from '@mail/utils/messaging_component';
|
||||
|
||||
const { Component } = owl;
|
||||
|
||||
export class PopoverManager extends Component {
|
||||
|
||||
get popoverManager() {
|
||||
return this.props.record;
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(PopoverManager, {
|
||||
props: { record: Object },
|
||||
template: 'mail.PopoverManager',
|
||||
});
|
||||
|
||||
registerMessagingComponent(PopoverManager);
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.PopoverManager" owl="1">
|
||||
<div class="o_PopoverManager" t-attf-class="{{ className }}" t-ref="root">
|
||||
<t t-foreach="popoverManager.popoverViews" t-as="popoverView" t-key="popoverView.localId">
|
||||
<PopoverView
|
||||
className="'o_PopoverManager_popover'"
|
||||
record="popoverView"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { useModels } from '@mail/component_hooks/use_models';
|
||||
// ensure components are registered beforehand.
|
||||
import '@mail/components/popover_manager/popover_manager';
|
||||
import { getMessagingComponent } from "@mail/utils/messaging_component";
|
||||
|
||||
const { Component } = owl;
|
||||
|
||||
export class PopoverManagerContainer extends Component {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
setup() {
|
||||
useModels();
|
||||
super.setup();
|
||||
}
|
||||
|
||||
get messaging() {
|
||||
return this.env.services.messaging.modelManager.messaging;
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(PopoverManagerContainer, {
|
||||
components: { PopoverManager: getMessagingComponent('PopoverManager') },
|
||||
template: 'mail.PopoverManagerContainer',
|
||||
});
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="mail.PopoverManagerContainer" owl="1">
|
||||
<PopoverManager t-if="messaging and messaging.popoverManager" record="messaging.popoverManager"/>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -61,9 +61,6 @@
|
||||
<button class="o_ThreadViewTopbar_inviteButton o_ThreadViewTopbar_button btn px-2 border-none rounded shadow-none" t-attf-class="{{ threadViewTopbar.invitePopoverView ? '' : '' }}" title="Add users" t-on-click="threadViewTopbar.onClickInviteButton" t-ref="inviteButton">
|
||||
<i class="fa fa-lg fa-user-plus" t-attf-class="{{ threadViewTopbar.invitePopoverView ? 'text-500' : 'text-700' }}"/>
|
||||
</button>
|
||||
<t t-if="threadViewTopbar.invitePopoverView">
|
||||
<PopoverView className="'o_ThreadViewTopbar_invitePopoverView'" record="threadViewTopbar.invitePopoverView"/>
|
||||
</t>
|
||||
</t>
|
||||
<!-- FIXME: guests should be able to see members but there currently is no route for it, so hide it for now -->
|
||||
<t t-if="!messaging.isCurrentUserGuest and threadViewTopbar.thread and threadViewTopbar.thread.hasMemberListFeature and threadViewTopbar.threadView.hasMemberList and !threadViewTopbar.threadView.isMemberListOpened">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { ChatWindowManagerContainer } from '@mail/components/chat_window_manager_container/chat_window_manager_container';
|
||||
import { DialogManagerContainer } from '@mail/components/dialog_manager_container/dialog_manager_container';
|
||||
import { DiscussContainer } from '@mail/components/discuss_container/discuss_container';
|
||||
import { PopoverManagerContainer } from '@mail/components/popover_manager_container/popover_manager_container';
|
||||
import { messagingService } from '@mail/services/messaging_service';
|
||||
import { systrayService } from '@mail/services/systray_service';
|
||||
import { makeMessagingToLegacyEnv } from '@mail/utils/make_messaging_to_legacy_env';
|
||||
@@ -25,3 +26,4 @@ registry.category('actions').add('mail.action_discuss', DiscussContainer);
|
||||
|
||||
registry.category('main_components').add('DialogManagerContainer', { Component: DialogManagerContainer });
|
||||
registry.category('main_components').add('ChatWindowManagerContainer', { Component: ChatWindowManagerContainer });
|
||||
registry.category('main_components').add('PopoverManagerContainer', { Component: PopoverManagerContainer });
|
||||
|
||||
@@ -73,6 +73,32 @@ registerModel({
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeAutocompleteMethod() {
|
||||
if (this.discussAsChannel) {
|
||||
return 'channel';
|
||||
}
|
||||
if (this.discussAsChat) {
|
||||
return 'chat';
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeCommandAddTitleText() {
|
||||
if (this.discussAsChannel) {
|
||||
return this.env._t("Add or join a channel");
|
||||
}
|
||||
if (this.discussAsChat) {
|
||||
return this.env._t("Start a conversation");
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
@@ -89,6 +115,29 @@ registerModel({
|
||||
}
|
||||
return replace(categoryItems);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean|FieldCommand}
|
||||
*/
|
||||
_computeHasAddCommand() {
|
||||
if (this.discussAsChannel) {
|
||||
return true;
|
||||
}
|
||||
if (this.discussAsChat) {
|
||||
return true;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean|FieldCommand}
|
||||
*/
|
||||
_computeHasViewCommand() {
|
||||
if (this.discussAsChannel) {
|
||||
return true;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
@@ -96,6 +145,58 @@ registerModel({
|
||||
_computeIsOpen() {
|
||||
return this.isPendingOpen !== undefined ? this.isPendingOpen : this.isServerOpen;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeName() {
|
||||
if (this.discussAsChannel) {
|
||||
return this.env._t("Channels");
|
||||
}
|
||||
if (this.discussAsChat) {
|
||||
return this.env._t("Direct Messages");
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeNewItemPlaceholderText() {
|
||||
if (this.discussAsChannel) {
|
||||
return this.env._t("Find or create a channel...");
|
||||
}
|
||||
if (this.discussAsChat) {
|
||||
return this.env._t("Find or start a conversation...");
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeSortComputeMethod() {
|
||||
if (this.discussAsChannel) {
|
||||
return 'name';
|
||||
}
|
||||
if (this.discussAsChat) {
|
||||
return 'last_action';
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string[]|FieldCommand}
|
||||
*/
|
||||
_computeSupportedChannelTypes() {
|
||||
if (this.discussAsChannel) {
|
||||
return ['channel'];
|
||||
}
|
||||
if (this.discussAsChat) {
|
||||
return ['chat', 'group'];
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {Array[]}
|
||||
@@ -213,11 +314,17 @@ registerModel({
|
||||
* Determines how the autocomplete of this category should behave.
|
||||
* Must be one of: 'channel', 'chat'.
|
||||
*/
|
||||
autocompleteMethod: attr(),
|
||||
autocompleteMethod: attr({
|
||||
compute: '_computeAutocompleteMethod',
|
||||
default: '',
|
||||
}),
|
||||
/**
|
||||
* The title text in UI for command `add`
|
||||
*/
|
||||
commandAddTitleText: attr(),
|
||||
commandAddTitleText: attr({
|
||||
compute: '_computeCommandAddTitleText',
|
||||
default: '',
|
||||
}),
|
||||
/**
|
||||
* Determines the discuss sidebar category items that are displayed by
|
||||
* this discuss sidebar category.
|
||||
@@ -255,17 +362,22 @@ registerModel({
|
||||
/**
|
||||
* Display name of the category.
|
||||
*/
|
||||
name: attr(),
|
||||
name: attr({
|
||||
compute: '_computeName',
|
||||
default: '',
|
||||
}),
|
||||
/**
|
||||
* Boolean that determines whether this category has a 'add' command.
|
||||
*/
|
||||
hasAddCommand: attr({
|
||||
compute: '_computeHasAddCommand',
|
||||
default: false,
|
||||
}),
|
||||
/**
|
||||
* Boolean that determines whether this category has a 'view' command.
|
||||
*/
|
||||
hasViewCommand: attr({
|
||||
compute: '_computeHasViewCommand',
|
||||
default: false,
|
||||
}),
|
||||
/**
|
||||
@@ -295,7 +407,9 @@ registerModel({
|
||||
/**
|
||||
* The placeholder text used when a new item is being added in UI.
|
||||
*/
|
||||
newItemPlaceholderText: attr(),
|
||||
newItemPlaceholderText: attr({
|
||||
compute: '_computeNewItemPlaceholderText',
|
||||
}),
|
||||
/**
|
||||
* The key used in the server side for the category state
|
||||
*/
|
||||
@@ -308,12 +422,14 @@ registerModel({
|
||||
* Must be one of: 'name', 'last_action'.
|
||||
*/
|
||||
sortComputeMethod: attr({
|
||||
compute: '_computeSortComputeMethod',
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
* Channel type which is supported by the category.
|
||||
*/
|
||||
supportedChannelTypes: attr({
|
||||
compute: '_computeSupportedChannelTypes',
|
||||
required: true,
|
||||
readonly: true,
|
||||
}),
|
||||
|
||||
@@ -461,6 +461,11 @@ registerModel({
|
||||
default: 0,
|
||||
}),
|
||||
partnerRoot: one('Partner'),
|
||||
popoverManager: one('PopoverManager', {
|
||||
default: insertAndReplace(),
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Determines which partners should be considered the public partners,
|
||||
* which are special partners notably used in livechat.
|
||||
|
||||
@@ -247,27 +247,12 @@ registerModel({
|
||||
});
|
||||
this.messaging.discuss.update({
|
||||
categoryChannel: insertAndReplace({
|
||||
autocompleteMethod: 'channel',
|
||||
commandAddTitleText: this.env._t("Add or join a channel"),
|
||||
hasAddCommand: true,
|
||||
hasViewCommand: true,
|
||||
isServerOpen: is_discuss_sidebar_category_channel_open,
|
||||
name: this.env._t("Channels"),
|
||||
newItemPlaceholderText: this.env._t("Find or create a channel..."),
|
||||
serverStateKey: 'is_discuss_sidebar_category_channel_open',
|
||||
sortComputeMethod: 'name',
|
||||
supportedChannelTypes: ['channel'],
|
||||
}),
|
||||
categoryChat: insertAndReplace({
|
||||
autocompleteMethod: 'chat',
|
||||
commandAddTitleText: this.env._t("Start a conversation"),
|
||||
hasAddCommand: true,
|
||||
isServerOpen: is_discuss_sidebar_category_chat_open,
|
||||
name: this.env._t("Direct Messages"),
|
||||
newItemPlaceholderText: this.env._t("Find or start a conversation..."),
|
||||
serverStateKey: 'is_discuss_sidebar_category_chat_open',
|
||||
sortComputeMethod: 'last_action',
|
||||
supportedChannelTypes: ['chat', 'group'],
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { many } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'PopoverManager',
|
||||
identifyingFields: ['messaging'],
|
||||
fields: {
|
||||
// FIXME: dependent on implementation that uses insert order in relations!!
|
||||
popoverViews: many('PopoverView', {
|
||||
inverse: 'manager',
|
||||
isCausal: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -123,6 +123,16 @@ registerModel({
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeManager() {
|
||||
if (this.messaging.popoverManager) {
|
||||
return replace(this.messaging.popoverManager);
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string}
|
||||
@@ -231,6 +241,11 @@ registerModel({
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
manager: one('PopoverManager', {
|
||||
compute: '_computeManager',
|
||||
inverse: 'popoverViews',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* If set, this popover view is owned by a message action list.
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@ import BusService from 'bus.BusService';
|
||||
import { ChatWindowManagerContainer } from '@mail/components/chat_window_manager_container/chat_window_manager_container';
|
||||
import { DialogManagerContainer } from '@mail/components/dialog_manager_container/dialog_manager_container';
|
||||
import { DiscussContainer } from '@mail/components/discuss_container/discuss_container';
|
||||
import { PopoverManagerContainer } from '@mail/components/popover_manager_container/popover_manager_container';
|
||||
import { messagingService } from '@mail/services/messaging_service';
|
||||
import { makeMessagingToLegacyEnv } from '@mail/utils/make_messaging_to_legacy_env';
|
||||
|
||||
@@ -36,6 +37,7 @@ const SERVICES_PARAMETER_NAMES = new Set([
|
||||
mainComponentRegistry.add('ChatWindowManagerContainer', { Component: ChatWindowManagerContainer });
|
||||
mainComponentRegistry.add('DialogManagerContainer', { Component: DialogManagerContainer });
|
||||
registry.category('actions').add('mail.action_discuss', DiscussContainer);
|
||||
mainComponentRegistry.add('PopoverManagerContainer', { Component: PopoverManagerContainer });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user