mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-28 06:08:13 -05:00
Various tournament queries migrated to Kysely (#2978)
This commit is contained in:
@@ -1,4 +1,78 @@
|
||||
import { sql } from "kysely";
|
||||
import { jsonArrayFrom } from "kysely/helpers/sqlite";
|
||||
import { db } from "~/db/sql";
|
||||
import type { Unwrapped } from "~/utils/types";
|
||||
|
||||
export type FindMatchById = NonNullable<Unwrapped<typeof findMatchById>>;
|
||||
export async function findMatchById(id: number) {
|
||||
const row = await db
|
||||
.selectFrom("TournamentMatch")
|
||||
.innerJoin(
|
||||
"TournamentStage",
|
||||
"TournamentStage.id",
|
||||
"TournamentMatch.stageId",
|
||||
)
|
||||
.innerJoin(
|
||||
"TournamentRound",
|
||||
"TournamentRound.id",
|
||||
"TournamentMatch.roundId",
|
||||
)
|
||||
.innerJoin("Tournament", "Tournament.id", "TournamentStage.tournamentId")
|
||||
.select(({ eb }) => [
|
||||
"TournamentMatch.id",
|
||||
"TournamentMatch.groupId",
|
||||
"TournamentMatch.opponentOne",
|
||||
"TournamentMatch.opponentTwo",
|
||||
"TournamentMatch.chatCode",
|
||||
"TournamentMatch.startedAt",
|
||||
"TournamentMatch.status",
|
||||
"Tournament.mapPickingStyle",
|
||||
"TournamentRound.id as roundId",
|
||||
"TournamentRound.maps as roundMaps",
|
||||
jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom("TournamentTeamMember")
|
||||
.innerJoin("User", "User.id", "TournamentTeamMember.userId")
|
||||
.select([
|
||||
"User.id",
|
||||
"User.username",
|
||||
"TournamentTeamMember.tournamentTeamId",
|
||||
sql<
|
||||
string | null
|
||||
>`coalesce("TournamentTeamMember"."inGameName", "User"."inGameName")`.as(
|
||||
"inGameName",
|
||||
),
|
||||
"User.discordId",
|
||||
"User.customUrl",
|
||||
"User.discordAvatar",
|
||||
"User.pronouns",
|
||||
])
|
||||
.where(({ or, eb: innerEb }) =>
|
||||
or([
|
||||
innerEb(
|
||||
"TournamentTeamMember.tournamentTeamId",
|
||||
"=",
|
||||
sql<number>`"TournamentMatch"."opponentOne" ->> '$.id'`,
|
||||
),
|
||||
innerEb(
|
||||
"TournamentTeamMember.tournamentTeamId",
|
||||
"=",
|
||||
sql<number>`"TournamentMatch"."opponentTwo" ->> '$.id'`,
|
||||
),
|
||||
]),
|
||||
),
|
||||
).as("players"),
|
||||
])
|
||||
.where("TournamentMatch.id", "=", id)
|
||||
.executeTakeFirst();
|
||||
|
||||
if (!row) return;
|
||||
|
||||
return {
|
||||
...row,
|
||||
bestOf: row.roundMaps.count,
|
||||
};
|
||||
}
|
||||
|
||||
export function findResultById(id: number) {
|
||||
return db
|
||||
|
||||
@@ -288,9 +288,8 @@ export const action: ActionFunction = async ({ params, request }) => {
|
||||
`Checking in (bracket try): tournament team id: ${teamMemberOf.id} - user id: ${user.id} - tournament id: ${tournament.ctx.id} - bracket idx: ${data.bracketIdx}`,
|
||||
);
|
||||
|
||||
await TournamentTeamRepository.checkIn({
|
||||
await TournamentTeamRepository.checkIn(teamMemberOf.id, {
|
||||
bracketIdx: data.bracketIdx,
|
||||
tournamentTeamId: teamMemberOf.id,
|
||||
});
|
||||
|
||||
logger.info(
|
||||
|
||||
@@ -29,14 +29,11 @@ import { deleteMatchPickBanEvents } from "../queries/deleteMatchPickBanEvents.se
|
||||
import { deleteParticipantsByMatchGameResultId } from "../queries/deleteParticipantsByMatchGameResultId.server";
|
||||
import { deletePickBanEvent } from "../queries/deletePickBanEvent.server";
|
||||
import { deleteTournamentMatchGameResultById } from "../queries/deleteTournamentMatchGameResultById.server";
|
||||
import {
|
||||
type FindMatchById,
|
||||
findMatchById,
|
||||
} from "../queries/findMatchById.server";
|
||||
import { findResultsByMatchId } from "../queries/findResultsByMatchId.server";
|
||||
import { insertTournamentMatchGameResult } from "../queries/insertTournamentMatchGameResult.server";
|
||||
import { insertTournamentMatchGameResultParticipant } from "../queries/insertTournamentMatchGameResultParticipant.server";
|
||||
import { updateMatchGameResultPoints } from "../queries/updateMatchGameResultPoints.server";
|
||||
import type { FindMatchById } from "../TournamentMatchRepository.server";
|
||||
import {
|
||||
matchPageParamsSchema,
|
||||
matchSchema,
|
||||
@@ -56,7 +53,9 @@ export const action: ActionFunction = async ({ params, request }) => {
|
||||
params,
|
||||
schema: matchPageParamsSchema,
|
||||
});
|
||||
const match = notFoundIfFalsy(findMatchById(matchId));
|
||||
const match = notFoundIfFalsy(
|
||||
await TournamentMatchRepository.findMatchById(matchId),
|
||||
);
|
||||
const data = await parseRequestPayload({
|
||||
request,
|
||||
schema: matchSchema,
|
||||
|
||||
@@ -15,8 +15,8 @@ import { executeRoll } from "../core/executeRoll.server";
|
||||
import { mapListFromResults, resolveMapList } from "../core/mapList.server";
|
||||
import * as PickBan from "../core/PickBan";
|
||||
import { tournamentFromDBCached } from "../core/Tournament.server";
|
||||
import { findMatchById } from "../queries/findMatchById.server";
|
||||
import { findResultsByMatchId } from "../queries/findResultsByMatchId.server";
|
||||
import * as TournamentMatchRepository from "../TournamentMatchRepository.server";
|
||||
import { matchPageParamsSchema } from "../tournament-bracket-schemas.server";
|
||||
import { matchEndedEarly } from "../tournament-bracket-utils";
|
||||
|
||||
@@ -33,7 +33,9 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||
user: undefined,
|
||||
});
|
||||
|
||||
const match = notFoundIfFalsy(findMatchById(matchId));
|
||||
const match = notFoundIfFalsy(
|
||||
await TournamentMatchRepository.findMatchById(matchId),
|
||||
);
|
||||
|
||||
const isBye = !match.opponentOne || !match.opponentTwo;
|
||||
if (isBye) {
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import type { Tables, TournamentRoundMaps } from "~/db/tables";
|
||||
import type { Match } from "~/modules/brackets-model";
|
||||
import { parseDBArray } from "~/utils/sql";
|
||||
|
||||
const stm = sql.prepare(/* sql */ `
|
||||
select
|
||||
"TournamentMatch"."id",
|
||||
"TournamentMatch"."groupId",
|
||||
"TournamentMatch"."opponentOne",
|
||||
"TournamentMatch"."opponentTwo",
|
||||
"TournamentMatch"."chatCode",
|
||||
"TournamentMatch"."startedAt",
|
||||
"TournamentMatch"."status",
|
||||
"Tournament"."mapPickingStyle",
|
||||
"TournamentRound"."id" as "roundId",
|
||||
"TournamentRound"."maps" as "roundMaps",
|
||||
json_group_array(
|
||||
json_object(
|
||||
'id',
|
||||
"User"."id",
|
||||
'username',
|
||||
"User"."username",
|
||||
'tournamentTeamId',
|
||||
"TournamentTeamMember"."tournamentTeamId",
|
||||
'inGameName',
|
||||
COALESCE("TournamentTeamMember"."inGameName", "User"."inGameName"),
|
||||
'discordId',
|
||||
"User"."discordId",
|
||||
'customUrl',
|
||||
"User"."customUrl",
|
||||
'discordAvatar',
|
||||
"User"."discordAvatar",
|
||||
'pronouns', json("User"."pronouns")
|
||||
)
|
||||
) as "players"
|
||||
from "TournamentMatch"
|
||||
left join "TournamentStage" on "TournamentStage"."id" = "TournamentMatch"."stageId"
|
||||
left join "TournamentRound" on "TournamentRound"."id" = "TournamentMatch"."roundId"
|
||||
left join "Tournament" on "Tournament"."id" = "TournamentStage"."tournamentId"
|
||||
left join "TournamentTeamMember" on
|
||||
"TournamentTeamMember"."tournamentTeamId" = "TournamentMatch"."opponentOne" ->> '$.id'
|
||||
or
|
||||
"TournamentTeamMember"."tournamentTeamId" = "TournamentMatch"."opponentTwo" ->> '$.id'
|
||||
left join "User" on "User"."id" = "TournamentTeamMember"."userId"
|
||||
where "TournamentMatch"."id" = @id
|
||||
group by "TournamentMatch"."id"
|
||||
`);
|
||||
|
||||
export type FindMatchById = ReturnType<typeof findMatchById>;
|
||||
|
||||
export const findMatchById = (id: number) => {
|
||||
const row = stm.get({ id }) as
|
||||
| ((Pick<
|
||||
Tables["TournamentMatch"],
|
||||
"id" | "groupId" | "chatCode" | "startedAt" | "status"
|
||||
> &
|
||||
Pick<Tables["Tournament"], "mapPickingStyle"> & { players: string }) & {
|
||||
opponentOne: string;
|
||||
opponentTwo: string;
|
||||
roundId: number;
|
||||
roundMaps: string;
|
||||
})
|
||||
| undefined;
|
||||
|
||||
if (!row) return;
|
||||
|
||||
const roundMaps = JSON.parse(row.roundMaps) as TournamentRoundMaps;
|
||||
|
||||
return {
|
||||
...row,
|
||||
bestOf: roundMaps.count,
|
||||
roundId: row.roundId,
|
||||
roundMaps,
|
||||
opponentOne: JSON.parse(row.opponentOne) as Match["opponent1"],
|
||||
opponentTwo: JSON.parse(row.opponentTwo) as Match["opponent2"],
|
||||
status: row.status,
|
||||
players: (
|
||||
parseDBArray(row.players) as Array<{
|
||||
id: Tables["User"]["id"];
|
||||
username: Tables["User"]["username"];
|
||||
tournamentTeamId: Tables["TournamentTeamMember"]["tournamentTeamId"];
|
||||
inGameName: Tables["User"]["inGameName"];
|
||||
discordId: Tables["User"]["discordId"];
|
||||
customUrl: Tables["User"]["customUrl"];
|
||||
discordAvatar: Tables["User"]["discordAvatar"];
|
||||
pronouns: Tables["User"]["pronouns"];
|
||||
}>
|
||||
).filter((player) => player.id),
|
||||
};
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import type { Transaction } from "kysely";
|
||||
import { sql } from "kysely";
|
||||
import { db } from "~/db/sql";
|
||||
import type { DB, Tables } from "~/db/tables";
|
||||
import type { MapPool } from "~/features/map-list-generator/core/map-pool";
|
||||
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
|
||||
import { flatZip } from "~/utils/arrays";
|
||||
import { databaseTimestampNow, dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
@@ -111,13 +112,11 @@ export function create({
|
||||
avatarFileName,
|
||||
userId,
|
||||
tournamentId,
|
||||
ownerInGameName,
|
||||
}: {
|
||||
team: Pick<Tables["TournamentTeam"], "name" | "prefersNotToHost" | "teamId">;
|
||||
avatarFileName?: string;
|
||||
userId: number;
|
||||
tournamentId: number;
|
||||
ownerInGameName: string | null;
|
||||
}) {
|
||||
return db.transaction().execute(async (trx) => {
|
||||
const avatarImgId = avatarFileName
|
||||
@@ -141,13 +140,15 @@ export function create({
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
const inGameName = await resolveInGameName(trx, tournamentId, userId);
|
||||
|
||||
await trx
|
||||
.insertInto("TournamentTeamMember")
|
||||
.values({
|
||||
tournamentTeamId: tournamentTeam.id,
|
||||
userId,
|
||||
role: "OWNER",
|
||||
inGameName: ownerInGameName,
|
||||
inGameName,
|
||||
})
|
||||
.execute();
|
||||
|
||||
@@ -155,6 +156,28 @@ export function create({
|
||||
});
|
||||
}
|
||||
|
||||
async function resolveInGameName(
|
||||
trx: Transaction<DB>,
|
||||
tournamentId: number,
|
||||
userId: number,
|
||||
) {
|
||||
const tournament = await trx
|
||||
.selectFrom("Tournament")
|
||||
.select("Tournament.settings")
|
||||
.where("Tournament.id", "=", tournamentId)
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
if (!tournament.settings.requireInGameNames) return null;
|
||||
|
||||
const user = await trx
|
||||
.selectFrom("User")
|
||||
.select("User.inGameName")
|
||||
.where("User.id", "=", userId)
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return user.inGameName;
|
||||
}
|
||||
|
||||
export function copyFromAnotherTournament({
|
||||
tournamentTeamId,
|
||||
destinationTournamentId,
|
||||
@@ -355,13 +378,17 @@ export function updateStartingBrackets(
|
||||
});
|
||||
}
|
||||
|
||||
export function checkIn({
|
||||
tournamentTeamId,
|
||||
bracketIdx,
|
||||
}: {
|
||||
tournamentTeamId: number;
|
||||
bracketIdx: number | null;
|
||||
}) {
|
||||
/**
|
||||
* Checks in a tournament team. Clears any existing check-out records before inserting the check-in.
|
||||
* When called without `bracketIdx`, checks in for the whole tournament.
|
||||
* When called with `bracketIdx`, checks in for a specific bracket (e.g. after progression).
|
||||
*/
|
||||
export function checkIn(
|
||||
tournamentTeamId: number,
|
||||
options?: { bracketIdx: number },
|
||||
) {
|
||||
const bracketIdx = options?.bracketIdx ?? null;
|
||||
|
||||
return db.transaction().execute(async (trx) => {
|
||||
let query = trx
|
||||
.deleteFrom("TournamentTeamCheckIn")
|
||||
@@ -467,6 +494,139 @@ export function undoDropOut(tournamentTeamId: number) {
|
||||
.execute();
|
||||
}
|
||||
|
||||
export function join({
|
||||
previousTeamId,
|
||||
whatToDoWithPreviousTeam,
|
||||
newTeamId,
|
||||
userId,
|
||||
checkOutTeam = false,
|
||||
}: {
|
||||
previousTeamId?: number;
|
||||
whatToDoWithPreviousTeam?: "LEAVE" | "DELETE";
|
||||
newTeamId: number;
|
||||
userId: number;
|
||||
checkOutTeam?: boolean;
|
||||
}) {
|
||||
return db.transaction().execute(async (trx) => {
|
||||
if (whatToDoWithPreviousTeam === "DELETE") {
|
||||
await trx
|
||||
.deleteFrom("TournamentTeam")
|
||||
.where("TournamentTeam.id", "=", previousTeamId!)
|
||||
.execute();
|
||||
} else if (whatToDoWithPreviousTeam === "LEAVE") {
|
||||
await trx
|
||||
.deleteFrom("TournamentTeamMember")
|
||||
.where("TournamentTeamMember.tournamentTeamId", "=", previousTeamId!)
|
||||
.where("TournamentTeamMember.userId", "=", userId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
if (checkOutTeam) {
|
||||
invariant(
|
||||
previousTeamId,
|
||||
"previousTeamId is required when checking out team",
|
||||
);
|
||||
await trx
|
||||
.deleteFrom("TournamentTeamCheckIn")
|
||||
.where("TournamentTeamCheckIn.tournamentTeamId", "=", previousTeamId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
const tournamentId = (
|
||||
await trx
|
||||
.selectFrom("TournamentTeam")
|
||||
.select("TournamentTeam.tournamentId")
|
||||
.where("TournamentTeam.id", "=", newTeamId)
|
||||
.executeTakeFirstOrThrow()
|
||||
).tournamentId;
|
||||
|
||||
const inGameName = await resolveInGameName(trx, tournamentId, userId);
|
||||
|
||||
await trx
|
||||
.insertInto("TournamentTeamMember")
|
||||
.values({
|
||||
tournamentTeamId: newTeamId,
|
||||
userId,
|
||||
inGameName,
|
||||
})
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
export function del(tournamentTeamId: number) {
|
||||
return db.transaction().execute(async (trx) => {
|
||||
await trx
|
||||
.deleteFrom("MapPoolMap")
|
||||
.where("MapPoolMap.tournamentTeamId", "=", tournamentTeamId)
|
||||
.execute();
|
||||
|
||||
await trx
|
||||
.deleteFrom("TournamentTeam")
|
||||
.where("TournamentTeam.id", "=", tournamentTeamId)
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
export function leave({ teamId, userId }: { teamId: number; userId: number }) {
|
||||
return db
|
||||
.deleteFrom("TournamentTeamMember")
|
||||
.where("TournamentTeamMember.tournamentTeamId", "=", teamId)
|
||||
.where("TournamentTeamMember.userId", "=", userId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
export function transferOwnership(
|
||||
tournamentTeamId: number,
|
||||
{
|
||||
oldCaptainId,
|
||||
newCaptainId,
|
||||
}: { oldCaptainId: number; newCaptainId: number },
|
||||
) {
|
||||
return db.transaction().execute(async (trx) => {
|
||||
await trx
|
||||
.updateTable("TournamentTeamMember")
|
||||
.set({ role: "REGULAR" })
|
||||
.where("TournamentTeamMember.tournamentTeamId", "=", tournamentTeamId)
|
||||
.where("TournamentTeamMember.userId", "=", oldCaptainId)
|
||||
.execute();
|
||||
|
||||
await trx
|
||||
.updateTable("TournamentTeamMember")
|
||||
.set({ role: "OWNER" })
|
||||
.where("TournamentTeamMember.tournamentTeamId", "=", tournamentTeamId)
|
||||
.where("TournamentTeamMember.userId", "=", newCaptainId)
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
export function upsertCounterpickMaps({
|
||||
tournamentTeamId,
|
||||
mapPool,
|
||||
}: {
|
||||
tournamentTeamId: Tables["TournamentTeam"]["id"];
|
||||
mapPool: MapPool;
|
||||
}) {
|
||||
return db.transaction().execute(async (trx) => {
|
||||
await trx
|
||||
.deleteFrom("MapPoolMap")
|
||||
.where("MapPoolMap.tournamentTeamId", "=", tournamentTeamId)
|
||||
.execute();
|
||||
|
||||
if (mapPool.stageModePairs.length > 0) {
|
||||
await trx
|
||||
.insertInto("MapPoolMap")
|
||||
.values(
|
||||
mapPool.stageModePairs.map(({ stageId, mode }) => ({
|
||||
tournamentTeamId,
|
||||
stageId,
|
||||
mode,
|
||||
})),
|
||||
)
|
||||
.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function findTeamRecentMaps(teamId: number, limit: number) {
|
||||
return db
|
||||
.selectFrom("TournamentMatchGameResult")
|
||||
@@ -485,6 +645,14 @@ async function findTeamRecentMaps(teamId: number, limit: number) {
|
||||
.execute();
|
||||
}
|
||||
|
||||
export function findByInviteCode(inviteCode: string) {
|
||||
return db
|
||||
.selectFrom("TournamentTeam")
|
||||
.select(["TournamentTeam.id", "TournamentTeam.tournamentId"])
|
||||
.where("TournamentTeam.inviteCode", "=", inviteCode)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
export async function findRecentlyPlayedMapsByIds({
|
||||
teamIds,
|
||||
limit = 5,
|
||||
|
||||
@@ -30,15 +30,9 @@ import {
|
||||
} from "~/utils/remix.server";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
import { idObject } from "../../../utils/zod";
|
||||
import { changeTeamOwner } from "../queries/changeTeamOwner.server";
|
||||
import { deleteTeam } from "../queries/deleteTeam.server";
|
||||
import { joinTeam, leaveTeam } from "../queries/joinLeaveTeam.server";
|
||||
import * as TournamentRepository from "../TournamentRepository.server";
|
||||
import { adminActionSchema } from "../tournament-schemas.server";
|
||||
import {
|
||||
endDroppedTeamMatches,
|
||||
inGameNameIfNeeded,
|
||||
} from "../tournament-utils.server";
|
||||
import { endDroppedTeamMatches } from "../tournament-utils.server";
|
||||
|
||||
export const action: ActionFunction = async ({ request, params }) => {
|
||||
const user = requireUser();
|
||||
@@ -70,16 +64,14 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
!tournament.teamMemberOfByUser({ id: data.userId }),
|
||||
"User already on a team",
|
||||
);
|
||||
const addTeamUser = await UserRepository.findLeanById(data.userId);
|
||||
errorToastIfFalsy(addTeamUser?.friendCode, "User has no friend code set");
|
||||
errorToastIfFalsy(
|
||||
(await UserRepository.findLeanById(data.userId))?.friendCode,
|
||||
"User has no friend code set",
|
||||
!tournament.ctx.settings.requireInGameNames || addTeamUser?.inGameName,
|
||||
"User has no in-game name set",
|
||||
);
|
||||
|
||||
await TournamentTeamRepository.create({
|
||||
ownerInGameName: await inGameNameIfNeeded({
|
||||
tournament,
|
||||
userId: data.userId,
|
||||
}),
|
||||
team: {
|
||||
name: data.teamName,
|
||||
prefersNotToHost: 0,
|
||||
@@ -112,10 +104,9 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
const newCaptain = team.members.find((m) => m.userId === data.memberId);
|
||||
errorToastIfFalsy(newCaptain, "Invalid member id");
|
||||
|
||||
changeTeamOwner({
|
||||
newCaptainId: data.memberId,
|
||||
await TournamentTeamRepository.transferOwnership(data.teamId, {
|
||||
oldCaptainId: oldCaptain.userId,
|
||||
tournamentTeamId: data.teamId,
|
||||
newCaptainId: data.memberId,
|
||||
});
|
||||
|
||||
message = "Team owner changed";
|
||||
@@ -152,11 +143,11 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
invariant(bracket, "Invalid bracket idx");
|
||||
errorToastIfFalsy(bracket.preview, "Bracket has been started");
|
||||
|
||||
await TournamentTeamRepository.checkIn({
|
||||
tournamentTeamId: data.teamId,
|
||||
await TournamentTeamRepository.checkIn(
|
||||
data.teamId,
|
||||
// no sources = regular check in
|
||||
bracketIdx: !bracket.sources ? null : data.bracketIdx,
|
||||
});
|
||||
bracket.sources ? { bracketIdx: data.bracketIdx } : undefined,
|
||||
);
|
||||
|
||||
message = "Checked team in";
|
||||
break;
|
||||
@@ -214,7 +205,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
});
|
||||
}
|
||||
|
||||
leaveTeam({
|
||||
await TournamentTeamRepository.leave({
|
||||
userId: data.memberId,
|
||||
teamId: team.id,
|
||||
});
|
||||
@@ -250,16 +241,22 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
"User trying to be added currently has an active ban from sendou.ink",
|
||||
);
|
||||
|
||||
const addMemberUser = await UserRepository.findLeanById(data.userId);
|
||||
errorToastIfFalsy(
|
||||
(await UserRepository.findLeanById(data.userId))?.friendCode,
|
||||
addMemberUser?.friendCode,
|
||||
"User has no friend code set",
|
||||
);
|
||||
errorToastIfFalsy(
|
||||
!tournament.ctx.settings.requireInGameNames ||
|
||||
addMemberUser?.inGameName,
|
||||
"User has no in-game name set",
|
||||
);
|
||||
|
||||
await TournamentLFGRepository.leaveLfg({
|
||||
userId: data.userId,
|
||||
tournamentId,
|
||||
});
|
||||
joinTeam({
|
||||
await TournamentTeamRepository.join({
|
||||
userId: data.userId,
|
||||
newTeamId: team.id,
|
||||
previousTeamId: previousTeam?.id,
|
||||
@@ -270,11 +267,6 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
tournament.hasStarted
|
||||
? "DELETE"
|
||||
: undefined,
|
||||
tournamentId,
|
||||
inGameName: await inGameNameIfNeeded({
|
||||
tournament,
|
||||
userId: data.userId,
|
||||
}),
|
||||
});
|
||||
|
||||
ShowcaseTournaments.addToCached({
|
||||
@@ -310,7 +302,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
errorToastIfFalsy(team, "Invalid team id");
|
||||
errorToastIfFalsy(!tournament.hasStarted, "Tournament has started");
|
||||
|
||||
deleteTeam(team.id);
|
||||
await TournamentTeamRepository.del(team.id);
|
||||
|
||||
for (const member of team.members) {
|
||||
ShowcaseTournaments.removeFromCached({
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { ActionFunction } from "react-router";
|
||||
import { redirect } from "react-router";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import * as ShowcaseTournaments from "~/features/front-page/core/ShowcaseTournaments.server";
|
||||
import * as TournamentTeamRepository from "~/features/tournament/TournamentTeamRepository.server";
|
||||
import {
|
||||
clearTournamentDataCache,
|
||||
tournamentFromDB,
|
||||
@@ -16,11 +17,8 @@ import {
|
||||
} from "~/utils/remix.server";
|
||||
import { tournamentPage } from "~/utils/urls";
|
||||
import { idObject } from "~/utils/zod";
|
||||
import { findByInviteCode } from "../queries/findTeamByInviteCode.server";
|
||||
import { joinTeam } from "../queries/joinLeaveTeam.server";
|
||||
import { validateCanJoinTeam } from "../tournament-utils";
|
||||
import {
|
||||
inGameNameIfNeeded,
|
||||
requireNotBannedByOrganization,
|
||||
requireSendouQParticipationIfNeeded,
|
||||
} from "../tournament-utils.server";
|
||||
@@ -35,7 +33,9 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
const inviteCode = url.searchParams.get("code");
|
||||
invariant(inviteCode, "code is missing");
|
||||
|
||||
const leanTeam = notFoundIfFalsy(findByInviteCode(inviteCode));
|
||||
const leanTeam = notFoundIfFalsy(
|
||||
await TournamentTeamRepository.findByInviteCode(inviteCode),
|
||||
);
|
||||
|
||||
const tournament = await tournamentFromDB({ tournamentId, user });
|
||||
|
||||
@@ -88,7 +88,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
: "LEAVE";
|
||||
|
||||
await TournamentLFGRepository.leaveLfg({ userId: user.id, tournamentId });
|
||||
joinTeam({
|
||||
await TournamentTeamRepository.join({
|
||||
userId: user.id,
|
||||
newTeamId: teamToJoin.id,
|
||||
previousTeamId: previousTeam?.id,
|
||||
@@ -99,11 +99,6 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
previousTeam &&
|
||||
previousTeam.members.length <= tournament.minMembersPerTeam,
|
||||
whatToDoWithPreviousTeam,
|
||||
tournamentId,
|
||||
inGameName: await inGameNameIfNeeded({
|
||||
tournament,
|
||||
userId: user.id,
|
||||
}),
|
||||
});
|
||||
|
||||
ShowcaseTournaments.addToCached({
|
||||
|
||||
@@ -22,12 +22,6 @@ import {
|
||||
} from "~/utils/remix.server";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
import { idObject } from "~/utils/zod";
|
||||
import { checkIn } from "../queries/checkIn.server";
|
||||
import { deleteTeam } from "../queries/deleteTeam.server";
|
||||
import deleteTeamMember from "../queries/deleteTeamMember.server";
|
||||
import { findOwnTournamentTeam } from "../queries/findOwnTournamentTeam.server";
|
||||
import { joinTeam } from "../queries/joinLeaveTeam.server";
|
||||
import { upsertCounterpickMaps } from "../queries/upsertCounterpickMaps.server";
|
||||
import { TOURNAMENT } from "../tournament-constants";
|
||||
import { registerSchema } from "../tournament-schemas.server";
|
||||
import {
|
||||
@@ -35,7 +29,6 @@ import {
|
||||
validateCounterPickMapPool,
|
||||
} from "../tournament-utils";
|
||||
import {
|
||||
inGameNameIfNeeded,
|
||||
requireNotBannedByOrganization,
|
||||
requireSendouQParticipationIfNeeded,
|
||||
} from "../tournament-utils.server";
|
||||
@@ -130,10 +123,6 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
tournamentId,
|
||||
});
|
||||
await TournamentTeamRepository.create({
|
||||
ownerInGameName: await inGameNameIfNeeded({
|
||||
tournament,
|
||||
userId: user.id,
|
||||
}),
|
||||
team: {
|
||||
name: data.teamName,
|
||||
prefersNotToHost: Number(data.prefersNotToHost),
|
||||
@@ -165,20 +154,18 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
);
|
||||
errorToastIfFalsy(data.userId !== user.id, "Can't kick yourself");
|
||||
|
||||
const detailedOwnTeam = findOwnTournamentTeam({
|
||||
tournamentId,
|
||||
userId: user.id,
|
||||
});
|
||||
// making sure they aren't unfilling one checking in condition i.e. having full roster
|
||||
// and then having members kicked without it affecting the checking in status
|
||||
errorToastIfFalsy(
|
||||
detailedOwnTeam &&
|
||||
(!detailedOwnTeam.checkedInAt ||
|
||||
ownTeam.members.length > tournament.minMembersPerTeam),
|
||||
!ownTeamCheckedIn ||
|
||||
ownTeam.members.length > tournament.minMembersPerTeam,
|
||||
"Can't kick a member after checking in",
|
||||
);
|
||||
|
||||
deleteTeamMember({ tournamentTeamId: ownTeam.id, userId: data.userId });
|
||||
await TournamentTeamRepository.leave({
|
||||
teamId: ownTeam.id,
|
||||
userId: data.userId,
|
||||
});
|
||||
|
||||
ShowcaseTournaments.removeFromCached({
|
||||
tournamentId,
|
||||
@@ -197,8 +184,8 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
"You cannot leave after checking in",
|
||||
);
|
||||
|
||||
deleteTeamMember({
|
||||
tournamentTeamId: teamMemberOf.id,
|
||||
await TournamentTeamRepository.leave({
|
||||
teamId: teamMemberOf.id,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
@@ -225,7 +212,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
"Invalid map pool",
|
||||
);
|
||||
|
||||
upsertCounterpickMaps({
|
||||
await TournamentTeamRepository.upsertCounterpickMaps({
|
||||
tournamentTeamId: ownTeam.id,
|
||||
mapPool: new MapPool(data.mapPool),
|
||||
});
|
||||
@@ -253,7 +240,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
`Can't check-in - ${tournament.checkInConditionsFulfilledByTeamId(teamMemberOf.id).reason}`,
|
||||
);
|
||||
|
||||
checkIn(teamMemberOf.id);
|
||||
await TournamentTeamRepository.checkIn(teamMemberOf.id);
|
||||
logger.info(
|
||||
`Checking in (success): tournament team id: ${teamMemberOf.id} - user id: ${user.id} - tournament id: ${tournamentId}`,
|
||||
);
|
||||
@@ -293,14 +280,9 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
userId: data.userId,
|
||||
tournamentId,
|
||||
});
|
||||
joinTeam({
|
||||
await TournamentTeamRepository.join({
|
||||
userId: data.userId,
|
||||
newTeamId: ownTeam.id,
|
||||
tournamentId,
|
||||
inGameName: await inGameNameIfNeeded({
|
||||
tournament,
|
||||
userId: data.userId,
|
||||
}),
|
||||
});
|
||||
|
||||
await SavedCalendarEventRepository.unsave({
|
||||
@@ -344,7 +326,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
"Unregistering from leagues is not possible after registration has closed",
|
||||
);
|
||||
|
||||
deleteTeam(ownTeam.id);
|
||||
await TournamentTeamRepository.del(ownTeam.id);
|
||||
|
||||
for (const member of ownTeam.members) {
|
||||
ShowcaseTournaments.removeFromCached({
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import type { LoaderFunctionArgs } from "react-router";
|
||||
import { findByInviteCode } from "../queries/findTeamByInviteCode.server";
|
||||
import * as TournamentTeamRepository from "~/features/tournament/TournamentTeamRepository.server";
|
||||
|
||||
export const loader = ({ request }: LoaderFunctionArgs) => {
|
||||
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||
const url = new URL(request.url);
|
||||
const inviteCode = url.searchParams.get("code");
|
||||
|
||||
const team = inviteCode
|
||||
? await TournamentTeamRepository.findByInviteCode(inviteCode)
|
||||
: null;
|
||||
|
||||
return {
|
||||
teamId: inviteCode ? findByInviteCode(inviteCode)?.id : null,
|
||||
teamId: team?.id ?? null,
|
||||
inviteCode,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,10 +3,10 @@ import { getUser } from "~/features/auth/core/user.server";
|
||||
import * as SQGroupRepository from "~/features/sendouq/SQGroupRepository.server";
|
||||
import * as TeamRepository from "~/features/team/TeamRepository.server";
|
||||
import * as SavedCalendarEventRepository from "~/features/tournament/SavedCalendarEventRepository.server";
|
||||
import { tournamentFromDBCached } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
import { findMapPoolByTeamId } from "~/features/tournament-bracket/queries/findMapPoolByTeamId.server";
|
||||
import { parseParams } from "~/utils/remix.server";
|
||||
import { idObject } from "~/utils/zod";
|
||||
import { findOwnTournamentTeam } from "../queries/findOwnTournamentTeam.server";
|
||||
|
||||
export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||
const user = getUser();
|
||||
@@ -17,11 +17,10 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||
schema: idObject,
|
||||
});
|
||||
|
||||
const ownTournamentTeam = findOwnTournamentTeam({
|
||||
tournamentId,
|
||||
userId: user.id,
|
||||
});
|
||||
if (!ownTournamentTeam) {
|
||||
const tournament = await tournamentFromDBCached({ tournamentId, user });
|
||||
const ownTeam = tournament.ownedTeamByUser(user);
|
||||
|
||||
if (!ownTeam) {
|
||||
return {
|
||||
mapPool: null,
|
||||
friendPlayers: null,
|
||||
@@ -34,7 +33,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||
}
|
||||
|
||||
return {
|
||||
mapPool: findMapPoolByTeamId(ownTournamentTeam.id),
|
||||
mapPool: findMapPoolByTeamId(ownTeam.id),
|
||||
friendPlayers: await SQGroupRepository.friendsAndTeammates(user.id),
|
||||
teams: await TeamRepository.findAllMemberOfByUserId(user.id),
|
||||
isSaved: false,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import type { Tables } from "~/db/tables";
|
||||
|
||||
const stm = sql.prepare(/* sql */ `
|
||||
update TournamentTeamMember
|
||||
set "role" = @role
|
||||
where
|
||||
"tournamentTeamId" = @tournamentTeamId and
|
||||
"userId" = @userId
|
||||
`);
|
||||
|
||||
export const changeTeamOwner = sql.transaction(
|
||||
(args: {
|
||||
tournamentTeamId: Tables["TournamentTeam"]["id"];
|
||||
oldCaptainId: Tables["User"]["id"];
|
||||
newCaptainId: Tables["User"]["id"];
|
||||
}) => {
|
||||
stm.run({
|
||||
tournamentTeamId: args.tournamentTeamId,
|
||||
userId: args.oldCaptainId,
|
||||
role: "REGULAR",
|
||||
});
|
||||
|
||||
stm.run({
|
||||
tournamentTeamId: args.tournamentTeamId,
|
||||
userId: args.newCaptainId,
|
||||
role: "OWNER",
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -1,12 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
|
||||
const stm = sql.prepare(/* sql */ `
|
||||
insert into "TournamentTeamCheckIn"
|
||||
("tournamentTeamId", "checkedInAt")
|
||||
values
|
||||
(@tournamentTeamId, strftime('%s', 'now'))
|
||||
`);
|
||||
|
||||
export function checkIn(tournamentTeamId: number) {
|
||||
stm.run({ tournamentTeamId });
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
|
||||
const stm = sql.prepare(/* sql */ `
|
||||
delete from "TournamentTeamCheckIn"
|
||||
where "tournamentTeamId" = @tournamentTeamId
|
||||
`);
|
||||
|
||||
export function checkOut(tournamentTeamId: number) {
|
||||
stm.run({ tournamentTeamId });
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
|
||||
const deleteTeamStm = sql.prepare(/*sql*/ `
|
||||
delete from "TournamentTeam"
|
||||
where "id" = @tournamentTeamId
|
||||
`);
|
||||
|
||||
const deleteMapPoolStm = sql.prepare(/*sql*/ `
|
||||
delete from "MapPoolMap"
|
||||
where "tournamentTeamId" = @tournamentTeamId
|
||||
`);
|
||||
|
||||
export const deleteTeam = sql.transaction((tournamentTeamId: number) => {
|
||||
deleteMapPoolStm.run({ tournamentTeamId });
|
||||
deleteTeamStm.run({ tournamentTeamId });
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
|
||||
const stm = sql.prepare(/*sql*/ `
|
||||
delete from "TournamentTeamMember"
|
||||
where userId = @userId
|
||||
and tournamentTeamId = @tournamentTeamId
|
||||
`);
|
||||
|
||||
export default function deleteTeamMember({
|
||||
userId,
|
||||
tournamentTeamId,
|
||||
}: {
|
||||
userId: number;
|
||||
tournamentTeamId: number;
|
||||
}) {
|
||||
stm.run({ userId, tournamentTeamId });
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import type { Tables } from "~/db/tables";
|
||||
|
||||
const stm = sql.prepare(/*sql*/ `
|
||||
select
|
||||
"TournamentTeam"."id",
|
||||
"TournamentTeam"."name",
|
||||
"TournamentTeamCheckIn"."checkedInAt",
|
||||
"TournamentTeam"."inviteCode"
|
||||
from
|
||||
"TournamentTeam"
|
||||
left join "TournamentTeamCheckIn" on
|
||||
"TournamentTeamCheckIn"."tournamentTeamId" = "TournamentTeam"."id"
|
||||
left join "TournamentTeamMember" on
|
||||
"TournamentTeamMember"."tournamentTeamId" = "TournamentTeam"."id"
|
||||
and "TournamentTeamMember"."role" = 'OWNER'
|
||||
where
|
||||
"TournamentTeam"."tournamentId" = @tournamentId
|
||||
and "TournamentTeam"."isPlaceholder" = 0
|
||||
and "TournamentTeamMember"."userId" = @userId
|
||||
`);
|
||||
|
||||
type FindOwnTeam =
|
||||
| (Pick<Tables["TournamentTeam"], "id" | "name" | "inviteCode"> &
|
||||
Pick<Tables["TournamentTeamCheckIn"], "checkedInAt">)
|
||||
| null;
|
||||
|
||||
export function findOwnTournamentTeam({
|
||||
tournamentId,
|
||||
userId,
|
||||
}: {
|
||||
tournamentId: number;
|
||||
userId: number;
|
||||
}) {
|
||||
return stm.get({
|
||||
tournamentId,
|
||||
userId,
|
||||
}) as FindOwnTeam;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
|
||||
const stm = sql.prepare(/*sql */ `
|
||||
select
|
||||
"TournamentTeam"."id",
|
||||
"TournamentTeam"."tournamentId"
|
||||
from "TournamentTeam"
|
||||
where "TournamentTeam"."inviteCode" = @inviteCode
|
||||
`);
|
||||
|
||||
export function findByInviteCode(inviteCode: string) {
|
||||
return stm.get({ inviteCode }) as {
|
||||
id: number;
|
||||
tournamentId: number;
|
||||
} | null;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import type { Tables } from "~/db/tables";
|
||||
|
||||
const stm = sql.prepare(/*sql*/ `
|
||||
select 1
|
||||
from "TournamentResult"
|
||||
where "TournamentResult"."tournamentId" = @tournamentId
|
||||
`);
|
||||
|
||||
export default function hasTournamentFinalized(
|
||||
tournamentId: Tables["Tournament"]["id"],
|
||||
) {
|
||||
return Boolean(stm.get({ tournamentId }));
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import type { Tables } from "~/db/tables";
|
||||
|
||||
const stm = sql.prepare(/*sql*/ `
|
||||
select 1
|
||||
from "TournamentStage"
|
||||
where "TournamentStage"."tournamentId" = @tournamentId
|
||||
`);
|
||||
|
||||
export default function hasTournamentStarted(
|
||||
tournamentId: Tables["Tournament"]["id"],
|
||||
) {
|
||||
return Boolean(stm.get({ tournamentId }));
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { checkOut } from "./checkOut.server";
|
||||
|
||||
const createTeamMemberStm = sql.prepare(/*sql*/ `
|
||||
insert into "TournamentTeamMember" (
|
||||
"tournamentTeamId",
|
||||
"inGameName",
|
||||
"userId"
|
||||
) values (
|
||||
@tournamentTeamId,
|
||||
@inGameName,
|
||||
@userId
|
||||
)
|
||||
`);
|
||||
|
||||
const deleteTeamStm = sql.prepare(/*sql*/ `
|
||||
delete from "TournamentTeam"
|
||||
where "id" = @tournamentTeamId
|
||||
`);
|
||||
|
||||
const deleteMemberStm = sql.prepare(/*sql*/ `
|
||||
delete from "TournamentTeamMember"
|
||||
where "tournamentTeamId" = @tournamentTeamId
|
||||
and "userId" = @userId
|
||||
`);
|
||||
|
||||
export const joinTeam = sql.transaction(
|
||||
({
|
||||
previousTeamId,
|
||||
whatToDoWithPreviousTeam,
|
||||
newTeamId,
|
||||
userId,
|
||||
inGameName,
|
||||
tournamentId: _tournamentId,
|
||||
checkOutTeam = false,
|
||||
}: {
|
||||
previousTeamId?: number;
|
||||
whatToDoWithPreviousTeam?: "LEAVE" | "DELETE";
|
||||
newTeamId: number;
|
||||
userId: number;
|
||||
inGameName: string | null;
|
||||
tournamentId: number;
|
||||
checkOutTeam?: boolean;
|
||||
}) => {
|
||||
if (whatToDoWithPreviousTeam === "DELETE") {
|
||||
deleteTeamStm.run({ tournamentTeamId: previousTeamId ?? null });
|
||||
} else if (whatToDoWithPreviousTeam === "LEAVE") {
|
||||
deleteMemberStm.run({ tournamentTeamId: previousTeamId ?? null, userId });
|
||||
}
|
||||
|
||||
if (checkOutTeam) {
|
||||
invariant(
|
||||
previousTeamId,
|
||||
"previousTeamId is required when checking out team",
|
||||
);
|
||||
checkOut(previousTeamId);
|
||||
}
|
||||
|
||||
createTeamMemberStm.run({
|
||||
tournamentTeamId: newTeamId,
|
||||
userId,
|
||||
inGameName,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export const leaveTeam = ({
|
||||
teamId,
|
||||
userId,
|
||||
}: {
|
||||
teamId: number;
|
||||
userId: number;
|
||||
}) => {
|
||||
deleteMemberStm.run({ tournamentTeamId: teamId, userId });
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { MapPool } from "~/features/map-list-generator/core/map-pool";
|
||||
|
||||
const deleteCounterpickMapsByTeamIdStm = sql.prepare(/* sql */ `
|
||||
delete from
|
||||
"MapPoolMap"
|
||||
where
|
||||
"tournamentTeamId" = @tournamentTeamId
|
||||
`);
|
||||
|
||||
const addCounterpickMapStm = sql.prepare(/* sql */ `
|
||||
insert into
|
||||
"MapPoolMap" ("tournamentTeamId", "stageId", "mode")
|
||||
values
|
||||
(@tournamentTeamId, @stageId, @mode)
|
||||
`);
|
||||
|
||||
export const upsertCounterpickMaps = sql.transaction(
|
||||
({
|
||||
tournamentTeamId,
|
||||
mapPool,
|
||||
}: {
|
||||
tournamentTeamId: Tables["TournamentTeam"]["id"];
|
||||
mapPool: MapPool;
|
||||
}) => {
|
||||
deleteCounterpickMapsByTeamIdStm.run({ tournamentTeamId });
|
||||
|
||||
for (const { stageId, mode } of mapPool.stageModePairs) {
|
||||
addCounterpickMapStm.run({
|
||||
tournamentTeamId,
|
||||
stageId,
|
||||
mode,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type LoaderFunctionArgs, redirect } from "react-router";
|
||||
import { tournamentFromDBCached } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
import { parseParams } from "~/utils/remix.server";
|
||||
import {
|
||||
tournamentBracketsPage,
|
||||
@@ -6,20 +7,23 @@ import {
|
||||
tournamentResultsPage,
|
||||
} from "~/utils/urls";
|
||||
import { idObject } from "~/utils/zod";
|
||||
import hasTournamentFinalized from "../queries/hasTournamentFinalized.server";
|
||||
import hasTournamentStarted from "../queries/hasTournamentStarted.server";
|
||||
|
||||
export const loader = ({ params }: LoaderFunctionArgs) => {
|
||||
export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||
const { id: tournamentId } = parseParams({
|
||||
params,
|
||||
schema: idObject,
|
||||
});
|
||||
|
||||
if (!hasTournamentStarted(tournamentId)) {
|
||||
const tournament = await tournamentFromDBCached({
|
||||
tournamentId,
|
||||
user: undefined,
|
||||
});
|
||||
|
||||
if (!tournament.hasStarted) {
|
||||
return redirect(tournamentRegisterPage(tournamentId));
|
||||
}
|
||||
|
||||
if (!hasTournamentFinalized(tournamentId)) {
|
||||
if (!tournament.ctx.isFinalized) {
|
||||
return redirect(tournamentBracketsPage({ tournamentId }));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { databaseTimestampNow } from "~/utils/dates";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { getServerTournamentManager } from "../tournament-bracket/core/brackets-manager/manager.server";
|
||||
import { tournamentFromDB } from "../tournament-bracket/core/Tournament.server";
|
||||
import { joinTeam } from "./queries/joinLeaveTeam.server";
|
||||
import { updateRoundMaps } from "./queries/updateRoundMaps.server";
|
||||
import * as TournamentTeamRepository from "./TournamentTeamRepository.server";
|
||||
|
||||
@@ -57,7 +56,6 @@ export async function dbInsertTournamentTeam({
|
||||
tournamentId?: number;
|
||||
}) {
|
||||
const tournamentTeam = await TournamentTeamRepository.create({
|
||||
ownerInGameName: null,
|
||||
team: {
|
||||
name: `Test Team ${ownerId}`,
|
||||
prefersNotToHost: 0,
|
||||
@@ -70,19 +68,13 @@ export async function dbInsertTournamentTeam({
|
||||
for (let i = 1; i < membersCount; i++) {
|
||||
const memberId = ownerId + i;
|
||||
|
||||
joinTeam({
|
||||
await TournamentTeamRepository.join({
|
||||
userId: memberId,
|
||||
newTeamId: tournamentTeam.id,
|
||||
tournamentId,
|
||||
inGameName: null,
|
||||
});
|
||||
}
|
||||
|
||||
await TournamentTeamRepository.checkIn({
|
||||
tournamentTeamId: tournamentTeam.id,
|
||||
// no sources = regular check in
|
||||
bracketIdx: null,
|
||||
});
|
||||
await TournamentTeamRepository.checkIn(tournamentTeam.id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,28 +1,11 @@
|
||||
import * as LeaderboardRepository from "~/features/leaderboards/LeaderboardRepository.server";
|
||||
import type { getServerTournamentManager } from "~/features/tournament-bracket/core/brackets-manager/manager.server";
|
||||
import * as TournamentOrganizationRepository from "~/features/tournament-organization/TournamentOrganizationRepository.server";
|
||||
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
||||
import { logger } from "~/utils/logger";
|
||||
import { errorToast, errorToastIfFalsy } from "~/utils/remix.server";
|
||||
import { MATCHES_COUNT_NEEDED_FOR_LEADERBOARD } from "../leaderboards/leaderboards-constants";
|
||||
import type { Tournament } from "../tournament-bracket/core/Tournament";
|
||||
|
||||
export const inGameNameIfNeeded = async ({
|
||||
tournament,
|
||||
userId,
|
||||
}: {
|
||||
tournament: Tournament;
|
||||
userId: number;
|
||||
}) => {
|
||||
if (!tournament.ctx.settings.requireInGameNames) return null;
|
||||
|
||||
const inGameName = await UserRepository.inGameNameByUserId(userId);
|
||||
|
||||
errorToastIfFalsy(inGameName, "No in-game name");
|
||||
|
||||
return inGameName;
|
||||
};
|
||||
|
||||
export async function requireNotBannedByOrganization({
|
||||
tournament,
|
||||
user,
|
||||
|
||||
@@ -480,6 +480,7 @@ test.describe("Tournament bracket", () => {
|
||||
test("shows tournament results on user profile after finalized tournament", async ({
|
||||
page,
|
||||
}) => {
|
||||
test.slow();
|
||||
const tournamentId = 4;
|
||||
|
||||
await seed(page, "SMALL_SOS");
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import "dotenv/config";
|
||||
import * as LeaderboardRepository from "~/features/leaderboards/LeaderboardRepository.server";
|
||||
import * as Seasons from "~/features/mmr/core/Seasons";
|
||||
import { joinTeam } from "~/features/tournament/queries/joinLeaveTeam.server";
|
||||
import * as TournamentTeamRepository from "~/features/tournament/TournamentTeamRepository.server";
|
||||
import { tournamentFromDB } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
||||
@@ -101,10 +100,6 @@ async function main() {
|
||||
const teamName = resolvedNames[i];
|
||||
const owner = entry.members[0];
|
||||
|
||||
const ownerInGameName = tournament.ctx.settings.requireInGameNames
|
||||
? await UserRepository.inGameNameByUserId(owner.id)
|
||||
: null;
|
||||
|
||||
const tournamentTeam = await TournamentTeamRepository.create({
|
||||
team: {
|
||||
name: teamName,
|
||||
@@ -113,19 +108,12 @@ async function main() {
|
||||
},
|
||||
userId: owner.id,
|
||||
tournamentId,
|
||||
ownerInGameName: ownerInGameName ?? null,
|
||||
});
|
||||
|
||||
for (const member of entry.members.slice(1)) {
|
||||
const memberInGameName = tournament.ctx.settings.requireInGameNames
|
||||
? await UserRepository.inGameNameByUserId(member.id)
|
||||
: null;
|
||||
|
||||
joinTeam({
|
||||
await TournamentTeamRepository.join({
|
||||
newTeamId: tournamentTeam.id,
|
||||
userId: member.id,
|
||||
inGameName: memberInGameName ?? null,
|
||||
tournamentId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user