sq initial

This commit is contained in:
Kalle 2026-04-06 15:12:24 +03:00
parent a47b3024cc
commit a4f9b8d251
4 changed files with 277 additions and 1518 deletions

View File

@ -1,21 +1,81 @@
import type { LoaderFunctionArgs } from "react-router";
import { getUser } from "~/features/auth/core/user.server";
import { chatAccessible } from "~/features/chat/chat-utils";
import { SendouQ } from "~/features/sendouq/core/SendouQ.server";
import * as PrivateUserNoteRepository from "~/features/sendouq/PrivateUserNoteRepository.server";
import { reportedWeaponsToArrayOfArrays } from "~/features/sendouq-match/core/reported-weapons.server";
import * as ReportedWeaponRepository from "~/features/sendouq-match/ReportedWeaponRepository.server";
import * as SQMatchRepository from "~/features/sendouq-match/SQMatchRepository.server";
import { databaseTimestampToDate } from "~/utils/dates";
import { notFoundIfFalsy, parseParams } from "~/utils/remix.server";
import { qMatchPageParamsSchema } from "../q-match-schemas";
// export const loader = async ({ params }: LoaderFunctionArgs) => {
// const user = getUser();
// const matchId = parseParams({
// params,
// schema: qMatchPageParamsSchema,
// }).id;
// const matchUnmapped = notFoundIfFalsy(
// await SQMatchRepository.findById(matchId),
// );
// const matchUsers = [
// ...matchUnmapped.groupAlpha.members,
// ...matchUnmapped.groupBravo.members,
// ].map((m) => m.id);
// const privateNotes = user
// ? await PrivateUserNoteRepository.byAuthorUserId(user.id, matchUsers)
// : undefined;
// const match = SendouQ.mapMatch(matchUnmapped, user, privateNotes);
// const rawReportedWeapons = match.reportedAt
// ? await ReportedWeaponRepository.findByMatchId(matchId)
// : null;
// return {
// match,
// reportedWeapons: match.reportedAt
// ? reportedWeaponsToArrayOfArrays({
// groupAlpha: match.groupAlpha,
// groupBravo: match.groupBravo,
// mapList: match.mapList,
// reportedWeapons: rawReportedWeapons,
// })
// : null,
// rawReportedWeapons,
// chatCode: (() => {
// const isStaff = user?.roles.includes("STAFF") ?? false;
// const isParticipant = user && matchUsers.includes(user.id);
// if (!(isStaff || isParticipant)) return null;
// const accessible = chatAccessible({
// isStaff,
// expiresAfterDays: 1,
// comparedTo: databaseTimestampToDate(matchUnmapped.createdAt),
// });
// if (!accessible) return null;
// if (!isParticipant) return match.chatCode ?? null;
// const codes = [
// match.chatCode,
// match.groupAlpha.chatCode,
// match.groupBravo.chatCode,
// ].filter((c): c is string => Boolean(c));
// if (codes.length === 0) return null;
// if (codes.length === 1) return codes[0];
// return codes;
// })(),
// };
// };
export const loader = async ({ params }: LoaderFunctionArgs) => {
const user = getUser();
const matchId = parseParams({
params,
schema: qMatchPageParamsSchema,
}).id;
const matchUnmapped = notFoundIfFalsy(
await SQMatchRepository.findById(matchId),
);
@ -24,51 +84,14 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
...matchUnmapped.groupAlpha.members,
...matchUnmapped.groupBravo.members,
].map((m) => m.id);
const privateNotes = user
? await PrivateUserNoteRepository.byAuthorUserId(user.id, matchUsers)
: undefined;
const match = SendouQ.mapMatch(matchUnmapped, user, privateNotes);
const rawReportedWeapons = match.reportedAt
? await ReportedWeaponRepository.findByMatchId(matchId)
: null;
return {
match,
reportedWeapons: match.reportedAt
? reportedWeaponsToArrayOfArrays({
groupAlpha: match.groupAlpha,
groupBravo: match.groupBravo,
mapList: match.mapList,
reportedWeapons: rawReportedWeapons,
})
: null,
rawReportedWeapons,
chatCode: (() => {
const isStaff = user?.roles.includes("STAFF") ?? false;
const isParticipant = user && matchUsers.includes(user.id);
if (!(isStaff || isParticipant)) return null;
const accessible = chatAccessible({
isStaff,
expiresAfterDays: 1,
comparedTo: databaseTimestampToDate(matchUnmapped.createdAt),
});
if (!accessible) return null;
if (!isParticipant) return match.chatCode ?? null;
const codes = [
match.chatCode,
match.groupAlpha.chatCode,
match.groupBravo.chatCode,
].filter((c): c is string => Boolean(c));
if (codes.length === 0) return null;
if (codes.length === 1) return codes[0];
return codes;
})(),
};
};

View File

@ -1,123 +0,0 @@
.stagePopoverButton {
background-color: transparent;
color: var(--color-text-high);
font-size: var(--font-xs);
padding: 0;
border: none;
text-decoration: underline;
text-decoration-style: dotted;
font-weight: var(--weight-body);
height: 19.8281px;
&:focus {
outline: none;
color: var(--color-text-accent);
}
}
.modePopoverButton {
background-color: transparent;
padding: 0;
border: none;
&:focus {
outline: none;
}
}
.container {
/** Push footer down to avoid it "flashing" when the score reporter animates */
padding-bottom: 14rem;
}
.header {
line-height: 1.2;
}
.teamsContainer {
display: grid;
grid-template-columns: 1fr;
gap: var(--s-8);
}
.mapListChatContainer {
display: grid;
grid-template-columns: 2fr 1fr 2fr;
place-items: center;
gap: var(--s-4);
}
.userNameContainer {
display: flex;
gap: var(--s-2);
width: 175px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.userWeaponContainer {
flex: 1;
}
.reportSection {
display: grid;
grid-template-columns: max-content 1fr;
row-gap: var(--s-2);
column-gap: var(--s-3);
align-items: center;
font-size: var(--font-xs);
}
.poolPassContainer {
display: flex;
gap: var(--s-2);
flex-direction: column;
max-width: max-content;
}
.bottomMidSection {
display: flex;
flex-direction: column;
align-self: flex-start;
top: var(--layout-sticky-top);
position: sticky;
}
.infoHeader {
text-transform: uppercase;
color: var(--color-text-high);
font-size: var(--font-xs);
line-height: 1.1;
}
.infoValue {
font-size: var(--font-xl);
font-weight: var(--weight-semi);
letter-spacing: 1px;
}
.screenLegality {
& svg {
width: 24px;
}
}
.screenLegalityButton {
width: 100%;
&:focus-visible {
outline: none !important;
}
}
.preferenceEmoji {
filter: grayscale(100%);
transition: all 0.2s;
}
@container (width >= 640px) {
.teamsContainer {
grid-template-columns: 1fr 1fr;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -155,6 +155,7 @@ class SendouQClass {
return this.groups.find((group) => group.inviteCode === inviteCode);
}
// xxx: only needed stuff here
/**
* Maps a database match to a format with appropriate censoring based on user permissions.
* Includes private notes for team members and censors sensitive data for non-participants.