From 442d5845a2fba11eb9b94c5c18f4ece4ec66d5b0 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 20 Sep 2026 10:42:28 +0300 Subject: [PATCH] Show text if in trusted group someone rejoined queue --- .../components/RejoinSections.tsx | 21 ++++++- .../components/SendouQMatchActionTab.tsx | 3 + .../loaders/q.match.$id.server.test.ts | 61 +++++++++++++++++-- .../loaders/q.match.$id.server.ts | 17 +++++- .../2026-09-20-sendouq-look-again-blocked.md | 5 ++ locales/da/q.json | 1 + locales/de/q.json | 1 + locales/en/q.json | 1 + locales/es-ES/q.json | 1 + locales/es-US/q.json | 1 + locales/fr-CA/q.json | 1 + locales/fr-EU/q.json | 1 + locales/he/q.json | 1 + locales/it/q.json | 1 + locales/ja/q.json | 1 + locales/ko/q.json | 1 + locales/nl/q.json | 1 + locales/pl/q.json | 1 + locales/pt-BR/q.json | 1 + locales/ru/q.json | 1 + locales/zh/q.json | 1 + 21 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 changelog/2026-09-20-sendouq-look-again-blocked.md diff --git a/app/features/sendouq-match/components/RejoinSections.tsx b/app/features/sendouq-match/components/RejoinSections.tsx index a641d6032..1978a1564 100644 --- a/app/features/sendouq-match/components/RejoinSections.tsx +++ b/app/features/sendouq-match/components/RejoinSections.tsx @@ -58,9 +58,11 @@ export function MatchmadeRejoinSection({ export function TrustedRejoinSection({ viewerGroup, hasJoinedNewGroup, + someGroupMemberHasJoinedNewGroup, }: { viewerGroup: NonNullable; hasJoinedNewGroup: boolean; + someGroupMemberHasJoinedNewGroup: boolean; }) { const { t } = useTranslation(["q"]); const lookAgain = useActionSubmit(matchSchema); @@ -77,6 +79,15 @@ export function TrustedRejoinSection({ ); } + // the whole group has to be free for it, so offering it here could only fail + if (someGroupMemberHasJoinedNewGroup) { + return ( + + ); + } + return (
; +} + +function RejoinQueueSection({ explanation }: { explanation: string }) { + const { t } = useTranslation(["q"]); const rejoinQueue = useActionSubmit(frontPageSchema, { action: SENDOUQ_PAGE, }); return (
-

- {t("q:match.rematch.declined")} -

+

{explanation}

) : null} {isOnReporterTeam ?
: null} diff --git a/app/features/sendouq-match/loaders/q.match.$id.server.test.ts b/app/features/sendouq-match/loaders/q.match.$id.server.test.ts index c96676663..4e94ca9a0 100644 --- a/app/features/sendouq-match/loaders/q.match.$id.server.test.ts +++ b/app/features/sendouq-match/loaders/q.match.$id.server.test.ts @@ -1,7 +1,9 @@ import { beforeEach, describe, expect, test } from "vitest"; +import * as SQGroupFactory from "~/db/seed/factories/SQGroupFactory"; import * as SQMatchFactory from "~/db/seed/factories/SQMatchFactory"; import * as UserFactory from "~/db/seed/factories/UserFactory"; import { db } from "~/db/sql"; +import { refreshSendouQInstance } from "~/features/sendouq/core/SendouQ.server"; import type { SerializeFrom } from "~/utils/remix"; import { wrappedLoader } from "~/utils/Test"; import { loader } from "./q.match.$id.server"; @@ -21,11 +23,14 @@ describe("q match loader", () => { await users.create(10); }); - const createMatch = () => - SQMatchFactory.create({ - alphaUserIds: alphaUserIds(), - bravoUserIds: bravoUserIds(), - }); + const createMatch = (options: { isConcluded?: boolean } = {}) => + SQMatchFactory.create( + { + alphaUserIds: alphaUserIds(), + bravoUserIds: bravoUserIds(), + }, + options, + ); const groupChatRoomId = async (groupId: number) => ( @@ -93,4 +98,50 @@ describe("q match loader", () => { expect(data.chatRooms).toEqual([]); }); + + describe("requeueing with the same group", () => { + const queueElsewhere = async (userId: number) => { + await SQGroupFactory.create({ memberUserIds: [userId] }); + await refreshSendouQInstance(); + }; + + test("offers the requeue while every member is free of other groups", async () => { + const match = await createMatch({ isConcluded: true }); + await refreshSendouQInstance(); + + const data = await loadAs(alphaUserIds()[0], match.id); + + expect(data.hasJoinedNewGroup).toBe(false); + expect(data.someGroupMemberHasJoinedNewGroup).toBe(false); + }); + + test("points the member who queued elsewhere to their new group", async () => { + const match = await createMatch({ isConcluded: true }); + await queueElsewhere(alphaUserIds()[1]); + + const data = await loadAs(alphaUserIds()[1], match.id); + + expect(data.hasJoinedNewGroup).toBe(true); + }); + + test("blocks the requeue for the rest of the group too", async () => { + const match = await createMatch({ isConcluded: true }); + await queueElsewhere(alphaUserIds()[1]); + + const data = await loadAs(alphaUserIds()[0], match.id); + + expect(data.hasJoinedNewGroup).toBe(false); + expect(data.someGroupMemberHasJoinedNewGroup).toBe(true); + }); + + test("leaves the other group's requeue alone", async () => { + const match = await createMatch({ isConcluded: true }); + await queueElsewhere(alphaUserIds()[1]); + + const data = await loadAs(bravoUserIds()[0], match.id); + + expect(data.hasJoinedNewGroup).toBe(false); + expect(data.someGroupMemberHasJoinedNewGroup).toBe(false); + }); + }); }); diff --git a/app/features/sendouq-match/loaders/q.match.$id.server.ts b/app/features/sendouq-match/loaders/q.match.$id.server.ts index 4f740f094..cc9e42d58 100644 --- a/app/features/sendouq-match/loaders/q.match.$id.server.ts +++ b/app/features/sendouq-match/loaders/q.match.$id.server.ts @@ -46,12 +46,23 @@ export const loader = async ({ params }: LoaderFunctionArgs) => { const match = SendouQ.mapMatch(matchUnmapped, user); - const currentGroup = user ? SendouQ.findOwnGroup(user.id) : undefined; + const viewerGroup = user + ? [matchUnmapped.groupAlpha, matchUnmapped.groupBravo].find((group) => + group.members.some((member) => member.id === user.id), + ) + : undefined; + + const joinedNewGroup = (userId: number) => { + const currentGroup = SendouQ.findOwnGroup(userId); + return Boolean(currentGroup && currentGroup.matchId !== matchId); + }; return { // e.g. the group already requeued, so the viewer has nothing left to requeue with - hasJoinedNewGroup: Boolean( - currentGroup && currentGroup.matchId !== matchId, + hasJoinedNewGroup: Boolean(user && joinedNewGroup(user.id)), + // requeueing with the same group needs every member of it free of other groups + someGroupMemberHasJoinedNewGroup: Boolean( + viewerGroup?.members.some((member) => joinedNewGroup(member.id)), ), ...(await UserCardRepository.findAllByUserIds({ userIds: matchUsers, diff --git a/changelog/2026-09-20-sendouq-look-again-blocked.md b/changelog/2026-09-20-sendouq-look-again-blocked.md new file mode 100644 index 000000000..046e5e32c --- /dev/null +++ b/changelog/2026-09-20-sendouq-look-again-blocked.md @@ -0,0 +1,5 @@ +--- +navItem: sendouq +type: bug +--- +"Look again with same group" is no longer offered when someone from your group has already joined another group. Instead you are told so and can rejoin the queue right away diff --git a/locales/da/q.json b/locales/da/q.json index 481743321..88cac963a 100644 --- a/locales/da/q.json +++ b/locales/da/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/de/q.json b/locales/de/q.json index 6f046c1b6..82a29e692 100644 --- a/locales/de/q.json +++ b/locales/de/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/en/q.json b/locales/en/q.json index c1874c883..44265a46c 100644 --- a/locales/en/q.json +++ b/locales/en/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "No, I'm done", "match.rematch.vote.noConfirm": "Vote no? You can't change your vote afterwards.", "match.rematch.declined": "You are not continuing with this group", + "match.rematch.memberJoinedNewGroup": "A member of your group already joined another group", "match.rematch.fizzled": "Nobody wanted to continue", "match.rematch.rejoinQueue": "Rejoin queue", "match.rematch.backToQueue": "Back to queue", diff --git a/locales/es-ES/q.json b/locales/es-ES/q.json index 13a1b1fe7..46a5c1a7f 100644 --- a/locales/es-ES/q.json +++ b/locales/es-ES/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "No, he terminado", "match.rematch.vote.noConfirm": "¿Votar no? No podrás cambiar tu voto después.", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "Nadie quiso continuar", "match.rematch.rejoinQueue": "Volver a la cola", "match.rematch.backToQueue": "Volver a la cola", diff --git a/locales/es-US/q.json b/locales/es-US/q.json index 1a64442a5..4473de20b 100644 --- a/locales/es-US/q.json +++ b/locales/es-US/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "No, he terminado", "match.rematch.vote.noConfirm": "¿Votar no? No podrás cambiar tu voto después.", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "Nadie quiso continuar", "match.rematch.rejoinQueue": "Volver a la cola", "match.rematch.backToQueue": "Volver a la cola", diff --git a/locales/fr-CA/q.json b/locales/fr-CA/q.json index 34ec52db6..65300be73 100644 --- a/locales/fr-CA/q.json +++ b/locales/fr-CA/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/fr-EU/q.json b/locales/fr-EU/q.json index 66ce5aca3..7a7830f4d 100644 --- a/locales/fr-EU/q.json +++ b/locales/fr-EU/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/he/q.json b/locales/he/q.json index 3f368d8d7..412896c8b 100644 --- a/locales/he/q.json +++ b/locales/he/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/it/q.json b/locales/it/q.json index 3da53aa93..0d3bd8add 100644 --- a/locales/it/q.json +++ b/locales/it/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/ja/q.json b/locales/ja/q.json index cf86f25b4..d4905ab24 100644 --- a/locales/ja/q.json +++ b/locales/ja/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/ko/q.json b/locales/ko/q.json index 6f046c1b6..82a29e692 100644 --- a/locales/ko/q.json +++ b/locales/ko/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/nl/q.json b/locales/nl/q.json index 6f046c1b6..82a29e692 100644 --- a/locales/nl/q.json +++ b/locales/nl/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/pl/q.json b/locales/pl/q.json index 6f046c1b6..82a29e692 100644 --- a/locales/pl/q.json +++ b/locales/pl/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/pt-BR/q.json b/locales/pt-BR/q.json index 061c13cef..557492df3 100644 --- a/locales/pt-BR/q.json +++ b/locales/pt-BR/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/ru/q.json b/locales/ru/q.json index 865aba72d..09351e25d 100644 --- a/locales/ru/q.json +++ b/locales/ru/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "", "match.rematch.vote.noConfirm": "", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "", "match.rematch.rejoinQueue": "", "match.rematch.backToQueue": "", diff --git a/locales/zh/q.json b/locales/zh/q.json index f4a459753..ee864a049 100644 --- a/locales/zh/q.json +++ b/locales/zh/q.json @@ -165,6 +165,7 @@ "match.rematch.vote.no": "不,到此为止", "match.rematch.vote.noConfirm": "确认投反对票吗?投票后将无法更改。", "match.rematch.declined": "", + "match.rematch.memberJoinedNewGroup": "", "match.rematch.fizzled": "无人想要继续排队", "match.rematch.rejoinQueue": "重新加入队列", "match.rematch.backToQueue": "返回队列",