[IMP] mail: Introduce view models
This commit moves code from components to models, by introduce some models and fields. It also cleans a few things in models, like: - use Timer model instead of setTimeout - replace local variables by model fields Task-2881075 All changes: - [IMP] mail: remove ChannelMemberListCategoryComponent/channelMemberListView Task-2831082 - [IMP] mail: introduce ChannelMemberListCategoryView/members Task-2864112 - [IMP] mail: introduce ChannelMemberListCategoryView/title Task-2864115 - [IMP] mail: introduce ChannelMemberView Task-2864119 - [IMP] mail: introduce 'Chatter/attachmentsLoaderTimer' Task-2868781 - [IMP] mail: introduce 'MessageView/highlightTimer' Task-2868794 - [IMP] mail: introduce 'CallView/showOverlayTimer' Task-2868867 - [IMP] mail: introduce RtcSession/broadcastTimer Task-2868929 - [IMP] mail: introduce CallView/tileContainerRef Task-2871859 - [IMP] mail: introduce CallView/tileWidth Task-2871869 - [IMP] mail: introduce CallView/tileHeight Task-2871878 - [IMP] mail: introduce ChatWindowManager/endGapWidth Task-2871960 - [IMP] mail: remove unused CallView/columnCount Task-2871885 - [IMP] mail: introduce ChatWindowManager/hiddenMenuWidth Task-2871963 - [IMP] mail: introduce CallView/calculateTessellation Task-2871867 - [IMP] mail: replace model methods of MediaPreview by record methods Task-2871934 - [IMP] mail: introduce ChatWindowManager/betweenGapWidth Task-2871956 - [IMP] mail: introduce ChatWindowManager/chatWindowWidth Task-2871957 - [IMP] mail: introduce ChatWindowManager/startGapWidth Task-2871965 - [IMP] mail: introduce ChatWindowManager/availableVisibleSlots Task-2871971 - [IMP] mail: introduce ChatWindowManager/visual/hiddenChatWindowLocalIds Task-2871980 - [IMP] mail: introduce ChatWindowManager/visual/isHiddenMenuVisible Task-2871981 - [IMP] mail: introduce ChatWindowManager/visual/hiddenMenuOffset Task-2871984 Part-of: odoo/odoo#93330
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { useRefToModel } from '@mail/component_hooks/use_ref_to_model';
|
||||
import { useUpdate } from '@mail/component_hooks/use_update';
|
||||
import { registerMessagingComponent } from '@mail/utils/messaging_component';
|
||||
|
||||
import Dialog from 'web.OwlDialog';
|
||||
|
||||
const { Component, useRef, useState } = owl;
|
||||
const { Component } = owl;
|
||||
|
||||
const components = {
|
||||
Dialog,
|
||||
@@ -20,12 +21,7 @@ export class CallView extends Component {
|
||||
*/
|
||||
setup() {
|
||||
super.setup();
|
||||
this.state = useState({
|
||||
tileWidth: 0,
|
||||
tileHeight: 0,
|
||||
columnCount: 0,
|
||||
});
|
||||
this.tileContainerRef = useRef('tileContainer');
|
||||
useRefToModel({ fieldName: 'tileContainerRef', refName: 'tileContainer', });
|
||||
useUpdate({ func: () => this._update() });
|
||||
}
|
||||
|
||||
@@ -44,52 +40,6 @@ export class CallView extends Component {
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Finds a tile layout and dimensions that respects param0.aspectRatio while maximizing
|
||||
* the total area covered by the tiles within the specified container dimensions.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} param0
|
||||
* @param {number} [param0.aspectRatio]
|
||||
* @param {number} param0.containerHeight
|
||||
* @param {number} param0.containerWidth
|
||||
* @param {number} param0.tileCount
|
||||
*/
|
||||
_computeTessellation({ aspectRatio = 1, containerHeight, containerWidth, tileCount }) {
|
||||
let optimalLayout = {
|
||||
area: 0,
|
||||
cols: 0,
|
||||
tileHeight: 0,
|
||||
tileWidth: 0,
|
||||
};
|
||||
|
||||
for (let columnCount = 1; columnCount <= tileCount; columnCount++) {
|
||||
const rowCount = Math.ceil(tileCount / columnCount);
|
||||
const potentialHeight = containerWidth / (columnCount * aspectRatio);
|
||||
const potentialWidth = containerHeight / rowCount;
|
||||
let tileHeight;
|
||||
let tileWidth;
|
||||
if (potentialHeight > potentialWidth) {
|
||||
tileHeight = Math.floor(potentialWidth);
|
||||
tileWidth = Math.floor(tileHeight * aspectRatio);
|
||||
} else {
|
||||
tileWidth = Math.floor(containerWidth / columnCount);
|
||||
tileHeight = Math.floor(tileWidth / aspectRatio);
|
||||
}
|
||||
const area = tileHeight * tileWidth;
|
||||
if (area <= optimalLayout.area) {
|
||||
continue;
|
||||
}
|
||||
optimalLayout = {
|
||||
area,
|
||||
columnCount,
|
||||
tileHeight,
|
||||
tileWidth,
|
||||
};
|
||||
}
|
||||
return optimalLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -97,21 +47,22 @@ export class CallView extends Component {
|
||||
if (!this.root.el) {
|
||||
return;
|
||||
}
|
||||
if (!this.tileContainerRef.el) {
|
||||
if (!this.callView.tileContainerRef.el) {
|
||||
return;
|
||||
}
|
||||
const { width, height } = this.tileContainerRef.el.getBoundingClientRect();
|
||||
const { width, height } = this.callView.tileContainerRef.el.getBoundingClientRect();
|
||||
|
||||
const { tileWidth, tileHeight, columnCount } = this._computeTessellation({
|
||||
const { tileWidth, tileHeight } = this.callView.calculateTessellation({
|
||||
aspectRatio: this.callView.aspectRatio,
|
||||
containerHeight: height,
|
||||
containerWidth: width,
|
||||
tileCount: this.tileContainerRef.el.children.length,
|
||||
tileCount: this.callView.tileContainerRef.el.children.length,
|
||||
});
|
||||
|
||||
this.state.tileWidth = tileWidth;
|
||||
this.state.tileHeight = tileHeight;
|
||||
this.state.columnCount = columnCount;
|
||||
this.callView.update({
|
||||
tileHeight,
|
||||
tileWidth,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
'o-isSidebar': callView.hasSidebar and callView.mainParticipantCard,
|
||||
}"
|
||||
t-ref="tileContainer"
|
||||
t-attf-style="--height:{{state.tileHeight}}px; --width:{{state.tileWidth}}px; --columns:{{state.columnCount}};"
|
||||
t-attf-style="--height:{{state.tileHeight}}px; --width:{{state.tileWidth}}px;"
|
||||
>
|
||||
<t t-foreach="callView.tileParticipantCards" t-as="participantCard" t-key="'grid_tile_'+participantCard.localId">
|
||||
<t t-if="!callView.filterVideoGrid or (participantCard.rtcSession and participantCard.rtcSession.videoStream)">
|
||||
|
||||
@@ -14,10 +14,10 @@ export class ChannelMember extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Partner}
|
||||
* @returns {ChannelMemberView}
|
||||
*/
|
||||
get member() {
|
||||
return this.props.member;
|
||||
get record() {
|
||||
return this.props.record;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ export class ChannelMember extends Component {
|
||||
Object.assign(ChannelMember, {
|
||||
props: {
|
||||
channel: Object,
|
||||
member: Object,
|
||||
record: Object,
|
||||
},
|
||||
template: 'mail.ChannelMember',
|
||||
});
|
||||
|
||||
@@ -2,23 +2,23 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.ChannelMember" owl="1">
|
||||
<div class="o_ChannelMember d-flex align-items-center mx-3 my-1" t-attf-class="{{ className }}" t-att-data-partner-local-id="member.localId" t-ref="root">
|
||||
<div class="o_ChannelMember_avatarContainer position-relative flex-shrink-0 o_cursor_pointer" t-on-click="() => channel.onClickMemberAvatar(member)">
|
||||
<img class="o_ChannelMember_avatar rounded-circle w-100 h-100" t-attf-src="/mail/channel/{{ channel.id }}/partner/{{ member.id }}/avatar_128" alt="Avatar"/>
|
||||
<div class="o_ChannelMember d-flex align-items-center mx-3 my-1" t-attf-class="{{ className }}" t-att-data-partner-local-id="record.partner.localId" t-ref="root">
|
||||
<div class="o_ChannelMember_avatarContainer position-relative flex-shrink-0 o_cursor_pointer" t-on-click="() => channel.onClickMemberAvatar(record.partner)">
|
||||
<img class="o_ChannelMember_avatar rounded-circle w-100 h-100" t-attf-src="/mail/channel/{{ channel.id }}/partner/{{ record.partner.id }}/avatar_128" alt="Avatar"/>
|
||||
|
||||
<t t-if="member.im_status and member.im_status !== 'im_partner'">
|
||||
<t t-if="record.partner.im_status and record.partner.im_status !== 'im_partner'">
|
||||
<PartnerImStatusIcon
|
||||
className="'o_ChannelMember_partnerImStatusIcon position-absolute bottom-0 end-0 d-flex align-items-center justify-content-center text-light'"
|
||||
classNameObj="{
|
||||
'o-isDeviceSmall': messaging.device.isSmall,
|
||||
'small': !messaging.device.isSmall,
|
||||
}"
|
||||
partner="member"
|
||||
partner="record.partner"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<span class="o_ChannelMember_name ml-2 flex-column-1 text-truncate o_cursor_pointer" t-on-click="() => channel.onClickMemberName(member)">
|
||||
<t t-esc="member.nameOrDisplayName"/>
|
||||
<span class="o_ChannelMember_name ml-2 flex-column-1 text-truncate o_cursor_pointer" t-on-click="() => channel.onClickMemberName(record.partner)">
|
||||
<t t-esc="record.partner.nameOrDisplayName"/>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -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" channelMemberListView="channelMemberListView"/>
|
||||
<ChannelMemberListCategory record="channelMemberListView.onlineCategoryView"/>
|
||||
</t>
|
||||
<t t-if="channelMemberListView.offlineCategoryView">
|
||||
<ChannelMemberListCategory record="channelMemberListView.offlineCategoryView" channelMemberListView="channelMemberListView"/>
|
||||
<ChannelMemberListCategory record="channelMemberListView.offlineCategoryView"/>
|
||||
</t>
|
||||
<t t-if="channelMemberListView.channel.unknownMemberCount === 1">
|
||||
<span class="mx-2 mt-2">And 1 other member.</span>
|
||||
|
||||
+1
-49
@@ -2,19 +2,10 @@
|
||||
|
||||
import { registerMessagingComponent } from '@mail/utils/messaging_component';
|
||||
|
||||
import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
const { Component } = owl;
|
||||
|
||||
export class ChannelMemberListCategory extends Component {
|
||||
|
||||
/**
|
||||
* @returns {ChannelMemberListView}
|
||||
*/
|
||||
get channelMemberListView() {
|
||||
return this.props.channelMemberListView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {ChannelMemberListCategoryView}
|
||||
*/
|
||||
@@ -22,49 +13,10 @@ export class ChannelMemberListCategory extends Component {
|
||||
return this.props.record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Partner[]}
|
||||
*/
|
||||
get members() {
|
||||
if (!this.channelMemberListView.exists()) {
|
||||
return [];
|
||||
}
|
||||
if (this.record.channelMemberListViewOwnerAsOnline) {
|
||||
return this.channelMemberListView.channel.orderedOnlineMembers;
|
||||
}
|
||||
if (this.record.channelMemberListViewOwnerAsOffline) {
|
||||
return this.channelMemberListView.channel.orderedOfflineMembers;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
get title() {
|
||||
let categoryText = "";
|
||||
if (this.record.channelMemberListViewOwnerAsOnline) {
|
||||
categoryText = this.env._t("Online");
|
||||
}
|
||||
if (this.record.channelMemberListViewOwnerAsOffline) {
|
||||
categoryText = this.env._t("Offline");
|
||||
}
|
||||
return sprintf(
|
||||
this.env._t("%(categoryText)s - %(memberCount)s"),
|
||||
{
|
||||
categoryText,
|
||||
memberCount: this.members.length,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Object.assign(ChannelMemberListCategory, {
|
||||
props: {
|
||||
channelMemberListView: Object,
|
||||
record: Object,
|
||||
},
|
||||
props: { record: Object },
|
||||
template: 'mail.ChannelMemberListCategory',
|
||||
});
|
||||
|
||||
|
||||
+3
-3
@@ -2,9 +2,9 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.ChannelMemberListCategory" owl="1">
|
||||
<h6 class="m-2" t-esc="title"/>
|
||||
<t t-foreach="members" t-as="member" t-key="member.localId">
|
||||
<ChannelMember channel="channelMemberListView.channel" member="member"/>
|
||||
<h6 class="m-2" t-esc="record.title"/>
|
||||
<t t-foreach="record.channelMemberViews" t-as="channelMemberView" t-key="channelMemberView.localId">
|
||||
<ChannelMember channel="record.channel" record="channelMemberView"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ export class ChatWindowHiddenMenu extends Component {
|
||||
const textDirection = this.messaging.locale.textDirection;
|
||||
const offsetFrom = textDirection === 'rtl' ? 'left' : 'right';
|
||||
const oppositeFrom = offsetFrom === 'right' ? 'left' : 'right';
|
||||
const offset = this.messaging.chatWindowManager.visual.hidden.offset;
|
||||
const offset = this.messaging.chatWindowManager.visual.hiddenMenuOffset;
|
||||
this.root.el.style[offsetFrom] = `${offset}px`;
|
||||
this.root.el.style[oppositeFrom] = 'auto';
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<t t-name="mail.ChatWindowManager" owl="1">
|
||||
<div class="o_ChatWindowManager flex-row-reverse d-flex" t-attf-class="{{ className }}" t-ref="root">
|
||||
<!-- Note: DOM elements are ordered from left to right -->
|
||||
<t t-if="messaging.chatWindowManager.visual.hidden.isVisible">
|
||||
<t t-if="messaging.chatWindowManager.visual.isHiddenMenuVisible">
|
||||
<ChatWindowHiddenMenu className="'o_ChatWindowManager_hiddenMenu'"/>
|
||||
</t>
|
||||
<t t-foreach="messaging.chatWindowManager.allOrderedVisible" t-as="chatWindow" t-key="chatWindow.localId">
|
||||
|
||||
@@ -17,11 +17,53 @@ registerModel({
|
||||
browser.addEventListener('fullscreenchange', this._onFullScreenChange);
|
||||
},
|
||||
_willDelete() {
|
||||
browser.clearTimeout(this.showOverlayTimeout);
|
||||
browser.removeEventListener('fullscreenchange', this._onFullScreenChange);
|
||||
},
|
||||
},
|
||||
recordMethods: {
|
||||
/**
|
||||
* Finds a tile layout and dimensions that respects param0.aspectRatio while maximizing
|
||||
* the total area covered by the tiles within the specified container dimensions.
|
||||
*
|
||||
* @param {Object} param0
|
||||
* @param {number} [param0.aspectRatio]
|
||||
* @param {number} param0.containerHeight
|
||||
* @param {number} param0.containerWidth
|
||||
* @param {number} param0.tileCount
|
||||
*/
|
||||
calculateTessellation({ aspectRatio = 1, containerHeight, containerWidth, tileCount }) {
|
||||
let optimalLayout = {
|
||||
area: 0,
|
||||
cols: 0,
|
||||
tileHeight: 0,
|
||||
tileWidth: 0,
|
||||
};
|
||||
|
||||
for (let columnCount = 1; columnCount <= tileCount; columnCount++) {
|
||||
const rowCount = Math.ceil(tileCount / columnCount);
|
||||
const potentialHeight = containerWidth / (columnCount * aspectRatio);
|
||||
const potentialWidth = containerHeight / rowCount;
|
||||
let tileHeight;
|
||||
let tileWidth;
|
||||
if (potentialHeight > potentialWidth) {
|
||||
tileHeight = Math.floor(potentialWidth);
|
||||
tileWidth = Math.floor(tileHeight * aspectRatio);
|
||||
} else {
|
||||
tileWidth = Math.floor(containerWidth / columnCount);
|
||||
tileHeight = Math.floor(tileWidth / aspectRatio);
|
||||
}
|
||||
const area = tileHeight * tileWidth;
|
||||
if (area <= optimalLayout.area) {
|
||||
continue;
|
||||
}
|
||||
optimalLayout = {
|
||||
area,
|
||||
tileHeight,
|
||||
tileWidth,
|
||||
};
|
||||
}
|
||||
return optimalLayout;
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
@@ -75,8 +117,8 @@ registerModel({
|
||||
markEventHandled(ev, 'CallView.MouseMoveOverlay');
|
||||
this.update({
|
||||
showOverlay: true,
|
||||
showOverlayTimer: clear(),
|
||||
});
|
||||
browser.clearTimeout(this.showOverlayTimeout);
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -84,6 +126,9 @@ registerModel({
|
||||
onRtcSettingsDialogClosed(ev) {
|
||||
this.messaging.userSetting.callSettingsMenu.toggle();
|
||||
},
|
||||
onShowOverlayTimeout() {
|
||||
this.update({ showOverlay: false });
|
||||
},
|
||||
async activateFullScreen() {
|
||||
const el = document.body;
|
||||
try {
|
||||
@@ -249,10 +294,7 @@ registerModel({
|
||||
_showOverlay() {
|
||||
this.update({
|
||||
showOverlay: true,
|
||||
});
|
||||
browser.clearTimeout(this.showOverlayTimeout);
|
||||
this.update({
|
||||
showOverlayTimeout: browser.setTimeout(this._onShowOverlayTimeout, 3000),
|
||||
showOverlayTimer: [clear(), insertAndReplace()],
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -266,15 +308,6 @@ registerModel({
|
||||
}
|
||||
this.update({ isFullScreen: false });
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onShowOverlayTimeout() {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ showOverlay: false });
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
/**
|
||||
@@ -288,6 +321,7 @@ registerModel({
|
||||
default: 16 / 9,
|
||||
compute: '_computeAspectRatio',
|
||||
}),
|
||||
tileContainerRef: attr(),
|
||||
/**
|
||||
* Determines whether we only display the videos or all the participants
|
||||
*/
|
||||
@@ -364,7 +398,10 @@ registerModel({
|
||||
showOverlay: attr({
|
||||
default: true,
|
||||
}),
|
||||
showOverlayTimeout: attr(),
|
||||
showOverlayTimer: one('Timer', {
|
||||
inverse: 'callViewAsShowOverlay',
|
||||
isCausal: true,
|
||||
}),
|
||||
/**
|
||||
* ThreadView on which the call view is attached.
|
||||
*/
|
||||
@@ -373,6 +410,9 @@ registerModel({
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
tileHeight: attr({
|
||||
default: 0,
|
||||
}),
|
||||
/**
|
||||
* List of all participant cards (can either be invitations or rtcSessions).
|
||||
*/
|
||||
@@ -381,6 +421,9 @@ registerModel({
|
||||
inverse: 'callViewOfTile',
|
||||
isCausal: true,
|
||||
}),
|
||||
tileWidth: attr({
|
||||
default: 0,
|
||||
}),
|
||||
},
|
||||
onChanges: [
|
||||
new OnChange({
|
||||
|
||||
@@ -1,12 +1,81 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { one } from '@mail/model/model_field';
|
||||
import { attr, many, one } from '@mail/model/model_field';
|
||||
import { clear, insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelMemberListCategoryView',
|
||||
identifyingFields: [['channelMemberListViewOwnerAsOffline', 'channelMemberListViewOwnerAsOnline']],
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeChannel() {
|
||||
if (this.channelMemberListViewOwnerAsOffline) {
|
||||
return replace(this.channelMemberListViewOwnerAsOffline.channel);
|
||||
}
|
||||
if (this.channelMemberListViewOwnerAsOnline) {
|
||||
return replace(this.channelMemberListViewOwnerAsOnline.channel);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeChannelMemberViews() {
|
||||
if (this.members.length === 0) {
|
||||
return clear();
|
||||
}
|
||||
return insertAndReplace(
|
||||
this.members.map(member => ({ partner: replace(member) })),
|
||||
);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeMembers() {
|
||||
if (!this.exists()) {
|
||||
return clear();
|
||||
}
|
||||
if (this.channelMemberListViewOwnerAsOnline) {
|
||||
return replace(this.channel.orderedOnlineMembers);
|
||||
}
|
||||
if (this.channelMemberListViewOwnerAsOffline) {
|
||||
return replace(this.channel.orderedOfflineMembers);
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_computeTitle() {
|
||||
let categoryText = "";
|
||||
if (this.channelMemberListViewOwnerAsOnline) {
|
||||
categoryText = this.env._t("Online");
|
||||
}
|
||||
if (this.channelMemberListViewOwnerAsOffline) {
|
||||
categoryText = this.env._t("Offline");
|
||||
}
|
||||
return sprintf(
|
||||
this.env._t("%(categoryText)s - %(memberCount)s"),
|
||||
{
|
||||
categoryText,
|
||||
memberCount: this.members.length,
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
channel: one('Thread', {
|
||||
compute: '_computeChannel',
|
||||
required: true,
|
||||
}),
|
||||
channelMemberListViewOwnerAsOffline: one('ChannelMemberListView', {
|
||||
inverse: 'offlineCategoryView',
|
||||
readonly: true,
|
||||
@@ -15,5 +84,16 @@ registerModel({
|
||||
inverse: 'onlineCategoryView',
|
||||
readonly: true,
|
||||
}),
|
||||
channelMemberViews: many('ChannelMemberView', {
|
||||
compute: '_computeChannelMemberViews',
|
||||
inverse: 'channelMemberListCategoryViewOwner',
|
||||
isCausal: true,
|
||||
}),
|
||||
members: many('Partner', {
|
||||
compute: '_computeMembers',
|
||||
}),
|
||||
title: attr({
|
||||
compute: '_computeTitle',
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'ChannelMemberView',
|
||||
identifyingFields: ['channelMemberListCategoryViewOwner', 'partner'],
|
||||
fields: {
|
||||
channelMemberListCategoryViewOwner: one('ChannelMemberListCategoryView', {
|
||||
inverse: 'channelMemberViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
partner: one('Partner', {
|
||||
inverse: 'channelMemberViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -6,28 +6,24 @@ import { clear, insertAndReplace, link, replace } from '@mail/model/model_field_
|
||||
|
||||
const BASE_VISUAL = {
|
||||
/**
|
||||
* @deprecated, should use ChatWindowManager/availableVisibleSlots instead
|
||||
* Amount of visible slots available for chat windows.
|
||||
*/
|
||||
availableVisibleSlots: 0,
|
||||
/**
|
||||
* Data related to the hidden menu.
|
||||
* List of hidden docked chat windows. Useful to compute counter.
|
||||
* Chat windows are ordered by their `chatWindows` order.
|
||||
*/
|
||||
hidden: {
|
||||
/**
|
||||
* List of hidden docked chat windows. Useful to compute counter.
|
||||
* Chat windows are ordered by their `chatWindows` order.
|
||||
*/
|
||||
chatWindowLocalIds: [],
|
||||
/**
|
||||
* Whether hidden menu is visible or not
|
||||
*/
|
||||
isVisible: false,
|
||||
/**
|
||||
* Offset of hidden menu starting point from the starting point
|
||||
* of chat window manager. Makes only sense if it is visible.
|
||||
*/
|
||||
offset: 0,
|
||||
},
|
||||
hiddenChatWindowLocalIds: [],
|
||||
/**
|
||||
* Whether hidden menu is visible or not
|
||||
*/
|
||||
isHiddenMenuVisible: false,
|
||||
/**
|
||||
* Offset of hidden menu starting point from the starting point
|
||||
* of chat window manager. Makes only sense if it is visible.
|
||||
*/
|
||||
hiddenMenuOffset: 0,
|
||||
/**
|
||||
* Data related to visible chat windows. Index determine order of
|
||||
* docked chat windows.
|
||||
@@ -223,7 +219,7 @@ registerModel({
|
||||
* @returns {ChatWindow[]}
|
||||
*/
|
||||
_computeAllOrderedHidden() {
|
||||
return replace(this.visual.hidden.chatWindowLocalIds.map(chatWindowLocalId =>
|
||||
return replace(this.visual.hiddenChatWindowLocalIds.map(chatWindowLocalId =>
|
||||
this.messaging.models['ChatWindow'].get(chatWindowLocalId)
|
||||
));
|
||||
},
|
||||
@@ -236,6 +232,23 @@ registerModel({
|
||||
this.messaging.models['ChatWindow'].get(chatWindowLocalId)
|
||||
));
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {integer}
|
||||
*/
|
||||
_computeAvailableVisibleSlots() {
|
||||
return this.visual.availableVisibleSlots;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {integer}
|
||||
*/
|
||||
_computeEndGapWidth() {
|
||||
if (this.messaging.device.isSmall) {
|
||||
return 0;
|
||||
}
|
||||
return 10;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
@@ -264,6 +277,16 @@ registerModel({
|
||||
}
|
||||
return replace(lastVisible);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {integer}
|
||||
*/
|
||||
_computeStartGapWidth() {
|
||||
if (this.messaging.device.isSmall) {
|
||||
return 0;
|
||||
}
|
||||
return 10;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {integer}
|
||||
@@ -289,23 +312,18 @@ registerModel({
|
||||
if (!this.messaging || !this.messaging.device) {
|
||||
return visual;
|
||||
}
|
||||
const BETWEEN_GAP_WIDTH = 5;
|
||||
const CHAT_WINDOW_WIDTH = 325;
|
||||
const END_GAP_WIDTH = this.messaging.device.isSmall ? 0 : 10;
|
||||
const HIDDEN_MENU_WIDTH = 200; // max width, including width of dropup list items
|
||||
const START_GAP_WIDTH = this.messaging.device.isSmall ? 0 : 10;
|
||||
if (!this.messaging.device.isSmall && this.messaging.discuss.discussView) {
|
||||
return visual;
|
||||
}
|
||||
if (!this.allOrdered.length) {
|
||||
return visual;
|
||||
}
|
||||
const relativeGlobalWindowWidth = this.messaging.device.globalWindowInnerWidth - START_GAP_WIDTH - END_GAP_WIDTH;
|
||||
const relativeGlobalWindowWidth = this.messaging.device.globalWindowInnerWidth - this.startGapWidth - this.endGapWidth;
|
||||
let maxAmountWithoutHidden = Math.floor(
|
||||
relativeGlobalWindowWidth / (CHAT_WINDOW_WIDTH + BETWEEN_GAP_WIDTH));
|
||||
relativeGlobalWindowWidth / (this.chatWindowWidth + this.betweenGapWidth));
|
||||
let maxAmountWithHidden = Math.floor(
|
||||
(relativeGlobalWindowWidth - HIDDEN_MENU_WIDTH - BETWEEN_GAP_WIDTH) /
|
||||
(CHAT_WINDOW_WIDTH + BETWEEN_GAP_WIDTH));
|
||||
(relativeGlobalWindowWidth - this.hiddenMenuWidth - this.betweenGapWidth) /
|
||||
(this.chatWindowWidth + this.betweenGapWidth));
|
||||
if (this.messaging.device.isSmall) {
|
||||
maxAmountWithoutHidden = 1;
|
||||
maxAmountWithHidden = 1;
|
||||
@@ -314,7 +332,7 @@ registerModel({
|
||||
// all visible
|
||||
for (let i = 0; i < this.allOrdered.length; i++) {
|
||||
const chatWindowLocalId = this.allOrdered[i].localId;
|
||||
const offset = START_GAP_WIDTH + i * (CHAT_WINDOW_WIDTH + BETWEEN_GAP_WIDTH);
|
||||
const offset = this.startGapWidth + i * (this.chatWindowWidth + this.betweenGapWidth);
|
||||
visual.visible.push({ chatWindowLocalId, offset });
|
||||
}
|
||||
visual.availableVisibleSlots = maxAmountWithoutHidden;
|
||||
@@ -322,23 +340,23 @@ registerModel({
|
||||
// some visible, some hidden
|
||||
for (let i = 0; i < maxAmountWithHidden; i++) {
|
||||
const chatWindowLocalId = this.allOrdered[i].localId;
|
||||
const offset = START_GAP_WIDTH + i * (CHAT_WINDOW_WIDTH + BETWEEN_GAP_WIDTH);
|
||||
const offset = this.startGapWidth + i * (this.chatWindowWidth + this.betweenGapWidth);
|
||||
visual.visible.push({ chatWindowLocalId, offset });
|
||||
}
|
||||
if (this.allOrdered.length > maxAmountWithHidden) {
|
||||
visual.hidden.isVisible = !this.messaging.device.isSmall;
|
||||
visual.hidden.offset = visual.visible[maxAmountWithHidden - 1].offset
|
||||
+ CHAT_WINDOW_WIDTH + BETWEEN_GAP_WIDTH;
|
||||
visual.isHiddenMenuVisible = !this.messaging.device.isSmall;
|
||||
visual.hiddenMenuOffset = visual.visible[maxAmountWithHidden - 1].offset
|
||||
+ this.chatWindowWidth + this.betweenGapWidth;
|
||||
}
|
||||
for (let j = maxAmountWithHidden; j < this.allOrdered.length; j++) {
|
||||
visual.hidden.chatWindowLocalIds.push(this.allOrdered[j].localId);
|
||||
visual.hiddenChatWindowLocalIds.push(this.allOrdered[j].localId);
|
||||
}
|
||||
visual.availableVisibleSlots = maxAmountWithHidden;
|
||||
} else {
|
||||
// all hidden
|
||||
visual.hidden.isVisible = !this.messaging.device.isSmall;
|
||||
visual.hidden.offset = START_GAP_WIDTH;
|
||||
visual.hidden.chatWindowLocalIds.concat(this.allOrdered.map(chatWindow => chatWindow.localId));
|
||||
visual.isHiddenMenuVisible = !this.messaging.device.isSmall;
|
||||
visual.hiddenMenuOffset = this.startGapWidth;
|
||||
visual.hiddenChatWindowLocalIds.concat(this.allOrdered.map(chatWindow => chatWindow.localId));
|
||||
console.warn('cannot display any visible chat windows (screen is too small)');
|
||||
visual.availableVisibleSlots = 0;
|
||||
}
|
||||
@@ -358,16 +376,35 @@ registerModel({
|
||||
allOrderedVisible: many('ChatWindow', {
|
||||
compute: '_computeAllOrderedVisible',
|
||||
}),
|
||||
/**
|
||||
* Amount of visible slots available for chat windows.
|
||||
*/
|
||||
availableVisibleSlots: attr({
|
||||
compute: '_computeAvailableVisibleSlots',
|
||||
default: 0,
|
||||
}),
|
||||
betweenGapWidth: attr({
|
||||
default: 5,
|
||||
}),
|
||||
chatWindows: many('ChatWindow', {
|
||||
inverse: 'manager',
|
||||
isCausal: true,
|
||||
}),
|
||||
chatWindowWidth: attr({
|
||||
default: 325,
|
||||
}),
|
||||
endGapWidth: attr({
|
||||
compute: '_computeEndGapWidth',
|
||||
}),
|
||||
hasVisibleChatWindows: attr({
|
||||
compute: '_computeHasVisibleChatWindows',
|
||||
}),
|
||||
hiddenChatWindowHeaderViews: many('ChatWindowHeaderView', {
|
||||
compute: '_computeHiddenChatWindowHeaderViews',
|
||||
}),
|
||||
hiddenMenuWidth: attr({
|
||||
default: 200, // max width, including width of dropup list items
|
||||
}),
|
||||
isHiddenMenuOpen: attr({
|
||||
default: false,
|
||||
}),
|
||||
@@ -378,6 +415,9 @@ registerModel({
|
||||
inverse: 'managerAsNewMessage',
|
||||
isCausal: true,
|
||||
}),
|
||||
startGapWidth: attr({
|
||||
compute: '_computeStartGapWidth',
|
||||
}),
|
||||
unreadHiddenConversationAmount: attr({
|
||||
compute: '_computeUnreadHiddenConversationAmount',
|
||||
}),
|
||||
|
||||
@@ -24,17 +24,15 @@ const getMessageNextTemporaryId = (function () {
|
||||
registerModel({
|
||||
name: 'Chatter',
|
||||
identifyingFields: ['id'],
|
||||
lifecycleHooks: {
|
||||
_willDelete() {
|
||||
this.messaging.browser.clearTimeout(this.attachmentsLoaderTimeout);
|
||||
},
|
||||
},
|
||||
recordMethods: {
|
||||
focus() {
|
||||
if (this.composerView) {
|
||||
this.composerView.update({ doFocus: true });
|
||||
}
|
||||
},
|
||||
onAttachmentsLoadingTimeout() {
|
||||
this.update({ isShowingAttachmentsLoading: true });
|
||||
},
|
||||
/**
|
||||
* Handles click on the attachments button.
|
||||
*
|
||||
@@ -246,7 +244,7 @@ registerModel({
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_computeIsPreparingAttachmentsLoading() {
|
||||
return this.attachmentsLoaderTimeout !== undefined;
|
||||
return Boolean(this.attachmentsLoaderTimer);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -259,15 +257,6 @@ registerModel({
|
||||
thread: this.thread ? replace(this.thread) : clear(),
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onAttachmentsLoadingTimeout() {
|
||||
this.update({
|
||||
attachmentsLoaderTimeout: clear(),
|
||||
isShowingAttachmentsLoading: true,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -325,19 +314,13 @@ registerModel({
|
||||
* @private
|
||||
*/
|
||||
_prepareAttachmentsLoading() {
|
||||
this.update({
|
||||
attachmentsLoaderTimeout: this.messaging.browser.setTimeout(
|
||||
this._onAttachmentsLoadingTimeout,
|
||||
this.messaging.loadingBaseDelayDuration,
|
||||
),
|
||||
});
|
||||
this.update({ attachmentsLoaderTimer: insertAndReplace() });
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_stopAttachmentsLoading() {
|
||||
this.messaging.browser.clearTimeout(this.attachmentsLoaderTimeout);
|
||||
this.update({ attachmentsLoaderTimeout: clear() });
|
||||
this.update({ attachmentsLoaderTimer: clear() });
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
@@ -350,7 +333,10 @@ registerModel({
|
||||
inverse: 'chatter',
|
||||
isCausal: true,
|
||||
}),
|
||||
attachmentsLoaderTimeout: attr(),
|
||||
attachmentsLoaderTimer: one('Timer', {
|
||||
inverse: 'chatterOwnerAsAttachmentsLoader',
|
||||
isCausal: true,
|
||||
}),
|
||||
/**
|
||||
* States the OWL Chatter component of this chatter.
|
||||
*/
|
||||
|
||||
@@ -6,19 +6,6 @@ import { registerModel } from '@mail/model/model_core';
|
||||
registerModel({
|
||||
name: 'MediaPreview',
|
||||
identifyingFields: ['welcomeView'],
|
||||
modelMethods: {
|
||||
/**
|
||||
* Iterates tracks of the provided MediaStream, calling the `stop`
|
||||
* method on each of them.
|
||||
*
|
||||
* @param {MediaStream} mediaStream
|
||||
*/
|
||||
stopTracksOnMediaStream(mediaStream) {
|
||||
for (const track of mediaStream.getTracks()) {
|
||||
track.stop();
|
||||
}
|
||||
},
|
||||
},
|
||||
recordMethods: {
|
||||
/**
|
||||
* Stops recording user's microphone.
|
||||
@@ -28,7 +15,7 @@ registerModel({
|
||||
if (!this.audioStream) {
|
||||
return;
|
||||
}
|
||||
this.messaging.models['MediaPreview'].stopTracksOnMediaStream(this.audioStream);
|
||||
this.stopTracksOnMediaStream(this.audioStream);
|
||||
this.update({ audioStream: null });
|
||||
},
|
||||
/**
|
||||
@@ -39,7 +26,7 @@ registerModel({
|
||||
if (!this.videoStream) {
|
||||
return;
|
||||
}
|
||||
this.messaging.models['MediaPreview'].stopTracksOnMediaStream(this.videoStream);
|
||||
this.stopTracksOnMediaStream(this.videoStream);
|
||||
this.update({ videoStream: null });
|
||||
},
|
||||
/**
|
||||
@@ -100,6 +87,17 @@ registerModel({
|
||||
onClickEnableVideoButton() {
|
||||
this.enableVideo();
|
||||
},
|
||||
/**
|
||||
* Iterates tracks of the provided MediaStream, calling the `stop`
|
||||
* method on each of them.
|
||||
*
|
||||
* @param {MediaStream} mediaStream
|
||||
*/
|
||||
stopTracksOnMediaStream(mediaStream) {
|
||||
for (const track of mediaStream.getTracks()) {
|
||||
track.stop();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
|
||||
@@ -13,15 +13,9 @@ registerModel({
|
||||
* Briefly highlights the message.
|
||||
*/
|
||||
highlight() {
|
||||
this.messaging.browser.clearTimeout(this.highlightTimeout);
|
||||
this.update({
|
||||
highlightTimer: [clear(), insertAndReplace()],
|
||||
isHighlighted: true,
|
||||
highlightTimeout: this.messaging.browser.setTimeout(() => {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ isHighlighted: false });
|
||||
}, 2000),
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -110,6 +104,9 @@ registerModel({
|
||||
this.threadView.handleVisibleMessage(this.message);
|
||||
}
|
||||
},
|
||||
onHighlightTimerTimeout() {
|
||||
this.update({ isHighlighted: false });
|
||||
},
|
||||
onMouseenter() {
|
||||
this.update({ isHovered: true });
|
||||
},
|
||||
@@ -368,9 +365,12 @@ registerModel({
|
||||
compute: '_computeHasAuthorOpenChat',
|
||||
}),
|
||||
/**
|
||||
* id of the current timeout that will reset isHighlighted to false.
|
||||
* Current timer that will reset isHighlighted to false.
|
||||
*/
|
||||
highlightTimeout: attr(),
|
||||
highlightTimer: one('Timer', {
|
||||
inverse: 'messageViewOwnerAsHighlight',
|
||||
isCausal: true,
|
||||
}),
|
||||
/**
|
||||
* Whether the message is "active", ie: hovered or clicked, and should
|
||||
* display additional things (date in sidebar, message actions, etc.)
|
||||
|
||||
@@ -376,6 +376,10 @@ registerModel({
|
||||
inverse: 'partner',
|
||||
isCausal: true,
|
||||
}),
|
||||
channelMemberViews: many('ChannelMemberView', {
|
||||
inverse: 'partner',
|
||||
isCausal: true,
|
||||
}),
|
||||
country: one('Country'),
|
||||
/**
|
||||
* Deprecated.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, many, one } from '@mail/model/model_field';
|
||||
import { clear, replace } from '@mail/model/model_field_command';
|
||||
import { clear, insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'RtcSession',
|
||||
@@ -13,17 +13,33 @@ registerModel({
|
||||
},
|
||||
},
|
||||
recordMethods: {
|
||||
onBroadcastTimeout() {
|
||||
this.messaging.rpc(
|
||||
{
|
||||
route: '/mail/rtc/session/update_and_broadcast',
|
||||
params: {
|
||||
session_id: this.id,
|
||||
values: {
|
||||
is_camera_on: this.isCameraOn,
|
||||
is_deaf: this.isDeaf,
|
||||
is_muted: this.isSelfMuted,
|
||||
is_screen_sharing_on: this.isScreenSharingOn,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ shadow: true },
|
||||
);
|
||||
},
|
||||
/**
|
||||
* restores the session to its default values
|
||||
*/
|
||||
reset() {
|
||||
this.messaging.browser.clearTimeout(this.broadcastTimeout);
|
||||
this.messaging.browser.clearTimeout(this.connectionRecoveryTimeout);
|
||||
this._removeAudio();
|
||||
this.removeVideo();
|
||||
this.update({
|
||||
audioElement: clear(),
|
||||
broadcastTimeout: clear(),
|
||||
broadcastTimer: clear(),
|
||||
connectionRecoveryTimeout: clear(),
|
||||
isTalking: clear(),
|
||||
localCandidateType: clear(),
|
||||
@@ -96,11 +112,8 @@ registerModel({
|
||||
if (!this.rtcAsCurrentSession) {
|
||||
return;
|
||||
}
|
||||
this.update(data);
|
||||
this.messaging.browser.clearTimeout(this.broadcastTimeout);
|
||||
this.update({
|
||||
broadcastTimeout: this.messaging.browser.setTimeout(this._onBroadcastTimeout, 3000),
|
||||
});
|
||||
const data2 = Object.assign({}, data, { broadcastTimer: [clear(), insertAndReplace()] });
|
||||
this.update(data2);
|
||||
},
|
||||
/**
|
||||
* Updates the rtcSession with information on the type of candidate used
|
||||
@@ -280,30 +293,6 @@ registerModel({
|
||||
return this.audioElement.volume;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onBroadcastTimeout() {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ broadcastTimeout: clear() });
|
||||
this.messaging.rpc(
|
||||
{
|
||||
route: '/mail/rtc/session/update_and_broadcast',
|
||||
params: {
|
||||
session_id: this.id,
|
||||
values: {
|
||||
is_camera_on: this.isCameraOn,
|
||||
is_deaf: this.isDeaf,
|
||||
is_muted: this.isSelfMuted,
|
||||
is_screen_sharing_on: this.isScreenSharingOn,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ shadow: true },
|
||||
);
|
||||
},
|
||||
/**
|
||||
* cleanly removes the audio stream of the session
|
||||
*
|
||||
@@ -396,7 +385,10 @@ registerModel({
|
||||
avatarUrl: attr({
|
||||
compute: '_computeAvatarUrl',
|
||||
}),
|
||||
broadcastTimeout: attr(),
|
||||
broadcastTimer: one('Timer', {
|
||||
inverse: 'rtcSessionOwnerAsBroadcast',
|
||||
isCausal: true,
|
||||
}),
|
||||
/**
|
||||
* The mail.channel of the session, rtc sessions are part and managed by
|
||||
* mail.channel
|
||||
|
||||
@@ -7,8 +7,12 @@ import { clear } from '@mail/model/model_field_command';
|
||||
registerModel({
|
||||
name: 'Timer',
|
||||
identifyingFields: [[
|
||||
'callViewAsShowOverlay',
|
||||
'chatterOwnerAsAttachmentsLoader',
|
||||
'messagingOwnerAsFetchImStatusTimer',
|
||||
'messageViewOwnerAsHighlight',
|
||||
'otherMemberLongTypingInThreadTimerOwner',
|
||||
'rtcSessionOwnerAsBroadcast',
|
||||
'threadAsCurrentPartnerInactiveTypingTimerOwner',
|
||||
'threadAsCurrentPartnerLongTypingTimerOwner',
|
||||
'throttleOwner',
|
||||
@@ -24,9 +28,21 @@ registerModel({
|
||||
* @returns {integer|FieldCommand}
|
||||
*/
|
||||
_computeDuration() {
|
||||
if (this.callViewAsShowOverlay) {
|
||||
return 3 * 1000;
|
||||
}
|
||||
if (this.chatterOwnerAsAttachmentsLoader) {
|
||||
return this.messaging.loadingBaseDelayDuration;
|
||||
}
|
||||
if (this.messageViewOwnerAsHighlight) {
|
||||
return 2 * 1000;
|
||||
}
|
||||
if (this.messagingOwnerAsFetchImStatusTimer) {
|
||||
return this.messagingOwnerAsFetchImStatusTimer.fetchImStatusTimerDuration;
|
||||
}
|
||||
if (this.rtcSessionOwnerAsBroadcast) {
|
||||
return 3 * 1000;
|
||||
}
|
||||
if (this.threadAsCurrentPartnerInactiveTypingTimerOwner) {
|
||||
return 5 * 1000;
|
||||
}
|
||||
@@ -67,10 +83,26 @@ registerModel({
|
||||
* @private
|
||||
*/
|
||||
_onTimeoutOwner() {
|
||||
if (this.callViewAsShowOverlay) {
|
||||
this.callViewAsShowOverlay.onShowOverlayTimeout();
|
||||
return;
|
||||
}
|
||||
if (this.chatterOwnerAsAttachmentsLoader) {
|
||||
this.chatterOwnerAsAttachmentsLoader.onAttachmentsLoadingTimeout();
|
||||
return;
|
||||
}
|
||||
if (this.messageViewOwnerAsHighlight) {
|
||||
this.messageViewOwnerAsHighlight.onHighlightTimerTimeout();
|
||||
return;
|
||||
}
|
||||
if (this.messagingOwnerAsFetchImStatusTimer) {
|
||||
this.messagingOwnerAsFetchImStatusTimer.onFetchImStatusTimerTimeout();
|
||||
return;
|
||||
}
|
||||
if (this.rtcSessionOwnerAsBroadcast) {
|
||||
this.rtcSessionOwnerAsBroadcast.onBroadcastTimeout();
|
||||
return;
|
||||
}
|
||||
if (this.threadAsCurrentPartnerInactiveTypingTimerOwner) {
|
||||
this.threadAsCurrentPartnerInactiveTypingTimerOwner.onCurrentPartnerInactiveTypingTimeout();
|
||||
return;
|
||||
@@ -90,6 +122,14 @@ registerModel({
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
callViewAsShowOverlay: one('CallView', {
|
||||
inverse: 'showOverlayTimer',
|
||||
readonly: true,
|
||||
}),
|
||||
chatterOwnerAsAttachmentsLoader: one('Chatter', {
|
||||
inverse: 'attachmentsLoaderTimer',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Duration, in milliseconds, until timer times out and calls the
|
||||
* timeout function.
|
||||
@@ -103,11 +143,19 @@ registerModel({
|
||||
inverse: 'fetchImStatusTimer',
|
||||
readonly: true,
|
||||
}),
|
||||
messageViewOwnerAsHighlight: one('MessageView', {
|
||||
inverse: 'highlightTimer',
|
||||
readonly: true,
|
||||
}),
|
||||
otherMemberLongTypingInThreadTimerOwner: one('OtherMemberLongTypingInThreadTimer', {
|
||||
inverse: 'timer',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
rtcSessionOwnerAsBroadcast: one('RtcSession', {
|
||||
inverse: 'broadcastTimer',
|
||||
readonly: true,
|
||||
}),
|
||||
threadAsCurrentPartnerInactiveTypingTimerOwner: one('Thread', {
|
||||
inverse: 'currentPartnerInactiveTypingTimer',
|
||||
readonly: true,
|
||||
|
||||
Reference in New Issue
Block a user