[IMP] mail: user searching by most recent chat
task-2622887 closes odoo/odoo#122486 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -142,7 +142,52 @@ export class SuggestionService {
|
||||
}
|
||||
}
|
||||
}
|
||||
const sortFunc = (p1, p2) => {
|
||||
return {
|
||||
type: "Partner",
|
||||
mainSuggestions: sort
|
||||
? this.sortPartnerSuggestions(mainSuggestionList, cleanedSearchTerm, thread)
|
||||
: mainSuggestionList,
|
||||
extraSuggestions: sort
|
||||
? this.sortPartnerSuggestions(extraSuggestionList, cleanedSearchTerm, thread)
|
||||
: extraSuggestionList,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {[import("@mail/core/persona_model").Persona]} [partners]
|
||||
* @param {String} [searchTerm]
|
||||
* @param {import("@mail/core/thread_model").Thread} thread
|
||||
* @returns {[import("@mail/core/persona_model").Persona]}
|
||||
*/
|
||||
sortPartnerSuggestions(partners, searchTerm = "", thread = undefined) {
|
||||
const cleanedSearchTerm = cleanTerm(searchTerm);
|
||||
/**
|
||||
* Ordering:
|
||||
* - recent chat partners
|
||||
* - internal users
|
||||
* - channel members
|
||||
* - thread followers
|
||||
* - longgest match of name, alphabetically if having same length of match
|
||||
* - longgest match of email, alphabetically if having same length of match
|
||||
* - id
|
||||
*/
|
||||
return partners.sort((p1, p2) => {
|
||||
const recentChatPartnerIds = this.personaService.getRecentChatPartnerIds();
|
||||
const recentChatIndex_p1 = recentChatPartnerIds.findIndex(
|
||||
(partnerId) => partnerId === p1.id
|
||||
);
|
||||
const recentChatIndex_p2 = recentChatPartnerIds.findIndex(
|
||||
(partnerId) => partnerId === p2.id
|
||||
);
|
||||
if (recentChatIndex_p1 !== -1 && recentChatIndex_p2 === -1) {
|
||||
return -1;
|
||||
} else if (recentChatIndex_p1 === -1 && recentChatIndex_p2 !== -1) {
|
||||
return 1;
|
||||
} else if (recentChatIndex_p1 < recentChatIndex_p2) {
|
||||
return -1;
|
||||
} else if (recentChatIndex_p1 > recentChatIndex_p2) {
|
||||
return 1;
|
||||
}
|
||||
const isAInternalUser = p1.user?.isInternalUser;
|
||||
const isBInternalUser = p2.user?.isInternalUser;
|
||||
if (isAInternalUser && !isBInternalUser) {
|
||||
@@ -151,7 +196,7 @@ export class SuggestionService {
|
||||
if (!isAInternalUser && isBInternalUser) {
|
||||
return 1;
|
||||
}
|
||||
if (thread.model === "discuss.channel") {
|
||||
if (thread?.model === "discuss.channel") {
|
||||
const isMember1 = thread.channelMembers.some((member) => member.persona === p1);
|
||||
const isMember2 = thread.channelMembers.some((member) => member.persona === p2);
|
||||
if (isMember1 && !isMember2) {
|
||||
@@ -212,12 +257,7 @@ export class SuggestionService {
|
||||
return 1;
|
||||
}
|
||||
return p1.id - p2.id;
|
||||
};
|
||||
return {
|
||||
type: "Partner",
|
||||
mainSuggestions: sort ? mainSuggestionList.sort(sortFunc) : mainSuggestionList,
|
||||
extraSuggestions: sort ? extraSuggestionList.sort(sortFunc) : extraSuggestionList,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
searchChannelSuggestions(cleanedSearchTerm, thread, sort) {
|
||||
|
||||
@@ -55,6 +55,30 @@ export class PersonaService {
|
||||
this.store.registeredImStatusPartners?.push(persona.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of known partner ids with a direct chat, ordered
|
||||
* by most recent interest (1st item being the most recent)
|
||||
*
|
||||
* @returns {[integer]}
|
||||
*/
|
||||
getRecentChatPartnerIds() {
|
||||
return Object.values(this.store.threads)
|
||||
.filter((thread) => thread.type === "chat")
|
||||
.sort((a, b) => {
|
||||
if (!a.lastInterestDateTime && !b.lastInterestDateTime) {
|
||||
return 0;
|
||||
}
|
||||
if (a.lastInterestDateTime && !b.lastInterestDateTime) {
|
||||
return -1;
|
||||
}
|
||||
if (!a.lastInterestDateTime && b.lastInterestDateTime) {
|
||||
return 1;
|
||||
}
|
||||
return b.lastInterestDateTime.ts - a.lastInterestDateTime.ts;
|
||||
})
|
||||
.map((thread) => thread.chatPartnerId);
|
||||
}
|
||||
}
|
||||
|
||||
export const personaService = {
|
||||
|
||||
@@ -19,6 +19,8 @@ export class ChannelInvitation extends Component {
|
||||
this.notification = useService("notification");
|
||||
this.threadService = useState(useService("mail.thread"));
|
||||
this.personaService = useService("mail.persona");
|
||||
/** @type {import("@mail/composer/suggestion_service").SuggestionService} */
|
||||
this.suggestionService = useService("mail.suggestion");
|
||||
this.ui = useService("ui");
|
||||
this.inputRef = useRef("input");
|
||||
this.searchStr = "";
|
||||
@@ -56,7 +58,11 @@ export class ChannelInvitation extends Component {
|
||||
});
|
||||
selectablePartners.push(newPartner);
|
||||
}
|
||||
this.state.selectablePartners = selectablePartners;
|
||||
this.state.selectablePartners = this.suggestionService.sortPartnerSuggestions(
|
||||
selectablePartners,
|
||||
this.searchStr,
|
||||
this.props.thread
|
||||
);
|
||||
this.state.searchResultCount = results["count"];
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ export class ChannelSelector extends Component {
|
||||
this.store = useStore();
|
||||
this.threadService = useState(useService("mail.thread"));
|
||||
this.personaService = useService("mail.persona");
|
||||
/** @type {import("@mail/composer/suggestion_service").SuggestionService} */
|
||||
this.suggestionService = useService("mail.suggestion");
|
||||
this.orm = useService("orm");
|
||||
this.state = useState({
|
||||
value: "",
|
||||
@@ -65,14 +67,16 @@ export class ChannelSelector extends Component {
|
||||
10,
|
||||
this.state.selectedPartners,
|
||||
]);
|
||||
const suggestions = results.map((data) => {
|
||||
this.personaService.insert({ ...data, type: "partner" });
|
||||
return {
|
||||
classList: "o-mail-ChannelSelector-suggestion",
|
||||
label: data.name,
|
||||
partner: data,
|
||||
};
|
||||
});
|
||||
const suggestions = this.suggestionService
|
||||
.sortPartnerSuggestions(results, cleanedTerm)
|
||||
.map((data) => {
|
||||
this.personaService.insert({ ...data, type: "partner" });
|
||||
return {
|
||||
classList: "o-mail-ChannelSelector-suggestion",
|
||||
label: data.name,
|
||||
partner: data,
|
||||
};
|
||||
});
|
||||
if (this.store.self.name.includes(cleanedTerm)) {
|
||||
suggestions.push({
|
||||
classList: "o-mail-ChannelSelector-suggestion",
|
||||
|
||||
@@ -32,17 +32,21 @@ commandProviderRegistry.add("mail.partner", {
|
||||
/** @type {import("@mail/core/messaging_service").Messaging} */
|
||||
const messaging = env.services["mail.messaging"];
|
||||
const threadService = env.services["mail.thread"];
|
||||
/** @type {import("@mail/composer/suggestion_service").SuggestionService} */
|
||||
const suggestionService = env.services["mail.suggestion"];
|
||||
const results = await messaging.searchPartners(options.searchValue);
|
||||
return results.map(function (partner) {
|
||||
return {
|
||||
Component: DialogCommand,
|
||||
action() {
|
||||
threadService.openChat({ partnerId: partner.id });
|
||||
},
|
||||
name: partner.name,
|
||||
props: { email: partner.email },
|
||||
};
|
||||
});
|
||||
return suggestionService
|
||||
.sortPartnerSuggestions(results, options.searchValue)
|
||||
.map(function (partner) {
|
||||
return {
|
||||
Component: DialogCommand,
|
||||
action() {
|
||||
threadService.openChat({ partnerId: partner.id });
|
||||
},
|
||||
name: partner.name,
|
||||
props: { email: partner.email },
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { Composer } from "@mail/composer/composer";
|
||||
import { click, insertText, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
import { Command } from "@mail/../tests/helpers/command";
|
||||
import { patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
|
||||
QUnit.module("suggestion", {
|
||||
@@ -62,3 +63,63 @@ QUnit.test(
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("Sort partner suggestions by recent chats", async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const [partner_1, partner_2, partner_3] = pyEnv["res.partner"].create([
|
||||
{ name: "User 1" },
|
||||
{ name: "User 2" },
|
||||
{ name: "User 3" },
|
||||
]);
|
||||
pyEnv["res.users"].create([
|
||||
{ partner_id: partner_1 },
|
||||
{ partner_id: partner_2 },
|
||||
{ partner_id: partner_3 },
|
||||
]);
|
||||
pyEnv["discuss.channel"].create([
|
||||
{ name: "General", channel_type: "channel" },
|
||||
{
|
||||
channel_member_ids: [
|
||||
Command.create({
|
||||
last_interest_dt: "2023-01-01 00:00:00",
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}),
|
||||
Command.create({ partner_id: partner_1 }),
|
||||
],
|
||||
channel_type: "chat",
|
||||
},
|
||||
{
|
||||
channel_member_ids: [
|
||||
Command.create({
|
||||
last_interest_dt: "2023-01-01 00:00:00",
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}),
|
||||
Command.create({ partner_id: partner_2 }),
|
||||
],
|
||||
channel_type: "chat",
|
||||
},
|
||||
{
|
||||
channel_member_ids: [
|
||||
Command.create({
|
||||
last_interest_dt: "2023-01-01 00:00:00",
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}),
|
||||
Command.create({ partner_id: partner_3 }),
|
||||
],
|
||||
channel_type: "chat",
|
||||
},
|
||||
]);
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
await click(".o-mail-DiscussCategoryItem:contains('User 2')");
|
||||
await insertText(".o-mail-Composer-input", "This is a test");
|
||||
await click(".o-mail-Composer-send:not(:disabled)");
|
||||
assert.containsOnce($, ".o-mail-Message:contains('This is a test')");
|
||||
await click(".o-mail-DiscussCategoryItem:contains('General')");
|
||||
await insertText(".o-mail-Composer-input", "@");
|
||||
await insertText(".o-mail-Composer-input", "User");
|
||||
assert.containsN($, ".o-mail-Composer-suggestion", 3);
|
||||
assert.strictEqual($(".o-mail-Composer-suggestion").eq(0).text(), "User 2");
|
||||
assert.strictEqual($(".o-mail-Composer-suggestion").eq(1).text(), "User 1");
|
||||
assert.strictEqual($(".o-mail-Composer-suggestion").eq(2).text(), "User 3");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user