[FIX] {im, website}_livechat: fix discuss sidebar test

Since [1], the test that ensures live chat threads are sorted
according to their last interrest date is broken. Indeed, the sort was
moved from `im_livechat` to `website_livechat`. This is incorrect.

Also, most of the compare functions used to sort by datetime were
incorrect: those datetimes can be undefined in which case the result
of the compare function would be `NaN`.

This PR moves back the sort function in the correct module and
introduce a standard way to compare by datetime which handles
undefined datetimes correctly.

[1]: https://github.com/odoo/odoo/pull/143382

fixes runbot-47784

closes odoo/odoo#146805

Related: odoo/enterprise#53065
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
tsm-odoo
2023-12-21 14:52:38 +00:00
parent 5506ca7479
commit 6f25d4b06f
9 changed files with 65 additions and 41 deletions
@@ -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);
},
@@ -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;
}
@@ -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
);
}
}
@@ -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);
}
}
@@ -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
);
}
@@ -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);
@@ -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;
}
@@ -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 () => {
@@ -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 () => {