[IMP] mail, *: move code from components to models (ThreadIcon)

Task-3002515.

* = hr_holidays, im_livechat

closes odoo/odoo#101774

Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Louis Wicket (wil)
2022-10-06 14:56:44 +02:00
parent 493a521466
commit 8bd03d3984
15 changed files with 130 additions and 73 deletions
@@ -2,13 +2,13 @@
<templates xml:space="preserve">
<t t-inherit="mail.ThreadIcon" t-inherit-mode="extension">
<xpath expr="//*[@name='noImStatusCondition']" position="before">
<t t-elif="thread.channel.correspondent.im_status === 'leave_online'">
<t t-elif="threadIconView.thread.channel.correspondent.im_status === 'leave_online'">
<div class="o_ThreadIcon_online fa fa-fw fa-plane" title="Online"/>
</t>
<t t-elif="thread.channel.correspondent.im_status === 'leave_away'">
<t t-elif="threadIconView.thread.channel.correspondent.im_status === 'leave_away'">
<div class="o_ThreadIcon_away fa fa-fw fa-plane text-warning" title="Away"/>
</t>
<t t-elif="thread.channel.correspondent.im_status === 'leave_offline'">
<t t-elif="threadIconView.thread.channel.correspondent.im_status === 'leave_offline'">
<div class="o_ThreadIcon_offline fa fa-fw fa-plane" title="Out of office"/>
</t>
</xpath>
@@ -2,12 +2,12 @@
<templates xml:space="preserve">
<t t-inherit="mail.ThreadIcon" t-inherit-mode="extension">
<xpath expr="//*[@name='root']" position="inside">
<t t-elif="thread.channel and thread.channel.channel_type === 'livechat'">
<t t-if="thread.orderedOtherTypingMembers.length > 0">
<t t-elif="threadIconView.thread.channel and threadIconView.thread.channel.channel_type === 'livechat'">
<t t-if="threadIconView.thread.orderedOtherTypingMembers.length > 0">
<ThreadTypingIcon
className="'o_ThreadIcon_typing'"
animation="'pulse'"
title="thread.typingStatusText"
title="threadIconView.thread.typingStatusText"
/>
</t>
<t t-else="">
@@ -32,14 +32,6 @@ registerPatch({
return this._super();
},
},
hasThreadIcon: {
compute() {
if (this.channel.channel_type === 'livechat') {
return clear();
}
return this._super();
},
},
hasUnpinCommand: {
compute() {
if (this.channel.channel_type === 'livechat') {
@@ -48,5 +40,13 @@ registerPatch({
return this._super();
},
},
threadIconView: {
compute() {
if (this.channel.channel_type === 'livechat') {
return clear();
}
return this._super();
},
},
},
});
@@ -9,10 +9,10 @@
<i class="fa fa-arrow-left"/>
</div>
</t>
<t t-if="chatWindow.thread and chatWindow.thread.model === 'mail.channel'">
<t t-if="chatWindowHeaderView.threadIconView">
<ThreadIcon
className="'o_ChatWindowHeader_icon o_ChatWindowHeader_item ms-3 me-1 my-0'"
thread="chatWindow.thread"
record="chatWindowHeaderView.threadIconView"
/>
</t>
<div class="o_ChatWindowHeader_item o_ChatWindowHeader_name mh-100 mx-1 my-0 text-truncate" t-att-class="{'ms-3':!chatWindow.thread or chatWindow.thread.model !== 'mail.channel'}" t-att-title="chatWindow.name">
@@ -14,8 +14,8 @@
<div class="o_DiscussSidebarCategoryItem_item o_DiscussSidebarCategoryItem_avatar ms-4">
<div class="o_DiscussSidebarCategoryItem_imageContainer position-relative d-flex">
<img class="o_DiscussSidebarCategoryItem_image w-100 h-100 rounded-circle" t-att-src="categoryItem.avatarUrl" alt="Thread Image"/>
<t t-if="categoryItem.hasThreadIcon">
<ThreadIcon className="'o_DiscussSidebarCategoryItem_threadIcon position-absolute bottom-0 end-0 d-flex align-items-center justify-content-center rounded-circle bg-100'" thread="categoryItem.thread"/>
<t t-if="categoryItem.threadIconView">
<ThreadIcon className="'o_DiscussSidebarCategoryItem_threadIcon position-absolute bottom-0 end-0 d-flex align-items-center justify-content-center rounded-circle bg-100'" record="categoryItem.threadIconView"/>
</t>
</div>
</div>
@@ -9,7 +9,7 @@
}" t-attf-class="{{ className }}" t-on-click="discussSidebarMailboxView.mailbox.thread.onClick" t-att-data-mailbox-local-id="discussSidebarMailboxView.mailbox.localId" t-att-data-mailbox-name="discussSidebarMailboxView.mailbox.name"
t-ref="root"
>
<ThreadIcon className="'o_DiscussSidebarMailbox_item ms-4 me-2'" thread="discussSidebarMailboxView.mailbox.thread"/>
<ThreadIcon className="'o_DiscussSidebarMailbox_item ms-4 me-2'" record="discussSidebarMailboxView.threadIconView"/>
<div class="o_DiscussSidebarMailbox_item o_DiscussSidebarMailbox_name me-2 text-truncate">
<t t-esc="discussSidebarMailboxView.mailbox.name"/>
</div>
@@ -7,16 +7,16 @@ const { Component } = owl;
export class ThreadIcon extends Component {
/**
* @returns {Thread}
* @returns {ThreadIconView}
*/
get thread() {
return this.props.thread;
get threadIconView() {
return this.props.record;
}
}
Object.assign(ThreadIcon, {
props: { thread: Object },
props: { record: Object },
template: 'mail.ThreadIcon',
});
@@ -2,51 +2,51 @@
<templates xml:space="preserve">
<t t-name="mail.ThreadIcon" owl="1">
<t t-if="thread">
<t t-if="threadIconView">
<div class="o_ThreadIcon d-flex justify-content-center flex-shrink-0" t-attf-class="{{ className }}" t-ref="root" name="root">
<t t-if="thread.channel and thread.channel.channel_type === 'channel'">
<t t-if="thread.authorizedGroupFullName">
<div class="o_ThreadIcon_groupRestrictedChannel fa fa-fw fa-hashtag" t-att-title="thread.accessRestrictedToGroupText"/>
<t t-if="threadIconView.thread.channel and threadIconView.thread.channel.channel_type === 'channel'">
<t t-if="threadIconView.thread.authorizedGroupFullName">
<div class="o_ThreadIcon_groupRestrictedChannel fa fa-fw fa-hashtag" t-att-title="threadIconView.thread.accessRestrictedToGroupText"/>
</t>
<t t-if="!thread.authorizedGroupFullName">
<t t-if="!threadIconView.thread.authorizedGroupFullName">
<div class="o_ThreadIcon_publicChannel fa fa-fw fa-globe" title="Public Channel"/>
</t>
</t>
<t t-elif="thread.channel and thread.channel.channel_type === 'chat' and thread.channel.correspondent">
<t t-if="thread.orderedOtherTypingMembers.length > 0">
<t t-elif="threadIconView.thread.channel and threadIconView.thread.channel.channel_type === 'chat' and threadIconView.thread.channel.correspondent">
<t t-if="threadIconView.thread.orderedOtherTypingMembers.length > 0">
<ThreadTypingIcon
className="'o_ThreadIcon_typing flex-grow-1'"
animation="'pulse'"
title="thread.typingStatusText"
title="threadIconView.thread.typingStatusText"
/>
</t>
<t t-elif="thread.channel.correspondent.im_status === 'online'">
<t t-elif="threadIconView.thread.channel.correspondent.im_status === 'online'">
<div class="o_ThreadIcon_online fa fa-fw fa-circle" title="Online"/>
</t>
<t t-elif="thread.channel.correspondent.im_status === 'offline'">
<t t-elif="threadIconView.thread.channel.correspondent.im_status === 'offline'">
<div class="o_ThreadIcon_offline fa fa-fw fa-circle-o" title="Offline"/>
</t>
<t t-elif="thread.channel.correspondent.im_status === 'away'">
<t t-elif="threadIconView.thread.channel.correspondent.im_status === 'away'">
<div class="o_ThreadIcon_away fa fa-fw fa-circle text-warning" title="Away"/>
</t>
<t t-elif="thread.channel.correspondent === messaging.partnerRoot">
<t t-elif="threadIconView.thread.channel.correspondent === messaging.partnerRoot">
<div class="o_ThreadIcon_online fa fa-fw fa-heart" title="Bot"/>
</t>
<t t-else="" name="noImStatusCondition">
<div class="o_ThreadIcon_noImStatus fa fa-fw fa-question-circle" title="No IM status available"/>
</t>
</t>
<t t-elif="thread.channel and thread.channel.channel_type === 'group'">
<t t-elif="threadIconView.thread.channel and threadIconView.thread.channel.channel_type === 'group'">
<div class="o_ThreadIcon fa fa-fw fa-users" title="Grouped Chat"/>
</t>
<t t-elif="thread.mailbox">
<t t-if="thread.mailbox === messaging.inbox">
<t t-elif="threadIconView.thread.mailbox">
<t t-if="threadIconView.thread.mailbox === messaging.inbox">
<div class="o_ThreadIcon_mailboxInbox fa fa-fw fa-inbox"/>
</t>
<t t-elif="thread.mailbox === messaging.starred">
<t t-elif="threadIconView.thread.mailbox === messaging.starred">
<div class="o_ThreadIcon_mailboxStarred fa fa-fw fa-star-o"/>
</t>
<t t-elif="thread.mailbox === messaging.history">
<t t-elif="threadIconView.thread.mailbox === messaging.history">
<div class="o_ThreadIcon_mailboxHistory fa fa-fw fa-history"/>
</t>
</t>
@@ -4,8 +4,8 @@
<t t-name="mail.ThreadViewTopbar" owl="1">
<t t-if="threadViewTopbar">
<div class="o_ThreadViewTopbar d-flex flex-shrink-0 w-100 px-3 bg-100" t-attf-class="{{ className }}" t-ref="root">
<t t-if="threadViewTopbar.thread">
<ThreadIcon className="'o_ThreadViewTopbar_threadIcon me-2 align-self-center'" thread="threadViewTopbar.thread"/>
<t t-if="threadViewTopbar.threadIconView">
<ThreadIcon className="'o_ThreadViewTopbar_threadIcon me-2 align-self-center'" record="threadViewTopbar.threadIconView"/>
</t>
<div class="o_ThreadViewTopbar_title d-flex flex-grow-1 align-self-center align-items-center">
<t t-if="threadViewTopbar.thread">
@@ -2,6 +2,7 @@
import { registerModel } from '@mail/model/model_core';
import { one } from '@mail/model/model_field';
import { clear } from '@mail/model/model_field_command';
import { isEventHandled } from '@mail/utils/utils';
registerModel({
@@ -33,5 +34,14 @@ registerModel({
identifying: true,
inverse: 'chatWindowHeaderView',
}),
threadIconView: one('ThreadIconView', {
compute() {
if (this.chatWindowOwner.thread && this.chatWindowOwner.thread.channel) {
return {};
}
return clear();
},
inverse: 'chatWindowHeaderViewOwner',
})
},
});
@@ -188,24 +188,6 @@ registerModel({
return this.channel.channel_type === 'channel';
},
}),
/**
* Boolean determines whether ThreadIcon will be displayed in UI.
*/
hasThreadIcon: attr({
compute() {
if (!this.thread) {
return clear();
}
switch (this.channel.channel_type) {
case 'channel':
return !Boolean(this.thread.authorizedGroupFullName);
case 'chat':
return true;
case 'group':
return false;
}
},
}),
/**
* Boolean determines whether the item has a "unpin" command.
*/
@@ -236,5 +218,21 @@ registerModel({
thread: one('Thread', {
related: 'channel.thread'
}),
threadIconView: one('ThreadIconView', {
compute() {
if (!this.thread) {
return clear();
}
switch (this.channel.channel_type) {
case 'channel':
return this.thread.authorizedGroupFullName ? clear() : {};
case 'chat':
return {};
case 'group':
return clear();
}
},
inverse: 'discussSidebarCategoryItemOwner',
}),
},
});
@@ -35,5 +35,9 @@ registerModel({
},
required: true,
}),
threadIconView: one('ThreadIconView', {
default: {},
inverse: 'discussSidebarMailboxViewOwner',
}),
},
});
@@ -7,7 +7,6 @@ import { htmlToTextContentInline } from '@mail/js/utils';
import { escape, sprintf } from '@web/core/utils/strings';
import { str_to_datetime } from 'web.time';
import { Markup } from 'web.utils';
import { renderToString } from "@web/core/utils/render";
const PREVIEW_MSG_MAX_SIZE = 350; // optimal for native English speakers
@@ -651,19 +650,12 @@ registerModel({
notificationTitle = this.env._t("New message");
} else {
if (channel.channel_type === 'channel') {
// hack: notification template does not support OWL components,
// so we simply use their template to make HTML as if it comes
// from component
const channelIcon = renderToString('mail.ThreadIcon', {
env: this.env,
thread: channel.thread,
});
const channelName = channel.thread.displayName;
const channelNameWithIcon = channelIcon + channelName;
notificationTitle = sprintf(
this.env._t("%s from %s"),
author.nameOrDisplayName,
channelNameWithIcon
this.env._t("%(author name)s from %(channel name)s"),
{
'author name': author.nameOrDisplayName,
'channel name': channel.displayName,
}
);
} else {
notificationTitle = author.nameOrDisplayName;
@@ -0,0 +1,44 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { one } from '@mail/model/model_field';
registerModel({
name: 'ThreadIconView',
identifyingMode: 'xor',
fields: {
chatWindowHeaderViewOwner: one('ChatWindowHeaderView', {
identifying: true,
inverse: 'threadIconView',
}),
discussSidebarCategoryItemOwner: one('DiscussSidebarCategoryItem', {
identifying: true,
inverse: 'threadIconView',
}),
discussSidebarMailboxViewOwner: one('DiscussSidebarMailboxView', {
identifying: true,
inverse: 'threadIconView',
}),
thread: one('Thread', {
compute() {
if (this.chatWindowHeaderViewOwner) {
return this.chatWindowHeaderViewOwner.chatWindowOwner.thread;
}
if (this.discussSidebarCategoryItemOwner) {
return this.discussSidebarCategoryItemOwner.thread;
}
if (this.discussSidebarMailboxViewOwner) {
return this.discussSidebarMailboxViewOwner.mailbox.thread;
}
if (this.threadViewTopbarOwner) {
return this.threadViewTopbarOwner.thread
}
},
required: true,
}),
threadViewTopbarOwner: one('ThreadViewTopbar', {
identifying: true,
inverse: 'threadIconView',
}),
},
});
@@ -688,6 +688,15 @@ registerModel({
* Useful to focus it, or to know when a click is done outside of it.
*/
threadDescriptionInputRef: attr(),
threadIconView: one('ThreadIconView', {
compute() {
if (this.thread) {
return {};
}
return clear();
},
inverse: 'threadViewTopbarOwner',
}),
/**
* States the thread view managing this top bar.
*/