Sort in tournament LFG according to private note sentiment

This commit is contained in:
Kalle
2026-09-23 20:44:47 +03:00
parent b2efb696f1
commit 7c2b6e7ae8
5 changed files with 66 additions and 15 deletions

View File

@@ -19,6 +19,7 @@ import { useUser } from "~/features/auth/core/user";
import { useTopicRevalidation } from "~/features/chat/chat-hooks";
import { useMarkSqLikesSeen } from "~/features/global-status/global-status-likes-seen";
import type { UserCardData } from "~/features/user-card/user-card-types";
import { privateNoteSentimentScore } from "~/features/user-card/user-card-utils";
import { useDateTimeFormat } from "~/hooks/intl/useDateTimeFormat";
import { useHydrated } from "~/hooks/useHydrated";
import { useMainContentWidth } from "~/hooks/useMainContentWidth";
@@ -478,18 +479,8 @@ function sortGroups<T extends { id: number; members?: { id: number }[] }>(
suggestedGroupIds: Set<number>;
},
): T[] {
const sentimentScore = (group: T) => {
if (!group.members) return 0;
let score = 0;
for (const member of group.members) {
const sentiment = userCards.get(member.id)?.privateNote?.sentiment;
if (sentiment === "NEGATIVE") return -1;
if (sentiment === "POSITIVE") score = 1;
}
return score;
};
const sentimentScore = (group: T) =>
group.members ? privateNoteSentimentScore(group.members, userCards) : 0;
return groups.toSorted((a, b) => {
const aIsSuggested = suggestedGroupIds.has(a.id);

View File

@@ -26,6 +26,7 @@ import {
UserCard,
useUserCardData,
} from "~/features/user-card/components/UserCard";
import { privateNoteSentimentScore } from "~/features/user-card/user-card-utils";
import { SendouForm } from "~/form/SendouForm";
import { useHydrated } from "~/hooks/useHydrated";
import { useMainContentWidth } from "~/hooks/useMainContentWidth";
@@ -83,9 +84,13 @@ function GroupsView({
data.ownGroup?.members.length ?? data.ownTeam?.members.length ?? 0;
const availableSlots = tournament.maxMembersPerTeam - ownMemberCount;
const compatibleGroups = data.groups.filter(
(group) => group.members.length <= availableSlots,
);
const compatibleGroups = data.groups
.filter((group) => group.members.length <= availableSlots)
.toSorted(
(a, b) =>
privateNoteSentimentScore(b.members, data.userCards) -
privateNoteSentimentScore(a.members, data.userCards),
);
const neutralGroups = compatibleGroups.filter(
(group) =>

View File

@@ -0,0 +1,31 @@
import { describe, expect, test } from "vitest";
import type { UserCardData } from "./user-card-types";
import { privateNoteSentimentScore } from "./user-card-utils";
const cardWithSentiment = (sentiment: "POSITIVE" | "NEUTRAL" | "NEGATIVE") =>
({
privateNote: { text: "", sentiment, updatedAt: 0 },
}) as unknown as UserCardData;
const userCards = new Map<number, UserCardData>([
[1, cardWithSentiment("POSITIVE")],
[2, cardWithSentiment("NEGATIVE")],
[3, cardWithSentiment("NEUTRAL")],
]);
describe("privateNoteSentimentScore", () => {
test.each([
{ why: "no notes", memberIds: [4, 5], expected: 0 },
{ why: "neutral note", memberIds: [3], expected: 0 },
{ why: "positive note", memberIds: [1, 4], expected: 1 },
{ why: "negative note", memberIds: [2, 4], expected: -1 },
{ why: "negative outweighs positive", memberIds: [1, 2], expected: -1 },
])("$why", ({ memberIds, expected }) => {
expect(
privateNoteSentimentScore(
memberIds.map((id) => ({ id })),
userCards,
),
).toBe(expected);
});
});

View File

@@ -1,4 +1,5 @@
import { USER_CARD } from "./user-card-constants";
import type { UserCardData } from "./user-card-types";
/**
* The claim must exceed the verified XP by no more than {@link USER_CARD.MAX_UNVERIFIED_XP_ABOVE_VERIFIED}.
@@ -17,3 +18,21 @@ export function isValidUnverifiedXp({
unverified <= verified + USER_CARD.MAX_UNVERIFIED_XP_ABOVE_VERIFIED
);
}
/**
* Scores a group by the viewer's private notes on its members: -1 if any member has a negative note,
* 1 if any has a positive one (and none negative), otherwise 0. Sort descending to float liked groups up.
*/
export function privateNoteSentimentScore(
members: { id: number }[],
userCards: Map<number, UserCardData>,
): number {
let score = 0;
for (const member of members) {
const sentiment = userCards.get(member.id)?.privateNote?.sentiment;
if (sentiment === "NEGATIVE") return -1;
if (sentiment === "POSITIVE") score = 1;
}
return score;
}

View File

@@ -0,0 +1,5 @@
---
navItem: medal
type: feature
---
Tournament LFG shows groups with players you have a positive private note on first and negative ones last, same as SendouQ