diff --git a/addons/mail/static/src/core/common/channel_member_model.js b/addons/mail/static/src/core/common/channel_member_model.js index bd938e68dcd..001124b8d52 100644 --- a/addons/mail/static/src/core/common/channel_member_model.js +++ b/addons/mail/static/src/core/common/channel_member_model.js @@ -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} */ diff --git a/addons/mail/static/src/core/common/message_model.js b/addons/mail/static/src/core/common/message_model.js index f4c57434002..dd8d39ca455 100644 --- a/addons/mail/static/src/core/common/message_model.js +++ b/addons/mail/static/src/core/common/message_model.js @@ -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(); diff --git a/addons/mail/static/src/core/common/notification_group_model.js b/addons/mail/static/src/core/common/notification_group_model.js index d0a55f1fa2a..3382d121af7 100644 --- a/addons/mail/static/src/core/common/notification_group_model.js +++ b/addons/mail/static/src/core/common/notification_group_model.js @@ -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) { diff --git a/addons/mail/static/src/core/common/record.js b/addons/mail/static/src/core/common/record.js index 706c17c2ae6..03aedb9ec1f 100644 --- a/addons/mail/static/src/core/common/record.js +++ b/addons/mail/static/src/core/common/record.js @@ -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) diff --git a/addons/mail/static/src/core/common/store_service.js b/addons/mail/static/src/core/common/store_service.js index 674715bdd45..159527f5f38 100644 --- a/addons/mail/static/src/core/common/store_service.js +++ b/addons/mail/static/src/core/common/store_service.js @@ -209,6 +209,26 @@ export const storeService = { const r1 = receiver; /** @type {RecordList} */ 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|RecordList} */ 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) diff --git a/addons/mail/static/src/core/common/thread_model.js b/addons/mail/static/src/core/common/thread_model.js index b0ce74a9999..089f455d324 100644 --- a/addons/mail/static/src/core/common/thread_model.js +++ b/addons/mail/static/src/core/common/thread_model.js @@ -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"); diff --git a/addons/mail/static/src/core/common/thread_service.js b/addons/mail/static/src/core/common/thread_service.js index 8384485a22b..fb51bd3fc45 100644 --- a/addons/mail/static/src/core/common/thread_service.js +++ b/addons/mail/static/src/core/common/thread_service.js @@ -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 }); } } } diff --git a/addons/mail/static/src/discuss/call/common/rtc_service.js b/addons/mail/static/src/discuss/call/common/rtc_service.js index 4c3cb3c9709..6b9ce6ffa06 100644 --- a/addons/mail/static/src/discuss/call/common/rtc_service.js +++ b/addons/mail/static/src/discuss/call/common/rtc_service.js @@ -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) { diff --git a/addons/mail/static/src/discuss/call/common/thread_model_patch.js b/addons/mail/static/src/discuss/call/common/thread_model_patch.js index 74e7e867f2a..bd154ce247e 100644 --- a/addons/mail/static/src/discuss/call/common/thread_model_patch.js +++ b/addons/mail/static/src/discuss/call/common/thread_model_patch.js @@ -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; } }, });