[IMP] mail, rating: turn computes and relateds readonly by default
Automatically add readonly on computed and related fields. This commit required to rewrite some preexisting fields to make them readonly. Task-2955927. closes odoo/odoo#98541 Related: odoo/enterprise#30625 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -67,7 +67,7 @@ class Channel(models.Model):
|
||||
groups='base.group_user')
|
||||
rtc_session_ids = fields.One2many('mail.channel.rtc.session', 'channel_id', groups="base.group_system")
|
||||
is_member = fields.Boolean('Is Member', compute='_compute_is_member', search='_search_is_member')
|
||||
member_count = fields.Integer(string="Member Count", compute='_compute_member_count', compute_sudo=True, help="Excluding guests from count.")
|
||||
member_count = fields.Integer(string="Member Count", compute='_compute_member_count', compute_sudo=True)
|
||||
group_ids = fields.Many2many(
|
||||
'res.groups', string='Auto Subscription',
|
||||
help="Members of those groups will automatically added as followers. "
|
||||
@@ -192,7 +192,7 @@ class Channel(models.Model):
|
||||
self.env['mail.channel.member'].sudo()._search(user_domain)
|
||||
)]
|
||||
|
||||
@api.depends('channel_partner_ids')
|
||||
@api.depends('channel_member_ids')
|
||||
def _compute_member_count(self):
|
||||
read_group_res = self.env['mail.channel.member']._read_group(domain=[('channel_id', 'in', self.ids)], fields=['channel_id'], groupby=['channel_id'])
|
||||
member_count_by_channel_id = {item['channel_id'][0]: item['channel_id_count'] for item in read_group_res}
|
||||
@@ -332,8 +332,11 @@ class Channel(models.Model):
|
||||
# post 'channel left' message as root since the partner just unsubscribed from the channel
|
||||
self.sudo().message_post(body=notification, subtype_xmlid="mail.mt_comment", author_id=partner.id)
|
||||
self.env['bus.bus']._sendone(self, 'mail.channel/insert', {
|
||||
'channel': [('insert-and-replace', {
|
||||
'id': self.id,
|
||||
'memberCount': self.member_count,
|
||||
})],
|
||||
'id': self.id,
|
||||
'memberCount': self.member_count,
|
||||
'members': [('insert-and-unlink', {'id': partner.id})],
|
||||
})
|
||||
return result
|
||||
@@ -414,9 +417,12 @@ class Channel(models.Model):
|
||||
'channel': member.channel_id.sudo().channel_info()[0],
|
||||
}))
|
||||
notifications.append((channel, 'mail.channel/insert', {
|
||||
'channel': [('insert-and-replace', {
|
||||
'id': channel.id,
|
||||
'memberCount': channel.member_count,
|
||||
})],
|
||||
'id': channel.id,
|
||||
'guestMembers': [('insert', guest_members_data)],
|
||||
'memberCount': channel.member_count,
|
||||
'members': [('insert', members_data)],
|
||||
}))
|
||||
if invite_to_rtc_call:
|
||||
@@ -768,6 +774,7 @@ class Channel(models.Model):
|
||||
channel_data = {
|
||||
'channel_type': channel.channel_type,
|
||||
'id': channel.id,
|
||||
'memberCount': channel.member_count,
|
||||
}
|
||||
info = {
|
||||
'avatarCacheKey': channel._get_avatar_cache_key(),
|
||||
@@ -785,7 +792,6 @@ class Channel(models.Model):
|
||||
}
|
||||
# add last message preview (only used in mobile)
|
||||
info['last_message_id'] = channel_last_message_ids.get(channel.id, False)
|
||||
info['memberCount'] = channel.member_count
|
||||
# find the channel member state, if logged user
|
||||
if self.env.user and self.env.user.partner_id:
|
||||
info['message_needaction_counter'] = channel.message_needaction_counter
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
record="discussView.discuss.threadView"
|
||||
/>
|
||||
</t>
|
||||
<t t-if="!discussView.discuss.thread and (!messaging.device.isSmall or discussView.discuss.activeMobileNavbarTabId === 'mailbox')">
|
||||
<t t-if="!discussView.discuss.activeThread and (!messaging.device.isSmall or discussView.discuss.activeMobileNavbarTabId === 'mailbox')">
|
||||
<div class="o_Discuss_noThread d-flex flex-grow-1 align-items-center justify-content-center w-100 bg-white">
|
||||
<h4 class="text-muted"><b><i>No conversation selected.</i></b></h4>
|
||||
</div>
|
||||
|
||||
@@ -34,7 +34,7 @@ export class DiscussContainer extends Component {
|
||||
await this.messaging.initializedPromise;
|
||||
if (!this.discuss.isInitThreadHandled) {
|
||||
this.discuss.update({ isInitThreadHandled: true });
|
||||
if (!this.discuss.thread) {
|
||||
if (!this.discuss.activeThread) {
|
||||
this.discuss.openInitThread();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<t t-name="mail.DiscussMobileMailboxSelectionItem" owl="1">
|
||||
<button class="o_DiscussMobileMailboxSelectionItem btn btn-secondary flex-grow-1 p-2"
|
||||
t-att-class="{
|
||||
'active o-active shadow-none': discussView.discuss.thread === mailbox.thread,
|
||||
'active o-active shadow-none': discussView.discuss.activeThread === mailbox.thread,
|
||||
}" t-attf-class="{{ className }}" t-on-click="() => discussView.onClickMobileMailboxSelectionItem(mailbox)" t-att-data-mailbox-local-id="mailbox.localId" type="button" t-ref="root"
|
||||
>
|
||||
<t t-esc="mailbox.name"/>
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
<t t-name="mail.DiscussSidebarMailbox" owl="1">
|
||||
<button class="o_DiscussSidebarMailbox btn d-flex align-items-center py-1 px-0 border-0 rounded-0 fw-normal text-dark"
|
||||
t-att-class="{
|
||||
'bg-100': discussSidebarMailboxView.mailbox.thread !== messaging.discuss.thread,
|
||||
'o-active bg-200': discussSidebarMailboxView.mailbox.thread === messaging.discuss.thread,
|
||||
'bg-100': discussSidebarMailboxView.mailbox.thread !== messaging.discuss.activeThread,
|
||||
'o-active bg-200': discussSidebarMailboxView.mailbox.thread === messaging.discuss.activeThread,
|
||||
'o-starred-box': discussSidebarMailboxView.mailbox === messaging.starred,
|
||||
}" 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"
|
||||
|
||||
@@ -140,12 +140,22 @@ export class ModelField {
|
||||
* model name this relation refers to.
|
||||
*/
|
||||
this.to = to;
|
||||
/**
|
||||
* Automatically make identifying fields readonly (and required for AND
|
||||
* identifying mode).
|
||||
*/
|
||||
if (this.identifying) {
|
||||
this.readonly = true;
|
||||
if (model.identifyingMode === 'and') {
|
||||
this.required = true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Automatically make computes and relateds readonly.
|
||||
*/
|
||||
if (this.compute || this.related) {
|
||||
this.readonly = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -491,17 +491,22 @@ export class ModelManager {
|
||||
}
|
||||
}
|
||||
// 4. Computed field.
|
||||
if (field.compute && !(typeof field.compute === 'string')) {
|
||||
throw new Error(`Property "compute" of field(${fieldName}) on ${model} must be a string (instance method name).`);
|
||||
}
|
||||
if (field.compute && !(model.prototype[field.compute])) {
|
||||
throw new Error(`Property "compute" of field(${fieldName}) on ${model} does not refer to an instance method of this model.`);
|
||||
if (field.compute) {
|
||||
if (!(typeof field.compute === 'string')) {
|
||||
throw new Error(`Property "compute" of field(${fieldName}) on ${model} must be a string (instance method name).`);
|
||||
}
|
||||
if (!model.prototype[field.compute]) {
|
||||
throw new Error(`Property "compute" of field(${fieldName}) on ${model} does not refer to an instance method of this model.`);
|
||||
}
|
||||
if ('readonly' in field) {
|
||||
throw new Error(`Computed field(${fieldName}) on ${model} has unnecessary "readonly" attribute (readonly is implicit for computed fields).`);
|
||||
}
|
||||
}
|
||||
// 5. Related field.
|
||||
if (field.compute && field.related) {
|
||||
throw new Error(`field(${fieldName}) on ${model} cannot be a related and compute field at the same time.`);
|
||||
}
|
||||
if (field.related) {
|
||||
if (field.compute) {
|
||||
throw new Error(`field(${fieldName}) on ${model} cannot be a related and compute field at the same time.`);
|
||||
}
|
||||
if (!(typeof field.related === 'string')) {
|
||||
throw new Error(`Property "related" of field(${fieldName}) on ${model} has invalid format.`);
|
||||
}
|
||||
@@ -543,6 +548,9 @@ export class ModelManager {
|
||||
) {
|
||||
throw new Error(`Related field(${fieldName}) on ${model} has mismatched target model name with its related model field.`);
|
||||
}
|
||||
if ('readonly' in field) {
|
||||
throw new Error(`Related field(${fieldName}) on ${model} has unnecessary "readonly" attribute (readonly is implicit for related fields).`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,6 @@ registerModel({
|
||||
fields: {
|
||||
activityViewOwner: one('ActivityView', {
|
||||
compute: '_computeActivityViewOwner',
|
||||
readonly: true,
|
||||
}),
|
||||
component: attr(),
|
||||
feedbackTextareaRef: attr(),
|
||||
|
||||
@@ -408,7 +408,6 @@ registerModel({
|
||||
threadsAsAttachmentsInWebClientView: many('Thread', {
|
||||
compute: '_computeThreadsAsAttachmentsInWebClientView',
|
||||
inverse: 'attachmentsInWebClientView',
|
||||
readonly: true,
|
||||
}),
|
||||
type: attr(),
|
||||
/**
|
||||
|
||||
@@ -31,7 +31,6 @@ registerModel({
|
||||
compute: '_computeAttachmentList',
|
||||
inverse: 'attachmentBoxViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
chatter: one('Chatter', {
|
||||
identifying: true,
|
||||
|
||||
@@ -72,7 +72,6 @@ registerModel({
|
||||
fields: {
|
||||
attachment: one('Attachment', {
|
||||
compute: '_computeAttachment',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
body: attr({
|
||||
|
||||
@@ -42,7 +42,6 @@ registerModel({
|
||||
}),
|
||||
rtcSession: one('RtcSession', {
|
||||
compute: '_computeRtcSession',
|
||||
readonly: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -171,7 +171,6 @@ registerModel({
|
||||
compute: '_computeCallSideBarView',
|
||||
inverse: 'callView',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
channel: one('Thread', {
|
||||
related: 'threadView.thread',
|
||||
|
||||
@@ -92,9 +92,10 @@ registerModel({
|
||||
id: attr({
|
||||
identifying: true,
|
||||
}),
|
||||
memberCount: attr({
|
||||
related: 'thread.memberCount',
|
||||
}),
|
||||
/**
|
||||
* States the number of members in this channel according to the server.
|
||||
*/
|
||||
memberCount: attr(),
|
||||
orderedOfflineMembers: many('ChannelMember', {
|
||||
inverse: 'channelAsOfflineMember',
|
||||
sort: '_sortMembers',
|
||||
@@ -107,7 +108,6 @@ registerModel({
|
||||
compute: '_computeThread',
|
||||
inverse: 'channel',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
|
||||
@@ -316,7 +316,6 @@ registerModel({
|
||||
*/
|
||||
thread: one('Thread', {
|
||||
compute: '_computeThread',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -28,7 +28,6 @@ registerModel({
|
||||
compute: '_computePersonaImStatusIconView',
|
||||
inverse: 'channelInvitationFormSelectablePartnerViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -56,7 +56,6 @@ registerModel({
|
||||
fields: {
|
||||
channel: one('Channel', {
|
||||
compute: '_computeChannel',
|
||||
readonly: true,
|
||||
}),
|
||||
chatWindowOwner: one('ChatWindow', {
|
||||
identifying: true,
|
||||
|
||||
@@ -63,7 +63,6 @@ registerModel({
|
||||
compute: '_computePersonaImStatusIconView',
|
||||
inverse: 'channelMemberViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -697,7 +697,6 @@ registerModel({
|
||||
compute: '_computeThreadViewer',
|
||||
inverse: 'chatWindow',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
|
||||
@@ -50,8 +50,7 @@ registerModel({
|
||||
*
|
||||
*/
|
||||
closeAll() {
|
||||
const chatWindows = this.allOrdered;
|
||||
for (const chatWindow of chatWindows) {
|
||||
for (const chatWindow of this.chatWindows) {
|
||||
chatWindow.close();
|
||||
}
|
||||
},
|
||||
@@ -140,16 +139,15 @@ registerModel({
|
||||
* @param {ChatWindow} chatWindow2
|
||||
*/
|
||||
swap(chatWindow1, chatWindow2) {
|
||||
const ordered = this.allOrdered;
|
||||
const index1 = ordered.findIndex(chatWindow => chatWindow === chatWindow1);
|
||||
const index2 = ordered.findIndex(chatWindow => chatWindow === chatWindow2);
|
||||
const index1 = this.chatWindows.findIndex(chatWindow => chatWindow === chatWindow1);
|
||||
const index2 = this.chatWindows.findIndex(chatWindow => chatWindow === chatWindow2);
|
||||
if (index1 === -1 || index2 === -1) {
|
||||
return;
|
||||
}
|
||||
const _newOrdered = [...this.allOrdered];
|
||||
const _newOrdered = [...this.chatWindows];
|
||||
_newOrdered[index1] = chatWindow2;
|
||||
_newOrdered[index2] = chatWindow1;
|
||||
this.update({ allOrdered: _newOrdered });
|
||||
this.update({ chatWindows: _newOrdered });
|
||||
for (const chatWindow of [chatWindow1, chatWindow2]) {
|
||||
if (chatWindow.threadView) {
|
||||
chatWindow.threadView.addComponentHint('adjust-scroll');
|
||||
@@ -260,7 +258,7 @@ registerModel({
|
||||
if (!this.messaging.device.isSmall && this.messaging.discuss.discussView) {
|
||||
return visual;
|
||||
}
|
||||
if (!this.allOrdered.length) {
|
||||
if (!this.chatWindows.length) {
|
||||
return visual;
|
||||
}
|
||||
const relativeGlobalWindowWidth = this.messaging.device.globalWindowInnerWidth - this.startGapWidth - this.endGapWidth;
|
||||
@@ -273,10 +271,10 @@ registerModel({
|
||||
maxAmountWithoutHidden = 1;
|
||||
maxAmountWithHidden = 1;
|
||||
}
|
||||
if (this.allOrdered.length <= maxAmountWithoutHidden) {
|
||||
if (this.chatWindows.length <= maxAmountWithoutHidden) {
|
||||
// all visible
|
||||
for (let i = 0; i < this.allOrdered.length; i++) {
|
||||
const chatWindow = this.allOrdered[i];
|
||||
for (let i = 0; i < this.chatWindows.length; i++) {
|
||||
const chatWindow = this.chatWindows[i];
|
||||
const offset = this.startGapWidth + i * (this.chatWindowWidth + this.betweenGapWidth);
|
||||
visual.visible.push({ chatWindow, offset });
|
||||
}
|
||||
@@ -284,24 +282,24 @@ registerModel({
|
||||
} else if (maxAmountWithHidden > 0) {
|
||||
// some visible, some hidden
|
||||
for (let i = 0; i < maxAmountWithHidden; i++) {
|
||||
const chatWindow = this.allOrdered[i];
|
||||
const chatWindow = this.chatWindows[i];
|
||||
const offset = this.startGapWidth + i * (this.chatWindowWidth + this.betweenGapWidth);
|
||||
visual.visible.push({ chatWindow, offset });
|
||||
}
|
||||
if (this.allOrdered.length > maxAmountWithHidden) {
|
||||
if (this.chatWindows.length > maxAmountWithHidden) {
|
||||
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.hiddenChatWindows.push(this.allOrdered[j]);
|
||||
for (let j = maxAmountWithHidden; j < this.chatWindows.length; j++) {
|
||||
visual.hiddenChatWindows.push(this.chatWindows[j]);
|
||||
}
|
||||
visual.availableVisibleSlots = maxAmountWithHidden;
|
||||
} else {
|
||||
// all hidden
|
||||
visual.isHiddenMenuVisible = !this.messaging.device.isSmall;
|
||||
visual.hiddenMenuOffset = this.startGapWidth;
|
||||
visual.hiddenChatWindows.push(...this.allOrdered);
|
||||
visual.hiddenChatWindows.push(...this.chatWindows);
|
||||
console.warn('cannot display any visible chat windows (screen is too small)');
|
||||
visual.availableVisibleSlots = 0;
|
||||
}
|
||||
@@ -309,12 +307,6 @@ registerModel({
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
/**
|
||||
* List of ordered chat windows.
|
||||
*/
|
||||
allOrdered: many('ChatWindow', {
|
||||
compute: '_computeAllOrdered',
|
||||
}),
|
||||
allOrderedHidden: many('ChatWindow', {
|
||||
compute: '_computeAllOrderedHidden',
|
||||
}),
|
||||
|
||||
@@ -305,7 +305,7 @@ registerModel({
|
||||
model: this.threadModel,
|
||||
}),
|
||||
});
|
||||
this.thread.cache.update({ messages: link(message) });
|
||||
this.thread.cache.update({ temporaryMessages: link(message) });
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -484,7 +484,6 @@ registerModel({
|
||||
compute: '_computeThreadViewer',
|
||||
inverse: 'chatter',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
topbar: one('ChatterTopbar', {
|
||||
|
||||
@@ -9,22 +9,17 @@ import { attr, many } from '@mail/model/model_field';
|
||||
registerModel({
|
||||
name: 'Clock',
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
// The date is set here rather than via a default value so that the
|
||||
// date set at first is the time of the record creation, and not the
|
||||
// time of the model initialization.
|
||||
this.update({ date: new Date() });
|
||||
},
|
||||
_willDelete() {
|
||||
this.messaging.browser.clearInterval(this.tickInterval);
|
||||
},
|
||||
},
|
||||
recordMethods: {
|
||||
/**
|
||||
* The date is set by a compute rather than using a default value. Thus,
|
||||
* the date set at first is the time of the record creation, and not the
|
||||
* time of the model initialization.
|
||||
*
|
||||
* @private
|
||||
* @returns {Date}
|
||||
*/
|
||||
_computeDate() {
|
||||
return new Date();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {integer}
|
||||
@@ -52,9 +47,7 @@ registerModel({
|
||||
* A Date object set to the current date at the time the record is
|
||||
* created, then updated at every tick.
|
||||
*/
|
||||
date: attr({
|
||||
compute: '_computeDate',
|
||||
}),
|
||||
date: attr(),
|
||||
/**
|
||||
* An integer representing the frequency in milliseconds at which `date`
|
||||
* must be recomputed.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, many, one } from '@mail/model/model_field';
|
||||
import { clear, unlink } from '@mail/model/model_field_command';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
import { sprintf } from '@web/core/utils/strings';
|
||||
|
||||
registerModel({
|
||||
@@ -47,47 +47,47 @@ registerModel({
|
||||
* @returns {Partner[]}
|
||||
*/
|
||||
_computeMentionedPartners() {
|
||||
const unmentionedPartners = [];
|
||||
const mentionedPartners = [];
|
||||
// ensure the same mention is not used multiple times if multiple
|
||||
// partners have the same name
|
||||
const namesIndex = {};
|
||||
for (const partner of this.mentionedPartners) {
|
||||
for (const partner of this.rawMentionedPartners) {
|
||||
const fromIndex = namesIndex[partner.name] !== undefined
|
||||
? namesIndex[partner.name] + 1 :
|
||||
0;
|
||||
const index = this.textInputContent.indexOf(`@${partner.name}`, fromIndex);
|
||||
if (index !== -1) {
|
||||
namesIndex[partner.name] = index;
|
||||
} else {
|
||||
unmentionedPartners.push(partner);
|
||||
if (index === -1) {
|
||||
continue;
|
||||
}
|
||||
namesIndex[partner.name] = index;
|
||||
mentionedPartners.push(partner);
|
||||
}
|
||||
return unlink(unmentionedPartners);
|
||||
return mentionedPartners;
|
||||
},
|
||||
/**
|
||||
* Detects if mentioned channels are still in the composer text input content
|
||||
* and removes them if not.
|
||||
*
|
||||
* @private
|
||||
* @returns {Partner[]}
|
||||
* @returns {Channels[]}
|
||||
*/
|
||||
_computeMentionedChannels() {
|
||||
const unmentionedChannels = [];
|
||||
const mentionedChannels = [];
|
||||
// ensure the same mention is not used multiple times if multiple
|
||||
// channels have the same name
|
||||
const namesIndex = {};
|
||||
for (const channel of this.mentionedChannels) {
|
||||
for (const channel of this.rawMentionedChannels) {
|
||||
const fromIndex = namesIndex[channel.name] !== undefined
|
||||
? namesIndex[channel.name] + 1 :
|
||||
0;
|
||||
const index = this.textInputContent.indexOf(`#${channel.name}`, fromIndex);
|
||||
if (index !== -1) {
|
||||
namesIndex[channel.name] = index;
|
||||
} else {
|
||||
unmentionedChannels.push(channel);
|
||||
if (index === -1) {
|
||||
continue;
|
||||
}
|
||||
namesIndex[channel.name] = index;
|
||||
mentionedChannels.push(channel);
|
||||
}
|
||||
return unlink(unmentionedChannels);
|
||||
return mentionedChannels;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -129,8 +129,8 @@ registerModel({
|
||||
_reset() {
|
||||
this.update({
|
||||
attachments: clear(),
|
||||
mentionedChannels: clear(),
|
||||
mentionedPartners: clear(),
|
||||
rawMentionedChannels: clear(),
|
||||
rawMentionedPartners: clear(),
|
||||
textInputContent: clear(),
|
||||
textInputCursorEnd: clear(),
|
||||
textInputCursorStart: clear(),
|
||||
@@ -144,7 +144,6 @@ registerModel({
|
||||
fields: {
|
||||
activeThread: one('Thread', {
|
||||
compute: '_computeActiveThread',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
@@ -194,6 +193,8 @@ registerModel({
|
||||
placeholder: attr({
|
||||
compute: '_computePlaceholder',
|
||||
}),
|
||||
rawMentionedChannels: many('Thread'),
|
||||
rawMentionedPartners: many('Partner'),
|
||||
/**
|
||||
* Determines the extra `Partner` (on top of existing followers)
|
||||
* that will receive the message being composed by `this`, and that will
|
||||
|
||||
@@ -12,7 +12,7 @@ registerModel({
|
||||
*/
|
||||
setFirstSuggestionViewActive() {
|
||||
const firstSuggestionView = this.suggestionViews[0];
|
||||
this.update({ activeSuggestionView: firstSuggestionView });
|
||||
this.update({ rawActiveSuggestionView: firstSuggestionView });
|
||||
},
|
||||
/**
|
||||
* Sets the last suggestion as active. Main and extra records are
|
||||
@@ -20,7 +20,7 @@ registerModel({
|
||||
*/
|
||||
setLastSuggestionViewActive() {
|
||||
const { length, [length - 1]: lastSuggestionView } = this.suggestionViews;
|
||||
this.update({ activeSuggestionView: lastSuggestionView });
|
||||
this.update({ rawActiveSuggestionView: lastSuggestionView });
|
||||
},
|
||||
/**
|
||||
* Sets the next suggestion as active. Main and extra records are
|
||||
@@ -36,7 +36,7 @@ registerModel({
|
||||
return;
|
||||
}
|
||||
const nextSuggestionView = this.suggestionViews[activeElementIndex + 1];
|
||||
this.update({ activeSuggestionView: nextSuggestionView });
|
||||
this.update({ rawActiveSuggestionView: nextSuggestionView });
|
||||
},
|
||||
/**
|
||||
* Sets the previous suggestion as active. Main and extra records are
|
||||
@@ -52,7 +52,7 @@ registerModel({
|
||||
return;
|
||||
}
|
||||
const previousSuggestionView = this.suggestionViews[activeElementIndex - 1];
|
||||
this.update({ activeSuggestionView: previousSuggestionView });
|
||||
this.update({ rawActiveSuggestionView: previousSuggestionView });
|
||||
},
|
||||
/**
|
||||
* Adapts the active suggestion it if the active suggestion is no longer
|
||||
@@ -62,8 +62,8 @@ registerModel({
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeActiveSuggestionView() {
|
||||
if (this.suggestionViews.includes(this.activeSuggestionView)) {
|
||||
return;
|
||||
if (this.suggestionViews.includes(this.rawActiveSuggestionView)) {
|
||||
return this.rawActiveSuggestionView;
|
||||
}
|
||||
const firstSuggestionView = this.suggestionViews[0];
|
||||
return firstSuggestionView;
|
||||
@@ -120,9 +120,9 @@ registerModel({
|
||||
hasToScrollToActiveSuggestionView: attr({
|
||||
default: false,
|
||||
}),
|
||||
rawActiveSuggestionView: one('ComposerSuggestionView'),
|
||||
suggestionViews: many('ComposerSuggestionView', {
|
||||
compute: '_computeSuggestionViews',
|
||||
readonly: true,
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ registerModel({
|
||||
*/
|
||||
onClick(ev) {
|
||||
ev.preventDefault();
|
||||
this.composerSuggestionListViewOwner.update({ activeSuggestionView: this });
|
||||
this.composerSuggestionListViewOwner.update({ rawActiveSuggestionView: this });
|
||||
const composerViewOwner = this.composerSuggestionListViewOwner.composerViewOwner;
|
||||
composerViewOwner.insertSuggestion();
|
||||
composerViewOwner.closeSuggestions();
|
||||
@@ -132,11 +132,9 @@ registerModel({
|
||||
compute: '_computePersonaImStatusIconView',
|
||||
inverse: 'composerSuggestionViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
suggestable: one('ComposerSuggestable', {
|
||||
compute: '_computeSuggestable',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, many, one } from '@mail/model/model_field';
|
||||
import { clear, link, unlink } from '@mail/model/model_field_command';
|
||||
import { clear, link } from '@mail/model/model_field_command';
|
||||
import { addLink, escapeAndCompactTextContent, parseAndTransform } from '@mail/js/utils';
|
||||
import { isEventHandled, markEventHandled } from '@mail/utils/utils';
|
||||
|
||||
@@ -144,10 +144,10 @@ registerModel({
|
||||
// the mention will appear in the target channel, or be notified to
|
||||
// the target partner.
|
||||
if (this.composerSuggestionListView.activeSuggestionView.suggestable.thread) {
|
||||
Object.assign(updateData, { mentionedChannels: link(this.composerSuggestionListView.activeSuggestionView.suggestable.thread) });
|
||||
Object.assign(updateData, { rawMentionedChannels: link(this.composerSuggestionListView.activeSuggestionView.suggestable.thread) });
|
||||
}
|
||||
if (this.composerSuggestionListView.activeSuggestionView.suggestable.partner) {
|
||||
Object.assign(updateData, { mentionedPartners: link(this.composerSuggestionListView.activeSuggestionView.suggestable.partner) });
|
||||
Object.assign(updateData, { rawMentionedPartners: link(this.composerSuggestionListView.activeSuggestionView.suggestable.partner) });
|
||||
}
|
||||
this.composer.update(updateData);
|
||||
for (const composerView of this.composer.composerViews) {
|
||||
@@ -793,24 +793,10 @@ registerModel({
|
||||
return clear();
|
||||
}
|
||||
if (this.threadView.threadViewer.discuss) {
|
||||
return this.threadView.threadViewer.discuss.thread === this.messaging.inbox.thread;
|
||||
return this.threadView.threadViewer.discuss.activeThread === this.messaging.inbox.thread;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* Clears the extra suggestions on closing mentions, and ensures
|
||||
* the extra list does not contain any element already present in the
|
||||
* main list, which is a requirement for the navigation process.
|
||||
*
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeExtraSuggestions() {
|
||||
if (this.suggestionDelimiterPosition === undefined) {
|
||||
return clear();
|
||||
}
|
||||
return unlink(this.mainSuggestions);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean|FieldCommand}
|
||||
@@ -969,17 +955,6 @@ registerModel({
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* Clears the main suggestions on closing mentions.
|
||||
*
|
||||
* @private
|
||||
* @returns {Record[]}
|
||||
*/
|
||||
_computeMainSuggestions() {
|
||||
if (this.suggestionDelimiterPosition === undefined) {
|
||||
return clear();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string}
|
||||
@@ -1018,7 +993,7 @@ registerModel({
|
||||
this.messaging.device.isSmall ||
|
||||
(
|
||||
this.messaging.discuss.threadView === this.threadView &&
|
||||
this.messaging.discuss.thread === this.messaging.inbox.thread
|
||||
this.messaging.discuss.activeThread === this.messaging.inbox.thread
|
||||
)
|
||||
) {
|
||||
return ['ctrl-enter', 'meta-enter'];
|
||||
@@ -1356,7 +1331,18 @@ registerModel({
|
||||
return {};
|
||||
}
|
||||
return clear();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onSuggestionDelimiterPositionChanged() {
|
||||
if (this.suggestionDelimiterPosition === undefined) {
|
||||
this.update({
|
||||
extraSuggestions: clear(),
|
||||
mainSuggestions: clear(),
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
/**
|
||||
@@ -1366,7 +1352,6 @@ registerModel({
|
||||
compute: '_computeAttachmentList',
|
||||
inverse: 'composerViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* States the ref to the html node of the emojis button.
|
||||
@@ -1423,9 +1408,7 @@ registerModel({
|
||||
inverse: 'composerViewOwnerAsEmoji',
|
||||
isCausal: true,
|
||||
}),
|
||||
extraSuggestions: many('ComposerSuggestable', {
|
||||
compute: '_computeExtraSuggestions',
|
||||
}),
|
||||
extraSuggestions: many('ComposerSuggestable'),
|
||||
fileUploader: one('FileUploader', {
|
||||
default: {},
|
||||
inverse: 'composerView',
|
||||
@@ -1534,9 +1517,7 @@ registerModel({
|
||||
textareaLastInputValue: attr({
|
||||
default: "",
|
||||
}),
|
||||
mainSuggestions: many('ComposerSuggestable', {
|
||||
compute: '_computeMainSuggestions',
|
||||
}),
|
||||
mainSuggestions: many('ComposerSuggestable'),
|
||||
/**
|
||||
* States the message view on which this composer allows editing (if any).
|
||||
*/
|
||||
@@ -1632,5 +1613,9 @@ registerModel({
|
||||
dependencies: ['suggestionDelimiterPosition', 'suggestionModelName', 'suggestionSearchTerm', 'composer.activeThread'],
|
||||
methodName: '_onChangeUpdateSuggestionList',
|
||||
},
|
||||
{
|
||||
dependencies: ['suggestionDelimiterPosition'],
|
||||
methodName: '_onSuggestionDelimiterPositionChanged',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -48,7 +48,6 @@ registerModel({
|
||||
}),
|
||||
message: one('Message', {
|
||||
compute: '_computeMessage',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
@@ -59,7 +58,6 @@ registerModel({
|
||||
compute: '_computeMessageView',
|
||||
inverse: 'deleteMessageConfirmViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -234,7 +234,6 @@ registerModel({
|
||||
manager: one('DialogManager', {
|
||||
compute: '_computeManager',
|
||||
inverse: 'dialogs',
|
||||
readonly: true,
|
||||
}),
|
||||
messageActionViewOwnerAsDeleteConfirm: one('MessageActionView', {
|
||||
identifying: true,
|
||||
@@ -248,7 +247,6 @@ registerModel({
|
||||
record: one('Record', {
|
||||
compute: '_computeRecord',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
style: attr({
|
||||
|
||||
@@ -192,10 +192,28 @@ registerModel({
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
_computeActiveId() {
|
||||
if (!this.activeThread) {
|
||||
return clear();
|
||||
}
|
||||
return this.threadToActiveId(this.activeThread);
|
||||
},
|
||||
/**
|
||||
* Only mailboxes and pinned channels are allowed in Discuss.
|
||||
*
|
||||
* @private
|
||||
* @returns {FieldCommand|Thread}
|
||||
*/
|
||||
_computeActiveThread() {
|
||||
if (!this.thread) {
|
||||
return clear();
|
||||
}
|
||||
return this.threadToActiveId(this.thread);
|
||||
if (this.thread.channel && this.thread.isPinned) {
|
||||
return this.thread;
|
||||
}
|
||||
if (this.thread.mailbox) {
|
||||
return this.thread;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -211,29 +229,19 @@ registerModel({
|
||||
_computeAddChatInputPlaceholder() {
|
||||
return this.env._t("Search user...");
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_computeAddingChannelValue() {
|
||||
if (!this.discussView) {
|
||||
return "";
|
||||
}
|
||||
return this.addingChannelValue;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_computeHasThreadView() {
|
||||
if (!this.thread || !this.discussView) {
|
||||
if (!this.activeThread || !this.discussView) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
this.messaging.device.isSmall &&
|
||||
(
|
||||
this.activeMobileNavbarTabId !== 'mailbox' ||
|
||||
!this.thread.mailbox
|
||||
!this.activeThread.mailbox
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
@@ -281,24 +289,6 @@ registerModel({
|
||||
_computeNotificationListView() {
|
||||
return (this.messaging.device.isSmall && this.activeMobileNavbarTabId !== 'mailbox') ? {} : clear();
|
||||
},
|
||||
/**
|
||||
* Only mailboxes and pinned channels are allowed in Discuss.
|
||||
*
|
||||
* @private
|
||||
* @returns {FieldCommand|undefined}
|
||||
*/
|
||||
_computeThread() {
|
||||
if (!this.thread) {
|
||||
return clear();
|
||||
}
|
||||
if (this.thread.channel && this.thread.isPinned) {
|
||||
return undefined;
|
||||
}
|
||||
if (this.thread.mailbox) {
|
||||
return undefined;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {ThreadViewer}
|
||||
@@ -308,7 +298,7 @@ registerModel({
|
||||
hasMemberList: true,
|
||||
hasThreadView: this.hasThreadView,
|
||||
hasTopbar: true,
|
||||
thread: this.thread ? this.thread : clear(),
|
||||
thread: this.activeThread ? this.activeThread : clear(),
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -322,6 +312,12 @@ registerModel({
|
||||
activeMobileNavbarTabId: attr({
|
||||
default: 'mailbox',
|
||||
}),
|
||||
/**
|
||||
* Determines the `Thread` that should be displayed by `this`.
|
||||
*/
|
||||
activeThread: one('Thread', {
|
||||
compute: '_computeActiveThread',
|
||||
}),
|
||||
addChannelInputPlaceholder: attr({
|
||||
compute: '_computeAddChannelInputPlaceholder',
|
||||
}),
|
||||
@@ -332,7 +328,6 @@ registerModel({
|
||||
* Value that is used to create a channel from the sidebar.
|
||||
*/
|
||||
addingChannelValue: attr({
|
||||
compute: '_computeAddingChannelValue',
|
||||
default: "",
|
||||
}),
|
||||
/**
|
||||
@@ -424,12 +419,7 @@ registerModel({
|
||||
sidebarQuickSearchValue: attr({
|
||||
default: "",
|
||||
}),
|
||||
/**
|
||||
* Determines the `Thread` that should be displayed by `this`.
|
||||
*/
|
||||
thread: one('Thread', {
|
||||
compute: '_computeThread',
|
||||
}),
|
||||
thread: one('Thread'),
|
||||
/**
|
||||
* States the `ThreadView` displaying `this.thread`.
|
||||
*/
|
||||
@@ -443,7 +433,6 @@ registerModel({
|
||||
compute: '_computeThreadViewer',
|
||||
inverse: 'discuss',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -72,7 +72,6 @@ registerModel({
|
||||
* States the thread view linked to this discuss public view.
|
||||
*/
|
||||
threadView: one('ThreadView', {
|
||||
readonly: true,
|
||||
related: 'threadViewer.threadView',
|
||||
}),
|
||||
/**
|
||||
|
||||
@@ -53,7 +53,7 @@ registerModel({
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeActiveItem() {
|
||||
const channel = this.messaging.discuss.thread && this.messaging.discuss.thread.channel;
|
||||
const channel = this.messaging.discuss.activeThread && this.messaging.discuss.activeThread.channel;
|
||||
if (channel && this.supportedChannelTypes.includes(channel.channel_type)) {
|
||||
return {
|
||||
thread: channel.thread,
|
||||
@@ -389,7 +389,6 @@ registerModel({
|
||||
*/
|
||||
filteredCategoryItems: many('DiscussSidebarCategoryItem', {
|
||||
compute: '_computeFilteredCategoryItems',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Display name of the category.
|
||||
@@ -465,7 +464,6 @@ registerModel({
|
||||
supportedChannelTypes: attr({
|
||||
compute: '_computeSupportedChannelTypes',
|
||||
required: true,
|
||||
readonly: true,
|
||||
}),
|
||||
},
|
||||
onChanges: [
|
||||
|
||||
@@ -96,7 +96,7 @@ registerModel({
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_computeIsActive() {
|
||||
return this.messaging.discuss && this.thread === this.messaging.discuss.thread;
|
||||
return this.messaging.discuss && this.thread === this.messaging.discuss.activeThread;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -234,10 +234,8 @@ registerModel({
|
||||
*/
|
||||
categoryCounterContribution: attr({
|
||||
compute: '_computeCategoryCounterContribution',
|
||||
readonly: true,
|
||||
}),
|
||||
channel: one('Channel', {
|
||||
readonly: true,
|
||||
related: 'thread.channel',
|
||||
}),
|
||||
/**
|
||||
|
||||
@@ -40,7 +40,6 @@ registerModel({
|
||||
}),
|
||||
mailbox: one('Mailbox', {
|
||||
compute: '_computeMailbox',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -111,7 +111,7 @@ registerModel({
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onDiscussThreadChanged() {
|
||||
_onDiscussActiveThreadChanged() {
|
||||
this.env.services.router.pushState({
|
||||
action: this.discuss.discussView.actionId,
|
||||
active_id: this.discuss.activeId,
|
||||
@@ -169,8 +169,8 @@ registerModel({
|
||||
},
|
||||
onChanges: [
|
||||
{
|
||||
dependencies: ['discuss.thread'],
|
||||
methodName: '_onDiscussThreadChanged',
|
||||
dependencies: ['discuss.activeThread'],
|
||||
methodName: '_onDiscussActiveThreadChanged',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -24,7 +24,6 @@ registerModel({
|
||||
emojiRegistry: one('EmojiRegistry', {
|
||||
compute: '_computeEmojiRegistry',
|
||||
inverse: 'allEmojis',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
emojiViews: many('EmojiView', {
|
||||
|
||||
@@ -26,7 +26,6 @@ registerModel({
|
||||
emojiViews: many('EmojiView', {
|
||||
compute: '_computeEmojiViews',
|
||||
inverse: 'emojiGridView',
|
||||
readonly: true,
|
||||
isCausal: true,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -208,7 +208,6 @@ registerModel({
|
||||
}),
|
||||
thread: one('Thread', {
|
||||
compute: '_computeThread',
|
||||
readonly: true,
|
||||
required: true,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -90,12 +90,10 @@ registerModel({
|
||||
fields: {
|
||||
isNonCompactActionContribution: attr({
|
||||
compute: '_computeIsNonCompactActionContribution',
|
||||
readonly: true,
|
||||
}),
|
||||
messageActionListOwner: one('MessageActionList', {
|
||||
compute: '_computeMessageActionListOwner',
|
||||
inverse: 'messageActions',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
messageActionListOwnerAsDelete: one('MessageActionList', {
|
||||
@@ -130,7 +128,6 @@ registerModel({
|
||||
compute: '_computeMessageActionView',
|
||||
inverse: 'messageAction',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* States the listing sequence of the action inside of the aciton list.
|
||||
|
||||
@@ -166,14 +166,12 @@ registerModel({
|
||||
}),
|
||||
firstActionView: one('MessageActionView', {
|
||||
compute: '_computeFirstActionView',
|
||||
readonly: true,
|
||||
}),
|
||||
isCompact: attr({
|
||||
default: true,
|
||||
}),
|
||||
lastActionView: one('MessageActionView', {
|
||||
compute: '_computeLastActionView',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* States the message on which this action message list operates.
|
||||
@@ -186,7 +184,6 @@ registerModel({
|
||||
isCausal: true,
|
||||
}),
|
||||
messageActionViews: many('MessageActionView', {
|
||||
readonly: true,
|
||||
related: 'messageActions.messageActionView',
|
||||
sort: '_sortMessageActionViews',
|
||||
}),
|
||||
|
||||
@@ -161,7 +161,7 @@ registerModel({
|
||||
const textInputContent = htmlDoc.body.textContent;
|
||||
this.update({
|
||||
composerForEditing: {
|
||||
mentionedPartners: this.message.recipients,
|
||||
rawMentionedPartners: this.message.recipients,
|
||||
textInputContent,
|
||||
textInputCursorEnd: textInputContent.length,
|
||||
textInputCursorStart: textInputContent.length,
|
||||
@@ -434,7 +434,6 @@ registerModel({
|
||||
compute: '_computeAttachmentList',
|
||||
inverse: 'messageViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
authorTitleText: attr({
|
||||
compute: '_computeAuthorTitleText',
|
||||
@@ -578,7 +577,6 @@ registerModel({
|
||||
compute: '_computeMessageActionList',
|
||||
inverse: 'messageView',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Determines the message that is displayed by this message view.
|
||||
@@ -586,7 +584,6 @@ registerModel({
|
||||
message: one('Message', {
|
||||
compute: '_computeMessage',
|
||||
inverse: 'messageViews',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
@@ -597,7 +594,6 @@ registerModel({
|
||||
compute: '_computeMessageInReplyToView',
|
||||
inverse: 'messageView',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
messageListViewMessageViewItemOwner: one('MessageListViewMessageViewItem', {
|
||||
identifying: true,
|
||||
@@ -615,7 +611,6 @@ registerModel({
|
||||
compute: '_computePersonaImStatusIconView',
|
||||
inverse: 'messageViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* States whether this message view is the last one of its thread view.
|
||||
|
||||
@@ -14,6 +14,7 @@ registerModel({
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
odoo.__DEBUG__.messaging = this;
|
||||
this.refreshIsNotificationPermissionDefault();
|
||||
},
|
||||
_willDelete() {
|
||||
this.env.bus.removeEventListener('window_focus', this._handleGlobalWindowFocus);
|
||||
@@ -200,7 +201,10 @@ registerModel({
|
||||
* provide an API to detect when this value changes.
|
||||
*/
|
||||
refreshIsNotificationPermissionDefault() {
|
||||
this.update({ isNotificationPermissionDefault: this._computeIsNotificationPermissionDefault() });
|
||||
const browserNotification = this.messaging.browser.Notification;
|
||||
this.update({
|
||||
isNotificationPermissionDefault: Boolean(browserNotification) && browserNotification.permission === 'default',
|
||||
});
|
||||
},
|
||||
async startFetchImStatus() {
|
||||
this.update({
|
||||
@@ -276,14 +280,6 @@ registerModel({
|
||||
}
|
||||
return new EventBus();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_computeIsNotificationPermissionDefault() {
|
||||
const browserNotification = this.messaging.browser.Notification;
|
||||
return browserNotification ? browserNotification.permission === 'default' : false;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
@@ -416,7 +412,6 @@ registerModel({
|
||||
initializedPromise: attr({
|
||||
compute: '_computeInitializedPromise',
|
||||
required: true,
|
||||
readonly: true,
|
||||
}),
|
||||
initializer: one('MessagingInitializer', {
|
||||
default: {},
|
||||
@@ -440,9 +435,7 @@ registerModel({
|
||||
* 'default' state. This means it is allowed to make a request to the
|
||||
* user to enable notifications.
|
||||
*/
|
||||
isNotificationPermissionDefault: attr({
|
||||
compute: '_computeIsNotificationPermissionDefault',
|
||||
}),
|
||||
isNotificationPermissionDefault: attr(),
|
||||
locale: one('Locale', {
|
||||
default: {},
|
||||
isCausal: true,
|
||||
@@ -460,7 +453,6 @@ registerModel({
|
||||
*/
|
||||
messagingBus: attr({
|
||||
compute: '_computeMessagingBus',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
messagingMenu: one('MessagingMenu', {
|
||||
@@ -470,7 +462,6 @@ registerModel({
|
||||
notificationHandler: one('MessagingNotificationHandler', {
|
||||
compute: '_computeNotificationHandler',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
outOfFocusUnreadMessageCounter: attr({
|
||||
default: 0,
|
||||
|
||||
@@ -326,8 +326,8 @@ registerModel({
|
||||
}
|
||||
if (this.messaging.currentPartner && this.messaging.currentPartner.id === partner_id) {
|
||||
channel.thread.update({
|
||||
lastSeenByCurrentPartnerMessageId: last_message_id,
|
||||
pendingSeenMessageId: undefined,
|
||||
rawLastSeenByCurrentPartnerMessageId: last_message_id,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ registerModel({
|
||||
this.discuss.update({ activeMobileNavbarTabId: tabId });
|
||||
if (
|
||||
this.discuss.activeMobileNavbarTabId === 'mailbox' &&
|
||||
(!this.discuss.thread || !this.discuss.thread.mailbox)
|
||||
(!this.discuss.activeThread || !this.discuss.activeThread.mailbox)
|
||||
) {
|
||||
this.discuss.update({ thread: this.messaging.inbox.thread });
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ registerModel({
|
||||
compute: '_computePersonaImStatusIconView',
|
||||
inverse: 'notificationRequestViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -340,10 +340,16 @@ registerModel({
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string|undefined}
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeDisplayName() {
|
||||
return this.display_name || this.user && this.user.display_name;
|
||||
if (this.display_name) {
|
||||
return this.display_name;
|
||||
}
|
||||
if (this.user && this.user.displayName) {
|
||||
return this.user.displayName;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -364,7 +370,7 @@ registerModel({
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
_computeNameOrDisplayName() {
|
||||
return this.name || this.display_name;
|
||||
return this.name || this.displayName;
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
@@ -392,7 +398,8 @@ registerModel({
|
||||
* And if a specific name format is required, it should be computed from
|
||||
* relevant fields instead.
|
||||
*/
|
||||
display_name: attr({
|
||||
display_name: attr(),
|
||||
displayName: attr({
|
||||
compute: '_computeDisplayName',
|
||||
default: "",
|
||||
}),
|
||||
@@ -413,7 +420,6 @@ registerModel({
|
||||
im_status: attr(),
|
||||
isImStatusSet: attr({
|
||||
compute: '_computeIsImStatusSet',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* States whether this partner is online.
|
||||
|
||||
@@ -46,11 +46,9 @@ registerModel({
|
||||
}),
|
||||
im_status: attr({
|
||||
compute: '_computeImStatus',
|
||||
readonly: true,
|
||||
}),
|
||||
name: attr({
|
||||
compute: '_computeName',
|
||||
readonly: true,
|
||||
}),
|
||||
partner: one('Partner', {
|
||||
identifying: true,
|
||||
|
||||
@@ -73,7 +73,6 @@ registerModel({
|
||||
}),
|
||||
persona: one('Persona', {
|
||||
compute: '_computePersona',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -176,7 +176,6 @@ registerModel({
|
||||
compute: '_computeActivityMarkDonePopoverContentView',
|
||||
inverse: 'popoverViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
activityViewOwnerAsMarkDone: one('ActivityView', {
|
||||
identifying: true,
|
||||
@@ -196,7 +195,6 @@ registerModel({
|
||||
compute: '_computeChannelInvitationForm',
|
||||
inverse: 'popoverViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* States the OWL component of this popover view.
|
||||
@@ -239,12 +237,10 @@ registerModel({
|
||||
compute: '_computeEmojiPickerView',
|
||||
inverse: 'popoverViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
manager: one('PopoverManager', {
|
||||
compute: '_computeManager',
|
||||
inverse: 'popoverViews',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* If set, this popover view is owned by a message action view.
|
||||
|
||||
@@ -99,9 +99,6 @@ registerModel({
|
||||
});
|
||||
data2.serverLastMessage = insert(messageData);
|
||||
}
|
||||
if ('memberCount' in data) {
|
||||
data2.memberCount = data.memberCount;
|
||||
}
|
||||
if ('message_needaction_counter' in data) {
|
||||
data2.message_needaction_counter = data.message_needaction_counter;
|
||||
}
|
||||
@@ -115,7 +112,7 @@ registerModel({
|
||||
data2.public = data.public;
|
||||
}
|
||||
if ('seen_message_id' in data) {
|
||||
data2.lastSeenByCurrentPartnerMessageId = data.seen_message_id || 0;
|
||||
data2.rawLastSeenByCurrentPartnerMessageId = data.seen_message_id;
|
||||
}
|
||||
if ('uuid' in data) {
|
||||
data2.uuid = data.uuid;
|
||||
@@ -1355,15 +1352,15 @@ registerModel({
|
||||
const firstMessage = this.orderedMessages[0];
|
||||
if (
|
||||
firstMessage &&
|
||||
this.lastSeenByCurrentPartnerMessageId &&
|
||||
this.lastSeenByCurrentPartnerMessageId < firstMessage.id
|
||||
this.rawLastSeenByCurrentPartnerMessageId &&
|
||||
this.rawLastSeenByCurrentPartnerMessageId < firstMessage.id
|
||||
) {
|
||||
// no deduction can be made if there is a gap
|
||||
return this.lastSeenByCurrentPartnerMessageId;
|
||||
return this.rawLastSeenByCurrentPartnerMessageId;
|
||||
}
|
||||
let lastSeenByCurrentPartnerMessageId = this.lastSeenByCurrentPartnerMessageId;
|
||||
let lastSeenByCurrentPartnerMessageId = this.rawLastSeenByCurrentPartnerMessageId;
|
||||
for (const message of this.orderedMessages) {
|
||||
if (message.id <= this.lastSeenByCurrentPartnerMessageId) {
|
||||
if (message.id <= this.rawLastSeenByCurrentPartnerMessageId) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
@@ -1800,7 +1797,6 @@ registerModel({
|
||||
compute: '_computeComposer',
|
||||
inverse: 'thread',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
correspondent: one('Partner', {
|
||||
compute: '_computeCorrespondent',
|
||||
@@ -1868,7 +1864,6 @@ registerModel({
|
||||
compute: '_computeDiscussSidebarCategoryItem',
|
||||
inverse: 'thread',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
displayName: attr({
|
||||
compute: '_computeDisplayName',
|
||||
@@ -2096,12 +2091,6 @@ registerModel({
|
||||
inverse: 'thread',
|
||||
}),
|
||||
mainAttachment: one('Attachment'),
|
||||
/**
|
||||
* States the number of members in this thread according to the server.
|
||||
* Guests are excluded from the count.
|
||||
* Only makes sense if this thread is a channel.
|
||||
*/
|
||||
memberCount: attr(),
|
||||
members: many('Partner', {
|
||||
sort: '_sortPartnerMembers',
|
||||
}),
|
||||
@@ -2155,12 +2144,10 @@ registerModel({
|
||||
messagingAsRingingThread: one('Messaging', {
|
||||
compute: '_computeMessagingAsRingingThread',
|
||||
inverse: 'ringingThreads',
|
||||
readonly: true,
|
||||
}),
|
||||
messagingMenuAsPinnedAndUnreadChannel: one('MessagingMenu', {
|
||||
compute: '_computeMessagingMenuAsPinnedAndUnreadChannel',
|
||||
inverse: 'pinnedAndUnreadChannels',
|
||||
readonly: true,
|
||||
}),
|
||||
model: attr({
|
||||
identifying: true,
|
||||
@@ -2238,6 +2225,9 @@ registerModel({
|
||||
*/
|
||||
pendingSeenMessageId: attr(),
|
||||
public: attr(),
|
||||
rawLastSeenByCurrentPartnerMessageId: attr({
|
||||
default: 0,
|
||||
}),
|
||||
/**
|
||||
* If set, the current thread is the thread that hosts the current RTC call.
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, many, one } from '@mail/model/model_field';
|
||||
import { clear, link, unlink } from '@mail/model/model_field_command';
|
||||
import { clear, link } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'ThreadCache',
|
||||
@@ -62,19 +62,13 @@ registerModel({
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {Message[]}
|
||||
* @returns {FieldCommand|Message[]}
|
||||
*/
|
||||
_computeFetchedMessages() {
|
||||
if (!this.thread) {
|
||||
return clear();
|
||||
}
|
||||
const toUnlinkMessages = [];
|
||||
for (const message of this.fetchedMessages) {
|
||||
if (!this.thread.messages.includes(message)) {
|
||||
toUnlinkMessages.push(message);
|
||||
}
|
||||
}
|
||||
return unlink(toUnlinkMessages);
|
||||
return this.rawFetchedMessages.filter(m => this.thread.messages.includes(m));
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -120,7 +114,7 @@ registerModel({
|
||||
message.id > this.lastFetchedMessage.id
|
||||
);
|
||||
}
|
||||
return this.fetchedMessages.concat(newerMessages);
|
||||
return [...this.fetchedMessages, ...this.temporaryMessages, ...newerMessages];
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -219,7 +213,7 @@ registerModel({
|
||||
return;
|
||||
}
|
||||
this.update({
|
||||
fetchedMessages: link(messages),
|
||||
rawFetchedMessages: link(messages),
|
||||
hasLoadingFailed: false,
|
||||
isLoaded: true,
|
||||
isLoading: false,
|
||||
@@ -356,6 +350,8 @@ registerModel({
|
||||
orderedNonEmptyMessages: many('Message', {
|
||||
compute: '_computeOrderedNonEmptyMessages',
|
||||
}),
|
||||
rawFetchedMessages: many('Message'),
|
||||
temporaryMessages: many('Message'),
|
||||
thread: one('Thread', {
|
||||
identifying: true,
|
||||
inverse: 'cache',
|
||||
|
||||
@@ -87,15 +87,12 @@ registerModel({
|
||||
inlineLastNeedactionMessageAsOriginThreadBody: attr({
|
||||
compute: '_computeInlineLastNeedactionMessageAsOriginThreadBody',
|
||||
default: "",
|
||||
readonly: true,
|
||||
}),
|
||||
isEmpty: attr({
|
||||
compute: '_computeIsEmpty',
|
||||
readonly: true,
|
||||
}),
|
||||
lastTrackingValue: one('TrackingValue', {
|
||||
compute: '_computeLastTrackingValue',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Reference of the "mark as read" button. Useful to disable the
|
||||
@@ -115,7 +112,6 @@ registerModel({
|
||||
compute: '_computePersonaImStatusIconView',
|
||||
inverse: 'threadNeedactionPreviewViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
thread: one('Thread', {
|
||||
identifying: true,
|
||||
|
||||
@@ -85,15 +85,12 @@ registerModel({
|
||||
inlineLastMessageBody: attr({
|
||||
compute: '_computeInlineLastMessageBody',
|
||||
default: "",
|
||||
readonly: true,
|
||||
}),
|
||||
isEmpty: attr({
|
||||
compute: '_computeIsEmpty',
|
||||
readonly: true,
|
||||
}),
|
||||
lastTrackingValue: one('TrackingValue', {
|
||||
compute: '_computeLastTrackingValue',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Reference of the "mark as read" button. Useful to disable the
|
||||
@@ -113,7 +110,6 @@ registerModel({
|
||||
compute: '_computePersonaImStatusIconView',
|
||||
inverse: 'threadPreviewViewOwner',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
thread: one('Thread', {
|
||||
identifying: true,
|
||||
|
||||
@@ -119,7 +119,7 @@ registerModel({
|
||||
*/
|
||||
_computeHasComposerThreadName() {
|
||||
if (this.threadViewer.discuss) {
|
||||
return this.threadViewer.discuss.thread === this.messaging.inbox.thread;
|
||||
return this.threadViewer.discuss.activeThread === this.messaging.inbox.thread;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
@@ -448,7 +448,6 @@ registerModel({
|
||||
lastMessageView: one('MessageView', {
|
||||
compute: '_computeLastMessageView',
|
||||
inverse: 'threadViewOwnerAsLastMessageView',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Most recent message in this ThreadView that has been shown to the
|
||||
@@ -482,14 +481,12 @@ registerModel({
|
||||
compute: '_computeCallView',
|
||||
inverse: 'threadView',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Determines the `Thread` currently displayed by `this`.
|
||||
*/
|
||||
thread: one('Thread', {
|
||||
inverse: 'threadViews',
|
||||
readonly: true,
|
||||
related: 'threadViewer.thread',
|
||||
}),
|
||||
/**
|
||||
@@ -497,7 +494,6 @@ registerModel({
|
||||
*/
|
||||
threadCache: one('ThreadCache', {
|
||||
inverse: 'threadViews',
|
||||
readonly: true,
|
||||
related: 'threadViewer.threadCache',
|
||||
}),
|
||||
threadCacheInitialScrollHeight: attr({
|
||||
@@ -534,7 +530,6 @@ registerModel({
|
||||
compute: '_computeTopbar',
|
||||
inverse: 'threadView',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
},
|
||||
onChanges: [
|
||||
|
||||
@@ -484,7 +484,6 @@ registerModel({
|
||||
avatarUrl: attr({
|
||||
compute: '_computeAvatarUrl',
|
||||
default: '',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Determines whether the guest name input needs to be focused.
|
||||
@@ -583,7 +582,6 @@ registerModel({
|
||||
*/
|
||||
hasGuestNameChanged: attr({
|
||||
compute: '_computeHasGuestNameChanged',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Determines whether description area should display on top bar.
|
||||
|
||||
@@ -63,7 +63,6 @@ registerModel({
|
||||
*/
|
||||
duration: attr({
|
||||
compute: '_computeDuration',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
|
||||
@@ -121,7 +121,6 @@ registerModel({
|
||||
*/
|
||||
duration: attr({
|
||||
compute: '_computeDuration',
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
messagingOwnerAsFetchImStatusTimer: one('Messaging', {
|
||||
|
||||
@@ -158,10 +158,16 @@ registerModel({
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string|undefined}
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeDisplayName() {
|
||||
return this.display_name || this.partner && this.partner.display_name;
|
||||
if (this.display_name) {
|
||||
return this.display_name;
|
||||
}
|
||||
if (this.partner && this.partner.displayName) {
|
||||
return this.partner.displayName;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -184,8 +190,10 @@ registerModel({
|
||||
* `share` field in python.
|
||||
*/
|
||||
isInternalUser: attr(),
|
||||
display_name: attr({
|
||||
display_name: attr(),
|
||||
displayName: attr({
|
||||
compute: '_computeDisplayName',
|
||||
default: "",
|
||||
}),
|
||||
model: attr({
|
||||
default: 'res.user',
|
||||
|
||||
@@ -151,7 +151,6 @@ registerModel({
|
||||
*/
|
||||
guestNameInputUniqueId: attr({
|
||||
compute: '_computeGuestNameInputUniqueId',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Determines whether the guest's name has been updated.
|
||||
@@ -161,7 +160,6 @@ registerModel({
|
||||
*/
|
||||
hasGuestNameChanged: attr({
|
||||
compute: '_computeHasGuestNameChanged',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Determines whether the 'guestNameInput' should be focused the next
|
||||
@@ -184,7 +182,6 @@ registerModel({
|
||||
compute: '_computeCallDemoView',
|
||||
inverse: 'welcomeView',
|
||||
isCausal: true,
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* States the name the guest had when landing on the welcome view.
|
||||
|
||||
@@ -1003,6 +1003,7 @@ patch(MockServer.prototype, 'mail', {
|
||||
const channelData = {
|
||||
channel_type: channel.channel_type,
|
||||
id: channel.id,
|
||||
memberCount: channel.member_count,
|
||||
};
|
||||
const res = Object.assign({}, channel, {
|
||||
last_message_id: lastMessageId,
|
||||
|
||||
@@ -90,7 +90,7 @@ QUnit.test('create', async function (assert) {
|
||||
assert.strictEqual(channel.name, "General");
|
||||
const partner = messaging.models['Partner'].findFromIdentifyingData({ id: 5 });
|
||||
assert.ok(partner);
|
||||
assert.strictEqual(partner.display_name, "Demo");
|
||||
assert.strictEqual(partner.displayName, "Demo");
|
||||
assert.strictEqual(partner.id, 5);
|
||||
});
|
||||
|
||||
|
||||
@@ -26,6 +26,5 @@ addRecordMethods('ThreadNeedactionPreviewView', {
|
||||
addFields('ThreadNeedactionPreviewView', {
|
||||
isRating: attr({
|
||||
compute: '_computeIsRating',
|
||||
readonly: true,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -26,6 +26,5 @@ addRecordMethods('ThreadPreviewView', {
|
||||
addFields('ThreadPreviewView', {
|
||||
isRating: attr({
|
||||
compute: '_computeIsRating',
|
||||
readonly: true,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -122,6 +122,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'channel',
|
||||
'id': self.channel_general.id,
|
||||
'memberCount': len(self.group_user.users | self.user_root),
|
||||
},
|
||||
'create_uid': self.user_root.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -134,7 +135,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_general.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': next(res['message_id'] for res in self.channel_general._channel_last_message_ids()),
|
||||
'memberCount': len(self.group_user.users | self.user_root),
|
||||
'message_needaction_counter': 0,
|
||||
'message_unread_counter': 5,
|
||||
'name': 'general',
|
||||
@@ -150,6 +150,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'channel',
|
||||
'id': self.channel_channel_public_1.id,
|
||||
'memberCount': 5,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -163,7 +164,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'last_interest_dt': self.channel_channel_public_1.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': next(res['message_id'] for res in self.channel_channel_public_1._channel_last_message_ids()),
|
||||
'message_needaction_counter': 1,
|
||||
'memberCount': 5,
|
||||
'message_unread_counter': 0,
|
||||
'name': 'public 1',
|
||||
'public': 'public',
|
||||
@@ -178,6 +178,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'channel',
|
||||
'id': self.channel_channel_public_2.id,
|
||||
'memberCount': 5,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -190,7 +191,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_channel_public_2.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': next(res['message_id'] for res in self.channel_channel_public_2._channel_last_message_ids()),
|
||||
'memberCount': 5,
|
||||
'message_needaction_counter': 0,
|
||||
'message_unread_counter': 0,
|
||||
'name': 'public 2',
|
||||
@@ -206,6 +206,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'channel',
|
||||
'id': self.channel_channel_group_1.id,
|
||||
'memberCount': 5,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -218,7 +219,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_channel_group_1.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': next(res['message_id'] for res in self.channel_channel_group_1._channel_last_message_ids()),
|
||||
'memberCount': 5,
|
||||
'message_needaction_counter': 0,
|
||||
'message_unread_counter': 0,
|
||||
'name': 'group 1',
|
||||
@@ -234,6 +234,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'channel',
|
||||
'id': self.channel_channel_group_2.id,
|
||||
'memberCount': 5,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -246,7 +247,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_channel_group_2.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': next(res['message_id'] for res in self.channel_channel_group_2._channel_last_message_ids()),
|
||||
'memberCount': 5,
|
||||
'message_needaction_counter': 0,
|
||||
'message_unread_counter': 0,
|
||||
'name': 'group 2',
|
||||
@@ -262,6 +262,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'channel',
|
||||
'id': self.channel_channel_private_1.id,
|
||||
'memberCount': 5,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -274,7 +275,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_channel_private_1.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': next(res['message_id'] for res in self.channel_channel_private_1._channel_last_message_ids()),
|
||||
'memberCount': 5,
|
||||
'message_needaction_counter': 0,
|
||||
'message_unread_counter': 0,
|
||||
'name': 'private 1',
|
||||
@@ -290,6 +290,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'channel',
|
||||
'id': self.channel_channel_private_2.id,
|
||||
'memberCount': 5,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -302,7 +303,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_channel_private_2.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': next(res['message_id'] for res in self.channel_channel_private_2._channel_last_message_ids()),
|
||||
'memberCount': 5,
|
||||
'message_needaction_counter': 0,
|
||||
'message_unread_counter': 0,
|
||||
'name': 'private 2',
|
||||
@@ -318,6 +318,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'group',
|
||||
'id': self.channel_group_1.id,
|
||||
'memberCount': 2,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -331,7 +332,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_group_1.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': False,
|
||||
'memberCount': 2,
|
||||
'members': [
|
||||
{
|
||||
'active': True,
|
||||
@@ -385,6 +385,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'chat',
|
||||
'id': self.channel_chat_1.id,
|
||||
'memberCount': 2,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -398,7 +399,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_chat_1.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': False,
|
||||
'memberCount': 2,
|
||||
'members': [
|
||||
{
|
||||
'active': True,
|
||||
@@ -452,6 +452,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'chat',
|
||||
'id': self.channel_chat_2.id,
|
||||
'memberCount': 2,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -465,7 +466,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_chat_2.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': False,
|
||||
'memberCount': 2,
|
||||
'members': [
|
||||
{
|
||||
'active': True,
|
||||
@@ -519,6 +519,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'chat',
|
||||
'id': self.channel_chat_3.id,
|
||||
'memberCount': 2,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -532,7 +533,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_chat_3.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': False,
|
||||
'memberCount': 2,
|
||||
'members': [
|
||||
{
|
||||
'active': True,
|
||||
@@ -586,6 +586,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'chat',
|
||||
'id': self.channel_chat_4.id,
|
||||
'memberCount': 2,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -599,7 +600,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_chat_4.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': False,
|
||||
'memberCount': 2,
|
||||
'members': [
|
||||
{
|
||||
'active': True,
|
||||
@@ -653,6 +653,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'livechat',
|
||||
'id': self.channel_livechat_1.id,
|
||||
'memberCount': 2,
|
||||
},
|
||||
'create_uid': self.env.user.id,
|
||||
'custom_channel_name': False,
|
||||
@@ -666,7 +667,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_livechat_1.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': next(res['message_id'] for res in self.channel_livechat_1._channel_last_message_ids()),
|
||||
'memberCount': 2,
|
||||
'livechat_visitor': {
|
||||
'country': False,
|
||||
'id': self.users[1].partner_id.id,
|
||||
@@ -720,6 +720,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'channel': {
|
||||
'channel_type': 'livechat',
|
||||
'id': self.channel_livechat_2.id,
|
||||
'memberCount': 2,
|
||||
},
|
||||
'create_uid': self.env.ref('base.public_user').id,
|
||||
'custom_channel_name': False,
|
||||
@@ -733,7 +734,6 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_livechat_2.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'last_message_id': next(res['message_id'] for res in self.channel_livechat_2._channel_last_message_ids()),
|
||||
'memberCount': 2,
|
||||
'livechat_visitor': {
|
||||
'country': (self.env.ref('base.be').id, 'Belgium'),
|
||||
'id': False,
|
||||
|
||||
Reference in New Issue
Block a user