mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-02 08:07:40 -05:00
Move app to apps/web-react in pnpm workspace layout
This commit is contained in:
154
apps/web-react/app/features/scrims/ScrimMapRepository.server.ts
Normal file
154
apps/web-react/app/features/scrims/ScrimMapRepository.server.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import type { Transaction } from "kysely";
|
||||
import { db } from "~/db/sql";
|
||||
import type { DB, TablesInsertable } from "~/db/tables";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
|
||||
import { databaseTimestampNow } from "~/utils/dates";
|
||||
import * as Scrim from "./core/Scrim";
|
||||
import * as ScrimMapByMap from "./core/ScrimMapByMap";
|
||||
import * as ScrimMapListRepository from "./ScrimMapListRepository.server";
|
||||
|
||||
interface ReportMapArgs {
|
||||
scrimPostId: number;
|
||||
mapId: number;
|
||||
winnerSide: NonNullable<TablesInsertable["ScrimMap"]["winnerSide"]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks an existing map as reported with the given winner side, and
|
||||
* (atomically) generates and inserts the next map for the scrim if no
|
||||
* unreported map is currently waiting.
|
||||
*/
|
||||
export async function reportMapAndGenerateNext(
|
||||
args: ReportMapArgs,
|
||||
): Promise<void> {
|
||||
await db.transaction().execute(async (trx) => {
|
||||
await trx
|
||||
.updateTable("ScrimMap")
|
||||
.set({
|
||||
winnerSide: args.winnerSide,
|
||||
reportedAt: databaseTimestampNow(),
|
||||
reportedByUserId: actorId(),
|
||||
})
|
||||
.where("id", "=", args.mapId)
|
||||
.where("reportedAt", "is", null)
|
||||
.execute();
|
||||
|
||||
await tryGenerateAndInsertNextMap(args.scrimPostId, trx);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the most recent report: deletes the currently unreported map (the
|
||||
* auto-generated next slot, if any) and clears the winner/reportedAt fields on
|
||||
* the most recently reported map so it can be played again.
|
||||
*/
|
||||
export async function undoMostRecentMap(scrimPostId: number): Promise<void> {
|
||||
await db.transaction().execute(async (trx) => {
|
||||
await trx
|
||||
.deleteFrom("ScrimMap")
|
||||
.where("scrimPostId", "=", scrimPostId)
|
||||
.where("reportedAt", "is", null)
|
||||
.execute();
|
||||
|
||||
const latestReported = await trx
|
||||
.selectFrom("ScrimMap")
|
||||
.select("id")
|
||||
.where("scrimPostId", "=", scrimPostId)
|
||||
.where("reportedAt", "is not", null)
|
||||
.orderBy("index", "desc")
|
||||
.limit(1)
|
||||
.executeTakeFirst();
|
||||
|
||||
if (!latestReported) return;
|
||||
|
||||
await trx
|
||||
.updateTable("ScrimMap")
|
||||
.set({
|
||||
reportedAt: null,
|
||||
winnerSide: null,
|
||||
reportedByUserId: null,
|
||||
})
|
||||
.where("id", "=", latestReported.id)
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
interface ReplaceCurrentMapArgs {
|
||||
scrimPostId: number;
|
||||
mode: ModeShort;
|
||||
stageId: StageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the currently unreported map for the scrim with the given
|
||||
* mode/stage. Used by both the "replay previous map" and "pick a map" actions.
|
||||
* The current map's index is preserved.
|
||||
*/
|
||||
export async function replaceCurrentMap(
|
||||
args: ReplaceCurrentMapArgs,
|
||||
): Promise<void> {
|
||||
await db
|
||||
.updateTable("ScrimMap")
|
||||
.set({
|
||||
mode: args.mode,
|
||||
stageId: args.stageId,
|
||||
})
|
||||
.where("scrimPostId", "=", args.scrimPostId)
|
||||
.where("reportedAt", "is", null)
|
||||
.execute();
|
||||
}
|
||||
|
||||
/** Returns the scrim's maps ordered by index ascending. */
|
||||
export function findMapsByScrimPostId(scrimPostId: number) {
|
||||
return db
|
||||
.selectFrom("ScrimMap")
|
||||
.select(["id", "index", "mode", "stageId", "winnerSide", "reportedAt"])
|
||||
.where("scrimPostId", "=", scrimPostId)
|
||||
.orderBy("index", "asc")
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* If a pool can be derived from the submitted map lists and no unreported map
|
||||
* is currently waiting, generates and inserts the next map. Runs entirely
|
||||
* within the caller's transaction so the read of the existing maps and the
|
||||
* insert see a consistent snapshot and no two concurrent report/submit actions
|
||||
* can insert a "next" map at the same index.
|
||||
*/
|
||||
export async function tryGenerateAndInsertNextMap(
|
||||
scrimPostId: number,
|
||||
trx: Transaction<DB>,
|
||||
): Promise<void> {
|
||||
const mapLists = await ScrimMapListRepository.findMapListsByScrimPostId(
|
||||
scrimPostId,
|
||||
trx,
|
||||
);
|
||||
if (mapLists.length === 0) return;
|
||||
|
||||
const pool = ScrimMapByMap.unionPool(mapLists);
|
||||
if (pool.isEmpty()) return;
|
||||
|
||||
const maps = await trx
|
||||
.selectFrom("ScrimMap")
|
||||
.select(["index", "mode", "stageId", "reportedAt"])
|
||||
.where("scrimPostId", "=", scrimPostId)
|
||||
.execute();
|
||||
|
||||
if (maps.some((m) => m.reportedAt === null)) return;
|
||||
|
||||
const next = ScrimMapByMap.generateNextMap({
|
||||
pool,
|
||||
history: maps.map((m) => ({ mode: m.mode, stageId: m.stageId })),
|
||||
});
|
||||
|
||||
await trx
|
||||
.insertInto("ScrimMap")
|
||||
.values({
|
||||
scrimPostId,
|
||||
index: Scrim.nextMapIndex(maps),
|
||||
mode: next.mode,
|
||||
stageId: next.stageId,
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
Reference in New Issue
Block a user