[FIX] mail, im_livechat: improve performance of sidebar item counter
Avoid iterating over all channels every time a new channel is added. Part of task-2702450 closes odoo/odoo#81264 X-original-commit: 7686be710ba919079f6ea97293ac99dde79f843b Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
committed by
Alexandre Kühn
parent
26a5d7d299
commit
80232c52db
+10
@@ -17,6 +17,16 @@ patchRecordMethods('mail.discuss_sidebar_category_item', {
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_computeCategoryCounterContribution() {
|
||||
switch (this.channel.channel_type) {
|
||||
case 'livechat':
|
||||
return this.channel.localMessageUnreadCounter > 0 ? 1 : 0;
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,6 @@ patchRecordMethods('mail.messaging_initializer', {
|
||||
_initResUsersSettings({ is_discuss_sidebar_category_livechat_open }) {
|
||||
this.messaging.discuss.update({
|
||||
categoryLivechat: insertAndReplace({
|
||||
counterComputeMethod: 'unread',
|
||||
isServerOpen: is_discuss_sidebar_category_livechat_open,
|
||||
name: this.env._t("Livechat"),
|
||||
serverStateKey: 'is_discuss_sidebar_category_livechat_open',
|
||||
|
||||
@@ -118,6 +118,16 @@ export class ModelField {
|
||||
* records.
|
||||
*/
|
||||
this.sort = sort;
|
||||
/**
|
||||
* Determines whether and how elements of this field should be summed
|
||||
* into a counter field (only makes sense for relational x2many).
|
||||
*
|
||||
* It must contain an array of object with 2 keys: `from` giving the
|
||||
* name of a field on the relation record that holds the contribution of
|
||||
* that record to the sum, and `to` giving the name of a field on the
|
||||
* current record which contains the sum.
|
||||
*/
|
||||
this.sumContributions = [];
|
||||
/**
|
||||
* This prop only makes sense in a relational field. Determine which
|
||||
* model name this relation refers to.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { decrement, increment } from '@mail/model/model_field_command';
|
||||
import { Listener } from '@mail/model/model_listener';
|
||||
import { followRelations } from '@mail/model/model_utils';
|
||||
|
||||
@@ -21,6 +22,14 @@ export class RelationSet {
|
||||
this.sortArray = new Array();
|
||||
this.sortListenerByValue = new Map();
|
||||
}
|
||||
if (this.field.sumContributions.length > 0) {
|
||||
this.sumByValueByField = new Map();
|
||||
this.sumListenerByValueByField = new Map();
|
||||
for (const { to: sumFieldName } of this.field.sumContributions) {
|
||||
this.sumByValueByField.set(sumFieldName, new Map());
|
||||
this.sumListenerByValueByField.set(sumFieldName, new Map());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,6 +112,34 @@ export class RelationSet {
|
||||
this.sortListenerByValue.set(value, listener);
|
||||
listener.onChange({ reason: 'initial call' });
|
||||
}
|
||||
for (const { from: contributionFieldName, to: sumFieldName } of this.field.sumContributions) {
|
||||
this.sumByValueByField.get(sumFieldName).set(value, 0);
|
||||
const listener = new Listener({
|
||||
isPartOfUpdateCycle: true,
|
||||
name: `sum of field(${sumFieldName}) of ${this.record} from field(${contributionFieldName}) of ${value} through relation ${this.field}`,
|
||||
onChange: info => {
|
||||
// listen to the contribution to mark it as dependency
|
||||
this.record.modelManager.startListening(listener);
|
||||
const contribution = value[contributionFieldName];
|
||||
this.record.modelManager.stopListening(listener);
|
||||
// retrieve the previous contribution and update it
|
||||
const previousContribution = this.sumByValueByField.get(sumFieldName).get(value);
|
||||
this.sumByValueByField.get(sumFieldName).set(value, contribution);
|
||||
this.record.modelManager._update(
|
||||
this.record,
|
||||
{
|
||||
[sumFieldName]: [
|
||||
decrement(previousContribution),
|
||||
increment(contribution),
|
||||
],
|
||||
},
|
||||
{ allowWriteReadonly: true },
|
||||
);
|
||||
},
|
||||
});
|
||||
this.sumListenerByValueByField.get(sumFieldName).set(value, listener);
|
||||
listener.onChange({ reason: 'initial call' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,6 +169,16 @@ export class RelationSet {
|
||||
this.sortListenerByValue.delete(listener);
|
||||
this.record.modelManager.removeListener(listener);
|
||||
}
|
||||
for (const { to: sumFieldName } of this.field.sumContributions) {
|
||||
// remove contribution of current value
|
||||
const contribution = this.sumByValueByField.get(sumFieldName).get(value);
|
||||
this.sumByValueByField.get(sumFieldName).delete(value);
|
||||
this.record.modelManager._update(this.record, { [sumFieldName]: decrement(contribution) }, { allowWriteReadonly: true });
|
||||
// remove listener on current value
|
||||
const listener = this.sumListenerByValueByField.get(sumFieldName).get(value);
|
||||
this.sumListenerByValueByField.get(sumFieldName).delete(value);
|
||||
this.record.modelManager.removeListener(listener);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -490,6 +490,7 @@ export class ModelManager {
|
||||
'readonly',
|
||||
'related',
|
||||
'required',
|
||||
'sum',
|
||||
].includes(key)
|
||||
);
|
||||
if (invalidKeys.length > 0) {
|
||||
@@ -1176,11 +1177,25 @@ export class ModelManager {
|
||||
* 1. Prepare fields.
|
||||
*/
|
||||
for (const Model of Object.values(this.models)) {
|
||||
const sumContributionsByFieldName = new Map();
|
||||
// Make fields aware of their field name.
|
||||
for (const [fieldName, fieldData] of registry.get(Model.name).get('fields')) {
|
||||
Model.fields[fieldName] = new ModelField(Object.assign({}, fieldData, {
|
||||
fieldName,
|
||||
}));
|
||||
if (fieldData.sum) {
|
||||
const [relationFieldName, contributionFieldName] = fieldData.sum.split('.');
|
||||
if (!sumContributionsByFieldName.has(relationFieldName)) {
|
||||
sumContributionsByFieldName.set(relationFieldName, []);
|
||||
}
|
||||
sumContributionsByFieldName.get(relationFieldName).push({
|
||||
from: contributionFieldName,
|
||||
to: fieldName,
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const [fieldName, sumContributions] of sumContributionsByFieldName) {
|
||||
Model.fields[fieldName].sumContributions = sumContributions;
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -73,18 +73,6 @@ registerModel({
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {integer}
|
||||
*/
|
||||
_computeCounter() {
|
||||
switch (this.counterComputeMethod) {
|
||||
case 'needaction':
|
||||
return this.categoryItems.filter(categoryItem => categoryItem.channel.message_needaction_counter > 0).length;
|
||||
case 'unread':
|
||||
return this.categoryItems.filter(categoryItem => categoryItem.channel.localMessageUnreadCounter > 0).length;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
@@ -237,17 +225,13 @@ registerModel({
|
||||
sort: '_sortDefinitionCategoryItems',
|
||||
}),
|
||||
/**
|
||||
* The total amount unread/action-needed threads in the category.
|
||||
* States the total amount of unread/action-needed threads in this
|
||||
* category.
|
||||
*/
|
||||
counter: attr({
|
||||
compute: '_computeCounter',
|
||||
}),
|
||||
/**
|
||||
* Determines how the counter of this category should be computed.
|
||||
* Supported methods: 'needaction', 'unread'.
|
||||
*/
|
||||
counterComputeMethod: attr({
|
||||
required: true,
|
||||
default: 0,
|
||||
readonly: true,
|
||||
sum: 'categoryItems.categoryCounterContribution',
|
||||
}),
|
||||
discussAsChannel: one2one('mail.discuss', {
|
||||
inverse: 'categoryChannel',
|
||||
|
||||
+17
-6
@@ -33,13 +33,16 @@ registerModel({
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {mail.thread}
|
||||
* @returns {integer}
|
||||
*/
|
||||
_computeChannel() {
|
||||
return link(this.messaging.models['mail.thread'].findFromIdentifyingData({
|
||||
id: this.channel.id,
|
||||
model: 'mail.channel',
|
||||
}));
|
||||
_computeCategoryCounterContribution() {
|
||||
switch (this.channel.channel_type) {
|
||||
case 'channel':
|
||||
return this.channel.message_needaction_counter > 0 ? 1 : 0;
|
||||
case 'chat':
|
||||
case 'group':
|
||||
return this.channel.localMessageUnreadCounter > 0 ? 1 : 0;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -219,6 +222,14 @@ registerModel({
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
* Determines the contribution of this discuss sidebar category item to
|
||||
* the counter of this category.
|
||||
*/
|
||||
categoryCounterContribution: attr({
|
||||
compute: '_computeCategoryCounterContribution',
|
||||
readonly: true,
|
||||
}),
|
||||
/**
|
||||
* Amount of unread/action-needed messages
|
||||
*/
|
||||
|
||||
@@ -244,7 +244,6 @@ registerModel({
|
||||
categoryChannel: insertAndReplace({
|
||||
autocompleteMethod: 'channel',
|
||||
commandAddTitleText: this.env._t("Add or join a channel"),
|
||||
counterComputeMethod: 'needaction',
|
||||
hasAddCommand: true,
|
||||
hasViewCommand: true,
|
||||
isServerOpen: is_discuss_sidebar_category_channel_open,
|
||||
@@ -257,7 +256,6 @@ registerModel({
|
||||
categoryChat: insertAndReplace({
|
||||
autocompleteMethod: 'chat',
|
||||
commandAddTitleText: this.env._t("Start a conversation"),
|
||||
counterComputeMethod: 'unread',
|
||||
hasAddCommand: true,
|
||||
isServerOpen: is_discuss_sidebar_category_chat_open,
|
||||
name: this.env._t("Direct Messages"),
|
||||
|
||||
Reference in New Issue
Block a user