mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-08 20:26:08 -05:00
More robust is sub resolution by persisting it
This commit is contained in:
@@ -881,6 +881,8 @@ export interface TournamentTeamMember {
|
||||
userId: number;
|
||||
role: Generated<"OWNER" | "MANAGER" | "REGULAR">;
|
||||
isStayAsSub: Generated<DBBoolean>;
|
||||
/** Set when the member was added to the roster after registration closed. */
|
||||
isSub: Generated<DBBoolean>;
|
||||
// denormalized from TournamentTeam.isLooking
|
||||
isLooking: Generated<DBBoolean>;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ const createMember = (userId: number) =>
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
}) as const;
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ describe("tournamentSummary()", () => {
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
})),
|
||||
name: `Team ${teamId}`,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -411,6 +411,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -428,6 +429,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -445,6 +447,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -462,6 +465,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -479,6 +483,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
],
|
||||
@@ -527,6 +532,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -544,6 +550,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -561,6 +568,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -578,6 +586,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
],
|
||||
@@ -621,6 +630,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -638,6 +648,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -655,6 +666,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -672,6 +684,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
],
|
||||
@@ -715,6 +728,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -732,6 +746,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -749,6 +764,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -766,6 +782,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -783,6 +800,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
],
|
||||
@@ -831,6 +849,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -848,6 +867,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -865,6 +885,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -882,6 +903,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -899,6 +921,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -916,6 +939,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
],
|
||||
@@ -959,6 +983,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -976,6 +1001,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -993,6 +1019,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -1010,6 +1037,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -1027,6 +1055,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
],
|
||||
@@ -1075,6 +1104,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -1092,6 +1122,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -1109,6 +1140,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
{
|
||||
@@ -1126,6 +1158,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({
|
||||
streamViewerCount: null,
|
||||
streamThumbnailUrl: null,
|
||||
role: "REGULAR",
|
||||
isSub: 0,
|
||||
customAvatarUrl: null,
|
||||
},
|
||||
],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -208,6 +208,7 @@ export async function findById(id: number) {
|
||||
"PlusTier.tier as plusTier",
|
||||
"TournamentTeamMember.role",
|
||||
"TournamentTeamMember.createdAt",
|
||||
"TournamentTeamMember.isSub",
|
||||
sql<string | null> /*sql*/`coalesce(
|
||||
"TournamentTeamMember"."inGameName",
|
||||
"User"."inGameName"
|
||||
|
||||
@@ -143,6 +143,8 @@ export function create({
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
const isSub = (await registrationClosedNow(trx, tournamentId)) ? 1 : 0;
|
||||
|
||||
const inGameName = await resolveInGameName(trx, tournamentId, userId);
|
||||
|
||||
await trx
|
||||
@@ -152,6 +154,7 @@ export function create({
|
||||
userId,
|
||||
role: "OWNER",
|
||||
inGameName,
|
||||
isSub,
|
||||
})
|
||||
.execute();
|
||||
|
||||
@@ -174,6 +177,7 @@ export function create({
|
||||
tournamentTeamId: tournamentTeam.id,
|
||||
userId: memberUserId,
|
||||
inGameName: memberInGameName,
|
||||
isSub,
|
||||
})
|
||||
.execute();
|
||||
|
||||
@@ -286,6 +290,12 @@ export function upsertRegistration({
|
||||
.execute();
|
||||
}
|
||||
|
||||
const isSub =
|
||||
membersToAdd.length > 0 &&
|
||||
(await registrationClosedNow(trx, tournamentId))
|
||||
? 1
|
||||
: 0;
|
||||
|
||||
for (const userId of membersToAdd) {
|
||||
const isOwner = isNew && userId === ownerUserId;
|
||||
const inGameName =
|
||||
@@ -298,6 +308,7 @@ export function upsertRegistration({
|
||||
tournamentTeamId: id,
|
||||
userId,
|
||||
inGameName,
|
||||
isSub,
|
||||
...(isOwner ? { role: "OWNER" as const } : {}),
|
||||
})
|
||||
.execute();
|
||||
@@ -344,6 +355,35 @@ export function upsertRegistration({
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the tournament's registration is closed at the current moment, based on
|
||||
* the organizer-set `regClosesAt` if present, otherwise the tournament start time.
|
||||
* Members added while registration is closed are persisted as subs.
|
||||
*/
|
||||
async function registrationClosedNow(
|
||||
trx: Transaction<DB>,
|
||||
tournamentId: number,
|
||||
) {
|
||||
const { regClosesAt } = await trx
|
||||
.selectFrom("Tournament")
|
||||
.innerJoin("CalendarEvent", "CalendarEvent.tournamentId", "Tournament.id")
|
||||
.innerJoin(
|
||||
"CalendarEventDate",
|
||||
"CalendarEventDate.eventId",
|
||||
"CalendarEvent.id",
|
||||
)
|
||||
.select(
|
||||
sql<number>`coalesce(
|
||||
"Tournament"."settings" ->> 'regClosesAt',
|
||||
min("CalendarEventDate"."startTime")
|
||||
)`.as("regClosesAt"),
|
||||
)
|
||||
.where("Tournament.id", "=", tournamentId)
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return regClosesAt <= databaseTimestampNow();
|
||||
}
|
||||
|
||||
async function resolveInGameName(
|
||||
trx: Transaction<DB>,
|
||||
tournamentId: number,
|
||||
@@ -409,6 +449,7 @@ export function copyFromAnotherTournament({
|
||||
"TournamentTeamMember.inGameName",
|
||||
"TournamentTeamMember.role",
|
||||
"TournamentTeamMember.userId",
|
||||
"TournamentTeamMember.isSub",
|
||||
|
||||
// -- exclude these
|
||||
// "TournamentTeamMember.tournamentTeamId"
|
||||
@@ -718,6 +759,7 @@ export function join({
|
||||
).tournamentId;
|
||||
|
||||
const inGameName = await resolveInGameName(trx, tournamentId, userId);
|
||||
const isSub = (await registrationClosedNow(trx, tournamentId)) ? 1 : 0;
|
||||
|
||||
await trx
|
||||
.insertInto("TournamentTeamMember")
|
||||
@@ -725,6 +767,7 @@ export function join({
|
||||
tournamentTeamId: newTeamId,
|
||||
userId,
|
||||
inGameName,
|
||||
isSub,
|
||||
})
|
||||
.execute();
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import { ModeImage, StageImage } from "~/components/Image";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import type { TournamentDataTeam } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import { userPage } from "~/utils/urls";
|
||||
import { accountCreatedInTheLastSixMonths } from "~/utils/users";
|
||||
import { useTournament, useTournamentFriendCodes } from "../routes/to.$id";
|
||||
@@ -62,9 +61,6 @@ export function TeamWithRoster({
|
||||
<ul className={styles.teamWithRosterMembers}>
|
||||
{team.members.map((member) => {
|
||||
const friendCode = friendCodes?.[member.userId];
|
||||
const isSub =
|
||||
databaseTimestampToDate(member.createdAt) >
|
||||
tournament.registrationClosesAt;
|
||||
|
||||
const name = () => {
|
||||
if (!tournament.ctx.settings.requireInGameNames) {
|
||||
@@ -79,7 +75,7 @@ export function TeamWithRoster({
|
||||
{member.role === "OWNER" ? (
|
||||
<span className={`${styles.teamMemberNameRole}`}>C</span>
|
||||
) : null}
|
||||
{isSub && member.role !== "OWNER" ? (
|
||||
{member.isSub && member.role !== "OWNER" ? (
|
||||
<span
|
||||
className={`${styles.teamMemberNameRole} ${styles.teamMemberNameRoleSub}`}
|
||||
>
|
||||
|
||||
@@ -104,6 +104,7 @@ describe("syncLiveStreams tournament streamers", () => {
|
||||
twitch: "player_one",
|
||||
plusTier: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
createdAt: 0,
|
||||
streamTwitch: null,
|
||||
streamViewerCount: null,
|
||||
@@ -145,6 +146,7 @@ describe("syncLiveStreams tournament streamers", () => {
|
||||
twitch: "dropped_player",
|
||||
plusTier: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
createdAt: 0,
|
||||
streamTwitch: null,
|
||||
streamViewerCount: null,
|
||||
@@ -199,6 +201,7 @@ describe("syncLiveStreams tournament streamers", () => {
|
||||
twitch: "different_account",
|
||||
plusTier: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
createdAt: 0,
|
||||
streamTwitch: null,
|
||||
streamViewerCount: null,
|
||||
@@ -236,6 +239,7 @@ describe("syncLiveStreams tournament streamers", () => {
|
||||
twitch: "streamer_a",
|
||||
plusTier: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
createdAt: 0,
|
||||
streamTwitch: null,
|
||||
streamViewerCount: null,
|
||||
@@ -276,6 +280,7 @@ describe("syncLiveStreams tournament streamers", () => {
|
||||
twitch: "streamer_b",
|
||||
plusTier: null,
|
||||
role: "OWNER",
|
||||
isSub: 0,
|
||||
createdAt: 0,
|
||||
streamTwitch: null,
|
||||
streamViewerCount: null,
|
||||
|
||||
BIN
db-test.sqlite3
BIN
db-test.sqlite3
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
31
migrations/155-tournament-team-member-is-sub.js
Normal file
31
migrations/155-tournament-team-member-is-sub.js
Normal file
@@ -0,0 +1,31 @@
|
||||
export function up(db) {
|
||||
db.transaction(() => {
|
||||
db.prepare(
|
||||
/*sql*/ `alter table "TournamentTeamMember" add column "isSub" integer not null default 0`,
|
||||
).run();
|
||||
|
||||
// backfill from the previous time-based detection: a member registered after
|
||||
// the tournament's registration closed (organizer-set regClosesAt, otherwise
|
||||
// the tournament start time) is considered a sub
|
||||
db.prepare(
|
||||
/*sql*/ `
|
||||
update "TournamentTeamMember"
|
||||
set "isSub" = 1
|
||||
where "createdAt" > (
|
||||
select coalesce(
|
||||
"Tournament"."settings" ->> 'regClosesAt',
|
||||
min("CalendarEventDate"."startTime")
|
||||
)
|
||||
from "TournamentTeam"
|
||||
inner join "Tournament"
|
||||
on "Tournament"."id" = "TournamentTeam"."tournamentId"
|
||||
inner join "CalendarEvent"
|
||||
on "CalendarEvent"."tournamentId" = "Tournament"."id"
|
||||
inner join "CalendarEventDate"
|
||||
on "CalendarEventDate"."eventId" = "CalendarEvent"."id"
|
||||
where "TournamentTeam"."id" = "TournamentTeamMember"."tournamentTeamId"
|
||||
)
|
||||
`,
|
||||
).run();
|
||||
})();
|
||||
}
|
||||
Reference in New Issue
Block a user