[REF] mail: support passing commands as data of relational field
This significantly eases processing command data on relational field received from server. closes odoo/odoo#137341 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -22,42 +22,30 @@ export class ChannelMember extends Record {
|
||||
* @returns {import("models").ChannelMember}
|
||||
*/
|
||||
static insert(data) {
|
||||
const memberData = Array.isArray(data) ? data[1] : data;
|
||||
/** @type {import("models").ChannelMember} */
|
||||
const member = this.preinsert(memberData);
|
||||
const member = this.preinsert(data);
|
||||
member.update(data);
|
||||
return member;
|
||||
}
|
||||
|
||||
update(data) {
|
||||
const [command, memberData] = Array.isArray(data) ? data : ["ADD", data];
|
||||
this.id = memberData.id;
|
||||
if ("persona" in memberData) {
|
||||
this.id = data.id;
|
||||
if ("persona" in data) {
|
||||
this.persona = {
|
||||
...(memberData.persona.partner ?? memberData.persona.guest),
|
||||
type: memberData.persona.guest ? "guest" : "partner",
|
||||
country: memberData.persona.partner?.country,
|
||||
channelId: memberData.persona.guest ? memberData.channel.id : null,
|
||||
...(data.persona.partner ?? data.persona.guest),
|
||||
type: data.persona.guest ? "guest" : "partner",
|
||||
country: data.persona.partner?.country,
|
||||
channelId: data.persona.guest ? data.channel.id : null,
|
||||
};
|
||||
}
|
||||
let thread = memberData.thread ?? this.thread;
|
||||
if (!thread && memberData.channel?.id) {
|
||||
let thread = data.thread ?? this.thread;
|
||||
if (!thread && data.channel?.id) {
|
||||
thread = {
|
||||
id: memberData.channel.id,
|
||||
id: data.channel.id,
|
||||
model: "discuss.channel",
|
||||
};
|
||||
}
|
||||
if (thread && !this.thread) {
|
||||
this.thread = thread;
|
||||
}
|
||||
switch (command) {
|
||||
case "ADD":
|
||||
this.thread?.channelMembers.add(this);
|
||||
break;
|
||||
case "DELETE":
|
||||
this.delete();
|
||||
break;
|
||||
}
|
||||
this.thread ??= thread;
|
||||
}
|
||||
|
||||
/** @type {number} */
|
||||
|
||||
@@ -103,31 +103,8 @@ export class Message extends Record {
|
||||
partner: this._store.self,
|
||||
};
|
||||
}
|
||||
if (data.messageReactionGroups) {
|
||||
const reactionContentToUnlink = new Set();
|
||||
const reactionsToInsert = [];
|
||||
for (const rawReaction of data.messageReactionGroups) {
|
||||
const [command, reactionData] = Array.isArray(rawReaction)
|
||||
? rawReaction
|
||||
: ["ADD", rawReaction];
|
||||
const reaction = this._store.MessageReactions.insert(reactionData);
|
||||
if (command === "ADD") {
|
||||
reactionsToInsert.push(reaction);
|
||||
} else {
|
||||
reactionContentToUnlink.add(reaction.content);
|
||||
}
|
||||
}
|
||||
this.reactions = this.reactions.filter(
|
||||
({ content }) => !reactionContentToUnlink.has(content)
|
||||
);
|
||||
reactionsToInsert.forEach((reaction) => {
|
||||
const idx = this.reactions.findIndex(({ content }) => reaction.content === content);
|
||||
if (idx !== -1) {
|
||||
this.reactions[idx] = reaction;
|
||||
} else {
|
||||
this.reactions.push(reaction);
|
||||
}
|
||||
});
|
||||
if ("messageReactionGroups" in data) {
|
||||
this.reactions = data.messageReactionGroups;
|
||||
}
|
||||
if (this.isNotification && !this.notificationType) {
|
||||
const parser = new DOMParser();
|
||||
|
||||
@@ -46,19 +46,9 @@ export class NotificationGroup extends Record {
|
||||
type: data.type ?? this.type,
|
||||
status: data.status ?? this.status,
|
||||
});
|
||||
const notifications = data.notifications ?? [];
|
||||
const alreadyKnownNotifications = new Set(this.notifications.map(({ id }) => id));
|
||||
const notificationIdsToRemove = new Set();
|
||||
for (const [commandName, notification] of notifications) {
|
||||
if (commandName === "ADD" && !alreadyKnownNotifications.has(notification.id)) {
|
||||
this.notifications.push(notification);
|
||||
} else if (commandName === "DELETE") {
|
||||
notificationIdsToRemove.add(notification.id);
|
||||
}
|
||||
if ("notifications" in data) {
|
||||
this.notifications = data.notifications;
|
||||
}
|
||||
this.notifications = this.notifications.filter(
|
||||
({ id }) => !notificationIdsToRemove.has(id)
|
||||
);
|
||||
this.lastMessage = this.notifications[0]?.message;
|
||||
for (const notification of this.notifications) {
|
||||
if (this.lastMessage?.id < notification.message?.id) {
|
||||
|
||||
@@ -130,6 +130,8 @@ export class RecordList extends Array {
|
||||
if (r2 && r2.notEq(r3)) {
|
||||
receiver.__deleteInverse__(r2);
|
||||
}
|
||||
const { onDelete } = receiver.owner.Model.__rels__.get(receiver.name);
|
||||
onDelete?.call(receiver.owner, r2);
|
||||
receiver.__list__[index] = r3?.localId;
|
||||
if (r3) {
|
||||
receiver.__addInverse__(r3);
|
||||
@@ -178,6 +180,8 @@ export class RecordList extends Array {
|
||||
if (r2) {
|
||||
this.__deleteInverse__(r2);
|
||||
}
|
||||
const { onDelete } = this.owner.Model.__rels__.get(this.name);
|
||||
onDelete?.call(this.owner, r2);
|
||||
return r2;
|
||||
}
|
||||
/** @returns {R} */
|
||||
@@ -186,6 +190,8 @@ export class RecordList extends Array {
|
||||
if (r2) {
|
||||
this.__deleteInverse__(r2);
|
||||
}
|
||||
const { onDelete } = this.owner.Model.__rels__.get(this.name);
|
||||
onDelete?.call(this.owner, r2);
|
||||
return r2;
|
||||
}
|
||||
/** @param {R[]} records */
|
||||
@@ -265,6 +271,8 @@ export class RecordList extends Array {
|
||||
this.__list__.splice(start, deleteCount, ...newRecords.map((r) => r.localId));
|
||||
for (const r of oldRecords) {
|
||||
this.__deleteInverse__(r);
|
||||
const { onDelete } = this.owner.Model.__rels__.get(this.name);
|
||||
onDelete?.call(this.owner, r);
|
||||
}
|
||||
for (const r of newRecords) {
|
||||
this.__addInverse__(r);
|
||||
@@ -419,19 +427,21 @@ export class Record {
|
||||
}
|
||||
/**
|
||||
* @template {keyof import("model ").Models} M
|
||||
* @param {M} modelName
|
||||
* @param {M} targetModel
|
||||
* @returns {import("models").Models[M]}
|
||||
*/
|
||||
static one(modelName) {
|
||||
return [ONE_SYM, modelName];
|
||||
static one(targetModel) {
|
||||
return [ONE_SYM, { targetModel }];
|
||||
}
|
||||
/**
|
||||
* @template {keyof import("model").Models} M
|
||||
* @param {M} modelName
|
||||
* @param {M} targetModel
|
||||
* @param {(r) => void} [onDelete] function that is called when a record is removed
|
||||
* from the relation.
|
||||
* @returns {import("models").Models[M][]}
|
||||
*/
|
||||
static many(modelName) {
|
||||
return [MANY_SYM, modelName];
|
||||
static many(targetModel, { onDelete } = {}) {
|
||||
return [MANY_SYM, { targetModel, onDelete }];
|
||||
}
|
||||
/**
|
||||
* @param {Object} data
|
||||
@@ -449,6 +459,9 @@ export class Record {
|
||||
static preinsert(data) {
|
||||
return this.get(data) ?? this.new(data);
|
||||
}
|
||||
static isCommand(data) {
|
||||
return ["ADD", "DELETE"].includes(data?.[0]?.[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw relational values of the record, each of which contains object id(s)
|
||||
|
||||
@@ -209,6 +209,26 @@ export const storeService = {
|
||||
const r1 = receiver;
|
||||
/** @type {RecordList<Record>} */
|
||||
const l1 = r1.__rels__.get(name);
|
||||
if (Record.isCommand(val)) {
|
||||
for (const [cmd, cmdData] of val) {
|
||||
if (Array.isArray(cmdData)) {
|
||||
for (const item of cmdData) {
|
||||
if (cmd === "ADD") {
|
||||
l1.add(item);
|
||||
} else {
|
||||
l1.delete(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cmd === "ADD") {
|
||||
l1.add(cmdData);
|
||||
} else {
|
||||
l1.delete(cmdData);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/** @type {Record[]|Set<Record>|RecordList<Record>} */
|
||||
const collection = val;
|
||||
const oldRecords = l1.slice();
|
||||
@@ -233,6 +253,10 @@ export const storeService = {
|
||||
if (r2) {
|
||||
r2.__invs__.delete(r1.localId, name);
|
||||
}
|
||||
if (Record.isCommand(val)) {
|
||||
const [cmd, cmdData] = val.at(-1);
|
||||
val = cmd === "ADD" ? cmdData : null;
|
||||
}
|
||||
if ([null, false, undefined].includes(val)) {
|
||||
delete receiver[name];
|
||||
return true;
|
||||
@@ -260,7 +284,7 @@ export const storeService = {
|
||||
if (![Record.one()[0], Record.many()[0]].includes(val?.[0])) {
|
||||
continue;
|
||||
}
|
||||
Model.__rels__.set(name, { targetModel: val[1] });
|
||||
Model.__rels__.set(name, val[1]);
|
||||
}
|
||||
}
|
||||
// Make true store (as a model)
|
||||
|
||||
@@ -131,16 +131,12 @@ export class Thread extends Record {
|
||||
this.customName = serverData.channel.custom_channel_name;
|
||||
}
|
||||
if (serverData.channel?.channelMembers) {
|
||||
for (const [command, membersData] of serverData.channel.channelMembers) {
|
||||
const members = Array.isArray(membersData) ? membersData : [membersData];
|
||||
for (const memberData of members) {
|
||||
const member = this._store.ChannelMember.insert([command, memberData]);
|
||||
if (this.type !== "chat") {
|
||||
continue;
|
||||
}
|
||||
this.channelMembers = serverData.channel.channelMembers;
|
||||
if (this.type === "chat") {
|
||||
for (const member of this.channelMembers) {
|
||||
if (
|
||||
member.persona.notEq(this._store.user) ||
|
||||
(serverData.channel.channelMembers[0][1].length === 1 &&
|
||||
(this.channelMembers.length === 1 &&
|
||||
member.persona?.eq(this._store.user))
|
||||
) {
|
||||
this.chatPartner = member.persona;
|
||||
@@ -149,26 +145,7 @@ export class Thread extends Record {
|
||||
}
|
||||
}
|
||||
if ("invitedMembers" in serverData) {
|
||||
if (!serverData.invitedMembers) {
|
||||
this.invitedMembers = [];
|
||||
return;
|
||||
}
|
||||
const command = serverData.invitedMembers[0][0];
|
||||
const members = serverData.invitedMembers[0][1];
|
||||
switch (command) {
|
||||
case "ADD":
|
||||
if (members) {
|
||||
for (const member of members) {
|
||||
this.invitedMembers.add(member);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "DELETE":
|
||||
for (const member of members) {
|
||||
this.invitedMembers.delete(member);
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.invitedMembers = serverData.invitedMembers;
|
||||
}
|
||||
if ("seen_partners_info" in serverData) {
|
||||
this.seenInfos = serverData.seen_partners_info.map(
|
||||
@@ -206,8 +183,13 @@ export class Thread extends Record {
|
||||
activeRtcSession = Record.one("RtcSession");
|
||||
/** @type {object|undefined} */
|
||||
channel;
|
||||
channelMembers = Record.many("ChannelMember");
|
||||
rtcSessions = Record.many("RtcSession");
|
||||
channelMembers = Record.many("ChannelMember", { onDelete: (r) => r.delete() });
|
||||
rtcSessions = Record.many("RtcSession", {
|
||||
/** @this {import("models").Thread} */
|
||||
onDelete(r) {
|
||||
this._store.env.services["discuss.rtc"].deleteSession(r.id);
|
||||
},
|
||||
});
|
||||
rtcInvitingSession = Record.one("RtcSession");
|
||||
invitedMembers = Record.many("ChannelMember");
|
||||
chatPartner = Record.one("Persona");
|
||||
|
||||
@@ -73,7 +73,7 @@ export class ThreadService {
|
||||
thread.memberCount = results["memberCount"];
|
||||
for (const channelMember of channelMembers) {
|
||||
if (channelMember.persona || channelMember.partner) {
|
||||
this.store.ChannelMember.insert({ ...channelMember, thread });
|
||||
thread.channelMembers.add({ ...channelMember, thread });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1522,24 +1522,13 @@ export class Rtc {
|
||||
}
|
||||
}
|
||||
|
||||
updateRtcSessions(channelId, sessionsData, command) {
|
||||
updateRtcSessions(channelId, sessionsData) {
|
||||
const channel = this.store.Thread.get({ model: "discuss.channel", id: channelId });
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
const oldCount = Object.keys(channel.rtcSessions).length;
|
||||
switch (command) {
|
||||
case "DELETE":
|
||||
for (const sessionData of sessionsData) {
|
||||
this.deleteSession(sessionData.id);
|
||||
}
|
||||
break;
|
||||
case "ADD":
|
||||
for (const sessionData of sessionsData) {
|
||||
channel.rtcSessions.add(sessionData);
|
||||
}
|
||||
break;
|
||||
}
|
||||
channel.rtcSessions = sessionsData;
|
||||
if (channel.rtcSessions.length > oldCount) {
|
||||
this.soundEffectsService.play("channel-join");
|
||||
} else if (channel.rtcSessions.length < oldCount) {
|
||||
@@ -1582,16 +1571,11 @@ export const rtcService = {
|
||||
services["bus_service"].subscribe(
|
||||
"discuss.channel/rtc_sessions_update",
|
||||
({ id, rtcSessions }) => {
|
||||
const sessionsData = rtcSessions[0][1];
|
||||
const command = rtcSessions[0][0];
|
||||
rtc.updateRtcSessions(id, sessionsData, command);
|
||||
rtc.updateRtcSessions(id, rtcSessions);
|
||||
}
|
||||
);
|
||||
services["bus_service"].subscribe("discuss.channel/joined", ({ channel }) => {
|
||||
const rtcSessions = channel.rtcSessions;
|
||||
const sessionsData = rtcSessions[0][1];
|
||||
const command = rtcSessions[0][0];
|
||||
rtc.updateRtcSessions(channel.id, sessionsData, command);
|
||||
rtc.updateRtcSessions(channel.id, channel.rtcSessions);
|
||||
});
|
||||
services["bus_service"].subscribe("mail.record/insert", (payload) => {
|
||||
if (payload.RtcSession) {
|
||||
|
||||
@@ -25,21 +25,7 @@ patch(Thread.prototype, {
|
||||
}
|
||||
}
|
||||
if (rtcContinue && "rtcSessions" in data) {
|
||||
for (const command of data.rtcSessions) {
|
||||
const sessionsData = command[1];
|
||||
switch (command[0]) {
|
||||
case "DELETE":
|
||||
for (const rtcSessionData of sessionsData) {
|
||||
this.Model.env.services["discuss.rtc"].deleteSession(rtcSessionData.id);
|
||||
}
|
||||
break;
|
||||
case "ADD":
|
||||
for (const rtcSessionData of sessionsData) {
|
||||
this.rtcSessions.add(rtcSessionData);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.rtcSessions = data.rtcSessions;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user