[IMP] mail, website_livechat: introduce Discuss View

Task-2694206

closes odoo/odoo#83246

Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
Alexandre Kühn
2022-01-28 16:09:26 +00:00
parent 0b22f4deee
commit cbe1bc8095
19 changed files with 198 additions and 138 deletions
@@ -2,7 +2,7 @@
<templates xml:space="preserve">
<t t-inherit="mail.DiscussSidebar" t-inherit-mode="extension">
<xpath expr="//*[@name='beforeCategoryChat']" position="before">
<t t-set="categoryLivechat" t-value="messaging.discuss.categoryLivechat"/>
<t t-set="categoryLivechat" t-value="discussView.discuss.categoryLivechat"/>
<t t-if="categoryLivechat and categoryLivechat.categoryItems.length">
<DiscussSidebarCategory
className="'o_DiscussSidebar_category o_DiscussSidebar_categoryLivechat'"
@@ -977,10 +977,10 @@ QUnit.test('chat window: composer state conservation on toggle discuss', async f
"composer should have 2 total attachments after adding 2 attachments"
);
await afterNextRender(() => this.messaging.discuss.update({ isOpen: true }));
await afterNextRender(() => this.messaging.discuss.open());
assert.containsNone(document.body, '.o_ChatWindow', "should not have any chat window after opening discuss");
await afterNextRender(() => this.messaging.discuss.update({ isOpen: false }));
await afterNextRender(() => this.messaging.discuss.close());
assert.strictEqual(
document.querySelector(`.o_ComposerTextInput_textarea`).value,
"XDU for the win !",
@@ -1039,12 +1039,12 @@ QUnit.test('chat window: scroll conservation on toggle discuss', async function
},
});
await afterNextRender(() => this.messaging.discuss.update({ isOpen: true }));
await afterNextRender(() => this.messaging.discuss.open());
assert.containsNone(document.body, '.o_ChatWindow', "should not have any chat window after opening discuss");
await this.afterEvent({
eventName: 'o-component-message-list-scrolled',
func: () => this.messaging.discuss.update({ isOpen: false }),
func: () => this.messaging.discuss.close(),
message: "should wait until channel 20 restored its scroll to 142 after closing discuss",
predicate: ({ scrollTop, thread }) => {
return (
@@ -1968,10 +1968,10 @@ QUnit.test('chat window with a thread: keep scroll position in message list on t
});
// fold chat window
await afterNextRender(() => document.querySelector('.o_ChatWindow_header').click());
await afterNextRender(() => this.messaging.discuss.update({ isOpen: true }));
await afterNextRender(() => this.messaging.discuss.open());
assert.containsNone(document.body, '.o_ChatWindow', "should not have any chat window after opening discuss");
await afterNextRender(() => this.messaging.discuss.update({ isOpen: false }));
await afterNextRender(() => this.messaging.discuss.close());
// unfold chat window
await this.afterEvent({
eventName: 'o-component-message-list-scrolled',
@@ -2039,10 +2039,10 @@ QUnit.test('chat window with a thread: keep scroll position in message list on t
});
// fold chat window
await afterNextRender(() => document.querySelector('.o_ChatWindow_header').click());
await afterNextRender(() => this.messaging.discuss.update({ isOpen: true }));
await afterNextRender(() => this.messaging.discuss.open());
assert.containsNone(document.body, '.o_ChatWindow', "should not have any chat window after opening discuss");
await afterNextRender(() => this.messaging.discuss.update({ isOpen: false }));
await afterNextRender(() => this.messaging.discuss.close());
// unfold chat window
await this.afterEvent({
eventName: 'o-component-message-list-scrolled',
@@ -1,9 +1,9 @@
/** @odoo-module **/
import { registerMessagingComponent } from '@mail/utils/messaging_component';
import { useUpdate } from '@mail/component_hooks/use_update/use_update';
import { registerMessagingComponent } from '@mail/utils/messaging_component';
const { Component, onWillUnmount } = owl;
const { Component } = owl;
export class Discuss extends Component {
@@ -17,39 +17,31 @@ export class Discuss extends Component {
this._onMobileAddItemHeaderInputSelect = this._onMobileAddItemHeaderInputSelect.bind(this);
this._onMobileAddItemHeaderInputSource = this._onMobileAddItemHeaderInputSource.bind(this);
useUpdate({ func: () => this._update() });
onWillUnmount(() => this._willUnmount());
this._onHideMobileAddItemHeader = this._onHideMobileAddItemHeader.bind(this);
}
_update() {
if (!this.discuss) {
if (!this.discussView) {
return;
}
this.discuss.update({ isOpen: true });
if (this.discuss.thread) {
if (this.discussView.discuss.thread) {
this.trigger('o-push-state-action-manager');
} else if (!this._activeThreadCache && this.discuss.messaging.isInitialized) {
this.discuss.openInitThread();
} else if (!this._activeThreadCache) {
this.discussView.discuss.openInitThread();
}
if (
this.discuss.thread &&
this.discuss.thread === this.messaging.inbox &&
this.discuss.threadView &&
this._lastThreadCache === this.discuss.threadView.threadCache.localId &&
this._lastThreadCounter > 0 && this.discuss.thread.counter === 0
this.discussView.discuss.thread &&
this.discussView.discuss.thread === this.messaging.inbox &&
this.discussView.discuss.threadView &&
this._lastThreadCache === this.discussView.discuss.threadView.threadCache.localId &&
this._lastThreadCounter > 0 && this.discussView.discuss.thread.counter === 0
) {
this.trigger('o-show-rainbow-man');
}
this._activeThreadCache = this.discuss.threadView && this.discuss.threadView.threadCache;
this._activeThreadCache = this.discussView.discuss.threadView && this.discussView.discuss.threadView.threadCache;
this._updateLocalStoreProps();
}
_willUnmount() {
if (this.discuss) {
this.discuss.close();
}
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
@@ -69,10 +61,10 @@ export class Discuss extends Component {
}
/**
* @returns {Discuss}
* @returns {DiscussView}
*/
get discuss() {
return this.messaging && this.messaging.discuss;
get discussView() {
return this.messaging && this.messaging.models['DiscussView'].get(this.props.localId);
}
//--------------------------------------------------------------------------
@@ -83,7 +75,7 @@ export class Discuss extends Component {
* @private
*/
_updateLocalStoreProps() {
if (!this.discuss) {
if (!this.discussView) {
return;
}
/**
@@ -92,17 +84,17 @@ export class Discuss extends Component {
* rainbox man on inbox.
*/
this._lastThreadCache = (
this.discuss.threadView &&
this.discuss.threadView.threadCache &&
this.discuss.threadView.threadCache.localId
this.discussView.discuss.threadView &&
this.discussView.discuss.threadView.threadCache &&
this.discussView.discuss.threadView.threadCache.localId
);
/**
* Locally tracked store props `threadCounter`.
* Useful to display the rainbow man on inbox.
*/
this._lastThreadCounter = (
this.discuss.thread &&
this.discuss.thread.counter
this.discussView.discuss.thread &&
this.discussView.discuss.thread.counter
);
}
@@ -114,7 +106,10 @@ export class Discuss extends Component {
* @private
*/
_onHideMobileAddItemHeader() {
this.discuss.clearIsAddingItem();
if (!this.discussView) {
return;
}
this.discussView.discuss.clearIsAddingItem();
}
/**
@@ -125,7 +120,10 @@ export class Discuss extends Component {
* @param {integer} ui.item.id
*/
_onMobileAddItemHeaderInputSelect(ev, ui) {
const discuss = this.discuss;
if (!this.discussView) {
return;
}
const discuss = this.discussView.discuss;
if (discuss.isAddingChannel) {
discuss.handleAddChannelAutocompleteSelect(ev, ui);
} else {
@@ -140,17 +138,20 @@ export class Discuss extends Component {
* @param {function} res
*/
_onMobileAddItemHeaderInputSource(req, res) {
if (this.discuss.isAddingChannel) {
this.discuss.handleAddChannelAutocompleteSource(req, res);
if (!this.discussView) {
return;
}
if (this.discussView.discuss.isAddingChannel) {
this.discussView.discuss.handleAddChannelAutocompleteSource(req, res);
} else {
this.discuss.handleAddChatAutocompleteSource(req, res);
this.discussView.discuss.handleAddChatAutocompleteSource(req, res);
}
}
}
Object.assign(Discuss, {
props: {},
props: { localId: String },
template: 'mail.Discuss',
});
@@ -30,17 +30,6 @@
flex-flow: column;
}
.o_Discuss_messagingNotInitialized {
flex: 1 1 auto;
display: flex;
align-items: center;
justify-content: center;
}
.o_Discuss_messagingNotInitializedIcon {
margin-right: map-get($spacers, 1);
}
.o_Discuss_mobileAddItemHeader {
display: flex;
justify-content: center;
@@ -4,17 +4,14 @@
<t t-name="mail.Discuss" owl="1">
<div class="o_Discuss" t-attf-class="{{ className }}"
t-att-class="{
'o-adding-item': discuss ? discuss.isAddingChannel or discuss.isAddingChat : false,
'o-adding-item': discussView ? discussView.discuss.isAddingChannel or discussView.discuss.isAddingChat : false,
'o-mobile': messaging ? messaging.device.isMobile : false,
}"
t-ref="root"
>
<t t-if="!discuss or !messaging.isInitialized">
<div class="o_Discuss_messagingNotInitialized"><i class="o_Discuss_messagingNotInitializedIcon fa fa-circle-o-notch fa-spin"/>Please wait...</div>
</t>
<t t-else="">
<t t-if="discussView">
<t t-if="!messaging.device.isMobile">
<DiscussSidebar className="'o_Discuss_sidebar bg-light border-right'" localId="discuss.localId"/>
<DiscussSidebar className="'o_Discuss_sidebar bg-light border-right'" localId="discussView.localId"/>
</t>
<t t-if="messaging.device.isMobile" t-call="mail.Discuss.content"/>
<t t-else="">
@@ -27,58 +24,58 @@
</t>
<t t-name="mail.Discuss.content" owl="1">
<t t-if="messaging.device.isMobile and discuss.activeMobileNavbarTabId === 'mailbox'">
<t t-if="messaging.device.isMobile and discussView.discuss.activeMobileNavbarTabId === 'mailbox'">
<DiscussMobileMailboxSelection className="'o_Discuss_mobileMailboxSelection border-bottom'"/>
</t>
<t t-if="messaging.device.isMobile and (discuss.isAddingChannel or discuss.isAddingChat)">
<t t-if="messaging.device.isMobile and (discussView.discuss.isAddingChannel or discussView.discuss.isAddingChat)">
<div class="o_Discuss_mobileAddItemHeader border-bottom">
<AutocompleteInput
className="'o_Discuss_mobileAddItemHeaderInput'"
isFocusOnMount="true"
isHtml="discuss.isAddingChannel"
placeholder="discuss.isAddingChannel ? addChannelInputPlaceholder : addChatInputPlaceholder"
isHtml="discussView.discuss.isAddingChannel"
placeholder="discussView.discuss.isAddingChannel ? addChannelInputPlaceholder : addChatInputPlaceholder"
select="_onMobileAddItemHeaderInputSelect"
source="_onMobileAddItemHeaderInputSource"
onHide="_onHideMobileAddItemHeader"
/>
</div>
</t>
<t t-if="discuss.threadView">
<t t-if="discussView.discuss.threadView">
<t name="beforeThread"/>
<ThreadView
className="'o_Discuss_thread'"
t-att-class="{ 'o-mobile': messaging.device.isMobile }"
hasComposerCurrentPartnerAvatar="!messaging.device.isMobile"
hasComposerDiscardButton="discuss.thread === messaging.inbox"
hasComposerThreadName="discuss.thread === messaging.inbox"
hasComposerDiscardButton="discussView.discuss.thread === messaging.inbox"
hasComposerThreadName="discussView.discuss.thread === messaging.inbox"
hasComposerThreadTyping="true"
localId="discuss.threadView.localId"
localId="discussView.discuss.threadView.localId"
/>
</t>
<t t-if="!discuss.thread and (!messaging.device.isMobile or discuss.activeMobileNavbarTabId === 'mailbox')">
<t t-if="!discussView.discuss.thread and (!messaging.device.isMobile or discussView.discuss.activeMobileNavbarTabId === 'mailbox')">
<div class="o_Discuss_noThread">
No conversation selected.
</div>
</t>
<t t-if="messaging.device.isMobile and discuss.activeMobileNavbarTabId !== 'mailbox'">
<t t-if="discuss.activeMobileNavbarTabId === 'chat'">
<button class="o_Discuss_mobileNewChatButton btn btn-secondary" t-on-click="discuss.onClickMobileNewChatButton.bind(discuss)">
<t t-if="messaging.device.isMobile and discussView.discuss.activeMobileNavbarTabId !== 'mailbox'">
<t t-if="discussView.discuss.activeMobileNavbarTabId === 'chat'">
<button class="o_Discuss_mobileNewChatButton btn btn-secondary" t-on-click="discussView.discuss.onClickMobileNewChatButton">
Start a conversation
</button>
</t>
<t t-if="discuss.activeMobileNavbarTabId === 'channel'">
<button class="o_Discuss_mobileNewChannelButton btn btn-secondary" t-on-click="discuss.onClickMobileNewChannelButton.bind(discuss)">
<t t-if="discussView.discuss.activeMobileNavbarTabId === 'channel'">
<button class="o_Discuss_mobileNewChannelButton btn btn-secondary" t-on-click="discussView.discuss.onClickMobileNewChannelButton">
New Channel
</button>
</t>
</t>
<t t-if="discuss.notificationListView">
<NotificationList className="'o_Discuss_notificationList'" localId="discuss.notificationListView.localId"/>
<t t-if="discussView.discuss.notificationListView">
<NotificationList className="'o_Discuss_notificationList'" localId="discussView.discuss.notificationListView.localId"/>
</t>
<t t-if="discuss.mobileMessagingNavbarView">
<t t-if="discussView.discuss.mobileMessagingNavbarView">
<MobileMessagingNavbar
className="'o_Discuss_mobileNavbar'"
localId="discuss.mobileMessagingNavbarView.localId"
localId="discussView.discuss.mobileMessagingNavbarView.localId"
/>
</t>
</t>
@@ -49,7 +49,7 @@ QUnit.test('messaging not created', async function (assert) {
messagingBeforeCreationDeferred,
waitUntilMessagingCondition: 'none'
});
assert.containsOnce(document.body, '.o_Discuss_messagingNotInitialized', "should display messaging not initialized");
assert.containsOnce(document.body, '.o_DiscussContainer_spinner', "should display messaging not initialized");
messagingBeforeCreationDeferred.resolve();
});
@@ -64,7 +64,7 @@ QUnit.test('discuss should be marked as opened if the component is already rende
await afterNextRender(() => messagingBeforeCreationDeferred.resolve());
assert.ok(
this.messaging.discuss.isOpen,
this.messaging.discuss.discussView,
"discuss should be marked as opened if the component is already rendered and messaging becomes created afterwards"
);
});
@@ -76,7 +76,7 @@ QUnit.test('discuss should be marked as closed when the component is unmounted',
await afterNextRender(() => widget.destroy());
assert.notOk(
this.messaging.discuss.isOpen,
this.messaging.discuss.discussView,
"discuss should be marked as closed when the component is unmounted"
);
});
@@ -95,7 +95,7 @@ QUnit.test('messaging not initialized', async function (assert) {
waitUntilMessagingCondition: 'created',
});
assert.strictEqual(
document.querySelectorAll('.o_Discuss_messagingNotInitialized').length,
document.querySelectorAll('.o_DiscussContainer_spinner').length,
1,
"should display messaging not initialized"
);
@@ -117,14 +117,14 @@ QUnit.test('messaging becomes initialized', async function (assert) {
waitUntilMessagingCondition: 'created',
});
assert.strictEqual(
document.querySelectorAll('.o_Discuss_messagingNotInitialized').length,
document.querySelectorAll('.o_DiscussContainer_spinner').length,
1,
"should display messaging not initialized"
);
await afterNextRender(() => messagingInitializedProm.resolve());
assert.strictEqual(
document.querySelectorAll('.o_Discuss_messagingNotInitialized').length,
document.querySelectorAll('.o_DiscussContainer_spinner').length,
0,
"should no longer display messaging not initialized"
);
@@ -0,0 +1,42 @@
/** @odoo-module **/
import { useModels } from "@mail/component_hooks/use_models/use_models";
import { useUpdate } from '@mail/component_hooks/use_update/use_update';
import { getMessagingComponent } from "@mail/utils/messaging_component";
const { Component, onWillUnmount } = owl;
export class DiscussContainer extends Component {
/**
* @override
*/
setup() {
// for now, the legacy env is needed for internal functions such as
// `useModels` to work
this.env = Component.env;
useModels();
super.setup();
useUpdate({ func: () => this._update() });
onWillUnmount(() => this._willUnmount());
}
_update() {
if (!this.messaging || !this.messaging.discuss) {
return;
}
this.messaging.discuss.open();
}
_willUnmount() {
if (this.messaging && this.messaging.discuss) {
this.messaging.discuss.close();
}
}
}
Object.assign(DiscussContainer, {
components: { Discuss: getMessagingComponent('Discuss') },
template: 'mail.DiscussContainer',
});
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mail.DiscussContainer" owl="1">
<div class="o_DiscussContainer d-flex flex-grow-1">
<Discuss t-if="messaging and messaging.discuss and messaging.discuss.discussView and messaging.isInitialized" className="'flex-grow-1'" localId="messaging.discuss.discussView.localId"/>
<div t-else="" class="o_DiscussContainer_spinner d-flex flex-grow-1 align-items-center justify-content-center">
<i class="o_DiscussContainer_spinnerIcon fa fa-circle-o-notch fa-spin me-2"/>Please wait...
</div>
</div>
</t>
</templates>
@@ -25,10 +25,10 @@ export class DiscussSidebar extends Component {
//--------------------------------------------------------------------------
/**
* @returns {Discuss}
* @returns {DiscussView}
*/
get discuss() {
return this.messaging && this.messaging.models['Discuss'].get(this.props.localId);
get discussView() {
return this.messaging && this.messaging.models['DiscussView'].get(this.props.localId);
}
//--------------------------------------------------------------------------
@@ -39,11 +39,11 @@ export class DiscussSidebar extends Component {
* @private
*/
_update() {
if (!this.discuss) {
if (!this.discussView) {
return;
}
if (this._quickSearchInputRef.el) {
this._quickSearchInputRef.el.value = this.discuss.sidebarQuickSearchValue;
this._quickSearchInputRef.el.value = this.discussView.discuss.sidebarQuickSearchValue;
}
}
@@ -57,7 +57,7 @@ export class DiscussSidebar extends Component {
*/
_onInputQuickSearch(ev) {
ev.stopPropagation();
this.discuss.onInputQuickSearch(this._quickSearchInputRef.el.value);
this.discussView.discuss.onInputQuickSearch(this._quickSearchInputRef.el.value);
}
}
@@ -3,30 +3,34 @@
<t t-name="mail.DiscussSidebar" owl="1">
<div name="root" class="o_DiscussSidebar" t-attf-class="{{ className }}" t-ref="root">
<div class="d-flex justify-content-center">
<button class="o_DiscussSidebar_startAMeetingButton btn btn-primary rounded" title="Start a meeting" t-on-click="discuss.onClickStartAMeetingButton">
Start a meeting
</button>
</div>
<hr class="o_DiscussSidebar_separator"/>
<div class="o_DiscussSidebar_category o_DiscussSidebar_categoryMailbox">
<DiscussSidebarMailbox threadLocalId="messaging.inbox.localId"/>
<DiscussSidebarMailbox threadLocalId="messaging.starred.localId"/>
<DiscussSidebarMailbox threadLocalId="messaging.history.localId"/>
</div>
<hr class="o_DiscussSidebar_separator"/>
<t t-if="messaging.models['Thread'].all(thread => thread.isPinned and thread.model === 'mail.channel').length > 19">
<input class="o_DiscussSidebar_quickSearch" t-on-input="_onInputQuickSearch" placeholder="Quick search..." t-ref="quickSearchInput" t-esc="discuss.sidebarQuickSearchValue"/>
<t t-if="discussView">
<div class="d-flex justify-content-center">
<button class="o_DiscussSidebar_startAMeetingButton btn btn-primary rounded" title="Start a meeting" t-on-click="discussView.discuss.onClickStartAMeetingButton">
Start a meeting
</button>
</div>
<hr class="o_DiscussSidebar_separator"/>
<div class="o_DiscussSidebar_category o_DiscussSidebar_categoryMailbox">
<DiscussSidebarMailbox threadLocalId="messaging.inbox.localId"/>
<DiscussSidebarMailbox threadLocalId="messaging.starred.localId"/>
<DiscussSidebarMailbox threadLocalId="messaging.history.localId"/>
</div>
<hr class="o_DiscussSidebar_separator"/>
<t t-if="messaging.models['Thread'].all(thread => thread.isPinned and thread.model === 'mail.channel').length > 19">
<input class="o_DiscussSidebar_quickSearch" t-on-input="_onInputQuickSearch" placeholder="Quick search..." t-ref="quickSearchInput" t-esc="discussView.discuss.sidebarQuickSearchValue"/>
</t>
<DiscussSidebarCategory
t-if="discussView.discuss.categoryChannel"
className="'o_DiscussSidebar_category o_DiscussSidebar_categoryChannel'"
localId="discussView.discuss.categoryChannel.localId"
/>
<t name="beforeCategoryChat"/>
<DiscussSidebarCategory
t-if="discussView.discuss.categoryChat"
className="'o_DiscussSidebar_category o_DiscussSidebar_categoryChat'"
localId="discussView.discuss.categoryChat.localId"
/>
</t>
<DiscussSidebarCategory
className="'o_DiscussSidebar_category o_DiscussSidebar_categoryChannel'"
localId="discuss.categoryChannel.localId"
/>
<t name="beforeCategoryChat"/>
<DiscussSidebarCategory
className="'o_DiscussSidebar_category o_DiscussSidebar_categoryChat'"
localId="discuss.categoryChat.localId"
/>
</div>
</t>
@@ -34,7 +34,7 @@
<t t-call="mail.MessagingMenu.newMessageButton"/>
</t>
<div class="o-autogrow"/>
<t t-if="!messaging.device.isMobile and !messaging.discuss.isOpen">
<t t-if="!messaging.device.isMobile and !messaging.discuss.discussView">
<t t-call="mail.MessagingMenu.newMessageButton"/>
</t>
<t t-if="messaging.device.isMobile and messagingMenu.isMobileNewMessageToggled">
@@ -19,7 +19,7 @@ registerModel({
if (notifyServer === undefined) {
notifyServer = !this.messaging.device.isMobile;
}
if (this.messaging.device.isMobile && !this.messaging.discuss.isOpen) {
if (this.messaging.device.isMobile && !this.messaging.discuss.discussView) {
// If we are in mobile and discuss is not open, it means the
// chat window was opened from the messaging menu. In that
// case it should be re-opened to simulate it was always
@@ -272,7 +272,7 @@ registerModel({
const HIDDEN_MENU_WIDTH = 200; // max width, including width of dropup list items
const START_GAP_WIDTH = device.isMobile ? 0 : 10;
const chatWindows = this.allOrdered;
if (!device.isMobile && discuss.isOpen) {
if (!device.isMobile && discuss.discussView) {
return visual;
}
if (!chatWindows.length) {
@@ -21,7 +21,7 @@ registerModel({
* Close the discuss app. Should reset its internal state.
*/
close() {
this.update({ isOpen: false });
this.update({ discussView: clear() });
},
focus() {
if (this.threadView && this.threadView.composerView) {
@@ -158,6 +158,9 @@ registerModel({
}
this.threadView.topbar.openInvitePopoverView();
},
open() {
this.update({ discussView: insertAndReplace() });
},
/**
* Open thread from init active id. `initActiveId` is used to refer to
* a thread that we may not have full data yet, such as when messaging
@@ -193,7 +196,7 @@ registerModel({
if (focus !== undefined ? focus : !this.messaging.device.isMobileDevice) {
this.focus();
}
if (!this.isOpen) {
if (!this.discussView) {
this.env.bus.trigger('do-action', {
action: 'mail.action_discuss',
options: {
@@ -237,7 +240,7 @@ registerModel({
* @returns {string}
*/
_computeAddingChannelValue() {
if (!this.isOpen) {
if (!this.discussView) {
return "";
}
return this.addingChannelValue;
@@ -247,7 +250,7 @@ registerModel({
* @returns {boolean}
*/
_computeHasThreadView() {
if (!this.thread || !this.isOpen) {
if (!this.thread || !this.discussView) {
return false;
}
if (
@@ -266,7 +269,7 @@ registerModel({
* @returns {boolean}
*/
_computeIsAddingChannel() {
if (!this.isOpen) {
if (!this.discussView) {
return false;
}
return this.isAddingChannel;
@@ -276,7 +279,7 @@ registerModel({
* @returns {boolean}
*/
_computeIsAddingChat() {
if (!this.isOpen) {
if (!this.discussView) {
return false;
}
return this.isAddingChat;
@@ -357,6 +360,10 @@ registerModel({
inverse: 'discussAsChat',
isCausal: true,
}),
discussView: one('DiscussView', {
inverse: 'discuss',
isCausal: true,
}),
/**
* Determines whether `this.thread` should be displayed.
*/
@@ -390,13 +397,6 @@ registerModel({
compute: '_computeIsAddingChat',
default: false,
}),
/**
* Whether the discuss app is open or not. Useful to determine
* whether the discuss or chat window logic should be applied.
*/
isOpen: attr({
default: false,
}),
/**
* The menu_id of discuss app, received on mail/init_messaging and
* used to open discuss from elsewhere.
@@ -0,0 +1,16 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { one } from '@mail/model/model_field';
registerModel({
name: 'DiscussView',
identifyingFields: ['discuss'],
fields: {
discuss: one('Discuss', {
inverse: 'discussView',
readonly: true,
required: true,
}),
},
});
@@ -40,7 +40,7 @@ registerModel({
route: '/mail/init_messaging',
}, { shadow: true }));
await this.async(() => this._init(data));
if (discuss.isOpen) {
if (discuss.discussView) {
discuss.openInitThread();
}
if (this.messaging.autofetchPartnerImStatus) {
@@ -942,7 +942,7 @@ registerModel({
const discuss = this.messaging.discuss;
// check if thread must be opened in form view
if (!['mail.box', 'mail.channel'].includes(this.model)) {
if (expanded || discuss.isOpen) {
if (expanded || discuss.discussView) {
// Close chat window because having the same thread opened
// both in chat window and as main document does not look
// good.
@@ -957,7 +957,7 @@ registerModel({
// check if thread must be opened in discuss
const device = this.messaging.device;
if (
(!device.isMobile && (discuss.isOpen || expanded)) ||
(!device.isMobile && (discuss.discussView || expanded)) ||
this.model === 'mail.box'
) {
return discuss.openThread(this, {
@@ -1,6 +1,6 @@
/** @odoo-module **/
import { getMessagingComponent } from "@mail/utils/messaging_component";
import { DiscussContainer } from "@mail/components/discuss_container/discuss_container";
import AbstractAction from 'web.AbstractAction';
@@ -63,8 +63,7 @@ export const DiscussWidget = AbstractAction.extend({
// prevent twice call to on_attach_callback (FIXME)
return;
}
const DiscussComponent = getMessagingComponent("Discuss");
this.component = new DiscussComponent();
this.component = new DiscussContainer();
this._pushStateActionManagerEventListener = ev => {
ev.stopPropagation();
if (this._lastPushStateActiveThread === this.discuss.thread) {
@@ -2,9 +2,9 @@
<templates xml:space="preserve">
<t t-inherit="mail.Discuss.content" t-inherit-mode="extension">
<xpath expr="//*[@name='beforeThread']" position="before">
<t t-if="discuss.thread.visitor">
<t t-if="discussView.discuss.thread.visitor">
<VisitorBanner
visitorLocalId="discuss.thread.visitor.localId"
visitorLocalId="discussView.discuss.thread.visitor.localId"
/>
</t>
</xpath>