diff --git a/app/components/EventsList.tsx b/app/components/EventsList.tsx index 6ada6f1aa..3fc9910ce 100644 --- a/app/components/EventsList.tsx +++ b/app/components/EventsList.tsx @@ -63,7 +63,7 @@ export function EventsList({ const groupedEvents = events.reduce>( (acc, event) => { - const key = getDayKey(event.startTime); + const key = getDayKey(event.startsAt); if (!acc[key]) { acc[key] = []; } @@ -79,7 +79,7 @@ export function EventsList({ <> {dayKeys.map((dayKey) => { const dayEvents = groupedEvents[dayKey]; - const firstDate = new Date(dayEvents[0].startTime * 1000); + const firstDate = new Date(dayEvents[0].startsAt * 1000); return (
@@ -89,7 +89,7 @@ export function EventsList({ key={`${event.type}-${event.id}`} to={event.url} imageUrl={event.logoUrl ?? undefined} - subtitle={timeFormatter.format(event.startTime)} + subtitle={timeFormatter.format(event.startsAt)} onClick={onClick} > {event.scrimStatus === "booked" diff --git a/app/components/elements/TournamentSearch.tsx b/app/components/elements/TournamentSearch.tsx index ee810fbff..cea978daf 100644 --- a/app/components/elements/TournamentSearch.tsx +++ b/app/components/elements/TournamentSearch.tsx @@ -99,7 +99,7 @@ function TournamentItem({ item }: { item: TournamentSearchItem }) {
{item.name} {result.name} diff --git a/app/components/layout/index.tsx b/app/components/layout/index.tsx index 7d893adac..25679d901 100644 --- a/app/components/layout/index.tsx +++ b/app/components/layout/index.tsx @@ -320,7 +320,7 @@ export function Layout({ imageUrl={event.logoUrl ?? undefined} subtitle={ isHydrated ? ( - formatRelativeDate(event.startTime) + formatRelativeDate(event.startsAt) ) : ( Placeholder ) diff --git a/app/db/seed/index.ts b/app/db/seed/index.ts index 2ad8201c4..3ff33b8cf 100644 --- a/app/db/seed/index.ts +++ b/app/db/seed/index.ts @@ -312,7 +312,7 @@ function finalizedBracket() { // CalendarEventDate — recent start time (within 7-day spoiler window) sql .prepare( - `insert into "CalendarEventDate" ("eventId", "startTime") + `insert into "CalendarEventDate" ("eventId", "startsAt") values ($eventId, $startTime)`, ) .run({ @@ -564,7 +564,7 @@ function abDivisionsTournament() { sql .prepare( - `insert into "CalendarEventDate" ("eventId", "startTime") + `insert into "CalendarEventDate" ("eventId", "startsAt") values ($eventId, $startTime)`, ) .run({ @@ -705,7 +705,7 @@ function fixAdminId() { function makeAdminPatron() { sql .prepare( - `update "User" set "patronTier" = 2, "patronSince" = 1674663454 where id = 1`, + `update "User" set "patronTier" = 2, "patronStartedAt" = 1674663454 where id = 1`, ) .run(); } @@ -1117,7 +1117,7 @@ async function lastMonthsVoting() { year, score: 1, tier: idToPlusTier(id), - validAfter: dateToDatabaseTimestamp(fiveMinutesAgo), + becomesValidAt: dateToDatabaseTimestamp(fiveMinutesAgo), votedId: id, }); } @@ -1129,7 +1129,7 @@ async function lastMonthsVoting() { year, score: -1, tier: idToPlusTier(id), - validAfter: dateToDatabaseTimestamp(fiveMinutesAgo), + becomesValidAt: dateToDatabaseTimestamp(fiveMinutesAgo), votedId: id, }); } @@ -1267,19 +1267,19 @@ function patrons() { ) as number[]; const givePatronStm = sql.prepare( - `update user set "patronTier" = $patronTier, "patronSince" = $patronSince where id = $id`, + `update user set "patronTier" = $patronTier, "patronStartedAt" = $patronStartedAt where id = $id`, ); for (const id of userIds) { givePatronStm.run({ id, - patronSince: dateToDatabaseTimestamp(faker.date.past()), + patronStartedAt: dateToDatabaseTimestamp(faker.date.past()), patronTier: faker.helpers.arrayElement([1, 1, 2, 2, 2, 3, 3, 4]), }); } givePatronStm.run({ id: ADMIN_ID, - patronSince: dateToDatabaseTimestamp(faker.date.past()), + patronStartedAt: dateToDatabaseTimestamp(faker.date.past()), patronTier: 2, }); @@ -1372,7 +1372,7 @@ function calendarEvents() { ` insert into "CalendarEventDate" ( "eventId", - "startTime" + "startsAt" ) values ( $eventId, $startTime @@ -1392,7 +1392,7 @@ function calendarEvents() { ` insert into "CalendarEventDate" ( "eventId", - "startTime" + "startsAt" ) values ( $eventId, $startTime @@ -1441,7 +1441,7 @@ async function calendarEventResults() { `select "CalendarEvent"."id" from "CalendarEvent" join "CalendarEventDate" on "CalendarEventDate"."eventId" = "CalendarEvent"."id" - where "CalendarEventDate"."startTime" < $startTime`, + where "CalendarEventDate"."startsAt" < $startTime`, ) .all({ startTime: dateToDatabaseTimestamp(new Date()) }) as any[] ).map((r) => r.id), @@ -1729,7 +1729,7 @@ function calendarEventWithToTools( ` insert into "CalendarEventDate" ( "eventId", - "startTime" + "startsAt" ) values ( $eventId, $startTime @@ -3306,8 +3306,8 @@ async function scrimPosts() { // scrim with admin on the ALPHA side and N-ZAP on the BRAVO side. const adminVsNzapAt = date(true); const adminVsNzapPostId = await ScrimPostRepository.insert({ - at: adminVsNzapAt, - rangeEnd: null, + startsAt: adminVsNzapAt, + rangeEndsAt: null, isScheduledForFuture: true, teamId: null, text: null, @@ -3329,8 +3329,8 @@ async function scrimPosts() { const atTime = date(); const hasRangeEnd = Math.random() > 0.5; await ScrimPostRepository.insert({ - at: atTime, - rangeEnd: hasRangeEnd + startsAt: atTime, + rangeEndsAt: hasRangeEnd ? dateToDatabaseTimestamp( add(databaseTimestampToDate(atTime), { hours: faker.helpers.rangeToNumber({ min: 1, max: 3 }), @@ -3355,7 +3355,7 @@ async function scrimPosts() { const adminPostAtTime = date(true); // admin's scrim is always at least 1 hour in the future const adminPostId = await ScrimPostRepository.insert({ - at: adminPostAtTime, + startsAt: adminPostAtTime, isScheduledForFuture: true, text: faker.number.float(1) > 0.5 @@ -3777,7 +3777,7 @@ function splatoonRotations() { sql .prepare( ` - insert into "SplatoonRotation" ("type", "mode", "stageId1", "stageId2", "startTime", "endTime") + insert into "SplatoonRotation" ("type", "mode", "stageId1", "stageId2", "startsAt", "endsAt") values ($type, $mode, $stageId1, $stageId2, $startTime, $endTime) `, ) diff --git a/app/db/tables.ts b/app/db/tables.ts index 03ec94ea8..c0739afbe 100644 --- a/app/db/tables.ts +++ b/app/db/tables.ts @@ -226,7 +226,7 @@ export interface CalendarEventBadge { export interface CalendarEventDate { eventId: number; id: GeneratedAlways; - startTime: number; + startsAt: number; } export interface CalendarEventResultPlayer { @@ -329,7 +329,7 @@ export interface LFGPost { plusTierVisibility: number | null; languages: string | null; updatedAt: Generated; - createdAt: GeneratedAlways; + createdAt: Generated; } export interface MapPoolMap { @@ -362,7 +362,7 @@ export interface PlayerResult { export interface PlusSuggestion { authorId: number; - createdAt: GeneratedAlways; + createdAt: Generated; id: GeneratedAlways; month: number; suggestedId: number; @@ -382,7 +382,8 @@ export interface PlusVote { month: number; score: number; tier: number; - validAfter: number; + /** When the vote stops being secret and starts counting towards the results i.e. the end of the voting range. */ + becomesValidAt: number; votedId: number; year: number; } @@ -417,6 +418,7 @@ export interface Skill { season: number; tournamentId: number | null; userId: number | null; + /** Can be null because we did not always save this. */ createdAt: number | null; } @@ -521,7 +523,7 @@ export interface TournamentMatchPickBanEvent { matchId: number; authorId: number | null; number: number; - createdAt: GeneratedAlways; + createdAt: Generated; } export interface TournamentMatchGameResult { @@ -585,8 +587,7 @@ export interface TournamentStage { settings: JSONColumnType; tournamentId: number; type: (typeof TOURNAMENT_STAGE_TYPES)[number]; - // not Generated<> because SQLite doesn't allow altering tables to add columns with default values :( - createdAt: number | null; + createdAt: Generated; } /** Tournament sub post, shown in a list of subs available for teams to pick from. */ @@ -681,7 +682,7 @@ export interface TournamentAuditLog { /** References {@link TournamentTeamHistory.id} so the team name stays resolvable after the team is hard-deleted. */ tournamentTeamHistoryId: number | null; metadata: JSONColumnTypeNullable; - createdAt: number; + createdAt: Generated; } export interface TournamentOrganization { @@ -769,7 +770,8 @@ export interface UnvalidatedVideo { title: string; type: string; validatedAt: number | null; - youtubeDate: number; + /** When the video was published on YouTube. Day precision only, stored as noon UTC of that day. */ + youtubePublishedAt: number; youtubeId: string; } @@ -806,9 +808,9 @@ export interface User { languages: string | null; motionSens: number | null; pronouns: JSONColumnTypeNullable; - patronSince: number | null; + patronStartedAt: number | null; patronTier: number | null; - patronTill: number | null; + patronExpiresAt: number | null; showDiscordUniqueName: Generated; stickSens: number | null; twitch: string | null; @@ -886,7 +888,7 @@ export interface UserFriendCode { friendCode: string; userId: number; submitterUserId: number; - createdAt: GeneratedAlways; + createdAt: Generated; } export interface UserWidget { @@ -899,7 +901,7 @@ export interface ApiToken { userId: number; token: string; type: Generated; - createdAt: GeneratedAlways; + createdAt: Generated; } export interface LiveStream { @@ -922,7 +924,7 @@ export interface ExternalStream { name: string; url: string; avatarImgId: number | null; - startTime: number; + startsAt: number; createdAt: Generated; } @@ -943,7 +945,7 @@ export interface BanLog { banned: number | null; bannedReason: string | null; bannedByUserId: number; - createdAt: GeneratedAlways; + createdAt: Generated; } export interface ModNote { @@ -951,7 +953,7 @@ export interface ModNote { userId: number; authorId: number; text: string; - createdAt: GeneratedAlways; + createdAt: Generated; isDeleted: Generated; } @@ -974,7 +976,8 @@ export interface Video { type: "SCRIM" | "TOURNAMENT" | "MATCHMAKING" | "CAST" | "SENDOUQ"; /** Never `null` in practice, the view filters unvalidated rows out. */ validatedAt: number | null; - youtubeDate: number; + /** When the video was published on YouTube. Day precision only, stored as noon UTC of that day. */ + youtubePublishedAt: number; youtubeId: string; } @@ -1014,9 +1017,9 @@ export interface XRankPlacement { export interface ScrimPost { id: GeneratedAlways; /** When is the scrim scheduled to happen */ - at: number; - /** Optional end of time range indicating team accepts scrims starting between at and rangeEnd */ - rangeEnd: number | null; + startsAt: number; + /** Optional end of time range indicating team accepts scrims starting between startsAt and rangeEndsAt */ + rangeEndsAt: number | null; /** Highest LUTI div accepted */ maxDiv: number | null; /** Lowest LUTI div accepted */ @@ -1043,7 +1046,7 @@ export interface ScrimPost { maps: "SZ" | "ALL" | "RANKED" | null; /** If set, specifies the maps of a tournament to play */ mapsTournamentId: number | null; - createdAt: GeneratedAlways; + createdAt: Generated; updatedAt: Generated; } @@ -1080,10 +1083,10 @@ export interface ScrimPostRequest { scrimPostId: number; teamId: number | null; message: string | null; - /** Specific time selected by requester (required when post has rangeEnd) */ - at: number | null; + /** Specific time selected by requester (required when post has rangeEndsAt) */ + startsAt: number | null; isAccepted: Generated; - createdAt: GeneratedAlways; + createdAt: Generated; } export interface ScrimPostRequestUser { @@ -1097,7 +1100,7 @@ export interface Association { id: GeneratedAlways; name: string; inviteCode: string; - createdAt: GeneratedAlways; + createdAt: Generated; } export interface AssociationMember { @@ -1111,7 +1114,7 @@ export interface Notification { type: NotificationValue["type"]; meta: JSONColumnTypeNullable>; pictureUrl: string | null; - createdAt: GeneratedAlways; + createdAt: Generated; } export interface NotificationUser { @@ -1133,8 +1136,8 @@ export interface SplatoonRotation { mode: string; stageId1: number; stageId2: number; - startTime: number; - endTime: number; + startsAt: number; + endsAt: number; } export type Tables = { [P in keyof DB]: Selectable }; diff --git a/app/features/admin/AdminRepository.server.ts b/app/features/admin/AdminRepository.server.ts index 173492ccd..45d8b891b 100644 --- a/app/features/admin/AdminRepository.server.ts +++ b/app/features/admin/AdminRepository.server.ts @@ -291,15 +291,15 @@ export async function linkUserAndPlayer({ export function forcePatron(args: { id: number; patronTier: Tables["User"]["patronTier"]; - patronSince: Date; - patronTill: Date; + patronStartedAt: Date; + patronExpiresAt: Date; }) { return db .updateTable("User") .set({ patronTier: args.patronTier, - patronSince: dateToDatabaseTimestamp(args.patronSince), - patronTill: dateToDatabaseTimestamp(args.patronTill), + patronStartedAt: dateToDatabaseTimestamp(args.patronStartedAt), + patronExpiresAt: dateToDatabaseTimestamp(args.patronExpiresAt), }) .where("User.id", "=", args.id) .execute(); diff --git a/app/features/admin/ExternalStreamRepository.server.ts b/app/features/admin/ExternalStreamRepository.server.ts index 6b52261d8..64b42391d 100644 --- a/app/features/admin/ExternalStreamRepository.server.ts +++ b/app/features/admin/ExternalStreamRepository.server.ts @@ -12,7 +12,7 @@ const RETENTION_SECONDS = 24 * 60 * 60; export function insert( args: Pick< TablesInsertable["ExternalStream"], - "name" | "url" | "avatarImgId" | "startTime" + "name" | "url" | "avatarImgId" | "startsAt" >, ) { return db.insertInto("ExternalStream").values(args).execute(); @@ -36,12 +36,12 @@ export function all() { "ExternalStream.id", "ExternalStream.name", "ExternalStream.url", - "ExternalStream.startTime", + "ExternalStream.startsAt", concatUserSubmittedImagePrefix(eb.ref("UserSubmittedImage.url")).as( "avatarUrl", ), ]) - .orderBy("ExternalStream.startTime", "asc") + .orderBy("ExternalStream.startsAt", "asc") .execute(); } @@ -58,13 +58,13 @@ export function forSidebar() { "ExternalStream.id", "ExternalStream.name", "ExternalStream.url", - "ExternalStream.startTime", + "ExternalStream.startsAt", concatUserSubmittedImagePrefix(eb.ref("UserSubmittedImage.url")).as( "avatarUrl", ), ]) .where( - "ExternalStream.startTime", + "ExternalStream.startsAt", ">=", databaseTimestampNow() - SIDEBAR_VISIBLE_SECONDS, ) @@ -75,6 +75,6 @@ export function forSidebar() { export function deleteOld() { return db .deleteFrom("ExternalStream") - .where("startTime", "<", databaseTimestampNow() - RETENTION_SECONDS) + .where("startsAt", "<", databaseTimestampNow() - RETENTION_SECONDS) .executeTakeFirst(); } diff --git a/app/features/admin/actions/admin.server.ts b/app/features/admin/actions/admin.server.ts index 840786395..f3c50e7f5 100644 --- a/app/features/admin/actions/admin.server.ts +++ b/app/features/admin/actions/admin.server.ts @@ -74,9 +74,9 @@ export const action = async ({ request }: ActionFunctionArgs) => { await AdminRepository.forcePatron({ id: data.user, - patronSince: new Date(), + patronStartedAt: new Date(), patronTier: data.patronTier, - patronTill: new Date(data.patronTill), + patronExpiresAt: new Date(data.patronExpiresAt), }); message = "Patron status updated"; @@ -207,7 +207,7 @@ export const adminActionSchema = z.union([ _action: _action("FORCE_PATRON"), user: z.preprocess(actualNumber, z.number().positive()), patronTier: z.preprocess(actualNumber, z.number()), - patronTill: z.string(), + patronExpiresAt: z.string(), }), z.object({ _action: _action("VIDEO_ADDER"), diff --git a/app/features/admin/actions/admin.streams.server.ts b/app/features/admin/actions/admin.streams.server.ts index c5aac042b..4601af07a 100644 --- a/app/features/admin/actions/admin.streams.server.ts +++ b/app/features/admin/actions/admin.streams.server.ts @@ -27,7 +27,7 @@ export const action = async ({ request }: ActionFunctionArgs) => { name: data.name, url: data.url, avatarImgId: data.avatar, - startTime: dateToDatabaseTimestamp(data.startTime), + startsAt: dateToDatabaseTimestamp(data.startTime), }); break; } diff --git a/app/features/admin/routes/admin.streams.tsx b/app/features/admin/routes/admin.streams.tsx index 9297aa26f..2880f9280 100644 --- a/app/features/admin/routes/admin.streams.tsx +++ b/app/features/admin/routes/admin.streams.tsx @@ -68,7 +68,7 @@ function ExternalStreamList() { - +
diff --git a/app/features/api-private/routes/seed.ts b/app/features/api-private/routes/seed.ts index 5281309b2..fc5897dc5 100644 --- a/app/features/api-private/routes/seed.ts +++ b/app/features/api-private/routes/seed.ts @@ -88,7 +88,7 @@ function adjustSeedDatesToCurrent(variation: SeedVariation) { REG_OPEN_TOURNAMENT_IDS.includes(tournamentId); sql - .prepare(`UPDATE "CalendarEventDate" SET startTime = ? WHERE eventId = ?`) + .prepare(`UPDATE "CalendarEventDate" SET startsAt = ? WHERE eventId = ?`) .run(isRegOpen ? halfAnHourFromNow : oneHourAgo, id); } @@ -103,17 +103,17 @@ function adjustSeedDatesToCurrent(variation: SeedVariation) { const scrimTimeOffset = now - SEED_REFERENCE_TIMESTAMP; sql .prepare( - `UPDATE "ScrimPost" SET "at" = "at" + ?, "createdAt" = "createdAt" + ?`, + `UPDATE "ScrimPost" SET "startsAt" = "startsAt" + ?, "createdAt" = "createdAt" + ?`, ) .run(scrimTimeOffset, scrimTimeOffset); sql .prepare( - `UPDATE "ScrimPost" SET "rangeEnd" = "rangeEnd" + ? WHERE "rangeEnd" IS NOT NULL`, + `UPDATE "ScrimPost" SET "rangeEndsAt" = "rangeEndsAt" + ? WHERE "rangeEndsAt" IS NOT NULL`, ) .run(scrimTimeOffset); sql .prepare( - `UPDATE "ScrimPostRequest" SET "at" = "at" + ? WHERE "at" IS NOT NULL`, + `UPDATE "ScrimPostRequest" SET "startsAt" = "startsAt" + ? WHERE "startsAt" IS NOT NULL`, ) .run(scrimTimeOffset); } diff --git a/app/features/api-public/routes/calendar.$year.$week.ts b/app/features/api-public/routes/calendar.$year.$week.ts index da7b8f3f0..49115b12a 100644 --- a/app/features/api-public/routes/calendar.$year.$week.ts +++ b/app/features/api-public/routes/calendar.$year.$week.ts @@ -24,7 +24,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => { const result: GetCalendarWeekResponse = events.map((event) => ({ name: event.name, - startTime: databaseTimestampToDate(event.startTime).toISOString(), + startTime: databaseTimestampToDate(event.startsAt).toISOString(), tournamentId: event.tournamentId, tournamentUrl: event.tournamentId ? `https://sendou.ink/to/${event.tournamentId}/brackets` @@ -48,19 +48,15 @@ function fetchEventsOfWeek(args: { week: number; year: number }) { .select([ "Tournament.id as tournamentId", "CalendarEvent.name", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", ]) .where( - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", ">=", dateToDatabaseTimestamp(startTime), ) - .where( - "CalendarEventDate.startTime", - "<=", - dateToDatabaseTimestamp(endTime), - ) + .where("CalendarEventDate.startsAt", "<=", dateToDatabaseTimestamp(endTime)) .where("CalendarEvent.hidden", "=", 0) - .orderBy("CalendarEventDate.startTime", "asc") + .orderBy("CalendarEventDate.startsAt", "asc") .execute(); } diff --git a/app/features/api-public/routes/tournament.$id.ts b/app/features/api-public/routes/tournament.$id.ts index aa0771d16..27359d3f0 100644 --- a/app/features/api-public/routes/tournament.$id.ts +++ b/app/features/api-public/routes/tournament.$id.ts @@ -26,7 +26,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => { .select(({ eb, exists, selectFrom }) => [ "CalendarEvent.name", "CalendarEvent.organizationId", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", "Tournament.settings", exists( selectFrom("TournamentResult") @@ -61,7 +61,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => { const result: GetTournamentResponse = { name: tournament.name, - startTime: databaseTimestampToDate(tournament.startTime).toISOString(), + startTime: databaseTimestampToDate(tournament.startsAt).toISOString(), url: `https://sendou.ink/to/${id}/brackets`, logoUrl: tournament.logoUrl, teams: { diff --git a/app/features/api/core/perms.test.ts b/app/features/api/core/perms.test.ts index 4f1ec8f82..125e7508b 100644 --- a/app/features/api/core/perms.test.ts +++ b/app/features/api/core/perms.test.ts @@ -46,8 +46,8 @@ describe("Permission logic consistency between allApiTokens and checkUserHasApiA await AdminRepository.forcePatron({ id: 1, patronTier: 2, - patronSince: new Date(), - patronTill: add(new Date(), { months: 3 }), + patronStartedAt: new Date(), + patronExpiresAt: add(new Date(), { months: 3 }), }); await ApiRepository.generateToken(1, "read"); @@ -64,8 +64,8 @@ describe("Permission logic consistency between allApiTokens and checkUserHasApiA await AdminRepository.forcePatron({ id: 1, patronTier: 1, - patronSince: new Date(), - patronTill: add(new Date(), { months: 3 }), + patronStartedAt: new Date(), + patronExpiresAt: add(new Date(), { months: 3 }), }); await ApiRepository.generateToken(1, "read"); diff --git a/app/features/calendar/CalendarRepository.server.ts b/app/features/calendar/CalendarRepository.server.ts index 03ccbb65f..7751816df 100644 --- a/app/features/calendar/CalendarRepository.server.ts +++ b/app/features/calendar/CalendarRepository.server.ts @@ -149,7 +149,7 @@ const withTeamsCount = ( .where((eb) => eb.or([ eb("TournamentTeamCheckIn.checkedInAt", "is not", null), - eb("CalendarEventDate.startTime", ">", databaseTimestampNow()), + eb("CalendarEventDate.startsAt", ">", databaseTimestampNow()), ]), ) .select(({ fn }) => [fn.countAll().as("teamsCount")]); @@ -176,10 +176,10 @@ function findAllBetweenTwoTimestampsQuery({ "Tournament.tier", "CalendarEvent.name", "CalendarEvent.tags", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", // events get grouped to their closest :00 or :30 so for example users can't make their event start at :59 to make it show at the top - sql`(("CalendarEventDate"."startTime" + 900) / 1800) * 1800`.as( - "normalizedStartTime", + sql`(("CalendarEventDate"."startsAt" + 900) / 1800) * 1800`.as( + "normalizedStartsAt", ), withOrganization(eb).as("organization"), withTeamsCount(eb).as("teamsCount"), @@ -205,15 +205,11 @@ function findAllBetweenTwoTimestampsQuery({ ]) .where("CalendarEvent.hidden", "=", 0) .where( - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", ">=", dateToDatabaseTimestamp(startTime), ) - .where( - "CalendarEventDate.startTime", - "<=", - dateToDatabaseTimestamp(endTime), - ) + .where("CalendarEventDate.startsAt", "<=", dateToDatabaseTimestamp(endTime)) .$narrowType<{ teamsCount: NotNull }>() .execute(); } @@ -224,14 +220,14 @@ function findAllBetweenTwoTimestampsMapped( at: number; events: Array; }> { - const mapped: Array = rows.map( + const mapped: Array = rows.map( (row) => { const tags = row.tags ? (row.tags.split(",") as CalendarEvent["tags"]) : []; const isPastEvent = - databaseTimestampToDate(row.startTime) < sub(new Date(), { days: 1 }); + databaseTimestampToDate(row.startsAt) < sub(new Date(), { days: 1 }); const tentativeTier = row.tier === null && row.organizationId !== null && @@ -241,7 +237,7 @@ function findAllBetweenTwoTimestampsMapped( : null; return { - at: databaseTimestampToJavascriptTimestamp(row.startTime), + at: databaseTimestampToJavascriptTimestamp(row.startsAt), type: "calendar", id: row.eventId, url: row.tournamentId @@ -265,11 +261,11 @@ function findAllBetweenTwoTimestampsMapped( : null, badges: row.badges, logoUrl: row.logoUrl, - startTime: row.normalizedStartTime, + startsAt: row.normalizedStartsAt, isRanked: row.tournamentSettings ? tournamentIsRanked({ isSetAsRanked: row.tournamentSettings.isRanked, - startTime: databaseTimestampToDate(row.startTime), + startsAt: databaseTimestampToDate(row.startsAt), minMembersPerTeam: row.tournamentSettings.minMembersPerTeam ?? 4, isTest: row.tournamentSettings.isTest ?? false, }) @@ -280,7 +276,7 @@ function findAllBetweenTwoTimestampsMapped( }, ); - const grouped = R.groupBy(mapped, (row) => row.startTime); + const grouped = R.groupBy(mapped, (row) => row.startsAt); const dates = Object.keys(grouped) .map((dbTimestamp) => ({ at: databaseTimestampToDate(Number(dbTimestamp)).getTime(), @@ -327,7 +323,7 @@ export async function findById( "CalendarEvent.avatarImgId", "Tournament.mapPickingStyle", "User.id as authorId", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", "CalendarEventDate.eventId", "User.username", "User.discordId", @@ -338,7 +334,7 @@ export async function findById( ), ]) .where("CalendarEvent.id", "=", id) - .orderBy("CalendarEventDate.startTime", "asc") + .orderBy("CalendarEventDate.startsAt", "asc") .execute(); if (!firstRow) return null; @@ -346,8 +342,8 @@ export async function findById( return { ...firstRow, tags: tagsArray(firstRow), - startTimes: [firstRow, ...rest].map((row) => row.startTime), - startTime: undefined, + startTimes: [firstRow, ...rest].map((row) => row.startsAt), + startsAt: undefined, }; } @@ -363,7 +359,7 @@ export async function findRecentTournamentsByAuthorId(authorId: number) { .select([ "CalendarEvent.id", "CalendarEvent.name", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", ]) .where("CalendarEvent.authorId", "=", authorId) .orderBy("CalendarEvent.id", "desc") @@ -425,7 +421,7 @@ type CreateArgs = Pick< | "bracketUrl" | "organizationId" > & { - startTimes: Array; + startTimes: Array; badges: Array; mapPoolMaps?: Array>; isFullTournament: boolean; @@ -750,7 +746,7 @@ function createDatesInTrx({ }) { return trx .insertInto("CalendarEventDate") - .values(startTimes.map((startTime) => ({ startTime, eventId }))) + .values(startTimes.map((startsAt) => ({ startsAt, eventId }))) .execute(); } diff --git a/app/features/calendar/calendar-types.ts b/app/features/calendar/calendar-types.ts index 6225f2cb9..4783a4e86 100644 --- a/app/features/calendar/calendar-types.ts +++ b/app/features/calendar/calendar-types.ts @@ -44,7 +44,7 @@ export interface CalendarEvent extends CommonEvent { export interface ShowcaseCalendarEvent extends CommonEvent { type: "showcase"; - startTime: number; + startsAt: number; /** Id of the organization the event belongs to, if any */ organizationId: number | null; /** Tournament is hidden from the public (test tournament) */ diff --git a/app/features/calendar/components/TournamentCard.tsx b/app/features/calendar/components/TournamentCard.tsx index 168df2c3d..b11400c8e 100644 --- a/app/features/calendar/components/TournamentCard.tsx +++ b/app/features/calendar/components/TournamentCard.tsx @@ -36,7 +36,7 @@ export function TournamentCard({ const isHostedOnSendouInk = typeof tournament.isRanked === "boolean"; const startDate = isShowcase - ? databaseTimestampToDate(tournament.startTime) + ? databaseTimestampToDate(tournament.startsAt) : null; return ( diff --git a/app/features/calendar/loaders/events.server.ts b/app/features/calendar/loaders/events.server.ts index a13ab271e..3d605f725 100644 --- a/app/features/calendar/loaders/events.server.ts +++ b/app/features/calendar/loaders/events.server.ts @@ -29,19 +29,19 @@ export const loader = async () => { const registered = tournamentsData.participatingFor .map(tournamentToSidebarEvent) - .sort((a, b) => a.startTime - b.startTime); + .sort((a, b) => a.startsAt - b.startsAt); const hosting = tournamentsData.organizingFor .map(tournamentToSidebarEvent) - .sort((a, b) => a.startTime - b.startTime); + .sort((a, b) => a.startsAt - b.startsAt); const scrims = scrimsData .map(scrimToSidebarEvent) - .sort((a, b) => a.startTime - b.startTime); + .sort((a, b) => a.startsAt - b.startsAt); const saved = savedTournaments .map(tournamentToSidebarEvent) - .sort((a, b) => a.startTime - b.startTime); + .sort((a, b) => a.startsAt - b.startsAt); const userOrganizationIds = new Set(userOrganizations.map((org) => org.id)); const organization = upcomingTournaments @@ -52,7 +52,7 @@ export const loader = async () => { userOrganizationIds.has(tournament.organizationId), ) .map(tournamentToSidebarEvent) - .sort((a, b) => a.startTime - b.startTime); + .sort((a, b) => a.startsAt - b.startsAt); return { registered, hosting, scrims, saved, organization }; }; diff --git a/app/features/calendar/routes/calendar.new.tsx b/app/features/calendar/routes/calendar.new.tsx index da200c7eb..9256e3f33 100644 --- a/app/features/calendar/routes/calendar.new.tsx +++ b/app/features/calendar/routes/calendar.new.tsx @@ -142,7 +142,7 @@ function useDefaultValues() { const regClosesAt: RegClosesAtOption = tournamentCtx?.settings.regClosesAt ? datesToRegClosesAt({ - startTime: databaseTimestampToDate(tournamentCtx.startTime), + startTime: databaseTimestampToDate(tournamentCtx.startsAt), regClosesAt: databaseTimestampToDate( tournamentCtx.settings.regClosesAt, ), @@ -243,7 +243,7 @@ function TemplateTournamentForm() { {recentTournaments.map((event) => ( ))} diff --git a/app/features/core/streams/streams.server.ts b/app/features/core/streams/streams.server.ts index b7d3facc8..10fa61f8a 100644 --- a/app/features/core/streams/streams.server.ts +++ b/app/features/core/streams/streams.server.ts @@ -39,7 +39,7 @@ export function getLiveTournamentStreams(): SidebarStream[] { imageUrl: tournament.ctx.logoUrl, url: tournamentStreamsPage(tournament.ctx.id), subtitle: deriveCurrentRound(tournament), - startsAt: dateToDatabaseTimestamp(tournament.ctx.startTime), + startsAt: dateToDatabaseTimestamp(tournament.ctx.startsAt), tier: tournament.ctx.tier, membersPerTeam: tournament.minMembersPerTeam, }); diff --git a/app/features/friends/FriendRepository.server.ts b/app/features/friends/FriendRepository.server.ts index 64a185bb7..6d522430c 100644 --- a/app/features/friends/FriendRepository.server.ts +++ b/app/features/friends/FriendRepository.server.ts @@ -105,7 +105,7 @@ function withLfgJoins>(qb: QB) { ...commonUserSelect(eb), "CalendarEvent.name as tournamentName", "TournamentTeam.tournamentId", - "CalendarEventDate.startTime as tournamentStartTime", + "CalendarEventDate.startsAt as tournamentStartTime", sql< number | null >`(SELECT COUNT(*) FROM "TournamentTeamMember" "ttm" WHERE "ttm"."tournamentTeamId" = "TournamentTeam"."id")`.as( diff --git a/app/features/front-page/components/SplatoonRotations.tsx b/app/features/front-page/components/SplatoonRotations.tsx index 9419ebdbd..b661ded6b 100644 --- a/app/features/front-page/components/SplatoonRotations.tsx +++ b/app/features/front-page/components/SplatoonRotations.tsx @@ -44,7 +44,7 @@ export function SplatoonRotations() { const nowUnixLive = useNowUnix(data.now); const allInThePast = data.rotations.every( - (rotation) => rotation.endTime <= nowUnixLive, + (rotation) => rotation.endsAt <= nowUnixLive, ); if (allInThePast) return null; @@ -63,8 +63,8 @@ export function SplatoonRotations() { if (activeFilter !== "ALL" && rotation.mode !== activeFilter) continue; const isCurrent = - rotation.startTime <= nowUnixLive && rotation.endTime > nowUnixLive; - const isNext = rotation.startTime > nowUnixLive; + rotation.startsAt <= nowUnixLive && rotation.endsAt > nowUnixLive; + const isNext = rotation.startsAt > nowUnixLive; if (!isCurrent && !isNext) continue; @@ -173,8 +173,8 @@ function RotationCard({ const progress = current ? rotationProgress( now, - databaseTimestampToDate(current.startTime), - databaseTimestampToDate(current.endTime), + databaseTimestampToDate(current.startsAt), + databaseTimestampToDate(current.endsAt), ) : null; const displayRotation = current ?? next; @@ -195,7 +195,7 @@ function RotationCard({ style={{ width: `${progress * 100}%` }} /> - {formatDistanceToNow(current.endTime)} + {formatDistanceToNow(current.endsAt)}
) : null} @@ -208,7 +208,7 @@ function RotationCard({ > @@ -233,11 +233,11 @@ function RotationCard({
{shownNext ? (
- {current && shownNext.startTime === current.endTime ? ( + {current && shownNext.startsAt === current.endsAt ? ( t("front:rotations.nextLabel") ) : ( diff --git a/app/features/front-page/core/ShowcaseTournaments.server.ts b/app/features/front-page/core/ShowcaseTournaments.server.ts index ca185d71f..e50a02dd7 100644 --- a/app/features/front-page/core/ShowcaseTournaments.server.ts +++ b/app/features/front-page/core/ShowcaseTournaments.server.ts @@ -190,7 +190,7 @@ function deleteExtraResults(tournaments: ShowcaseCalendarEvent[]) { (tournament) => tournament.firstPlacers.length === 0 && !tournament.isFinalized && - tournament.startTime > threeDaysAgo, + tournament.startsAt > threeDaysAgo, ); const rankedResults = tournaments @@ -214,7 +214,7 @@ function deleteExtraResults(tournaments: ShowcaseCalendarEvent[]) { return { results: [...rankedResultsToKeep, ...nonRankedResultsToKeep].sort( - (a, b) => b.startTime - a.startTime, + (a, b) => b.startsAt - a.startsAt, ), upcoming: nonResults, }; @@ -225,8 +225,8 @@ function resolveShowcaseTournaments( ): ShowcaseCalendarEvent[] { const happeningDuringNextWeek = tournaments.filter( (tournament) => - tournament.startTime > databaseTimestampSixHoursAgo() && - tournament.startTime < databaseTimestampWeekFromNow(), + tournament.startsAt > databaseTimestampSixHoursAgo() && + tournament.startsAt < databaseTimestampWeekFromNow(), ); const sorted = happeningDuringNextWeek.sort( (a, b) => b.teamsCount - a.teamsCount, @@ -238,7 +238,7 @@ function resolveShowcaseTournaments( .filter((tournament) => !tournament.isRanked) .slice(0, 6 - ranked.length); - return [...ranked, ...nonRanked].sort((a, b) => a.startTime - b.startTime); + return [...ranked, ...nonRanked].sort((a, b) => a.startsAt - b.startsAt); } async function tournamentsToParticipationInfoMap( @@ -302,7 +302,7 @@ function mapTournamentFromDB( authorId: tournament.authorId, organizationId: tournament.organizationId, name: tournament.name, - startTime: tournament.startTime, + startsAt: tournament.startsAt, teamsCount: tournament.teamsCount, logoUrl: tournament.logoUrl, organization: tournament.organization @@ -313,7 +313,7 @@ function mapTournamentFromDB( : null, isRanked: tournamentIsRanked({ isSetAsRanked: tournament.settings.isRanked, - startTime: databaseTimestampToDate(tournament.startTime), + startsAt: databaseTimestampToDate(tournament.startsAt), minMembersPerTeam: tournament.settings.minMembersPerTeam ?? 4, isTest: tournament.settings.isTest ?? false, }), diff --git a/app/features/mmr/SkillRepository.server.ts b/app/features/mmr/SkillRepository.server.ts index 6c957de2b..3ba155fc4 100644 --- a/app/features/mmr/SkillRepository.server.ts +++ b/app/features/mmr/SkillRepository.server.ts @@ -169,7 +169,7 @@ export async function seasonProgressionByUserId({ ) .select(({ fn }) => [ fn.max("Skill.ordinal").as("ordinal"), - sql`date(coalesce("Skill"."createdAt", "GroupMatch"."createdAt", "CalendarEventDate"."startTime"), 'unixepoch')`.as( + sql`date(coalesce("Skill"."createdAt", "GroupMatch"."createdAt", "CalendarEventDate"."startsAt"), 'unixepoch')`.as( "date", ), ]) diff --git a/app/features/plus-voting/PlusVotingRepository.server.ts b/app/features/plus-voting/PlusVotingRepository.server.ts index d9245e816..0780cb390 100644 --- a/app/features/plus-voting/PlusVotingRepository.server.ts +++ b/app/features/plus-voting/PlusVotingRepository.server.ts @@ -37,7 +37,7 @@ export async function allPlusTiersFromLatestVoting() { const latestVoting = await db .selectFrom("PlusVote") .select(["PlusVote.year", "PlusVote.month"]) - .where("PlusVote.validAfter", "<", sql`strftime('%s', 'now')`) + .where("PlusVote.becomesValidAt", "<", sql`strftime('%s', 'now')`) .orderBy("PlusVote.year", "desc") .orderBy("PlusVote.month", "desc") .limit(1) @@ -201,7 +201,13 @@ export async function hasVoted(args: { export type UpsertManyPlusVotesArgs = Pick< TablesInsertable["PlusVote"], - "month" | "year" | "tier" | "authorId" | "votedId" | "score" | "validAfter" + | "month" + | "year" + | "tier" + | "authorId" + | "votedId" + | "score" + | "becomesValidAt" >[]; export function upsertMany(votes: UpsertManyPlusVotesArgs) { const firstVote = votes[0]; diff --git a/app/features/plus-voting/actions/plus.voting.server.ts b/app/features/plus-voting/actions/plus.voting.server.ts index 7a879169a..9c47557eb 100644 --- a/app/features/plus-voting/actions/plus.voting.server.ts +++ b/app/features/plus-voting/actions/plus.voting.server.ts @@ -59,7 +59,7 @@ export const action: ActionFunction = async ({ request }) => { month, year, tier: user.plusTier!, // no clue why i couldn't make narrowing the type down above work - validAfter: dateToDatabaseTimestamp(votingRange.endDate), + becomesValidAt: dateToDatabaseTimestamp(votingRange.endDate), })), ); diff --git a/app/features/scrims/ScrimPostRepository.server.test.ts b/app/features/scrims/ScrimPostRepository.server.test.ts index 48cee26a2..f9ae6d9ff 100644 --- a/app/features/scrims/ScrimPostRepository.server.test.ts +++ b/app/features/scrims/ScrimPostRepository.server.test.ts @@ -16,17 +16,17 @@ const WINDOW = { }; function insertPost({ - at, - rangeEnd = null, + startsAt, + rangeEndsAt = null, users, }: { - at: Date; - rangeEnd?: Date | null; + startsAt: Date; + rangeEndsAt?: Date | null; users: Array<{ userId: number; isOwner: 0 | 1 }>; }) { return ScrimPostRepository.insert({ - at: dbTs(at), - rangeEnd: rangeEnd ? dbTs(rangeEnd) : null, + startsAt: dbTs(startsAt), + rangeEndsAt: rangeEndsAt ? dbTs(rangeEndsAt) : null, maxDiv: null, minDiv: null, teamId: null, @@ -51,7 +51,7 @@ describe("findPendingOverlapsForUsers", () => { test("returns a specific-time pending post in window with its member ids", async () => { const postId = await insertPost({ - at: BOOKED_AT, + startsAt: BOOKED_AT, users: [ { userId: 1, isOwner: 1 }, { userId: 2, isOwner: 0 }, @@ -71,8 +71,8 @@ describe("findPendingOverlapsForUsers", () => { test("returns a ranged post whose interval overlaps the window even if its start is outside", async () => { const postId = await insertPost({ - at: sub(BOOKED_AT, { hours: 2 }), - rangeEnd: BOOKED_AT, + startsAt: sub(BOOKED_AT, { hours: 2 }), + rangeEndsAt: BOOKED_AT, users: [{ userId: 1, isOwner: 1 }], }); @@ -87,8 +87,8 @@ describe("findPendingOverlapsForUsers", () => { test("does not return a ranged post whose interval does not overlap the window", async () => { await insertPost({ - at: sub(BOOKED_AT, { hours: 5 }), - rangeEnd: sub(BOOKED_AT, { hours: 3 }), + startsAt: sub(BOOKED_AT, { hours: 5 }), + rangeEndsAt: sub(BOOKED_AT, { hours: 3 }), users: [{ userId: 1, isOwner: 1 }], }); @@ -103,7 +103,7 @@ describe("findPendingOverlapsForUsers", () => { test("excludes the just-booked post even when it overlaps", async () => { const postId = await insertPost({ - at: BOOKED_AT, + startsAt: BOOKED_AT, users: [{ userId: 1, isOwner: 1 }], }); @@ -118,7 +118,7 @@ describe("findPendingOverlapsForUsers", () => { test("does not return posts that involve none of the given users", async () => { await insertPost({ - at: BOOKED_AT, + startsAt: BOOKED_AT, users: [{ userId: 3, isOwner: 1 }], }); @@ -133,14 +133,14 @@ describe("findPendingOverlapsForUsers", () => { test("excludes already-accepted (booked) posts", async () => { const postId = await insertPost({ - at: BOOKED_AT, + startsAt: BOOKED_AT, users: [{ userId: 1, isOwner: 1 }], }); await ScrimPostRepository.insertRequest({ scrimPostId: postId, teamId: null, message: null, - at: dbTs(BOOKED_AT), + startsAt: dbTs(BOOKED_AT), users: [{ userId: 3, isOwner: 1 }], }); const post = await ScrimPostRepository.findById(postId); @@ -159,14 +159,14 @@ describe("findPendingOverlapsForUsers", () => { test("returns pending request ids whose effective time falls in the window", async () => { const postId = await insertPost({ - at: add(BOOKED_AT, { hours: 3 }), + startsAt: add(BOOKED_AT, { hours: 3 }), users: [{ userId: 3, isOwner: 1 }], }); await ScrimPostRepository.insertRequest({ scrimPostId: postId, teamId: null, message: null, - at: dbTs(BOOKED_AT), + startsAt: dbTs(BOOKED_AT), users: [{ userId: 1, isOwner: 1 }], }); const post = await ScrimPostRepository.findById(postId); @@ -185,14 +185,14 @@ describe("findPendingOverlapsForUsers", () => { test("does not return pending requests whose effective time is outside the window", async () => { const postId = await insertPost({ - at: add(BOOKED_AT, { hours: 3 }), + startsAt: add(BOOKED_AT, { hours: 3 }), users: [{ userId: 3, isOwner: 1 }], }); await ScrimPostRepository.insertRequest({ scrimPostId: postId, teamId: null, message: null, - at: dbTs(add(BOOKED_AT, { hours: 3 })), + startsAt: dbTs(add(BOOKED_AT, { hours: 3 })), users: [{ userId: 1, isOwner: 1 }], }); @@ -218,14 +218,14 @@ describe("findUserScrims", () => { test("passed-over requester does not see the scrim booked between the post and another team", async () => { const postId = await insertPost({ - at: BOOKED_AT, + startsAt: BOOKED_AT, users: [{ userId: 3, isOwner: 1 }], }); await ScrimPostRepository.insertRequest({ scrimPostId: postId, teamId: null, message: null, - at: null, + startsAt: null, users: [ { userId: 1, isOwner: 1 }, { userId: 2, isOwner: 0 }, @@ -235,7 +235,7 @@ describe("findUserScrims", () => { scrimPostId: postId, teamId: null, message: null, - at: null, + startsAt: null, users: [ { userId: 4, isOwner: 1 }, { userId: 5, isOwner: 0 }, @@ -256,21 +256,21 @@ describe("findUserScrims", () => { test("post owner sees the accepted request's side as the opponent", async () => { const postId = await insertPost({ - at: BOOKED_AT, + startsAt: BOOKED_AT, users: [{ userId: 3, isOwner: 1 }], }); await ScrimPostRepository.insertRequest({ scrimPostId: postId, teamId: null, message: null, - at: null, + startsAt: null, users: [{ userId: 1, isOwner: 1 }], }); await ScrimPostRepository.insertRequest({ scrimPostId: postId, teamId: null, message: null, - at: null, + startsAt: null, users: [{ userId: 4, isOwner: 1 }], }); @@ -309,13 +309,13 @@ describe("insertRequest", () => { scrimPostId, teamId, message: null, - at: null, + startsAt: null, users: [{ userId, isOwner: 1 }], }); test("throws if the team already has a request for the post", async () => { const postId = await insertPost({ - at: BOOKED_AT, + startsAt: BOOKED_AT, users: [{ userId: 1, isOwner: 1 }], }); const team = await TeamRepository.create({ @@ -340,11 +340,11 @@ describe("insertRequest", () => { test("allows the team to request another post", async () => { const postId = await insertPost({ - at: BOOKED_AT, + startsAt: BOOKED_AT, users: [{ userId: 1, isOwner: 1 }], }); const otherPostId = await insertPost({ - at: BOOKED_AT, + startsAt: BOOKED_AT, users: [{ userId: 4, isOwner: 1 }], }); const team = await TeamRepository.create({ diff --git a/app/features/scrims/ScrimPostRepository.server.ts b/app/features/scrims/ScrimPostRepository.server.ts index 62f9586f7..a147dbf5c 100644 --- a/app/features/scrims/ScrimPostRepository.server.ts +++ b/app/features/scrims/ScrimPostRepository.server.ts @@ -24,8 +24,8 @@ import { getPostRequestCensor, parseLutiDiv } from "./scrims-utils"; type InsertArgs = Pick< TablesInsertable["ScrimPost"], - | "at" - | "rangeEnd" + | "startsAt" + | "rangeEndsAt" | "maxDiv" | "minDiv" | "teamId" @@ -49,8 +49,8 @@ export function insert(args: InsertArgs) { const newPost = await trx .insertInto("ScrimPost") .values({ - at: args.at, - rangeEnd: args.rangeEnd, + startsAt: args.startsAt, + rangeEndsAt: args.rangeEndsAt, maxDiv: args.maxDiv, minDiv: args.minDiv, teamId: args.teamId, @@ -76,7 +76,7 @@ export function insert(args: InsertArgs) { type InsertRequestArgs = Pick< Insertable, - "scrimPostId" | "teamId" | "message" | "at" + "scrimPostId" | "teamId" | "message" | "startsAt" > & { users: Array< Pick, "userId" | "isOwner"> @@ -113,7 +113,7 @@ export function insertRequest(args: InsertRequestArgs) { scrimPostId: args.scrimPostId, teamId: args.teamId, message: args.message, - at: args.at, + startsAt: args.startsAt, }) .returning("id") .executeTakeFirstOrThrow(); @@ -146,8 +146,8 @@ const baseFindQuery = db ) .select((eb) => [ "ScrimPost.id", - "ScrimPost.at", - "ScrimPost.rangeEnd", + "ScrimPost.startsAt", + "ScrimPost.rangeEndsAt", "ScrimPost.createdAt", "ScrimPost.visibility", "ScrimPost.maxDiv", @@ -197,7 +197,7 @@ const baseFindQuery = db "ScrimPostRequest.isAccepted", "ScrimPostRequest.createdAt", "ScrimPostRequest.message", - "ScrimPostRequest.at", + "ScrimPostRequest.startsAt", jsonBuildObject({ name: innerEb.ref("Team.name"), customUrl: innerEb.ref("Team.customUrl"), @@ -229,8 +229,8 @@ function findMany() { const min = sub(new Date(), { hours: 3 }); return baseFindQuery - .orderBy("at", "asc") - .where("ScrimPost.at", ">=", dateToDatabaseTimestamp(min)) + .orderBy("startsAt", "asc") + .where("ScrimPost.startsAt", ">=", dateToDatabaseTimestamp(min)) .execute(); } @@ -277,8 +277,8 @@ const mapDBRowToScrimPost = ( const result = { id: row.id, - at: row.at, - rangeEnd: row.rangeEnd, + startsAt: row.startsAt, + rangeEndsAt: row.rangeEndsAt, createdAt: row.createdAt, visibility: row.visibility, text: row.text, @@ -309,7 +309,7 @@ const mapDBRowToScrimPost = ( isAccepted: Boolean(request.isAccepted), createdAt: request.createdAt, message: request.message, - at: request.at, + startsAt: request.startsAt, team: request.team.name ? { name: request.team.name, @@ -347,8 +347,8 @@ const mapDBRowToScrimPost = ( return { ...result, - at: Scrim.getStartTime(result), - rangeEnd: null, + startsAt: Scrim.getStartTime(result), + rangeEndsAt: null, }; }; @@ -447,8 +447,8 @@ export async function findAcceptedScrimsBetweenTwoTimestamps({ excludeRecentlyCreated: Date; }) { const rows = await baseFindQuery - .where("ScrimPost.at", ">=", dateToDatabaseTimestamp(startTime)) - .where("ScrimPost.at", "<", dateToDatabaseTimestamp(endTime)) + .where("ScrimPost.startsAt", ">=", dateToDatabaseTimestamp(startTime)) + .where("ScrimPost.startsAt", "<", dateToDatabaseTimestamp(endTime)) .where("ScrimPost.canceledAt", "is", null) .where( "ScrimPost.createdAt", @@ -481,7 +481,7 @@ export async function findPendingOverlapsForUsers({ endTime: number; excludePostId: number; }): Promise<{ - posts: Array<{ id: number; at: number; memberIds: number[] }>; + posts: Array<{ id: number; startsAt: number; memberIds: number[] }>; requestIds: number[]; }> { if (userIds.length === 0) { @@ -492,7 +492,7 @@ export async function findPendingOverlapsForUsers({ const rows = await baseFindQuery .where("ScrimPost.canceledAt", "is", null) - .where("ScrimPost.at", ">=", now) + .where("ScrimPost.startsAt", ">=", now) .where((eb) => eb.or([ eb.exists( @@ -520,7 +520,8 @@ export async function findPendingOverlapsForUsers({ const userIdSet = new Set(userIds); - const posts: Array<{ id: number; at: number; memberIds: number[] }> = []; + const posts: Array<{ id: number; startsAt: number; memberIds: number[] }> = + []; const requestIds: number[] = []; for (const post of rows @@ -530,18 +531,19 @@ export async function findPendingOverlapsForUsers({ const postInvolvesUser = post.users.some((u) => userIdSet.has(u.id)); const postIntervalOverlaps = - post.at <= endTime && (post.rangeEnd ?? post.at) >= startTime; + post.startsAt <= endTime && + (post.rangeEndsAt ?? post.startsAt) >= startTime; if (postInvolvesUser && postIntervalOverlaps) { posts.push({ id: post.id, - at: post.at, + startsAt: post.startsAt, memberIds: post.users.map((u) => u.id), }); } for (const request of post.requests) { if (request.isAccepted) continue; - const effectiveAt = request.at ?? post.at; + const effectiveAt = request.startsAt ?? post.startsAt; const requestInvolvesUser = request.users.some((u) => userIdSet.has(u.id), ); @@ -560,7 +562,7 @@ export async function findPendingOverlapsForUsers({ export type SidebarScrim = { id: number; - at: number; + startsAt: number; opponentName: string | null; opponentAvatarUrl: string | null; status: "booked" | "looking" | "requestPending"; @@ -571,7 +573,7 @@ export async function findUserScrims(userId: number): Promise { const rows = await baseFindQuery .where("ScrimPost.canceledAt", "is", null) - .where("ScrimPost.at", ">=", now) + .where("ScrimPost.startsAt", ">=", now) .where((eb) => eb.or([ eb.exists( @@ -595,7 +597,7 @@ export async function findUserScrims(userId: number): Promise { ), ]), ) - .orderBy("ScrimPost.at", "asc") + .orderBy("ScrimPost.startsAt", "asc") .execute(); return rows @@ -610,7 +612,7 @@ export async function findUserScrims(userId: number): Promise { if (!isAccepted) { return { id: post.id, - at: post.at, + startsAt: post.startsAt, opponentName: null, opponentAvatarUrl: null, status: userIsInPost @@ -627,7 +629,7 @@ export async function findUserScrims(userId: number): Promise { return { id: post.id, - at: post.at, + startsAt: post.startsAt, opponentName: opponentTeam?.name ?? null, opponentAvatarUrl: opponentTeam?.avatarUrl ?? opponentOwner?.discordAvatar ?? null, diff --git a/app/features/scrims/actions/scrims.new.server.ts b/app/features/scrims/actions/scrims.new.server.ts index d44001873..ead6f07ca 100644 --- a/app/features/scrims/actions/scrims.new.server.ts +++ b/app/features/scrims/actions/scrims.new.server.ts @@ -57,8 +57,8 @@ export const action = async ({ request }: ActionFunctionArgs) => { const resolvedDivs = data.divs ? resolveDivs(data.divs) : null; await ScrimPostRepository.insert({ - at: dateToDatabaseTimestamp(data.at), - rangeEnd: rangeEndDate ? dateToDatabaseTimestamp(rangeEndDate) : null, + startsAt: dateToDatabaseTimestamp(data.at), + rangeEndsAt: rangeEndDate ? dateToDatabaseTimestamp(rangeEndDate) : null, maxDiv: resolvedDivs?.[0] ? serializeLutiDiv(resolvedDivs[0]) : null, minDiv: resolvedDivs?.[1] ? serializeLutiDiv(resolvedDivs[1]) : null, text: data.postText, diff --git a/app/features/scrims/actions/scrims.server.ts b/app/features/scrims/actions/scrims.server.ts index d47698d1d..3a7663fd5 100644 --- a/app/features/scrims/actions/scrims.server.ts +++ b/app/features/scrims/actions/scrims.server.ts @@ -74,17 +74,17 @@ export const action = async ({ request }: ActionFunctionArgs) => { errorToastIfFalsy(canSeePost, "Post not found"); } - if (post.rangeEnd && !data.at) { + if (post.rangeEndsAt && !data.at) { return actionError({ msg: "Please select a time for the scrim", field: "at", }); } - if (post.rangeEnd && data.at) { + if (post.rangeEndsAt && data.at) { const validTimeOptions = generateTimeOptions( - databaseTimestampToDate(post.at), - databaseTimestampToDate(post.rangeEnd), + databaseTimestampToDate(post.startsAt), + databaseTimestampToDate(post.rangeEndsAt), ); const requestTime = data.at.getTime(); @@ -101,7 +101,7 @@ export const action = async ({ request }: ActionFunctionArgs) => { scrimPostId: data.scrimPostId, teamId: data.from.mode === "TEAM" ? data.from.teamId : null, message: data.message, - at: data.at ? dateToDatabaseTimestamp(data.at) : null, + startsAt: data.at ? dateToDatabaseTimestamp(data.at) : null, users: ( await usersListForPost({ authorId: user.id, from: data.from }) ).map((userId) => ({ @@ -154,15 +154,18 @@ export const action = async ({ request }: ActionFunctionArgs) => { ChatSystemMessage.setMetadata({ chatCode: fullPost.chatCode, header: datePlaceholder( - databaseTimestampToDate(request.at ?? post.at), + databaseTimestampToDate(request.startsAt ?? post.startsAt), ), subtitle: "Scrim", url: scrimPage(post.id), imageUrl: `${navIconUrl("scrims")}.avif`, participantUserIds: Scrim.participantIdsListFromAccepted(fullPost), - expiresAt: add(databaseTimestampToDate(request.at ?? post.at), { - hours: 3, - }), + expiresAt: add( + databaseTimestampToDate(request.startsAt ?? post.startsAt), + { + hours: 3, + }, + ), }); } @@ -218,7 +221,7 @@ export const action = async ({ request }: ActionFunctionArgs) => { defaultSeenUserIds: [user.id], notification: { type: "SCRIM_AUTO_DELETED", - meta: { at: removed.at }, + meta: { at: removed.startsAt }, }, }); } diff --git a/app/features/scrims/components/ScrimCard.tsx b/app/features/scrims/components/ScrimCard.tsx index aa3194eb2..4de96ccec 100644 --- a/app/features/scrims/components/ScrimCard.tsx +++ b/app/features/scrims/components/ScrimCard.tsx @@ -57,8 +57,8 @@ export function ScrimPostCard({ const isPickup = !post.team?.name; const teamName = post.team?.name ?? owner.username; - const flexTimeDisplay = post.rangeEnd - ? formatFlexTimeDisplay(post.at, post.rangeEnd) + const flexTimeDisplay = post.rangeEndsAt + ? formatFlexTimeDisplay(post.startsAt, post.rangeEndsAt) : null; useEffect(() => { @@ -109,7 +109,7 @@ export function ScrimPostCard({ @@ -486,13 +486,13 @@ function ScrimActionButtons({
{userRequest.message}
) : null} - {userRequest.at ? ( + {userRequest.startsAt ? (
{t("scrims:requestModal.at.label")}
r.isAccepted); const scheduledAt = databaseTimestampToDate( - acceptedRequest?.at ?? data.post.at, + acceptedRequest?.startsAt ?? data.post.startsAt, ); return ( diff --git a/app/features/scrims/components/ScrimMatchHeader.tsx b/app/features/scrims/components/ScrimMatchHeader.tsx index 8e2a1f33e..d6ecf7066 100644 --- a/app/features/scrims/components/ScrimMatchHeader.tsx +++ b/app/features/scrims/components/ScrimMatchHeader.tsx @@ -19,7 +19,7 @@ export function ScrimMatchHeader() { const canCancel = allowedToCancel && !isCanceled && - databaseTimestampToDate(data.post.at) > new Date(); + databaseTimestampToDate(data.post.startsAt) > new Date(); const acceptedRequest = data.post.requests.find((r) => r.isAccepted); const viewerSide = data.mapByMap.viewerSide; diff --git a/app/features/scrims/components/ScrimRequestModal.tsx b/app/features/scrims/components/ScrimRequestModal.tsx index ce78f540b..fac497a47 100644 --- a/app/features/scrims/components/ScrimRequestModal.tsx +++ b/app/features/scrims/components/ScrimRequestModal.tsx @@ -29,10 +29,10 @@ export function ScrimRequestModal({ minute: "numeric", }); - const timeOptions = post.rangeEnd + const timeOptions = post.rangeEndsAt ? generateTimeOptions( - databaseTimestampToDate(post.at), - databaseTimestampToDate(post.rangeEnd), + databaseTimestampToDate(post.startsAt), + databaseTimestampToDate(post.rangeEndsAt), ).map((timestamp) => ({ value: String(timestamp), label: timeFormatter.format(new Date(timestamp)) ?? "", @@ -55,7 +55,7 @@ export function ScrimRequestModal({ ) as unknown as number[], }, message: "", - at: post.rangeEnd && timeOptions[0] ? timeOptions[0].value : null, + at: post.rangeEndsAt && timeOptions[0] ? timeOptions[0].value : null, }} > {({ FormField }) => ( @@ -74,7 +74,7 @@ export function ScrimRequestModal({ )} - {post.rangeEnd ? ( + {post.rangeEndsAt ? ( ) : null} diff --git a/app/features/scrims/core/Scrim.test.ts b/app/features/scrims/core/Scrim.test.ts index 53554ee83..ac2b21517 100644 --- a/app/features/scrims/core/Scrim.test.ts +++ b/app/features/scrims/core/Scrim.test.ts @@ -108,14 +108,14 @@ describe("sideDisplayName", () => { describe("applyFilters", () => { function createPostForFilters( - at: Date, - rangeEnd?: Date, + startsAt: Date, + rangeEndsAt?: Date, divs?: { min: string; max: string }, ): ScrimPost { return { id: 1, - at: dateToDatabaseTimestamp(at), - rangeEnd: rangeEnd ? dateToDatabaseTimestamp(rangeEnd) : null, + startsAt: dateToDatabaseTimestamp(startsAt), + rangeEndsAt: rangeEndsAt ? dateToDatabaseTimestamp(rangeEndsAt) : null, divs: divs ? { min: divs.min as any, max: divs.max as any } : null, users: [], requests: [], diff --git a/app/features/scrims/core/Scrim.ts b/app/features/scrims/core/Scrim.ts index 853293ce6..e800487ed 100644 --- a/app/features/scrims/core/Scrim.ts +++ b/app/features/scrims/core/Scrim.ts @@ -43,12 +43,12 @@ export function participantIdsListFromAccepted(post: ScrimPost) { /** * Returns the actual start time of the scrim. - * When the post has a time range (rangeEnd is set), returns the accepted request's specific time if available. + * When the post has a time range (rangeEndsAt is set), returns the accepted request's specific time if available. * Otherwise returns the post's start time. */ export function getStartTime(post: ScrimPost): number { const acceptedRequest = post.requests.find((r) => r.isAccepted); - return acceptedRequest?.at ?? post.at; + return acceptedRequest?.startsAt ?? post.startsAt; } /** @@ -91,14 +91,14 @@ export function applyFilters(post: ScrimPost, filters: ScrimFilters): boolean { } } - const timeFilters = isWeekend(databaseTimestampToDate(post.at)) + const timeFilters = isWeekend(databaseTimestampToDate(post.startsAt)) ? filters.weekendTimes : filters.weekdayTimes; if (timeFilters) { - const startDate = databaseTimestampToDate(post.at); - const endDate = post.rangeEnd - ? databaseTimestampToDate(post.rangeEnd) + const startDate = databaseTimestampToDate(post.startsAt); + const endDate = post.rangeEndsAt + ? databaseTimestampToDate(post.rangeEndsAt) : startDate; const startTimeString = format(startDate, "HH:mm"); diff --git a/app/features/scrims/routes/scrims.tsx b/app/features/scrims/routes/scrims.tsx index 25c4486eb..25853e831 100644 --- a/app/features/scrims/routes/scrims.tsx +++ b/app/features/scrims/routes/scrims.tsx @@ -199,7 +199,7 @@ function ScrimsDaySeparatedCards({ autoScrollToPostId: number | null; }) { const postsByDay = R.groupBy(posts, (post) => - format(databaseTimestampToDate(post.at), "yyyy-MM-dd"), + format(databaseTimestampToDate(post.startsAt), "yyyy-MM-dd"), ); return ( @@ -251,7 +251,7 @@ function ScrimsDaySection({

- format(databaseTimestampToDate(post.at), "yyyy-MM-dd"), + format(databaseTimestampToDate(post.startsAt), "yyyy-MM-dd"), ); return ( @@ -380,7 +380,7 @@ function ScrimsDaySeparatedOwnedCards({ posts }: { posts: ScrimPost[] }) {

- format(databaseTimestampToDate(post.at), "yyyy-MM-dd"), + format(databaseTimestampToDate(post.startsAt), "yyyy-MM-dd"), ); return ( @@ -450,7 +450,7 @@ function ScrimsDaySeparatedBookedCards({ posts }: { posts: ScrimPost[] }) {

diff --git a/app/features/scrims/scrims-types.ts b/app/features/scrims/scrims-types.ts index 36a5fbcb1..012f411ad 100644 --- a/app/features/scrims/scrims-types.ts +++ b/app/features/scrims/scrims-types.ts @@ -8,8 +8,8 @@ export type ScrimSide = "ALPHA" | "BRAVO"; export interface ScrimPost { id: number; - at: number; - rangeEnd: number | null; + startsAt: number; + rangeEndsAt: number | null; createdAt: number; visibility: AssociationVisibility | null; text: string | null; @@ -53,7 +53,7 @@ export interface ScrimPostRequest { users: Array; team: ScrimPostTeam | null; message: string | null; - at: number | null; + startsAt: number | null; permissions: { CANCEL: number[]; }; diff --git a/app/features/search/routes/search.ts b/app/features/search/routes/search.ts index 4bafdb5af..b1fb9473d 100644 --- a/app/features/search/routes/search.ts +++ b/app/features/search/routes/search.ts @@ -98,7 +98,7 @@ async function searchByType({ id: t.id, name: t.name, logoUrl: t.logoUrl, - startTime: t.startTime, + startsAt: t.startsAt, })); } } diff --git a/app/features/sendouq-match/SQMatchRepository.server.ts b/app/features/sendouq-match/SQMatchRepository.server.ts index 153b517c6..5f00df1cb 100644 --- a/app/features/sendouq-match/SQMatchRepository.server.ts +++ b/app/features/sendouq-match/SQMatchRepository.server.ts @@ -233,7 +233,7 @@ const tournamentResultsSubQuery = ( "TournamentResult.setResults", "TournamentResult.tournamentId", "TournamentResult.tournamentTeamId", - "CalendarEventDate.startTime as tournamentStartTime", + "CalendarEventDate.startsAt as tournamentStartTime", "CalendarEvent.name as tournamentName", tournamentLogoWithDefault(eb).as("logoUrl"), ]) diff --git a/app/features/sidebar/core/sidebar.server.ts b/app/features/sidebar/core/sidebar.server.ts index 92ac00293..51e50cf62 100644 --- a/app/features/sidebar/core/sidebar.server.ts +++ b/app/features/sidebar/core/sidebar.server.ts @@ -43,7 +43,7 @@ export type SidebarEvent = { name: string; url: string; logoUrl: string | null; - startTime: number; + startsAt: number; type: "tournament" | "scrim"; scrimStatus?: "booked" | "looking" | "requestPending"; }; @@ -116,7 +116,7 @@ export async function resolveSidebarData(userId: number | null) { const scrimEvents: SidebarEvent[] = scrimsData.map(scrimToSidebarEvent); const events = [...tournamentEvents, ...savedEvents, ...scrimEvents] - .sort((a, b) => a.startTime - b.startTime) + .sort((a, b) => a.startsAt - b.startsAt) .slice(0, MAX_EVENTS_VISIBLE); const friends = resolveFriends(friendsWithActivity); @@ -170,7 +170,7 @@ async function combinedStreams(): Promise { imageUrl: externalStream.avatarUrl ?? BLANK_IMAGE_URL, url: externalStream.url, subtitle: "", - startsAt: externalStream.startTime, + startsAt: externalStream.startsAt, tier: null, }, score: StreamRanking.EXTERNAL_STREAM_SCORE, @@ -245,8 +245,8 @@ async function combinedStreams(): Promise { for (const event of upcomingTournaments) { const effectiveTier = event.tier ?? event.tentativeTier; if (effectiveTier === null) continue; - if (event.startTime < nowTimestamp) continue; - if (event.startTime > threeDaysFromNow) continue; + if (event.startsAt < nowTimestamp) continue; + if (event.startsAt > threeDaysFromNow) continue; if (event.hidden) continue; const membersPerTeam = event.minMembersPerTeam ?? 4; @@ -258,7 +258,7 @@ async function combinedStreams(): Promise { imageUrl: event.logoUrl ?? BLANK_IMAGE_URL, url: event.url, subtitle: "", - startsAt: event.startTime, + startsAt: event.startsAt, tier: (event.tier as TournamentTierNumber) ?? null, membersPerTeam, tentativeTier: event.tentativeTier ?? undefined, @@ -397,7 +397,7 @@ export function tournamentToSidebarEvent( name: t.name, url: t.url, logoUrl: t.logoUrl, - startTime: t.startTime, + startsAt: t.startsAt, type: "tournament" as const, }; } @@ -415,7 +415,7 @@ export function scrimToSidebarEvent(s: SidebarScrim): SidebarEvent { ? `${href("/scrims")}?pendingRequestPostId=${s.id}` : href("/scrims"), logoUrl: s.opponentAvatarUrl ?? SCRIMS_ICON_URL, - startTime: s.at, + startsAt: s.startsAt, type: "scrim" as const, scrimStatus: s.status, }; diff --git a/app/features/splatoon-rotations/SplatoonRotationRepository.server.ts b/app/features/splatoon-rotations/SplatoonRotationRepository.server.ts index 16bb6137a..f4a2ebf28 100644 --- a/app/features/splatoon-rotations/SplatoonRotationRepository.server.ts +++ b/app/features/splatoon-rotations/SplatoonRotationRepository.server.ts @@ -36,9 +36,9 @@ function queryAll() { "SplatoonRotation.mode", "SplatoonRotation.stageId1", "SplatoonRotation.stageId2", - "SplatoonRotation.startTime", - "SplatoonRotation.endTime", + "SplatoonRotation.startsAt", + "SplatoonRotation.endsAt", ]) - .orderBy("SplatoonRotation.startTime", "asc") + .orderBy("SplatoonRotation.startsAt", "asc") .execute(); } diff --git a/app/features/splatoon-rotations/splatoon-rotations.server.ts b/app/features/splatoon-rotations/splatoon-rotations.server.ts index 90d4e3b93..62ad7870e 100644 --- a/app/features/splatoon-rotations/splatoon-rotations.server.ts +++ b/app/features/splatoon-rotations/splatoon-rotations.server.ts @@ -106,8 +106,8 @@ export async function fetchRotations(): Promise< mode, stageId1, stageId2, - startTime: Math.floor(new Date(node.startTime).getTime() / 1000), - endTime: Math.floor(new Date(node.endTime).getTime() / 1000), + startsAt: Math.floor(new Date(node.startTime).getTime() / 1000), + endsAt: Math.floor(new Date(node.endTime).getTime() / 1000), }); } } @@ -127,8 +127,8 @@ export async function fetchRotations(): Promise< mode, stageId1, stageId2, - startTime: Math.floor(new Date(node.startTime).getTime() / 1000), - endTime: Math.floor(new Date(node.endTime).getTime() / 1000), + startsAt: Math.floor(new Date(node.startTime).getTime() / 1000), + endsAt: Math.floor(new Date(node.endTime).getTime() / 1000), }); } diff --git a/app/features/team/TeamRepository.server.ts b/app/features/team/TeamRepository.server.ts index cd41e6930..96b04efbd 100644 --- a/app/features/team/TeamRepository.server.ts +++ b/app/features/team/TeamRepository.server.ts @@ -261,7 +261,7 @@ export async function findResultsById(teamId: number) { "results.participantCount", "results.tournamentTeamId", "CalendarEvent.name as tournamentName", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", "Tournament.tier", tournamentLogoOrNull(eb).as("logoUrl"), jsonArrayFrom( @@ -285,7 +285,7 @@ export async function findResultsById(teamId: number) { .select((eb) => commonUserSelect(eb)), ).as("participants"), ]) - .orderBy("CalendarEventDate.startTime", "desc") + .orderBy("CalendarEventDate.startsAt", "desc") .execute(); const members = await allMembersById(teamId); diff --git a/app/features/team/components/TeamResultsTable.tsx b/app/features/team/components/TeamResultsTable.tsx index a6842e2f0..fa7a28b0a 100644 --- a/app/features/team/components/TeamResultsTable.tsx +++ b/app/features/team/components/TeamResultsTable.tsx @@ -44,7 +44,7 @@ export function TeamResultsTable({ results }: TeamResultsTableProps) { { it("returns empty array if all participants are current members", () => { const result = { participants: [{ id: 1 }, { id: 2 }], - startTime: 1000, + startsAt: 1000, }; const members = [ { userId: 1, createdAt: 500, leftAt: null }, @@ -18,7 +18,7 @@ describe("subsOfResult()", () => { it("returns participant not in members as sub", () => { const result = { participants: [{ id: 1 }, { id: 2 }], - startTime: 1000, + startsAt: 1000, }; const members = [{ userId: 1, createdAt: 500, leftAt: null }]; const subs = subsOfResult(result, members); @@ -28,7 +28,7 @@ describe("subsOfResult()", () => { it("returns participant as sub if they left before result startTime", () => { const result = { participants: [{ id: 1 }, { id: 2 }], - startTime: 1000, + startsAt: 1000, }; const members = [ { userId: 1, createdAt: 500, leftAt: 900 }, @@ -41,7 +41,7 @@ describe("subsOfResult()", () => { it("does not return participant as sub if they were a member during result", () => { const result = { participants: [{ id: 1 }, { id: 2 }], - startTime: 1000, + startsAt: 1000, }; const members = [ { userId: 1, createdAt: 500, leftAt: 2000 }, @@ -54,7 +54,7 @@ describe("subsOfResult()", () => { it("returns multiple subs correctly", () => { const result = { participants: [{ id: 1 }, { id: 2 }, { id: 3 }], - startTime: 1000, + startsAt: 1000, }; const members = [ { userId: 1, createdAt: 500, leftAt: 900 }, @@ -67,7 +67,7 @@ describe("subsOfResult()", () => { it("returns empty array if no participants", () => { const result = { participants: [], - startTime: 1000, + startsAt: 1000, }; const members = [{ userId: 1, createdAt: 500, leftAt: null }]; const subs = subsOfResult(result, members); diff --git a/app/features/team/team-utils.ts b/app/features/team/team-utils.ts index 394f52c32..880495819 100644 --- a/app/features/team/team-utils.ts +++ b/app/features/team/team-utils.ts @@ -101,7 +101,7 @@ export function getMemberRoleType(member: { * - They are not a past member who was part of the team during the result's start time. */ export function subsOfResult( - result: { participants: Array; startTime: number }, + result: { participants: Array; startsAt: number }, members: Array>, ) { const currentMembers = members.filter((member) => !member.leftAt); @@ -113,9 +113,9 @@ export function subsOfResult( pastMembers.some( (member) => member.userId === cur.id && - member.createdAt < result.startTime && + member.createdAt < result.startsAt && member.leftAt && - member.leftAt > result.startTime, + member.leftAt > result.startsAt, ) ) { return acc; diff --git a/app/features/tournament-bracket/BracketRepository.server.ts b/app/features/tournament-bracket/BracketRepository.server.ts index 7c0abd1f9..a260c13c7 100644 --- a/app/features/tournament-bracket/BracketRepository.server.ts +++ b/app/features/tournament-bracket/BracketRepository.server.ts @@ -162,7 +162,6 @@ export function insertBracket(args: { type: stageInput.type, settings: JSON.stringify(stageInput.settings), number: kyselySql`(select coalesce(max("number"), 0) + 1 from "TournamentStage" where "tournamentId" = ${args.tournamentId})`, - createdAt: databaseTimestampNow(), }) .returning(["id"]) .executeTakeFirstOrThrow(); diff --git a/app/features/tournament-bracket/TournamentMatchVodRepository.server.ts b/app/features/tournament-bracket/TournamentMatchVodRepository.server.ts index ede4e681d..2063b74aa 100644 --- a/app/features/tournament-bracket/TournamentMatchVodRepository.server.ts +++ b/app/features/tournament-bracket/TournamentMatchVodRepository.server.ts @@ -70,7 +70,7 @@ export function findTournamentsNeedingVodSync() { .where(({ or, and, eb }) => or([ and([ - eb("CalendarEventDate.startTime", ">", oneDayAgo), + eb("CalendarEventDate.startsAt", ">", oneDayAgo), eb("Tournament.vodsSyncCount", "=", 0), ]), and([ @@ -122,7 +122,7 @@ export function deleteObsolete() { "CalendarEvent.id", ) .select("TournamentMatch.id") - .where("CalendarEventDate.startTime", "<", cutoff), + .where("CalendarEventDate.startsAt", "<", cutoff), ) .executeTakeFirst(); } diff --git a/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts b/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts index 9e3274c09..d45f4af73 100644 --- a/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts +++ b/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts @@ -218,7 +218,7 @@ function resolveFinalizationSeason(tournament: Tournament) { // league divisions might be running for many weeks const attributionDate = tournament.isLeagueDivision ? new Date() - : tournament.ctx.startTime; + : tournament.ctx.startsAt; const season = Seasons.current(attributionDate); if (!season) return undefined; diff --git a/app/features/tournament-bracket/components/Bracket/useBracketSpoilerCensor.ts b/app/features/tournament-bracket/components/Bracket/useBracketSpoilerCensor.ts index 6335c1785..8130ecaa2 100644 --- a/app/features/tournament-bracket/components/Bracket/useBracketSpoilerCensor.ts +++ b/app/features/tournament-bracket/components/Bracket/useBracketSpoilerCensor.ts @@ -11,7 +11,7 @@ export function useBracketSpoilerCensor() { const withinSpoilerWindow = tournament.ctx.isFinalized && - differenceInDays(new Date(), tournament.ctx.startTime) < + differenceInDays(new Date(), tournament.ctx.startsAt) < TOURNAMENT.VOD_VISIBILITY_DAYS; const censored = withinSpoilerWindow && isCensored(tournament.ctx.id); diff --git a/app/features/tournament-bracket/core/Tournament.server.ts b/app/features/tournament-bracket/core/Tournament.server.ts index 38c221acc..68f331e6c 100644 --- a/app/features/tournament-bracket/core/Tournament.server.ts +++ b/app/features/tournament-bracket/core/Tournament.server.ts @@ -144,7 +144,7 @@ function mostRecentStartTime(tournament: Tournament) { .filter((b) => b.startTime) .map((b) => databaseTimestampToDate(b.startTime!)); - const allStartTimes = [tournament.ctx.startTime, ...bracketStartTimes]; + const allStartTimes = [tournament.ctx.startsAt, ...bracketStartTimes]; return allStartTimes .filter((t) => t <= new Date()) diff --git a/app/features/tournament-bracket/core/Tournament.ts b/app/features/tournament-bracket/core/Tournament.ts index 03c912021..5c67c8678 100644 --- a/app/features/tournament-bracket/core/Tournament.ts +++ b/app/features/tournament-bracket/core/Tournament.ts @@ -66,7 +66,7 @@ export class Tournament { ? // after the start the teams who did not check-in are irrelevant teamsInSeedOrder.filter((team) => team.checkIns.length > 0) : teamsInSeedOrder, - startTime: databaseTimestampToDate(ctx.startTime), + startsAt: databaseTimestampToDate(ctx.startsAt), }; this.initBrackets(data); @@ -477,7 +477,7 @@ export class Tournament { get ranked() { return tournamentIsRanked({ isSetAsRanked: this.ctx.settings.isRanked, - startTime: this.ctx.startTime, + startsAt: this.ctx.startsAt, minMembersPerTeam: this.minMembersPerTeam, isTest: this.isTest, }); @@ -726,7 +726,7 @@ export class Tournament { return ( !this.ctx.settings.regClosesAt || this.ctx.settings.regClosesAt === - dateToDatabaseTimestamp(this.ctx.startTime) || + dateToDatabaseTimestamp(this.ctx.startsAt) || this.registrationOpen ); } @@ -753,7 +753,7 @@ export class Tournament { /** Has the regular check-in (check-in for the whole tournament) ended? */ get regularCheckInHasEnded() { - return this.ctx.startTime < new Date(); + return this.ctx.startsAt < new Date(); } /** Has the regular check-in (check-in for the whole tournament) started? Note it is also considered started if it has ended. */ @@ -763,21 +763,21 @@ export class Tournament { /** Date when the regular check-in is scheduled to start. */ get regularCheckInStartsAt() { - const result = new Date(this.ctx.startTime); + const result = new Date(this.ctx.startsAt); result.setMinutes(result.getMinutes() - 60); return result; } /** Date when the regular check-in is scheduled to start. */ get regularCheckInEndsAt() { - return this.ctx.startTime; + return this.ctx.startsAt; } /** Date when the tournament registration is scheduled to end. This can be set by the organizer. */ get registrationClosesAt() { return this.ctx.settings.regClosesAt ? databaseTimestampToDate(this.ctx.settings.regClosesAt) - : this.ctx.startTime; + : this.ctx.startsAt; } /** Is the tournament registration open at this time? */ @@ -789,11 +789,11 @@ export class Tournament { /** Can participants submit/undo their own weapon reports right now? * Always open while the tournament is running; once finalized it stays open only for tournaments - * whose startTime is inside the current-season-plus-adjacent-off-season window. */ + * whose start time is inside the current-season-plus-adjacent-off-season window. */ get weaponReportingOpen() { if (!this.ctx.isFinalized) return true; return tournamentInWeaponReportingWindow({ - tournamentStartTime: this.ctx.startTime, + tournamentStartTime: this.ctx.startsAt, }); } diff --git a/app/features/tournament-bracket/core/tests/mocks-li.ts b/app/features/tournament-bracket/core/tests/mocks-li.ts index 3d86044aa..b3d846ba3 100644 --- a/app/features/tournament-bracket/core/tests/mocks-li.ts +++ b/app/features/tournament-bracket/core/tests/mocks-li.ts @@ -6248,7 +6248,7 @@ export const LOW_INK_DECEMBER_2024 = (): TournamentData => ({ mapPickingStyle: "TO", hasRules: true, name: "Low Ink December 2024", - startTime: 1734199200, + startsAt: 1734199200, organization: { id: 3, name: "Inkling Performance Labs", diff --git a/app/features/tournament-bracket/core/tests/mocks-sos.ts b/app/features/tournament-bracket/core/tests/mocks-sos.ts index 766b6e8a3..e38a27899 100644 --- a/app/features/tournament-bracket/core/tests/mocks-sos.ts +++ b/app/features/tournament-bracket/core/tests/mocks-sos.ts @@ -1752,7 +1752,7 @@ export const SWIM_OR_SINK_167 = ( mapPickingStyle: "TO", hasRules: true, name: "Swim or Sink 167", - startTime: 1730941200, + startsAt: 1730941200, organization: { id: 3, name: "Inkling Performance Labs", diff --git a/app/features/tournament-bracket/core/tests/mocks-zones-weekly.ts b/app/features/tournament-bracket/core/tests/mocks-zones-weekly.ts index 7317f3ddc..e8c7a7854 100644 --- a/app/features/tournament-bracket/core/tests/mocks-zones-weekly.ts +++ b/app/features/tournament-bracket/core/tests/mocks-zones-weekly.ts @@ -303,7 +303,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({ parentTournamentId: null, parentTournamentName: null, name: "Zones Weekly 38", - startTime: 1734685200, + startsAt: 1734685200, isFinalized: 0, organization: null, logoUrl: "tournament-logo-hfX5gzVyrt5QCV8fiQA4n-1716906622859.webp", diff --git a/app/features/tournament-bracket/core/tests/mocks.ts b/app/features/tournament-bracket/core/tests/mocks.ts index a2af8955a..f04666586 100644 --- a/app/features/tournament-bracket/core/tests/mocks.ts +++ b/app/features/tournament-bracket/core/tests/mocks.ts @@ -1250,7 +1250,7 @@ export const PADDLING_POOL_257 = () => name: "Paddling Pool 257", hasRules: false, logoUrl: "/test.avif", - startTime: 1709748000, + startsAt: 1709748000, author: { id: 860, username: "공주 Alice", @@ -8033,7 +8033,7 @@ export const PADDLING_POOL_255 = () => name: "Paddling Pool 255", hasRules: false, logoUrl: "/test.avif", - startTime: 1708538400, + startsAt: 1708538400, author: { id: 860, username: "공주 Alice", @@ -15193,7 +15193,7 @@ export const IN_THE_ZONE_32 = ({ name: "In The Zone 32", hasRules: false, logoUrl: "/test.avif", - startTime: 1707588000, + startsAt: 1707588000, author: { id: 274, username: "Sendou", diff --git a/app/features/tournament-bracket/core/tests/test-utils.ts b/app/features/tournament-bracket/core/tests/test-utils.ts index 61455fb81..a28ddbb7c 100644 --- a/app/features/tournament-bracket/core/tests/test-utils.ts +++ b/app/features/tournament-bracket/core/tests/test-utils.ts @@ -69,7 +69,7 @@ export const testTournament = ({ hasRules: false, logoUrl: "/test.avif", discordUrl: null, - startTime: 1705858842, + startsAt: 1705858842, isFinalized: 0, name: "test", castTwitchAccounts: [], diff --git a/app/features/tournament-bracket/loaders/to.$id.brackets.finalize.server.ts b/app/features/tournament-bracket/loaders/to.$id.brackets.finalize.server.ts index a5b990fe9..c28c8aa10 100644 --- a/app/features/tournament-bracket/loaders/to.$id.brackets.finalize.server.ts +++ b/app/features/tournament-bracket/loaders/to.$id.brackets.finalize.server.ts @@ -60,7 +60,7 @@ async function standingsWithSetParticipation(tournament: Tournament) { ); invariant(results.length > 0, "No results found"); - const season = Seasons.current(tournament.ctx.startTime)?.nth; + const season = Seasons.current(tournament.ctx.startsAt)?.nth; const seedingSkillCountsFor = tournament.skillCountsFor; diff --git a/app/features/tournament-lfg/actions/to.$id.looking.server.ts b/app/features/tournament-lfg/actions/to.$id.looking.server.ts index a9aa117b2..e9e44e74d 100644 --- a/app/features/tournament-lfg/actions/to.$id.looking.server.ts +++ b/app/features/tournament-lfg/actions/to.$id.looking.server.ts @@ -82,7 +82,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { id: tournamentId, name: tournament.ctx.name, logoUrl: tournament.ctx.logoUrl, - startTime: tournament.ctx.startTime, + startTime: tournament.ctx.startsAt, }, }); } @@ -211,7 +211,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { id: tournamentId, name: tournament.ctx.name, logoUrl: tournament.ctx.logoUrl, - startTime: tournament.ctx.startTime, + startTime: tournament.ctx.startsAt, }, }); } diff --git a/app/features/tournament-match/actions/to.$id.matches.$mid.server.ts b/app/features/tournament-match/actions/to.$id.matches.$mid.server.ts index 4a6f060ef..11603dbfa 100644 --- a/app/features/tournament-match/actions/to.$id.matches.$mid.server.ts +++ b/app/features/tournament-match/actions/to.$id.matches.$mid.server.ts @@ -767,7 +767,7 @@ export const action: ActionFunction = async ({ params, request }) => { tournamentMatchId: matchId, mapIndex: data.mapIndex, weaponSplId: data.weaponSplId, - createdAt: dateToDatabaseTimestamp(tournament.ctx.startTime), + createdAt: dateToDatabaseTimestamp(tournament.ctx.startsAt), }); break; diff --git a/app/features/tournament-match/loaders/to.$id.matches.$mid.server.ts b/app/features/tournament-match/loaders/to.$id.matches.$mid.server.ts index bf47b936d..42d3d43e2 100644 --- a/app/features/tournament-match/loaders/to.$id.matches.$mid.server.ts +++ b/app/features/tournament-match/loaders/to.$id.matches.$mid.server.ts @@ -199,7 +199,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => { ? true : !chatAccessible({ expiresAfterDays: tournament.isLeagueDivision ? 30 : 7, - comparedTo: tournament.ctx.startTime, + comparedTo: tournament.ctx.startsAt, }); const visibleChatCode = diff --git a/app/features/tournament-organization/TournamentOrganizationRepository.server.ts b/app/features/tournament-organization/TournamentOrganizationRepository.server.ts index 712510d71..c85ba8c78 100644 --- a/app/features/tournament-organization/TournamentOrganizationRepository.server.ts +++ b/app/features/tournament-organization/TournamentOrganizationRepository.server.ts @@ -231,7 +231,7 @@ const findEventsBaseQuery = (organizationId: number) => "CalendarEvent.id as eventId", "CalendarEvent.name", "CalendarEvent.tournamentId", - eb.fn.min("CalendarEventDate.startTime").as("startTime"), + eb.fn.min("CalendarEventDate.startsAt").as("startsAt"), tournamentLogoWithDefault(eb).as("logoUrl"), jsonArrayFrom( eb @@ -347,16 +347,16 @@ export async function findEventsByMonth({ const events = await findEventsBaseQuery(organizationId) .where( - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", ">=", dateToDatabaseTimestamp(firstDayOfTheMonth), ) .where( - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", "<=", dateToDatabaseTimestamp(lastDayOfTheMonth), ) - .orderBy("CalendarEventDate.startTime", "asc") + .orderBy("CalendarEventDate.startsAt", "asc") .execute(); return events.map(mapEvent); @@ -387,7 +387,7 @@ const findSeriesEventsBaseQuery = ({ ), ), ) - .orderBy("CalendarEventDate.startTime", "desc"); + .orderBy("CalendarEventDate.startsAt", "desc"); export async function findPaginatedEventsBySeries({ organizationId, @@ -458,8 +458,8 @@ export async function countActiveParticipants({ ) .select(({ fn }) => fn.count("tmgrp.userId").distinct().as("count")) .where("ce.organizationId", "=", organizationId) - .where("ced.startTime", ">=", startTime) - .where("ced.startTime", "<", endTime) + .where("ced.startsAt", ">=", startTime) + .where("ced.startsAt", "<", endTime) .where("ttci.checkedInAt", "is not", null) .where("ttci.isCheckOut", "=", 0) .executeTakeFirst(); @@ -580,12 +580,12 @@ export function update({ "Tournament.id as tournamentId", "CalendarEvent.name", "Tournament.tier", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", ]) .where("Tournament.isFinalized", "=", 1) .where("CalendarEvent.organizationId", "=", id) .where("CalendarEvent.hidden", "=", 0) - .orderBy("CalendarEventDate.startTime", "asc") + .orderBy("CalendarEventDate.startsAt", "asc") .execute(); for (const s of insertedSeries) { diff --git a/app/features/tournament-organization/components/EventCalendar.tsx b/app/features/tournament-organization/components/EventCalendar.tsx index d9b7dc2f9..654f439a3 100644 --- a/app/features/tournament-organization/components/EventCalendar.tsx +++ b/app/features/tournament-organization/components/EventCalendar.tsx @@ -44,7 +44,7 @@ export function EventCalendar({ ))} {dates.map((date, i) => { const daysEvents = events.filter((event) => { - const startTimeDate = databaseTimestampToDate(event.startTime); + const startTimeDate = databaseTimestampToDate(event.startsAt); return ( isHydrated && diff --git a/app/features/tournament-organization/loaders/org.$slug.server.ts b/app/features/tournament-organization/loaders/org.$slug.server.ts index e2a290784..cf31f90bb 100644 --- a/app/features/tournament-organization/loaders/org.$slug.server.ts +++ b/app/features/tournament-organization/loaders/org.$slug.server.ts @@ -137,6 +137,6 @@ async function seriesStuff({ : null, eventsCount: events.length, logoUrl: events[0].logoUrl, - established: events.at(-1)!.startTime, + established: events.at(-1)!.startsAt, }; } diff --git a/app/features/tournament-organization/routes/org.$slug.tsx b/app/features/tournament-organization/routes/org.$slug.tsx index 9ad95e571..dd3fc3d8a 100644 --- a/app/features/tournament-organization/routes/org.$slug.tsx +++ b/app/features/tournament-organization/routes/org.$slug.tsx @@ -485,11 +485,11 @@ function EventsList({ const events = filteredByMonth ? data.events.filter( (event) => - databaseTimestampToDate(event.startTime).getMonth() === data.month, + databaseTimestampToDate(event.startsAt).getMonth() === data.month, ) : data.events; - const pastEvents = events.filter((event) => event.startTime < now); - const upcomingEvents = events.filter((event) => event.startTime >= now); + const pastEvents = events.filter((event) => event.startsAt < now); + const upcomingEvents = events.filter((event) => event.startsAt >= now); return (
@@ -540,7 +540,7 @@ function EventInfo({
{event.name}
, args: InsertArgs) { subjectUserId: args.subjectUserId ?? null, tournamentTeamHistoryId, metadata: args.metadata ? JSON.stringify(args.metadata) : null, - createdAt: databaseTimestampNow(), }) .execute(); } diff --git a/app/features/tournament/TournamentRepository.server.ts b/app/features/tournament/TournamentRepository.server.ts index 9e5dbc94b..cab1e33a1 100644 --- a/app/features/tournament/TournamentRepository.server.ts +++ b/app/features/tournament/TournamentRepository.server.ts @@ -67,7 +67,7 @@ export async function findById(id: number) { .as("parentTournamentName"), "Tournament.tier", "CalendarEvent.name", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", "Tournament.isFinalized", jsonObjectFrom( eb @@ -549,7 +549,7 @@ export function forShowcase() { "CalendarEvent.authorId", "CalendarEvent.name", "CalendarEvent.organizationId", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", "CalendarEvent.hidden", eb .selectFrom("TournamentTeam") @@ -567,7 +567,7 @@ export function forShowcase() { .where((eb) => eb.or([ eb("TournamentTeamCheckIn.checkedInAt", "is not", null), - eb("CalendarEventDate.startTime", ">", databaseTimestampNow()), + eb("CalendarEventDate.startsAt", ">", databaseTimestampNow()), ]), ) .select(({ fn }) => [ @@ -639,8 +639,8 @@ export function forShowcase() { .select(({ fn }) => [fn.countAll().as("count")]) .as("vodCount"), ]) - .where("CalendarEventDate.startTime", ">", databaseTimestampWeekAgo()) - .orderBy("CalendarEventDate.startTime", "asc") + .where("CalendarEventDate.startsAt", ">", databaseTimestampWeekAgo()) + .orderBy("CalendarEventDate.startsAt", "asc") .$narrowType<{ teamsCount: NotNull }>() .execute(); } @@ -670,15 +670,11 @@ export function findAllBetweenTwoTimestamps({ .innerJoin("Tournament", "CalendarEvent.tournamentId", "Tournament.id") .select(["Tournament.id as tournamentId"]) .where( - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", ">", dateToDatabaseTimestamp(startTime), ) - .where( - "CalendarEventDate.startTime", - "<=", - dateToDatabaseTimestamp(endTime), - ) + .where("CalendarEventDate.startsAt", "<=", dateToDatabaseTimestamp(endTime)) .where("CalendarEvent.hidden", "=", 0) .execute(); } @@ -1304,17 +1300,17 @@ export async function searchByName({ .select((eb) => [ "Tournament.id", "CalendarEvent.name", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", tournamentLogoWithDefault(eb).as("logoUrl"), ]) .where("CalendarEvent.name", "like", `%${query}%`) .where("CalendarEvent.hidden", "=", 0) - .orderBy("CalendarEventDate.startTime", "desc") + .orderBy("CalendarEventDate.startsAt", "desc") .limit(limit); if (minStartTime) { sqlQuery = sqlQuery.where( - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", ">=", dateToDatabaseTimestamp(minStartTime), ); @@ -1322,7 +1318,7 @@ export async function searchByName({ if (maxStartTime) { sqlQuery = sqlQuery.where( - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", "<=", dateToDatabaseTimestamp(maxStartTime), ); @@ -1398,8 +1394,8 @@ export async function findRunningTournamentIds() { ) .select("Tournament.id") .where("Tournament.isFinalized", "=", 0) - .where("CalendarEventDate.startTime", "<", dateToDatabaseTimestamp(now)) - .where("CalendarEventDate.startTime", ">", dateToDatabaseTimestamp(cutoff)) + .where("CalendarEventDate.startsAt", "<", dateToDatabaseTimestamp(now)) + .where("CalendarEventDate.startsAt", ">", dateToDatabaseTimestamp(cutoff)) .where((eb) => eb.exists( eb diff --git a/app/features/tournament/TournamentTeamRepository.server.ts b/app/features/tournament/TournamentTeamRepository.server.ts index 28ac0ba42..c4e294075 100644 --- a/app/features/tournament/TournamentTeamRepository.server.ts +++ b/app/features/tournament/TournamentTeamRepository.server.ts @@ -52,7 +52,7 @@ const regOpenTournamentTeamsByJoinedUserId = (userId: number) => .where( sql`coalesce( "Tournament"."settings" ->> 'regClosesAt', - "CalendarEventDate"."startTime" + "CalendarEventDate"."startsAt" )`, ">", databaseTimestampNow(), @@ -375,7 +375,7 @@ async function registrationClosedNow( .select( sql`coalesce( "Tournament"."settings" ->> 'regClosesAt', - min("CalendarEventDate"."startTime") + min("CalendarEventDate"."startsAt") )`.as("regClosesAt"), ) .where("Tournament.id", "=", tournamentId) diff --git a/app/features/tournament/components/TournamentHeader.tsx b/app/features/tournament/components/TournamentHeader.tsx index b427315e3..4bbffe4a8 100644 --- a/app/features/tournament/components/TournamentHeader.tsx +++ b/app/features/tournament/components/TournamentHeader.tsx @@ -24,7 +24,7 @@ export function TournamentHeader({ tournament }: { tournament: Tournament }) { const startTimes = R.uniqueBy( [ - tournament.ctx.startTime, + tournament.ctx.startsAt, ...tournament.ctx.settings.bracketProgression .filter((b) => b.startTime) .map((b) => databaseTimestampToDate(b.startTime!)), diff --git a/app/features/tournament/loaders/to.$id.server.ts b/app/features/tournament/loaders/to.$id.server.ts index 731759ea7..27770a9c3 100644 --- a/app/features/tournament/loaders/to.$id.server.ts +++ b/app/features/tournament/loaders/to.$id.server.ts @@ -37,7 +37,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => { const friendCodeVisibilityDays = tournament.ctx.parentTournamentId ? 120 : 30; const tournamentStartedRecently = isAfter( - databaseTimestampToDate(tournament.ctx.startTime), + databaseTimestampToDate(tournament.ctx.startsAt), subDays(new Date(), friendCodeVisibilityDays), ); const isTournamentAdmin = @@ -73,7 +73,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => { const showVods = tournament.ctx.isFinalized && isAfter( - databaseTimestampToDate(tournament.ctx.startTime), + databaseTimestampToDate(tournament.ctx.startsAt), subDays(new Date(), TOURNAMENT.VOD_VISIBILITY_DAYS), ); diff --git a/app/features/tournament/routes/to.$id.info.tsx b/app/features/tournament/routes/to.$id.info.tsx index 69fbc427f..2c9231b4e 100644 --- a/app/features/tournament/routes/to.$id.info.tsx +++ b/app/features/tournament/routes/to.$id.info.tsx @@ -86,7 +86,7 @@ function useFacts( const showsEstimatedTier = !tournament.ctx.tier && !tournament.hasStarted; - const rankedSeason = Seasons.current(tournament.ctx.startTime); + const rankedSeason = Seasons.current(tournament.ctx.startsAt); return [ { diff --git a/app/features/tournament/routes/to.$id.register.tsx b/app/features/tournament/routes/to.$id.register.tsx index f04b13b7b..d4e40d955 100644 --- a/app/features/tournament/routes/to.$id.register.tsx +++ b/app/features/tournament/routes/to.$id.register.tsx @@ -300,12 +300,12 @@ function RegistrationProgress({ const regClosesBeforeStart = tournament.registrationClosesAt.getTime() !== - tournament.ctx.startTime.getTime(); + tournament.ctx.startsAt.getTime(); const registrationClosesAtString = registrationClosesFormatter.format( tournament.isLeagueSignup - ? tournament.ctx.startTime + ? tournament.ctx.startsAt : tournament.registrationClosesAt, ) ?? ""; diff --git a/app/features/tournament/routes/to.$id.results.tsx b/app/features/tournament/routes/to.$id.results.tsx index 9160ff019..d662dffc9 100644 --- a/app/features/tournament/routes/to.$id.results.tsx +++ b/app/features/tournament/routes/to.$id.results.tsx @@ -34,7 +34,7 @@ export default function TournamentResultsPage() { const { isCensored, reveal } = useSpoilerFree(); const withinSpoilerWindow = - differenceInDays(new Date(), tournament.ctx.startTime) < + differenceInDays(new Date(), tournament.ctx.startsAt) < TOURNAMENT.VOD_VISIBILITY_DAYS; const censored = withinSpoilerWindow && isCensored(tournament.ctx.id); diff --git a/app/features/tournament/tournament-utils.ts b/app/features/tournament/tournament-utils.ts index 0c63fe804..adef80e66 100644 --- a/app/features/tournament/tournament-utils.ts +++ b/app/features/tournament/tournament-utils.ts @@ -139,18 +139,18 @@ export function validateCounterPickMapPool( export function tournamentIsRanked({ isSetAsRanked, - startTime, + startsAt, minMembersPerTeam, isTest, }: { isSetAsRanked?: boolean; - startTime: Date; + startsAt: Date; minMembersPerTeam: number; isTest: boolean; }) { if (isTest) return false; - const seasonIsActive = Boolean(Seasons.current(startTime)); + const seasonIsActive = Boolean(Seasons.current(startsAt)); if (!seasonIsActive) return false; // 1v1, 2v2 and 3v3 are always considered "gimmicky" @@ -160,7 +160,7 @@ export function tournamentIsRanked({ } /** - * Whether a tournament's startTime falls inside the active weapon-reporting window + * Whether a tournament's start time falls inside the active weapon-reporting window * for late (post-finalization) reporting. * * - In-season: window is `(previousSeason.ends, now]` — current season plus the off-season immediately before it. diff --git a/app/features/user-page/UserRepository.server.ts b/app/features/user-page/UserRepository.server.ts index dda2bb090..5750d0a19 100644 --- a/app/features/user-page/UserRepository.server.ts +++ b/app/features/user-page/UserRepository.server.ts @@ -472,7 +472,7 @@ export function findAllPatrons() { .select(["User.id", "User.discordId", "User.username", "User.patronTier"]) .where("User.patronTier", "is not", null) .orderBy("User.patronTier", "desc") - .orderBy("User.patronSince", "asc") + .orderBy("User.patronStartedAt", "asc") .execute(); } @@ -507,9 +507,9 @@ export async function findChatUsersByUserIds(userIds: number[]) { const withMaxEventStartTime = (eb: ExpressionBuilder) => { return eb .selectFrom("CalendarEventDate") - .select(({ fn }) => [fn.max("CalendarEventDate.startTime").as("startTime")]) + .select(({ fn }) => [fn.max("CalendarEventDate.startsAt").as("startsAt")]) .whereRef("CalendarEventDate.eventId", "=", "CalendarEvent.id") - .as("startTime"); + .as("startsAt"); }; const baseCalendarEventResultsQuery = (userId: number) => @@ -669,8 +669,8 @@ export function findResultsByUserId( let query = calendarEventResultsQuery .unionAll(tournamentResultsQuery) - .orderBy("startTime", "desc") - .$narrowType<{ startTime: NotNull }>(); + .orderBy("startsAt", "desc") + .$narrowType<{ startsAt: NotNull }>(); if (limit !== undefined) { query = query.limit(limit); @@ -973,14 +973,14 @@ export async function inGameNameByUserId(userId: number) { )?.inGameName; } -export async function patronSinceByUserId(userId: number) { +export async function patronStartedAtByUserId(userId: number) { return ( await db .selectFrom("User") - .select("User.patronSince") + .select("User.patronStartedAt") .where("id", "=", userId) .executeTakeFirst() - )?.patronSince; + )?.patronStartedAt; } export async function joinOrderByUserId(userId: number) { @@ -1248,7 +1248,7 @@ export function updateOwnBuildSorting(buildSorting: BuildSort[] | null) { } export type UpdatePatronDataArgs = Array< - Pick + Pick >; export function updatePatronData(users: UpdatePatronDataArgs) { return db.transaction().execute(async (trx) => { @@ -1256,13 +1256,13 @@ export function updatePatronData(users: UpdatePatronDataArgs) { .updateTable("User") .set({ patronTier: null, - patronSince: null, - patronTill: null, + patronStartedAt: null, + patronExpiresAt: null, }) .where((eb) => eb.or([ - eb("patronTill", "<", dateToDatabaseTimestamp(new Date())), - eb("patronTill", "is", null), + eb("patronExpiresAt", "<", dateToDatabaseTimestamp(new Date())), + eb("patronExpiresAt", "is", null), ]), ) .execute(); @@ -1272,8 +1272,8 @@ export function updatePatronData(users: UpdatePatronDataArgs) { .updateTable("User") .set({ patronTier: user.patronTier, - patronSince: user.patronSince, - patronTill: null, + patronStartedAt: user.patronStartedAt, + patronExpiresAt: null, }) .where("User.discordId", "=", user.discordId) .execute(); diff --git a/app/features/user-page/UserRepository.test.ts b/app/features/user-page/UserRepository.test.ts index 5fbe59ccc..ac8a79f16 100644 --- a/app/features/user-page/UserRepository.test.ts +++ b/app/features/user-page/UserRepository.test.ts @@ -154,8 +154,8 @@ describe("UserRepository", () => { await AdminRepository.forcePatron({ id, patronTier: 1, - patronSince: now, - patronTill: oneYearFromNow, + patronStartedAt: now, + patronExpiresAt: oneYearFromNow, }); const user = await UserRepository.findLeanById(id); @@ -178,8 +178,8 @@ describe("UserRepository", () => { await AdminRepository.forcePatron({ id, patronTier: 2, - patronSince: now, - patronTill: oneYearFromNow, + patronStartedAt: now, + patronExpiresAt: oneYearFromNow, }); const user = await UserRepository.findLeanById(id); @@ -305,8 +305,8 @@ describe("UserRepository", () => { await AdminRepository.forcePatron({ id, patronTier: 2, - patronSince: now, - patronTill: oneYearFromNow, + patronStartedAt: now, + patronExpiresAt: oneYearFromNow, }); await AdminRepository.makeArtistByUserId(id); diff --git a/app/features/user-page/components/UserResultsTable.tsx b/app/features/user-page/components/UserResultsTable.tsx index c0d223fbe..f1685e3ad 100644 --- a/app/features/user-page/components/UserResultsTable.tsx +++ b/app/features/user-page/components/UserResultsTable.tsx @@ -120,7 +120,7 @@ export function UserResultsTable({ ; }) { const { t } = useTranslation(["user", "badges", "team", "org", "lfg"]); - const { formatter: patronSinceFormatter } = useDateTimeFormat({ + const { formatter: patronStartedAtFormatter } = useDateTimeFormat({ day: "numeric", month: "numeric", year: "numeric", @@ -184,7 +184,9 @@ export function Widget({ case "patron-since": if (!widget.data) return null; return ( - + ); case "join-date": if (!widget.data) return null; @@ -418,7 +420,7 @@ function HighlightedResults({ ) : null}
{ - return UserRepository.patronSinceByUserId(userId); + return UserRepository.patronStartedAtByUserId(userId); }, "join-date": async (userId: number) => { return UserRepository.joinOrderByUserId(userId); diff --git a/app/features/vods/VodRepository.server.ts b/app/features/vods/VodRepository.server.ts index 866e857b1..3ddff1050 100644 --- a/app/features/vods/VodRepository.server.ts +++ b/app/features/vods/VodRepository.server.ts @@ -95,7 +95,7 @@ export async function findVods({ } const result = await query .groupBy("Video.id") - .orderBy("Video.youtubeDate", "desc") + .orderBy("Video.youtubePublishedAt", "desc") .limit(limit) .offset(offset) .execute(); @@ -163,7 +163,7 @@ export async function findVodById(id: Tables["Video"]["id"]) { .select([ "id", "title", - "youtubeDate", + "youtubePublishedAt", "youtubeId", "type", "submitterUserId", @@ -259,7 +259,7 @@ export async function insert( const video = { title: args.title, type: args.type, - youtubeDate: dayMonthYearToDatabaseTimestamp(args.date), + youtubePublishedAt: dayMonthYearToDatabaseTimestamp(args.date), eventId: args.eventId ?? null, youtubeId, submitterUserId: args.submitterUserId, diff --git a/app/features/vods/routes/vods.$id.tsx b/app/features/vods/routes/vods.$id.tsx index 749989eb7..c19efadb0 100644 --- a/app/features/vods/routes/vods.$id.tsx +++ b/app/features/vods/routes/vods.$id.tsx @@ -97,7 +97,7 @@ export default function VodPage() {
; submitterUserId: Tables["Video"]["submitterUserId"]; @@ -28,7 +28,7 @@ export type VodMatch = Pick< weapons: Array; }; -export type ListVod = Omit & { +export type ListVod = Omit & { weapons: Array; type: Tables["Video"]["type"]; }; diff --git a/app/features/vods/vods-utils.test.ts b/app/features/vods/vods-utils.test.ts index 9444e9612..f4c3b422a 100644 --- a/app/features/vods/vods-utils.test.ts +++ b/app/features/vods/vods-utils.test.ts @@ -223,7 +223,7 @@ describe("generateYoutubeTimestamps", () => { }); describe("vodToVideoBeingAdded", () => { - // youtubeDate is stored as noon UTC of the chosen day (see + // youtubePublishedAt is stored as noon UTC of the chosen day (see // dayMonthYearToDatabaseTimestamp), so reading the day/month/year back out // must also use UTC. Force a timezone east of UTC+12 where local time has // already rolled over to the next day, making the bug deterministic. @@ -242,7 +242,7 @@ describe("vodToVideoBeingAdded", () => { title: "Test VOD", type: "TOURNAMENT", youtubeId: "dQw4w9WgXcQ", - youtubeDate: dayMonthYearToDatabaseTimestamp(date), + youtubePublishedAt: dayMonthYearToDatabaseTimestamp(date), submitterUserId: 1, matches: [], }; diff --git a/app/features/vods/vods-utils.ts b/app/features/vods/vods-utils.ts index ef4c92130..c3dc56302 100644 --- a/app/features/vods/vods-utils.ts +++ b/app/features/vods/vods-utils.ts @@ -5,7 +5,7 @@ import { HOURS_MINUTES_SECONDS_REGEX } from "./vods-schemas"; import type { VideoBeingAdded, Vod } from "./vods-types"; export function vodToVideoBeingAdded(vod: Vod): VideoBeingAdded { - const dateObj = databaseTimestampToDate(vod.youtubeDate); + const dateObj = databaseTimestampToDate(vod.youtubePublishedAt); return { title: vod.title, diff --git a/app/modules/patreon/updater.ts b/app/modules/patreon/updater.ts index 934a454fc..203f9f6b9 100644 --- a/app/modules/patreon/updater.ts +++ b/app/modules/patreon/updater.ts @@ -41,7 +41,7 @@ export async function updatePatreonData(): Promise { ).map((discordId) => ({ discordId, patronTier: 4, - patronSince: dateToDatabaseTimestamp(new Date()), + patronStartedAt: dateToDatabaseTimestamp(new Date()), })), ]; @@ -130,7 +130,7 @@ function parsePatronData({ patronsWithIds.push({ patreonId: patron.relationships.user.data.id, - patronSince: dateToDatabaseTimestamp( + patronStartedAt: dateToDatabaseTimestamp( new Date(patron.attributes.pledge_relationship_start ?? Date.now()), ), patronTier: idToTierNumber(tier), @@ -162,7 +162,7 @@ function parsePatronData({ } result.patrons.push({ - patronSince: patronData.patronSince, + patronStartedAt: patronData.patronStartedAt, discordId, patronTier: patronData.patronTier, }); diff --git a/db-test.sqlite3 b/db-test.sqlite3 index 784d80a05..a687b38ca 100644 Binary files a/db-test.sqlite3 and b/db-test.sqlite3 differ diff --git a/docs/dev/database-schemas.md b/docs/dev/database-schemas.md index fea9694c4..4ec1f5262 100644 --- a/docs/dev/database-schemas.md +++ b/docs/dev/database-schemas.md @@ -25,14 +25,18 @@ Counter-examples that look like booleans but aren't: `User.banned` (`1` = permab ## Generated vs. GeneratedAlways - `Generated` — column has a DB default, so it's optional on insert but you may still pass a value (`createdAt`, every boolean flag). -- `GeneratedAlways` — never insertable or updatable: primary keys, computed columns, `createdAt` columns whose value should never be supplied by app code. +- `GeneratedAlways` — never insertable or updatable: primary keys and computed columns (`User.username`, the `UserSearch` FTS columns). - Plain `T` — required on every insert. +Pick by what the DB allows, not by what app code happens to do today: if the column has a default it is `Generated`, even when nothing currently overrides it. + ## Timestamps Unix seconds as `number`, named `*At`. Never ISO strings, never milliseconds. -Columns with `default (strftime('%s', 'now'))` are `Generated`; use `GeneratedAlways` when app code should never set them. Nullable `*At` columns double as markers (`deletedAt`, `validatedAt`, `leftAt`) — document that meaning in JSDoc. +Name every instant with an `*At` suffix, including future ones: `startsAt`, `endsAt`, `expiresAt`, `becomesValidAt`. Not `startTime`, `youtubeDate` or a bare `at`. Domain objects and repository return shapes that carry a column's value keep the column's name; form fields, query range bounds and external API payloads are free to use their own names. + +Every `createdAt` gets `default (strftime('%s', 'now'))` and is typed `Generated`. The two nullable ones (`User`, `Skill`) predate the column existing and have no backfillable value — JSDoc says so on each. Other nullable `*At` columns double as markers (`deletedAt`, `validatedAt`, `leftAt`) — document that meaning in JSDoc too. Convert with `~/utils/dates`: `dateToDatabaseTimestamp`, `databaseTimestampToDate`, `databaseTimestampNow`. @@ -63,7 +67,7 @@ Non-obvious columns get a JSDoc comment: what `null` means, what a magic number - SQLite can't add `not null` to an existing column. To tighten one: add `"_new" integer not null default 0`, `update ... set "_new" = coalesce("", 0)`, drop the old column, rename the new one. See `migrations/162-boolean-columns-not-null.js`. - `drop column` fails if the column is indexed, part of a constraint, or referenced by a trigger, view, generated column or partial index. Check `sqlite_master` first. - Only when that isn't enough, rebuild the table: `pragma foreign_keys = OFF`, create `X_new`, copy, drop, rename, recreate every index, then `pragma foreign_key_check` inside the transaction. See `migrations/161-tournament-match-nullable-opponents.js`. -- New columns can't be added with a non-constant default, so `createdAt` added later ends up nullable (see `TournamentStage.createdAt`). +- New columns can't be added with a non-constant default, so a `createdAt` added later starts out nullable. Backfill it and rebuild the table in the same migration rather than leaving the nullability behind — see the `TournamentStage` rebuild in `migrations/162-boolean-columns-not-null.js`. ## After changing the schema diff --git a/e2e/seeds/db-seed-AB_RR.sqlite3 b/e2e/seeds/db-seed-AB_RR.sqlite3 index 07f9773ec..7c5795fe1 100644 Binary files a/e2e/seeds/db-seed-AB_RR.sqlite3 and b/e2e/seeds/db-seed-AB_RR.sqlite3 differ diff --git a/e2e/seeds/db-seed-DEFAULT.sqlite3 b/e2e/seeds/db-seed-DEFAULT.sqlite3 index dcc7b50c7..5a55f3fd4 100644 Binary files a/e2e/seeds/db-seed-DEFAULT.sqlite3 and b/e2e/seeds/db-seed-DEFAULT.sqlite3 differ diff --git a/e2e/seeds/db-seed-FINALIZED_BRACKET.sqlite3 b/e2e/seeds/db-seed-FINALIZED_BRACKET.sqlite3 index 3b6944bb8..ca8be051f 100644 Binary files a/e2e/seeds/db-seed-FINALIZED_BRACKET.sqlite3 and b/e2e/seeds/db-seed-FINALIZED_BRACKET.sqlite3 differ diff --git a/e2e/seeds/db-seed-IN_SQ_MATCH.sqlite3 b/e2e/seeds/db-seed-IN_SQ_MATCH.sqlite3 index eacbd08cc..1ba947b38 100644 Binary files a/e2e/seeds/db-seed-IN_SQ_MATCH.sqlite3 and b/e2e/seeds/db-seed-IN_SQ_MATCH.sqlite3 differ diff --git a/e2e/seeds/db-seed-NO_SCRIMS.sqlite3 b/e2e/seeds/db-seed-NO_SCRIMS.sqlite3 index 7509904e4..38b0569ab 100644 Binary files a/e2e/seeds/db-seed-NO_SCRIMS.sqlite3 and b/e2e/seeds/db-seed-NO_SCRIMS.sqlite3 differ diff --git a/e2e/seeds/db-seed-NO_SQ_GROUPS.sqlite3 b/e2e/seeds/db-seed-NO_SQ_GROUPS.sqlite3 index 149dc7f3c..4be7ed5e3 100644 Binary files a/e2e/seeds/db-seed-NO_SQ_GROUPS.sqlite3 and b/e2e/seeds/db-seed-NO_SQ_GROUPS.sqlite3 differ diff --git a/e2e/seeds/db-seed-NO_TOURNAMENT_TEAMS.sqlite3 b/e2e/seeds/db-seed-NO_TOURNAMENT_TEAMS.sqlite3 index bd5811afb..2793a6eaa 100644 Binary files a/e2e/seeds/db-seed-NO_TOURNAMENT_TEAMS.sqlite3 and b/e2e/seeds/db-seed-NO_TOURNAMENT_TEAMS.sqlite3 differ diff --git a/e2e/seeds/db-seed-NZAP_IN_TEAM.sqlite3 b/e2e/seeds/db-seed-NZAP_IN_TEAM.sqlite3 index 4b4cf0404..0d79befce 100644 Binary files a/e2e/seeds/db-seed-NZAP_IN_TEAM.sqlite3 and b/e2e/seeds/db-seed-NZAP_IN_TEAM.sqlite3 differ diff --git a/e2e/seeds/db-seed-REG_OPEN.sqlite3 b/e2e/seeds/db-seed-REG_OPEN.sqlite3 index 65dea24d7..0a91c5916 100644 Binary files a/e2e/seeds/db-seed-REG_OPEN.sqlite3 and b/e2e/seeds/db-seed-REG_OPEN.sqlite3 differ diff --git a/e2e/seeds/db-seed-SMALL_SOS.sqlite3 b/e2e/seeds/db-seed-SMALL_SOS.sqlite3 index bd86954c1..6b10456f5 100644 Binary files a/e2e/seeds/db-seed-SMALL_SOS.sqlite3 and b/e2e/seeds/db-seed-SMALL_SOS.sqlite3 differ diff --git a/e2e/seeds/db-seed-TEAM_MAP_PREFS.sqlite3 b/e2e/seeds/db-seed-TEAM_MAP_PREFS.sqlite3 index ed4e939c4..645479738 100644 Binary files a/e2e/seeds/db-seed-TEAM_MAP_PREFS.sqlite3 and b/e2e/seeds/db-seed-TEAM_MAP_PREFS.sqlite3 differ diff --git a/migrations/162-boolean-columns-not-null.js b/migrations/162-boolean-columns-not-null.js index 11eab53bf..8d4879d5c 100644 --- a/migrations/162-boolean-columns-not-null.js +++ b/migrations/162-boolean-columns-not-null.js @@ -7,7 +7,23 @@ const NULLABLE_BOOLEAN_COLUMNS = [ { table: "User", column: "commissionsOpen" }, ]; +const RENAMED_TIMESTAMP_COLUMNS = [ + { table: "CalendarEventDate", from: "startTime", to: "startsAt" }, + { table: "ExternalStream", from: "startTime", to: "startsAt" }, + { table: "SplatoonRotation", from: "startTime", to: "startsAt" }, + { table: "SplatoonRotation", from: "endTime", to: "endsAt" }, + { table: "ScrimPost", from: "at", to: "startsAt" }, + { table: "ScrimPost", from: "rangeEnd", to: "rangeEndsAt" }, + { table: "ScrimPostRequest", from: "at", to: "startsAt" }, + { table: "PlusVote", from: "validAfter", to: "becomesValidAt" }, + { table: "UnvalidatedVideo", from: "youtubeDate", to: "youtubePublishedAt" }, + { table: "User", from: "patronSince", to: "patronStartedAt" }, + { table: "User", from: "patronTill", to: "patronExpiresAt" }, +]; + export function up(db) { + db.pragma("foreign_keys = OFF"); + db.transaction(() => { db.prepare( /*sql*/ `alter table "Build" add column "isPrivate" integer not null default 0`, @@ -37,6 +53,116 @@ export function up(db) { ).run(); } + for (const { table, from, to } of RENAMED_TIMESTAMP_COLUMNS) { + db.prepare( + /*sql*/ `alter table "${table}" rename column "${from}" to "${to}"`, + ).run(); + } + + db.prepare( + /*sql*/ "drop index calendar_event_date_event_id_start_time", + ).run(); + db.prepare( + /*sql*/ `create index calendar_event_date_event_id_starts_at on "CalendarEventDate"("eventId", "startsAt" desc)`, + ).run(); + + db.prepare(/*sql*/ "drop index scrim_post_at").run(); + db.prepare( + /*sql*/ `create index scrim_post_starts_at on "ScrimPost"("startsAt")`, + ).run(); + + db.prepare( + /* sql */ ` + create table "TournamentAuditLog_new" ( + "id" integer primary key autoincrement, + "tournamentId" integer not null, + "type" text not null, + "actorUserId" integer not null, + "subjectUserId" integer, + "tournamentTeamHistoryId" integer, + "metadata" text, + "createdAt" integer default (strftime('%s', 'now')) not null, + foreign key ("tournamentId") references "Tournament"("id") on delete cascade, + foreign key ("actorUserId") references "User"("id"), + foreign key ("subjectUserId") references "User"("id"), + foreign key ("tournamentTeamHistoryId") references "TournamentTeamHistory"("id") + ) strict + `, + ).run(); + + db.prepare( + /* sql */ ` + insert into "TournamentAuditLog_new" ("id", "tournamentId", "type", "actorUserId", "subjectUserId", "tournamentTeamHistoryId", "metadata", "createdAt") + select "id", "tournamentId", "type", "actorUserId", "subjectUserId", "tournamentTeamHistoryId", "metadata", "createdAt" + from "TournamentAuditLog" + `, + ).run(); + + db.prepare(/* sql */ `drop table "TournamentAuditLog"`).run(); + + db.prepare( + /* sql */ `alter table "TournamentAuditLog_new" rename to "TournamentAuditLog"`, + ).run(); + + db.prepare( + /* sql */ `create index tournament_audit_log_tournament_id_created_at_idx on "TournamentAuditLog"("tournamentId", "createdAt")`, + ).run(); + db.prepare( + /* sql */ `create index tournament_audit_log_tournament_id_team_history_id_type_created_at_idx on "TournamentAuditLog"("tournamentId", "tournamentTeamHistoryId", "type", "createdAt")`, + ).run(); + + db.prepare( + /* sql */ ` + update "TournamentStage" + set "createdAt" = coalesce( + ( + select min("CalendarEventDate"."startsAt") + from "CalendarEvent" + inner join "CalendarEventDate" on "CalendarEventDate"."eventId" = "CalendarEvent"."id" + where "CalendarEvent"."tournamentId" = "TournamentStage"."tournamentId" + ), + strftime('%s', 'now') + ) + where "createdAt" is null + `, + ).run(); + + db.prepare( + /* sql */ ` + create table "TournamentStage_new" ( + "id" integer primary key, + "tournamentId" integer not null, + "name" text not null, + "type" text not null, + "settings" text not null, + "number" integer not null, + "createdAt" integer default (strftime('%s', 'now')) not null, + foreign key ("tournamentId") references "Tournament"("id") on delete cascade, + unique("number", "tournamentId") on conflict rollback + ) strict + `, + ).run(); + + db.prepare( + /* sql */ ` + insert into "TournamentStage_new" ("id", "tournamentId", "name", "type", "settings", "number", "createdAt") + select "id", "tournamentId", "name", "type", "settings", "number", "createdAt" + from "TournamentStage" + `, + ).run(); + + db.prepare(/* sql */ `drop table "TournamentStage"`).run(); + + db.prepare( + /* sql */ `alter table "TournamentStage_new" rename to "TournamentStage"`, + ).run(); + + db.prepare( + /* sql */ `create index tournament_stage_tournament_id on "TournamentStage"("tournamentId")`, + ).run(); + db.pragma("foreign_key_check"); })(); + + db.pragma("foreign_keys = ON"); } diff --git a/scripts/benchmark-db/cases.ts b/scripts/benchmark-db/cases.ts index ca6e9df22..278208378 100644 --- a/scripts/benchmark-db/cases.ts +++ b/scripts/benchmark-db/cases.ts @@ -1014,8 +1014,8 @@ export function buildCases(fx: Fixtures): { add("UserRepository.inGameNameByUserId", fx.heavyUser, (user) => UserRepository.inGameNameByUserId(user.id), ); - add("UserRepository.patronSinceByUserId", fx.heavyUser, (user) => - UserRepository.patronSinceByUserId(user.id), + add("UserRepository.patronStartedAtByUserId", fx.heavyUser, (user) => + UserRepository.patronStartedAtByUserId(user.id), ); add("UserRepository.joinOrderByUserId", fx.heavyUser, (user) => UserRepository.joinOrderByUserId(user.id), diff --git a/scripts/benchmark-db/fixtures.ts b/scripts/benchmark-db/fixtures.ts index 0000649fb..29047416c 100644 --- a/scripts/benchmark-db/fixtures.ts +++ b/scripts/benchmark-db/fixtures.ts @@ -644,9 +644,9 @@ async function resolveHeavyOrg() { "CalendarEventDate.eventId", "CalendarEvent.id", ) - .select(["CalendarEvent.name", "CalendarEventDate.startTime"]) + .select(["CalendarEvent.name", "CalendarEventDate.startsAt"]) .where("CalendarEvent.organizationId", "=", orgRow.id) - .orderBy("CalendarEventDate.startTime", "desc") + .orderBy("CalendarEventDate.startsAt", "desc") .limit(1) .executeTakeFirst(); if (!latestEvent) return null; diff --git a/scripts/check-season-status.ts b/scripts/check-season-status.ts index c453323fd..f06252bd9 100644 --- a/scripts/check-season-status.ts +++ b/scripts/check-season-status.ts @@ -55,11 +55,11 @@ const tournaments = await db "Tournament.settings", "Tournament.isFinalized", "CalendarEvent.name", - "CalendarEventDate.startTime", + "CalendarEventDate.startsAt", ]) - .where("CalendarEventDate.startTime", ">=", seasonStartTimestamp) - .where("CalendarEventDate.startTime", "<=", seasonEndTimestamp) - .orderBy("CalendarEventDate.startTime") + .where("CalendarEventDate.startsAt", ">=", seasonStartTimestamp) + .where("CalendarEventDate.startsAt", "<=", seasonEndTimestamp) + .orderBy("CalendarEventDate.startsAt") .execute(); const unfinalizedTournaments = tournaments.filter( diff --git a/scripts/create-league-divisions.ts b/scripts/create-league-divisions.ts index d1869f308..b5471f236 100644 --- a/scripts/create-league-divisions.ts +++ b/scripts/create-league-divisions.ts @@ -75,7 +75,7 @@ async function main() { name: `${tournament.ctx.name} - ${div.startsWith("Division") ? div : `Division ${div}`}`, organizationId: tournament.ctx.organization?.id ?? null, rules: tournament.ctx.rules, - startTimes: [dateToDatabaseTimestamp(tournament.ctx.startTime)], + startTimes: [dateToDatabaseTimestamp(tournament.ctx.startsAt)], tags: null, tournamentToCopyId: tournament.ctx.id, avatarImgId: calendarEvent.avatarImgId ?? undefined, diff --git a/scripts/org-monthly-active-players.ts b/scripts/org-monthly-active-players.ts index c7589e51f..22b03ba77 100644 --- a/scripts/org-monthly-active-players.ts +++ b/scripts/org-monthly-active-players.ts @@ -78,8 +78,8 @@ async function getParticipantsForOrgInMonth( ) .select(({ fn }) => fn.count("tmgrp.userId").distinct().as("count")) .where("ce.organizationId", "=", organizationId) - .where("ced.startTime", ">=", startTimestamp) - .where("ced.startTime", "<", endTimestamp) + .where("ced.startsAt", ">=", startTimestamp) + .where("ced.startsAt", "<", endTimestamp) .where("ttci.checkedInAt", "is not", null) .where("ttci.isCheckOut", "=", 0) .executeTakeFirst(); @@ -98,7 +98,7 @@ async function getOrgsWithRecentTournaments( .select("ce.organizationId") .distinct() .where("ce.organizationId", "is not", null) - .where("ced.startTime", ">=", earliestTimestamp) + .where("ced.startsAt", ">=", earliestTimestamp) .execute(); return orgs.map((org) => org.organizationId!);