Fix scrim map my map locking too early

This commit is contained in:
Kalle
2026-08-28 07:21:50 +03:00
parent 317a3a3251
commit c5ea764b4c
4 changed files with 84 additions and 32 deletions

View File

@@ -219,7 +219,13 @@ async function loadMapByMapContext({
ScrimMapListRepository.findMapListsByScrimPostId(post.id),
]);
if (Scrim.isTrackingLocked(maps, mapLists)) {
if (
Scrim.isTrackingLocked({
startTime: Scrim.getStartTime(post),
maps,
mapLists,
})
) {
errorToast("Tracking is locked");
}

View File

@@ -532,46 +532,78 @@ describe("sideOfUser", () => {
describe("isTrackingLocked", () => {
const ONE_HOUR_MS = 60 * 60 * 1000;
const lockWindowMs = SCRIM_TRACKING_AUTO_LOCK_HOURS * ONE_HOUR_MS;
const now = 1_000_000_000;
const secondsAgo = (ms: number) => (now - ms) / 1000;
test("returns false when no map list submitted yet", () => {
expect(isTrackingLocked([], [], Date.now())).toBe(false);
expect(isTrackingLocked({ startTime: secondsAgo(ONE_HOUR_MS), now })).toBe(
false,
);
});
test("returns false just inside the auto-lock window from list submission", () => {
const now = 1_000_000_000;
const updatedAt = (now - (lockWindowMs - ONE_HOUR_MS)) / 1000;
expect(isTrackingLocked([], [{ updatedAt }], now)).toBe(false);
const startTime = secondsAgo(lockWindowMs);
const updatedAt = secondsAgo(lockWindowMs - ONE_HOUR_MS);
expect(
isTrackingLocked({ startTime, mapLists: [{ updatedAt }], now }),
).toBe(false);
});
test("returns true just past the auto-lock window from list submission", () => {
const now = 1_000_000_000;
const updatedAt = (now - (lockWindowMs + ONE_HOUR_MS)) / 1000;
expect(isTrackingLocked([], [{ updatedAt }], now)).toBe(true);
const startTime = secondsAgo(lockWindowMs * 2);
const updatedAt = secondsAgo(lockWindowMs + ONE_HOUR_MS);
expect(
isTrackingLocked({ startTime, mapLists: [{ updatedAt }], now }),
).toBe(true);
});
test("uses the most recent reported map as the reference point", () => {
const now = 1_000_000_000;
const oldUpdatedAt = (now - lockWindowMs * 2) / 1000;
const recentMapSeconds = (now - ONE_HOUR_MS) / 1000;
const startTime = secondsAgo(lockWindowMs * 2);
expect(
isTrackingLocked(
[{ reportedAt: recentMapSeconds }],
[{ updatedAt: oldUpdatedAt }],
isTrackingLocked({
startTime,
maps: [{ reportedAt: secondsAgo(ONE_HOUR_MS) }],
mapLists: [{ updatedAt: secondsAgo(lockWindowMs * 2) }],
now,
),
}),
).toBe(false);
});
test("uses the most recent list update when there are no reported maps", () => {
const now = 1_000_000_000;
const oldUpdatedAt = (now - lockWindowMs * 2) / 1000;
const recentUpdatedAt = (now - ONE_HOUR_MS) / 1000;
const startTime = secondsAgo(lockWindowMs * 2);
expect(
isTrackingLocked(
[],
[{ updatedAt: oldUpdatedAt }, { updatedAt: recentUpdatedAt }],
isTrackingLocked({
startTime,
mapLists: [
{ updatedAt: secondsAgo(lockWindowMs * 2) },
{ updatedAt: secondsAgo(ONE_HOUR_MS) },
],
now,
),
}),
).toBe(false);
});
test("returns false when the map list was submitted long before a scrim that just started", () => {
const startTime = secondsAgo(ONE_HOUR_MS);
const updatedAt = secondsAgo(lockWindowMs * 10);
expect(
isTrackingLocked({ startTime, mapLists: [{ updatedAt }], now }),
).toBe(false);
});
test("returns true once the auto-lock window has elapsed since the start time", () => {
const startTime = secondsAgo(lockWindowMs + ONE_HOUR_MS);
const updatedAt = secondsAgo(lockWindowMs * 10);
expect(
isTrackingLocked({ startTime, mapLists: [{ updatedAt }], now }),
).toBe(true);
});
test("returns false for a scrim that has not started yet", () => {
const startTime = (now + lockWindowMs) / 1000;
const updatedAt = secondsAgo(lockWindowMs * 10);
expect(
isTrackingLocked({ startTime, mapLists: [{ updatedAt }], now }),
).toBe(false);
});
});

View File

@@ -157,23 +157,33 @@ export function sideOfUser(post: ScrimPost, userId: number): ScrimSide | null {
/**
* Returns true when map-by-map tracking is locked: the auto-lock window has
* elapsed since the last activity (most recent reported map, falling back to
* the most recently updated submitted map list). Returns false when no map
* list has been submitted yet (tracking is not active).
* the most recently updated submitted map list). Activity happening before the
* scrim starts does not begin the window, it only starts running once the
* scrim is under way. Returns false when no map list has been submitted yet
* (tracking is not active).
*/
export function isTrackingLocked(
maps: Pick<Tables["ScrimMap"], "reportedAt">[] = [],
mapLists: Pick<Tables["ScrimMapList"], "updatedAt">[] = [],
now: number = Date.now(),
): boolean {
export function isTrackingLocked({
startTime,
maps = [],
mapLists = [],
now = Date.now(),
}: {
startTime: number;
maps?: Pick<Tables["ScrimMap"], "reportedAt">[];
mapLists?: Pick<Tables["ScrimMapList"], "updatedAt">[];
now?: number;
}): boolean {
const latestReported = R.firstBy(
maps.filter((m) => m.reportedAt !== null),
[(m) => m.reportedAt!, "desc"],
);
const latestList = R.firstBy(mapLists, [(l) => l.updatedAt, "desc"]);
const referenceSeconds =
const latestActivitySeconds =
latestReported?.reportedAt ?? latestList?.updatedAt ?? null;
if (referenceSeconds === null) return false;
if (latestActivitySeconds === null) return false;
const referenceSeconds = Math.max(latestActivitySeconds, startTime);
const elapsedHours = (now - referenceSeconds * 1000) / (60 * 60 * 1000);

View File

@@ -83,7 +83,11 @@ async function resolveMapByMap({
const pool = mapLists.length > 0 ? ScrimMapByMap.unionPool(mapLists) : null;
const currentMap = maps.find((m) => m.reportedAt === null) ?? null;
const viewerSide = Scrim.sideOfUser(post, user.id);
const locked = Scrim.isTrackingLocked(maps, mapLists);
const locked = Scrim.isTrackingLocked({
startTime: Scrim.getStartTime(post),
maps,
mapLists,
});
const ownList = viewerSide
? mapLists.find((l) => l.side === viewerSide)