diff --git a/addons/website_livechat/static/src/web/discuss_app_category_model_patch.js b/addons/im_livechat/static/src/core/web/discuss_app_category_model_patch.js similarity index 68% rename from addons/website_livechat/static/src/web/discuss_app_category_model_patch.js rename to addons/im_livechat/static/src/core/web/discuss_app_category_model_patch.js index a781ff68abb..629755f9207 100644 --- a/addons/website_livechat/static/src/web/discuss_app_category_model_patch.js +++ b/addons/im_livechat/static/src/core/web/discuss_app_category_model_patch.js @@ -2,6 +2,7 @@ import { patch } from "@web/core/utils/patch"; import { DiscussAppCategory } from "@mail/core/common/discuss_app_category_model"; +import { compareDatetime } from "@mail/utils/common/misc"; patch(DiscussAppCategory.prototype, { /** @@ -10,7 +11,9 @@ patch(DiscussAppCategory.prototype, { */ sortThreads(t1, t2) { if (this.id === "livechat") { - return t2.lastInterestDateTime?.ts - t1.lastInterestDateTime?.ts; + return ( + compareDatetime(t2.lastInterestDateTime, t1.lastInterestDateTime) || t2.id - t1.id + ); } return super.sortThreads(t1, t2); }, diff --git a/addons/im_livechat/static/src/core/web/thread_service_patch.js b/addons/im_livechat/static/src/core/web/thread_service_patch.js index aa59cff9944..008ac56a499 100644 --- a/addons/im_livechat/static/src/core/web/thread_service_patch.js +++ b/addons/im_livechat/static/src/core/web/thread_service_patch.js @@ -1,6 +1,7 @@ /* @odoo-module */ import { ThreadService } from "@mail/core/common/thread_service"; +import { compareDatetime } from "@mail/utils/common/misc"; import { patch } from "@web/core/utils/patch"; @@ -33,7 +34,8 @@ patch(ThreadService.prototype, { super.sortChannels(); // Live chats are sorted by most recent interest date time in the sidebar. this.store.discuss.livechat.threads.sort( - (t1, t2) => t2.lastInterestDateTime?.ts - t1.lastInterestDateTime?.ts + (t1, t2) => + compareDatetime(t2.lastInterestDateTime, t1.lastInterestDateTime) || t2.id - t1.id ); }, @@ -43,7 +45,11 @@ patch(ThreadService.prototype, { goToOldestUnreadLivechatThread() { const oldestUnreadThread = this.store.discuss.livechat.threads .filter((thread) => thread.isUnread) - .sort((t1, t2) => t1.lastInterestDateTime?.ts - t2.lastInterestDateTime?.ts)[0]; + .sort( + (t1, t2) => + compareDatetime(t1.lastInterestDateTime, t2.lastInterestDateTime) || + t1.id - t2.id + )[0]; if (!oldestUnreadThread) { return false; } diff --git a/addons/mail/static/src/core/common/discuss_app_category_model.js b/addons/mail/static/src/core/common/discuss_app_category_model.js index 4f2730e4004..3ca0aa4b519 100644 --- a/addons/mail/static/src/core/common/discuss_app_category_model.js +++ b/addons/mail/static/src/core/common/discuss_app_category_model.js @@ -1,5 +1,6 @@ /* @odoo-module */ +import { compareDatetime } from "@mail/utils/common/misc"; import { Record } from "./record"; export class DiscussAppCategory extends Record { @@ -22,7 +23,9 @@ export class DiscussAppCategory extends Record { return String.prototype.localeCompare.call(t1.name, t2.name); } if (this.id === "chats") { - return t2.lastInterestDateTime.ts - t1.lastInterestDateTime.ts; + return ( + compareDatetime(t2.lastInterestDateTime, t1.lastInterestDateTime) || t2.id - t1.id + ); } } diff --git a/addons/mail/static/src/core/common/persona_service.js b/addons/mail/static/src/core/common/persona_service.js index 58b0abdb955..b5ff84e0024 100644 --- a/addons/mail/static/src/core/common/persona_service.js +++ b/addons/mail/static/src/core/common/persona_service.js @@ -3,6 +3,7 @@ import { registry } from "@web/core/registry"; import { useSequential } from "@mail/utils/common/hooks"; import { markRaw } from "@odoo/owl"; +import { compareDatetime } from "@mail/utils/common/misc"; export const DEFAULT_AVATAR = "/mail/static/src/img/smiley/avatar.jpg"; @@ -83,19 +84,10 @@ export class PersonaService { getRecentChatPartnerIds() { return Object.values(this.store.Thread.records) .filter((thread) => thread.type === "chat") - .sort((a, b) => { - const [a_dt , b_dt] = [a.lastInterestDateTime, b.lastInterestDateTime] - if (!a_dt && !b_dt) { - return 0; - } - if (a_dt && !b_dt) { - return -1; - } - if (!a_dt && b_dt) { - return 1; - } - return b_dt.ts - a_dt.ts; - }) + .sort( + (a, b) => + compareDatetime(b.lastInterestDateTime, a.lastInterestDateTime) || b.id - a.id + ) .map((thread) => thread.chatPartner?.id); } } diff --git a/addons/mail/static/src/core/common/thread_service.js b/addons/mail/static/src/core/common/thread_service.js index bc214be2b3e..56ee60be5e6 100644 --- a/addons/mail/static/src/core/common/thread_service.js +++ b/addons/mail/static/src/core/common/thread_service.js @@ -11,6 +11,7 @@ import { registry } from "@web/core/registry"; import { memoize } from "@web/core/utils/functions"; import { url } from "@web/core/utils/urls"; import { escape } from "@web/core/utils/strings"; +import { compareDatetime } from "@mail/utils/common/misc"; const FETCH_LIMIT = 30; @@ -429,7 +430,8 @@ export class ThreadService { String.prototype.localeCompare.call(t1.name, t2.name) ); this.store.discuss.chats.threads.sort( - (t1, t2) => t2.lastInterestDateTime.ts - t1.lastInterestDateTime.ts + (t1, t2) => + compareDatetime(t2.lastInterestDateTime, t1.lastInterestDateTime) || t2.id - t1.id ); } diff --git a/addons/mail/static/src/core/web/thread_service_patch.js b/addons/mail/static/src/core/web/thread_service_patch.js index ed96a39ab0d..67a4b26a8d9 100644 --- a/addons/mail/static/src/core/web/thread_service_patch.js +++ b/addons/mail/static/src/core/web/thread_service_patch.js @@ -6,6 +6,7 @@ import { parseEmail } from "@mail/js/utils"; import { _t } from "@web/core/l10n/translation"; import { patch } from "@web/core/utils/patch"; import { Record } from "@mail/core/common/record"; +import { compareDatetime } from "@mail/utils/common/misc"; let nextId = 1; @@ -241,18 +242,10 @@ patch(ThreadService.prototype, { getRecentChannels() { return Object.values(this.store.Thread.records) .filter((thread) => thread.model === "discuss.channel") - .sort((a, b) => { - if (a.lastInterestDateTime?.ts !== b.lastInterestDateTime?.ts) { - if (!b.lastInterestDateTime) { - return -1; - } - if (!a.lastInterestDateTime) { - return 1; - } - return b.lastInterestDateTime.ts - a.lastInterestDateTime.ts; - } - return a.id - b.id; - }); + .sort( + (a, b) => + compareDatetime(b.lastInterestDateTime, a.lastInterestDateTime) || b.id - a.id + ); }, getNeedactionChannels() { return this.getRecentChannels().filter((channel) => this.getCounter(channel) > 0); diff --git a/addons/mail/static/src/utils/common/misc.js b/addons/mail/static/src/utils/common/misc.js index 788479dc7f2..5bc52ddf52a 100644 --- a/addons/mail/static/src/utils/common/misc.js +++ b/addons/mail/static/src/utils/common/misc.js @@ -69,3 +69,24 @@ export function onChange(target, key, callback) { export function closeStream(stream) { stream?.getTracks?.().forEach((track) => track.stop()); } + +/** + * Compare two Luxon datetime. + * + * @param {import("@web/core/l10n/dates").NullableDateTime} date1 + * @param {import("@web/core/l10n/dates").NullableDateTime} date2 + * @returns {number} Negative if date1 is less than date2, positive if date1 is + * greater than date2, and 0 if they are equal. + */ +export function compareDatetime(date1, date2) { + if (date1?.ts === date2?.ts) { + return 0; + } + if (!date1) { + return -1; + } + if (!date2) { + return 1; + } + return date1.ts - date2.ts; +} diff --git a/addons/mail/static/tests/discuss/core/suggestion_tests.js b/addons/mail/static/tests/discuss/core/suggestion_tests.js index 928ca48ef0a..8bf00714888 100644 --- a/addons/mail/static/tests/discuss/core/suggestion_tests.js +++ b/addons/mail/static/tests/discuss/core/suggestion_tests.js @@ -78,12 +78,16 @@ QUnit.test("Sort partner suggestions by recent chats", async () => { { partner_id: partner_3 }, ]); pyEnv["discuss.channel"].create([ - { name: "General", channel_type: "channel", channel_member_ids: [ - Command.create({ partner_id: pyEnv.currentPartnerId }), - Command.create({ partner_id: partner_1 }), - Command.create({ partner_id: partner_2 }), - Command.create({ partner_id: partner_3 }), - ]}, + { + name: "General", + channel_type: "channel", + channel_member_ids: [ + Command.create({ partner_id: pyEnv.currentPartnerId }), + Command.create({ partner_id: partner_1 }), + Command.create({ partner_id: partner_2 }), + Command.create({ partner_id: partner_3 }), + ], + }, { channel_member_ids: [ Command.create({ @@ -126,8 +130,8 @@ QUnit.test("Sort partner suggestions by recent chats", async () => { await insertText(".o-mail-Composer-input", "User"); await contains(".o-mail-Composer-suggestion strong", { count: 3 }); await contains(":nth-child(1 of .o-mail-Composer-suggestion) strong", { text: "User 2" }); - await contains(":nth-child(2 of .o-mail-Composer-suggestion) strong", { text: "User 1" }); - await contains(":nth-child(3 of .o-mail-Composer-suggestion) strong", { text: "User 3" }); + await contains(":nth-child(2 of .o-mail-Composer-suggestion) strong", { text: "User 3" }); + await contains(":nth-child(3 of .o-mail-Composer-suggestion) strong", { text: "User 1" }); }); QUnit.test("mention suggestion are shown after deleting a character", async () => { diff --git a/addons/mail/static/tests/discuss/core/web/command_palette_tests.js b/addons/mail/static/tests/discuss/core/web/command_palette_tests.js index 1134db922e9..accdfc6d533 100644 --- a/addons/mail/static/tests/discuss/core/web/command_palette_tests.js +++ b/addons/mail/static/tests/discuss/core/web/command_palette_tests.js @@ -59,10 +59,10 @@ QUnit.test("open the chatWindow of a channel from the command palette", async () await insertText(".o_command_palette_search input", "#"); advanceTime(commandSetupRegistry.get("#").debounceDelay); await contains(".o_command", { count: 2 }); - await contains(".o_command", { text: "general", before: [".o_command", { text: "project" }] }); + await contains(".o_command", { text: "project", before: [".o_command", { text: "general" }] }); await contains(".o_command.focused"); - await click(".o_command.focused", { text: "general" }); - await contains(".o-mail-ChatWindow", { text: "general" }); + await click(".o_command.focused", { text: "project" }); + await contains(".o-mail-ChatWindow", { text: "project" }); }); QUnit.test("Channel mentions in the command palette of Discuss app with @", async () => {