diff --git a/app/features/sendouq-match/components/RematchVotePanel.tsx b/app/features/sendouq-match/components/RematchVotePanel.tsx
index 2358a7e4f..9e809af82 100644
--- a/app/features/sendouq-match/components/RematchVotePanel.tsx
+++ b/app/features/sendouq-match/components/RematchVotePanel.tsx
@@ -38,7 +38,9 @@ export function RematchVotePanel({
members.map((m) => m.id),
).length;
- const voteResolved = RejoinVote.result(votes).type === "RESOLVED";
+ const voteResult = RejoinVote.result(votes);
+ const voteResolved = voteResult.type === "RESOLVED";
+ const voteFailed = voteResult.type === "FAILED";
const viewerVotedYes =
RejoinVote.userContinueStatus(votes, viewerUserId) === true;
const viewerVotedNo =
@@ -47,9 +49,11 @@ export function RematchVotePanel({
return (
- {voteResolved
- ? t("q:match.rematch.resolved", { count: currentRoundSize })
- : t("q:match.rematch.prompt", { count: currentRoundSize })}
+ {voteFailed
+ ? t("q:match.rematch.fizzled")
+ : voteResolved
+ ? t("q:match.rematch.resolved", { count: currentRoundSize })
+ : t("q:match.rematch.prompt", { count: currentRoundSize })}
{members.map((member) => {
@@ -71,7 +75,7 @@ export function RematchVotePanel({
- ) : viewerVotedNo ? null : (
+ ) : voteFailed || viewerVotedNo ? null : (
{
continuingUserIds: [1, 3],
});
});
+
+ test("fails when fewer than two members want to continue", () => {
+ expect(
+ RejoinVote.result([
+ { userId: 1, isContinuing: true },
+ { userId: 2, isContinuing: false },
+ { userId: 3, isContinuing: false },
+ { userId: 4, isContinuing: false },
+ ]),
+ ).toEqual({ type: "FAILED" });
+ });
});
describe("RejoinVote.userContinueStatus()", () => {
diff --git a/app/features/sendouq-match/core/RejoinVote.ts b/app/features/sendouq-match/core/RejoinVote.ts
index 2ed597e55..8d91438f0 100644
--- a/app/features/sendouq-match/core/RejoinVote.ts
+++ b/app/features/sendouq-match/core/RejoinVote.ts
@@ -7,21 +7,29 @@ export interface RejoinVote {
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 returns the ids of those who chose to continue.
+ * 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.
*/
-// xxx: what about FAILED?
export function result(votes: RejoinVote[]) {
if (votes.length !== FULL_GROUP_SIZE) {
return { type: "ONGOING" as const };
}
- const willContinue = votes.filter((vote) => vote.isContinuing);
+ 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: willContinue.map((vote) => vote.userId),
+ continuingUserIds,
};
}