sendou.ink/app/features/sendouq-match/core/RejoinVote.ts
Kalle 2b5b1b1948
Some checks are pending
E2E Tests / e2e (push) Waiting to run
Tests and checks on push / run-checks-and-tests (push) Waiting to run
Updates translation progress / update-translation-progress-issue (push) Waiting to run
New match page (#3032)
2026-05-04 18:15:10 +03:00

97 lines
2.5 KiB
TypeScript

import type { SQMatch } from "~/features/sendouq/core/SendouQ.server";
import { FULL_GROUP_SIZE } from "~/features/sendouq/q-constants";
import * as SendouQMatch from "./SendouQMatch";
export interface RejoinVote {
userId: number;
isContinuing: boolean;
}
const MIN_CONTINUING_GROUP_SIZE = 2;
/**
* Resolves the overall vote state. ONGOING until every member of a full group
* has cast a vote, then RESOLVED with the ids of those who chose to continue,
* or FAILED if too few want to continue to form a viable group.
*/
export function result(votes: RejoinVote[]) {
if (votes.length !== FULL_GROUP_SIZE) {
return { type: "ONGOING" as const };
}
const continuingUserIds = votes
.filter((vote) => vote.isContinuing)
.map((vote) => vote.userId);
if (continuingUserIds.length < MIN_CONTINUING_GROUP_SIZE) {
return { type: "FAILED" as const };
}
return {
type: "RESOLVED" as const,
continuingUserIds,
};
}
/**
* Returns the given user's vote (true/false), or null if they have not voted.
*/
export function userContinueStatus(votes: RejoinVote[], userId: number) {
return votes.find((vote) => vote.userId === userId)?.isContinuing ?? null;
}
/**
* Whether the given user is still eligible to cast their vote.
*/
export function canCastVote(votes: RejoinVote[], userId: number) {
return !votes.some((vote) => vote.userId === userId);
}
/**
* Collects the votes cast within the viewing user's own group. Returns null if
* the user is not a member of either side of the match.
*/
export function extractOwnGroupVotesFromSendouqMatch(
match: Pick<SQMatch, "groupAlpha" | "groupBravo">,
userId: number,
): RejoinVote[] | null {
const ownSide = SendouQMatch.resolveGroupMemberOf({
groupAlpha: match.groupAlpha,
groupBravo: match.groupBravo,
userId,
});
const ownGroup =
ownSide === "ALPHA"
? match.groupAlpha
: ownSide === "BRAVO"
? match.groupBravo
: null;
if (!ownGroup) return null;
return ownGroup.members.flatMap((member) =>
typeof member.isContinuing === "boolean"
? {
userId: member.id,
isContinuing: member.isContinuing,
}
: [],
);
}
/**
* Returns the group member ids that remain after removing anyone who voted
* against continuing.
*/
export function currentUserIds(
votes: RejoinVote[],
groupMemberIds: number[],
): number[] {
const dropped = new Set(droppedUserIds(votes));
return groupMemberIds.filter((id) => !dropped.has(id));
}
function droppedUserIds(votes: RejoinVote[]): number[] {
return votes.filter((v) => !v.isContinuing).map((v) => v.userId);
}