mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-08 12:16:12 -05:00
Split off from tables.ts to tables-json.ts
This commit is contained in:
@@ -2,15 +2,17 @@ import clsx from "clsx";
|
||||
import { Lock, MessageCircleMore, SquarePen, Trash } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router";
|
||||
import type { GearType, Tables, UserWithPlusTier } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import type { BuildWeaponWithTop500Info } from "~/features/builds/builds-types";
|
||||
import type {
|
||||
Ability as AbilityType,
|
||||
BuildAbilitiesTuple,
|
||||
GearType,
|
||||
ModeShort,
|
||||
} from "~/modules/in-game-lists/types";
|
||||
import { canonicalWeaponSplId } from "~/modules/in-game-lists/weapon-ids";
|
||||
import type { UserWithPlusTier } from "~/utils/kysely.server";
|
||||
import { gearTypeToInitial } from "~/utils/strings";
|
||||
import {
|
||||
analyzerPage,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Check, Clipboard, PencilLine } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { CustomTheme } from "~/db/tables-json";
|
||||
import {
|
||||
CUSTOM_THEME_VARS,
|
||||
type CustomTheme,
|
||||
type CustomThemeVar,
|
||||
} from "~/db/tables";
|
||||
} from "~/features/theme/theme-constants";
|
||||
import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
|
||||
import {
|
||||
ACCENT_CHROMA_MULTIPLIERS,
|
||||
|
||||
@@ -5,13 +5,13 @@ import {
|
||||
SendouSelectItemSection,
|
||||
} from "~/components/elements/Select";
|
||||
import { Image } from "~/components/Image";
|
||||
import type { GearType } from "~/db/tables";
|
||||
import { brandIds } from "~/modules/in-game-lists/brand-ids";
|
||||
import {
|
||||
clothesGearBrandGrouped,
|
||||
headGearBrandGrouped,
|
||||
shoesGearBrandGrouped,
|
||||
} from "~/modules/in-game-lists/gear-ids";
|
||||
import type { GearType } from "~/modules/in-game-lists/types";
|
||||
import { brandImageUrl, gearImageUrl } from "~/utils/urls";
|
||||
|
||||
import styles from "./WeaponSelect.module.css";
|
||||
|
||||
@@ -9,7 +9,10 @@ import {
|
||||
} from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { LocaleTime } from "~/components/LocaleTime";
|
||||
import type { GroupSkillDifference, UserSkillDifference } from "~/db/tables";
|
||||
import type {
|
||||
GroupSkillDifference,
|
||||
UserSkillDifference,
|
||||
} from "~/db/tables-json";
|
||||
import { shortStageName } from "~/modules/in-game-lists/stage-ids";
|
||||
import type {
|
||||
MainWeaponId,
|
||||
|
||||
@@ -36,6 +36,7 @@ import { clearAllTournamentDataCache } from "~/features/tournament-bracket/core/
|
||||
import * as TournamentLFGRepository from "~/features/tournament-lfg/TournamentLFGRepository.server";
|
||||
import * as TournamentOrganizationRepository from "~/features/tournament-organization/TournamentOrganizationRepository.server";
|
||||
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
||||
import { USER_REPORT_CATEGORIES } from "~/features/user-report/user-report-constants";
|
||||
import * as VodRepository from "~/features/vods/VodRepository.server";
|
||||
import {
|
||||
secondsToHoursMinutesSecondString,
|
||||
@@ -76,12 +77,8 @@ import {
|
||||
SEED_TEAM_IMAGES,
|
||||
SEED_TOURNAMENT_IMAGES,
|
||||
} from "../../../scripts/seed-art-urls";
|
||||
import {
|
||||
type ParsedMemento,
|
||||
type Tables,
|
||||
USER_REPORT_CATEGORIES,
|
||||
type UserMapModePreferences,
|
||||
} from "../tables";
|
||||
import type { Tables } from "../tables";
|
||||
import type { ParsedMemento, UserMapModePreferences } from "../tables-json";
|
||||
import {
|
||||
ADMIN_TEST_AVATAR,
|
||||
AMOUNT_OF_CALENDAR_EVENTS,
|
||||
|
||||
246
app/db/tables-json.ts
Normal file
246
app/db/tables-json.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* Shapes of JSON column payloads that have no natural feature home. Kept apart from
|
||||
* `tables.ts` so that what is declared there stays close to the `DB` interface. Payloads
|
||||
* that do belong to a feature (e.g. `StoredWidget`, `Notification`) live with that feature
|
||||
* and are imported by `tables.ts` directly.
|
||||
*/
|
||||
|
||||
import type { CalendarFilters } from "~/features/calendar/calendar-types";
|
||||
import type { TieredSkill } from "~/features/mmr/tiered.server";
|
||||
import type { ScrimFilters } from "~/features/scrims/scrims-types";
|
||||
import type { CustomThemeVar } from "~/features/theme/theme-constants";
|
||||
import type * as PickBan from "~/features/tournament-bracket/core/PickBan";
|
||||
import type * as Progression from "~/features/tournament-bracket/core/Progression";
|
||||
import type {
|
||||
ActionType,
|
||||
WhoSide,
|
||||
} from "~/features/tournament-bracket/tournament-bracket-constants";
|
||||
import type {
|
||||
ObjectPronoun,
|
||||
SubjectPronoun,
|
||||
} from "~/features/user-page/user-page-constants";
|
||||
import type {
|
||||
MainWeaponId,
|
||||
ModeShort,
|
||||
StageId,
|
||||
} from "~/modules/in-game-lists/types";
|
||||
|
||||
export type CustomTheme = Omit<Record<CustomThemeVar, number>, "--_chat-h"> & {
|
||||
"--_chat-h": number | null;
|
||||
};
|
||||
|
||||
// missing means "neutral"
|
||||
export type Preference = "AVOID" | "PREFER";
|
||||
|
||||
export interface UserMapModePreferences {
|
||||
modes: Array<{
|
||||
mode: ModeShort;
|
||||
/** Users opinion on the mode, `undefined` means neutral */
|
||||
preference?: Preference;
|
||||
}>;
|
||||
pool: Array<{
|
||||
mode: ModeShort;
|
||||
stages: StageId[];
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface WeaponPoolEntry {
|
||||
weaponSplId: MainWeaponId;
|
||||
isFavorite: number;
|
||||
}
|
||||
|
||||
export interface UserPreferences {
|
||||
disableBuildAbilitySorting?: boolean;
|
||||
disallowScrimPickupsFromUntrusted?: boolean;
|
||||
defaultCalendarFilters?: CalendarFilters;
|
||||
defaultScrimsFilters?: ScrimFilters;
|
||||
/**
|
||||
* What time format the user prefers?
|
||||
*
|
||||
* "auto" = use browser default (default value)
|
||||
* "24h" = 24 hour format (e.g. 14:00)
|
||||
* "12h" = 12 hour format (e.g. 2:00 PM)
|
||||
* */
|
||||
clockFormat?: "24h" | "12h" | "auto";
|
||||
/** Is the new widget based user page enabled? (Supporter early preview) */
|
||||
newProfileEnabled?: boolean;
|
||||
/** Is spoiler-free mode enabled? Hides recent tournament results and scores until the user chooses to reveal them. */
|
||||
spoilerFreeMode?: boolean;
|
||||
weaponReportDefaultOpen?: boolean;
|
||||
}
|
||||
|
||||
export type Pronouns = {
|
||||
subject: SubjectPronoun;
|
||||
object: ObjectPronoun;
|
||||
};
|
||||
|
||||
export interface PeakXP {
|
||||
/** Peak XP across all divisions */
|
||||
overall: number;
|
||||
/** Peak XP (Takoroka division) */
|
||||
takoroka: number | null;
|
||||
/** Peak XP (Tentatek division) */
|
||||
tentatek: number | null;
|
||||
}
|
||||
|
||||
export type UserSkillDifference =
|
||||
| {
|
||||
calculated: true;
|
||||
spDiff: number;
|
||||
oldSp?: number;
|
||||
newSp?: number;
|
||||
}
|
||||
| CalculatingSkill;
|
||||
|
||||
export type GroupSkillDifference =
|
||||
| {
|
||||
calculated: true;
|
||||
oldSp: number;
|
||||
newSp: number;
|
||||
}
|
||||
| CalculatingSkill;
|
||||
|
||||
export type ParsedMemento = {
|
||||
users: Record<
|
||||
number,
|
||||
{
|
||||
skill?: TieredSkill | "CALCULATING";
|
||||
skillDifference?: UserSkillDifference;
|
||||
}
|
||||
>;
|
||||
groups: Record<
|
||||
number,
|
||||
{
|
||||
tier?: TieredSkill["tier"];
|
||||
skillDifference?: GroupSkillDifference;
|
||||
}
|
||||
>;
|
||||
modePreferences?: Partial<
|
||||
Record<ModeShort, Array<{ userId: number; preference?: Preference }>>
|
||||
>;
|
||||
/** mapPreferences of season 2 */
|
||||
mapPreferences?: Array<{ userId: number; preference?: Preference }[]>;
|
||||
pools: Array<{
|
||||
userId: number;
|
||||
pool: UserMapModePreferences["pool"];
|
||||
teamName?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export interface TournamentSettings {
|
||||
bracketProgression: Progression.ParsedBracket[];
|
||||
/** @deprecated use bracketProgression instead */
|
||||
teamsPerGroup?: number;
|
||||
/** @deprecated use bracketProgression instead */
|
||||
thirdPlaceMatch?: boolean;
|
||||
isRanked?: boolean;
|
||||
enableNoScreenToggle?: boolean;
|
||||
/** Enable the subs tab, default true */
|
||||
enableSubs?: boolean;
|
||||
requireInGameNames?: boolean;
|
||||
isInvitational?: boolean;
|
||||
/** Can teams add subs on their own while tournament is in progress? */
|
||||
autonomousSubs?: boolean;
|
||||
/** Timestamp (SQLite format) when reg closes, if missing then means closes at start time */
|
||||
regClosesAt?: number;
|
||||
/** @deprecated use bracketProgression instead */
|
||||
swiss?: {
|
||||
groupCount: number;
|
||||
roundCount: number;
|
||||
};
|
||||
minMembersPerTeam?: number;
|
||||
/** Maximum number of team members that can be registered (only applies to 4v4 tournaments) */
|
||||
maxMembersPerTeam?: number;
|
||||
isTest?: boolean;
|
||||
isDraft?: boolean;
|
||||
requireSendouQParticipation?: boolean;
|
||||
}
|
||||
|
||||
export interface CastedMatchesInfo {
|
||||
/** Array for matches that are locked because they are pending to be casted */
|
||||
lockedMatches: Array<{ twitchAccount: string; matchId: number }>;
|
||||
/** What matches are streamed currently & where */
|
||||
castedMatches: { twitchAccount: string; matchId: number }[];
|
||||
castedMatchHistory?: Array<{
|
||||
twitchAccount: string;
|
||||
matchId: number;
|
||||
timestamp: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface SeedingSnapshot {
|
||||
savedAt: number;
|
||||
teams: Array<{
|
||||
teamId: number;
|
||||
members: Array<{ userId: number; username: string }>;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface PreparedMaps {
|
||||
authorId: number;
|
||||
createdAt: number;
|
||||
maps: Array<TournamentRoundMaps & { roundId: number; groupId: number }>;
|
||||
eliminationTeamCount?: number;
|
||||
}
|
||||
|
||||
export interface TournamentRoundMaps {
|
||||
list?: Array<{ mode: ModeShort; stageId: StageId }> | null;
|
||||
count: number;
|
||||
type: "BEST_OF" | "PLAY_ALL";
|
||||
pickBan?: PickBan.Type | null;
|
||||
customFlow?: CustomPickBanFlow | null;
|
||||
}
|
||||
|
||||
export interface CustomPickBanStep {
|
||||
action: ActionType;
|
||||
side?: WhoSide;
|
||||
}
|
||||
|
||||
export interface CustomPickBanFlow {
|
||||
preSet: CustomPickBanStep[];
|
||||
postGame: CustomPickBanStep[];
|
||||
}
|
||||
|
||||
// when updating this also update `defaultBracketSettings` in tournament-utils.ts
|
||||
export interface TournamentStageSettings {
|
||||
// SE
|
||||
thirdPlaceMatch?: boolean;
|
||||
// RR
|
||||
teamsPerGroup?: number;
|
||||
/** (RR only) When true, teams are split into A and B divisions and matches only pair A-vs-B. Only valid on starting brackets. */
|
||||
hasAbDivisions?: boolean;
|
||||
// SWISS
|
||||
groupCount?: number;
|
||||
// SWISS
|
||||
roundCount?: number;
|
||||
/** (Swiss only) Number of wins required for a team to advance early. When set, teams advance at this win count and are eliminated at (roundCount - advanceThreshold + 1) losses. */
|
||||
advanceThreshold?: number;
|
||||
}
|
||||
|
||||
export interface TournamentAuditLogMetadata {
|
||||
bracketIdx?: number;
|
||||
/** The new in-game name, for `UPDATE_IN_GAME_NAME` events. */
|
||||
inGameName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of sets in the tournament.
|
||||
* E.g. ["W", "L", null] would mean the user won the first set, lost the second and did not play the third.
|
||||
* */
|
||||
export type WinLossParticipationArray = Array<"W" | "L" | null>;
|
||||
|
||||
export interface NotificationSubscription {
|
||||
endpoint: string;
|
||||
keys: {
|
||||
auth: string;
|
||||
p256dh: string;
|
||||
};
|
||||
}
|
||||
|
||||
type CalculatingSkill = {
|
||||
calculated: false;
|
||||
matchesCount: number;
|
||||
matchesCountNeeded: number;
|
||||
/** Freshly calculated skill */
|
||||
newSp?: number;
|
||||
};
|
||||
471
app/db/tables.ts
471
app/db/tables.ts
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* The database tables, 1:1 with the {@link DB} interface at the bottom. JSON column payload
|
||||
* shapes live either with their feature or, when they have no natural feature home, in
|
||||
* `tables-json.ts`; domain constants live in their feature's constants file.
|
||||
*/
|
||||
|
||||
import type {
|
||||
ColumnType,
|
||||
GeneratedAlways,
|
||||
@@ -5,18 +11,49 @@ import type {
|
||||
JSONColumnType,
|
||||
Selectable,
|
||||
} from "kysely";
|
||||
import type {
|
||||
CastedMatchesInfo,
|
||||
CustomTheme,
|
||||
NotificationSubscription,
|
||||
ParsedMemento,
|
||||
PeakXP,
|
||||
PreparedMaps,
|
||||
Pronouns,
|
||||
SeedingSnapshot,
|
||||
TournamentAuditLogMetadata,
|
||||
TournamentRoundMaps,
|
||||
TournamentSettings,
|
||||
UserMapModePreferences,
|
||||
UserPreferences,
|
||||
WeaponPoolEntry,
|
||||
WinLossParticipationArray,
|
||||
} from "~/db/tables-json";
|
||||
import type { ApiTokenType } from "~/features/api/api-types";
|
||||
import type { AssociationVisibility } from "~/features/associations/associations-types";
|
||||
import type { tags } from "~/features/calendar/calendar-constants";
|
||||
import type { CalendarFilters } from "~/features/calendar/calendar-types";
|
||||
import type { TieredSkill } from "~/features/mmr/tiered.server";
|
||||
import type { LFGType } from "~/features/lfg/lfg-constants";
|
||||
import type { Notification as NotificationValue } from "~/features/notifications/notifications-types";
|
||||
import type { ScrimFilters } from "~/features/scrims/scrims-types";
|
||||
import type { TEAM_MEMBER_ROLES } from "~/features/team/team-constants";
|
||||
import type { SplatoonRotationType } from "~/features/splatoon-rotations/splatoon-rotations-constants";
|
||||
import type {
|
||||
MemberRole,
|
||||
MemberRoleType,
|
||||
} from "~/features/team/team-constants";
|
||||
import type { XRankPlacementRegion } from "~/features/top-search/top-search-types";
|
||||
import type { TournamentTierNumber } from "~/features/tournament/core/tiering";
|
||||
import type * as PickBan from "~/features/tournament-bracket/core/PickBan";
|
||||
import type * as Progression from "~/features/tournament-bracket/core/Progression";
|
||||
import type {
|
||||
TournamentAuditLogType,
|
||||
TournamentMapPickingStyle,
|
||||
TournamentStaffRole,
|
||||
} from "~/features/tournament/tournament-constants";
|
||||
import type { TournamentOrganizationRole } from "~/features/tournament-organization/tournament-organization-constants";
|
||||
import type { HideableUserCardStat } from "~/features/user-card/user-card-types";
|
||||
import type { StoredWidget } from "~/features/user-page/core/widgets/types";
|
||||
import type { ParticipantResult } from "~/modules/brackets-model";
|
||||
import type { BuildSort } from "~/features/user-page/user-page-constants";
|
||||
import type { UserReportCategory } from "~/features/user-report/user-report-constants";
|
||||
import type {
|
||||
ParticipantResult,
|
||||
StageType,
|
||||
Status,
|
||||
} from "~/modules/brackets-model";
|
||||
import type {
|
||||
Ability,
|
||||
BuildAbilitiesTuple,
|
||||
@@ -31,51 +68,9 @@ type Generated<T> =
|
||||
? ColumnType<S, I | undefined, U>
|
||||
: ColumnType<T, T | undefined, T>;
|
||||
|
||||
export type MemberRole = (typeof TEAM_MEMBER_ROLES)[number];
|
||||
|
||||
export type MemberRoleType = "PLAYER" | "OTHER";
|
||||
|
||||
/** In SQLite booleans are presented as 0 (false) and 1 (true) */
|
||||
export type DBBoolean = number;
|
||||
|
||||
export const CUSTOM_THEME_VARS = [
|
||||
"--_base-h",
|
||||
"--_base-c-0",
|
||||
"--_base-c-1",
|
||||
"--_base-c-2",
|
||||
"--_base-c-3",
|
||||
"--_base-c-4",
|
||||
"--_base-c-5",
|
||||
"--_base-c-6",
|
||||
"--_base-c-7",
|
||||
"--_acc-h",
|
||||
"--_acc-c-0",
|
||||
"--_acc-c-1",
|
||||
"--_acc-c-2",
|
||||
"--_acc-c-3",
|
||||
"--_acc-c-4",
|
||||
"--_acc-c-5",
|
||||
"--_second-h",
|
||||
"--_second-c-0",
|
||||
"--_second-c-1",
|
||||
"--_second-c-2",
|
||||
"--_second-c-3",
|
||||
"--_second-c-4",
|
||||
"--_second-c-5",
|
||||
"--_chat-h",
|
||||
"--_radius-box",
|
||||
"--_radius-field",
|
||||
"--_radius-selector",
|
||||
"--_border-width",
|
||||
"--_size-field",
|
||||
"--_size-selector",
|
||||
"--_size-spacing",
|
||||
] as const;
|
||||
export type CustomThemeVar = (typeof CUSTOM_THEME_VARS)[number];
|
||||
export type CustomTheme = Omit<Record<CustomThemeVar, number>, "--_chat-h"> & {
|
||||
"--_chat-h": number | null;
|
||||
};
|
||||
|
||||
export interface Team {
|
||||
avatarImgId: number | null;
|
||||
bannerImgId: number | null;
|
||||
@@ -149,11 +144,11 @@ export interface BadgeManager {
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export type BadgeOwner = {
|
||||
export interface BadgeOwner {
|
||||
badgeId: number;
|
||||
userId: number;
|
||||
count: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Build {
|
||||
clothesGearSplId: number | null;
|
||||
@@ -172,8 +167,6 @@ export interface Build {
|
||||
abilitiesSignature: string | null;
|
||||
}
|
||||
|
||||
export type GearType = "HEAD" | "CLOTHES" | "SHOES";
|
||||
|
||||
export interface BuildWeapon {
|
||||
buildId: number;
|
||||
weaponSplId: MainWeaponId;
|
||||
@@ -200,8 +193,6 @@ export interface BuildWeaponAbility {
|
||||
abilityPoints: number;
|
||||
}
|
||||
|
||||
export type CalendarEventTag = keyof typeof tags;
|
||||
|
||||
export interface CalendarEvent {
|
||||
authorId: number;
|
||||
bracketUrl: string;
|
||||
@@ -261,56 +252,6 @@ export interface GroupLike {
|
||||
isRechallenge: DBBoolean | null;
|
||||
}
|
||||
|
||||
type CalculatingSkill = {
|
||||
calculated: false;
|
||||
matchesCount: number;
|
||||
matchesCountNeeded: number;
|
||||
/** Freshly calculated skill */
|
||||
newSp?: number;
|
||||
};
|
||||
export type UserSkillDifference =
|
||||
| {
|
||||
calculated: true;
|
||||
spDiff: number;
|
||||
oldSp?: number;
|
||||
newSp?: number;
|
||||
}
|
||||
| CalculatingSkill;
|
||||
export type GroupSkillDifference =
|
||||
| {
|
||||
calculated: true;
|
||||
oldSp: number;
|
||||
newSp: number;
|
||||
}
|
||||
| CalculatingSkill;
|
||||
|
||||
export type ParsedMemento = {
|
||||
users: Record<
|
||||
number,
|
||||
{
|
||||
skill?: TieredSkill | "CALCULATING";
|
||||
skillDifference?: UserSkillDifference;
|
||||
}
|
||||
>;
|
||||
groups: Record<
|
||||
number,
|
||||
{
|
||||
tier?: TieredSkill["tier"];
|
||||
skillDifference?: GroupSkillDifference;
|
||||
}
|
||||
>;
|
||||
modePreferences?: Partial<
|
||||
Record<ModeShort, Array<{ userId: number; preference?: Preference }>>
|
||||
>;
|
||||
/** mapPreferences of season 2 */
|
||||
mapPreferences?: Array<{ userId: number; preference?: Preference }[]>;
|
||||
pools: Array<{
|
||||
userId: number;
|
||||
pool: UserMapModePreferences["pool"];
|
||||
teamName?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export interface GroupMatch {
|
||||
alphaGroupId: number;
|
||||
bravoGroupId: number;
|
||||
@@ -368,23 +309,6 @@ export interface LogInLink {
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export type LFGType =
|
||||
| "PLAYER_FOR_TEAM"
|
||||
| "PLAYER_FOR_COACH"
|
||||
| "TEAM_FOR_PLAYER"
|
||||
| "TEAM_FOR_COACH"
|
||||
| "TEAM_FOR_SCRIM"
|
||||
| "COACH_FOR_TEAM";
|
||||
|
||||
export const LFG_TYPES = [
|
||||
"PLAYER_FOR_TEAM",
|
||||
"PLAYER_FOR_COACH",
|
||||
"TEAM_FOR_PLAYER",
|
||||
"TEAM_FOR_COACH",
|
||||
"TEAM_FOR_SCRIM",
|
||||
"COACH_FOR_TEAM",
|
||||
] as const;
|
||||
|
||||
export interface LFGPost {
|
||||
id: GeneratedAlways<number>;
|
||||
type: LFGType;
|
||||
@@ -500,15 +424,6 @@ export interface SeedingSkill {
|
||||
type: "RANKED" | "UNRANKED";
|
||||
}
|
||||
|
||||
export interface PeakXP {
|
||||
/** Peak XP across all divisions */
|
||||
overall: number;
|
||||
/** Peak XP (Takoroka division) */
|
||||
takoroka: number | null;
|
||||
/** Peak XP (Tentatek division) */
|
||||
tentatek: number | null;
|
||||
}
|
||||
|
||||
export interface SplatoonPlayer {
|
||||
id: GeneratedAlways<number>;
|
||||
splId: string;
|
||||
@@ -517,62 +432,6 @@ export interface SplatoonPlayer {
|
||||
peakXp: JSONColumnTypeNullable<PeakXP>;
|
||||
}
|
||||
|
||||
export interface TaggedArt {
|
||||
artId: number;
|
||||
tagId: number;
|
||||
}
|
||||
|
||||
// AUTO = style where teams pick their map pool ahead of time and the map lists are automatically made for each round
|
||||
// could also have the traditional style where TO picks the maps later
|
||||
type TournamentMapPickingStyle =
|
||||
| "TO"
|
||||
| "AUTO_ALL"
|
||||
| "AUTO_SZ"
|
||||
| "AUTO_TC"
|
||||
| "AUTO_RM"
|
||||
| "AUTO_CB";
|
||||
|
||||
export interface TournamentSettings {
|
||||
bracketProgression: Progression.ParsedBracket[];
|
||||
/** @deprecated use bracketProgression instead */
|
||||
teamsPerGroup?: number;
|
||||
/** @deprecated use bracketProgression instead */
|
||||
thirdPlaceMatch?: boolean;
|
||||
isRanked?: boolean;
|
||||
enableNoScreenToggle?: boolean;
|
||||
/** Enable the subs tab, default true */
|
||||
enableSubs?: boolean;
|
||||
requireInGameNames?: boolean;
|
||||
isInvitational?: boolean;
|
||||
/** Can teams add subs on their own while tournament is in progress? */
|
||||
autonomousSubs?: boolean;
|
||||
/** Timestamp (SQLite format) when reg closes, if missing then means closes at start time */
|
||||
regClosesAt?: number;
|
||||
/** @deprecated use bracketProgression instead */
|
||||
swiss?: {
|
||||
groupCount: number;
|
||||
roundCount: number;
|
||||
};
|
||||
minMembersPerTeam?: number;
|
||||
/** Maximum number of team members that can be registered (only applies to 4v4 tournaments) */
|
||||
maxMembersPerTeam?: number;
|
||||
isTest?: boolean;
|
||||
isDraft?: boolean;
|
||||
requireSendouQParticipation?: boolean;
|
||||
}
|
||||
|
||||
export interface CastedMatchesInfo {
|
||||
/** Array for matches that are locked because they are pending to be casted */
|
||||
lockedMatches: Array<{ twitchAccount: string; matchId: number }>;
|
||||
/** What matches are streamed currently & where */
|
||||
castedMatches: { twitchAccount: string; matchId: number }[];
|
||||
castedMatchHistory?: Array<{
|
||||
twitchAccount: string;
|
||||
matchId: number;
|
||||
timestamp: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface Tournament {
|
||||
settings: JSONColumnType<TournamentSettings>;
|
||||
id: GeneratedAlways<number>;
|
||||
@@ -595,21 +454,6 @@ export interface Tournament {
|
||||
vodsSyncCount: Generated<number>;
|
||||
}
|
||||
|
||||
export interface SeedingSnapshot {
|
||||
savedAt: number;
|
||||
teams: Array<{
|
||||
teamId: number;
|
||||
members: Array<{ userId: number; username: string }>;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface PreparedMaps {
|
||||
authorId: number;
|
||||
createdAt: number;
|
||||
maps: Array<TournamentRoundMaps & { roundId: number; groupId: number }>;
|
||||
eliminationTeamCount?: number;
|
||||
}
|
||||
|
||||
export interface SavedCalendarEvent {
|
||||
id: GeneratedAlways<number>;
|
||||
userId: number;
|
||||
@@ -644,23 +488,6 @@ export interface TournamentGroup {
|
||||
stageId: number;
|
||||
}
|
||||
|
||||
export const TournamentMatchStatus = {
|
||||
/** The two matches leading to this one are not completed yet. */
|
||||
Locked: 0,
|
||||
|
||||
/** One participant is ready and waiting for the other one. */
|
||||
Waiting: 1,
|
||||
|
||||
/** Both participants are ready to start. */
|
||||
Ready: 2,
|
||||
|
||||
/** The match is running. */
|
||||
Running: 3,
|
||||
|
||||
/** The match is completed. */
|
||||
Completed: 4,
|
||||
};
|
||||
|
||||
export interface TournamentMatch {
|
||||
chatCode: string | null;
|
||||
groupId: number;
|
||||
@@ -670,7 +497,7 @@ export interface TournamentMatch {
|
||||
opponentTwo: JSONColumnType<ParticipantResult>;
|
||||
roundId: number;
|
||||
stageId: number;
|
||||
status: (typeof TournamentMatchStatus)[keyof typeof TournamentMatchStatus];
|
||||
status: Status;
|
||||
// set when match becomes ongoing (both teams ready and no earlier matches for either team)
|
||||
// for swiss: set at creation time
|
||||
startedAt: number | null;
|
||||
@@ -707,8 +534,6 @@ export interface TournamentMatchGameResultParticipant {
|
||||
tournamentTeamId: number;
|
||||
}
|
||||
|
||||
export type WinLossParticipationArray = Array<"W" | "L" | null>;
|
||||
|
||||
export interface TournamentResult {
|
||||
isHighlight: Generated<DBBoolean>;
|
||||
participantCount: number;
|
||||
@@ -727,46 +552,6 @@ export interface TournamentResult {
|
||||
div: string | null;
|
||||
}
|
||||
|
||||
export interface TournamentRoundMaps {
|
||||
list?: Array<{ mode: ModeShort; stageId: StageId }> | null;
|
||||
count: number;
|
||||
type: "BEST_OF" | "PLAY_ALL";
|
||||
pickBan?: PickBan.Type | null;
|
||||
customFlow?: CustomPickBanFlow | null;
|
||||
}
|
||||
|
||||
export const WHO_SIDES = [
|
||||
"RANDOM",
|
||||
"RANDOM_OTHER",
|
||||
"ALPHA",
|
||||
"BRAVO",
|
||||
"HIGHER_SEED",
|
||||
"LOWER_SEED",
|
||||
"WINNER",
|
||||
"LOSER",
|
||||
] as const;
|
||||
export type WhoSide = (typeof WHO_SIDES)[number];
|
||||
|
||||
export const ACTION_TYPES = [
|
||||
"ROLL",
|
||||
"PICK",
|
||||
"PICK_NO_MODE_REPEAT",
|
||||
"BAN",
|
||||
"MODE_PICK",
|
||||
"MODE_BAN",
|
||||
] as const;
|
||||
export type ActionType = (typeof ACTION_TYPES)[number];
|
||||
|
||||
export interface CustomPickBanStep {
|
||||
action: ActionType;
|
||||
side?: WhoSide;
|
||||
}
|
||||
|
||||
export interface CustomPickBanFlow {
|
||||
preSet: CustomPickBanStep[];
|
||||
postGame: CustomPickBanStep[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A round is a logical structure used to group multiple matches together.
|
||||
|
||||
@@ -782,29 +567,6 @@ export interface TournamentRound {
|
||||
maps: JSONColumnType<TournamentRoundMaps>;
|
||||
}
|
||||
|
||||
// when updating this also update `defaultBracketSettings` in tournament-utils.ts
|
||||
export interface TournamentStageSettings {
|
||||
// SE
|
||||
thirdPlaceMatch?: boolean;
|
||||
// RR
|
||||
teamsPerGroup?: number;
|
||||
/** (RR only) When true, teams are split into A and B divisions and matches only pair A-vs-B. Only valid on starting brackets. */
|
||||
hasAbDivisions?: boolean;
|
||||
// SWISS
|
||||
groupCount?: number;
|
||||
// SWISS
|
||||
roundCount?: number;
|
||||
/** (Swiss only) Number of wins required for a team to advance early. When set, teams advance at this win count and are eliminated at (roundCount - advanceThreshold + 1) losses. */
|
||||
advanceThreshold?: number;
|
||||
}
|
||||
|
||||
export const TOURNAMENT_STAGE_TYPES = [
|
||||
"single_elimination",
|
||||
"double_elimination",
|
||||
"round_robin",
|
||||
"swiss",
|
||||
] as const;
|
||||
|
||||
/** A stage is an intermediate phase in a tournament. In essence a bracket. */
|
||||
export interface TournamentStage {
|
||||
id: GeneratedAlways<number>;
|
||||
@@ -812,7 +574,7 @@ export interface TournamentStage {
|
||||
number: number;
|
||||
settings: string;
|
||||
tournamentId: number;
|
||||
type: (typeof TOURNAMENT_STAGE_TYPES)[number];
|
||||
type: StageType;
|
||||
// not Generated<> because SQLite doesn't allow altering tables to add columns with default values :(
|
||||
createdAt: number | null;
|
||||
}
|
||||
@@ -836,9 +598,6 @@ export interface TournamentLFGLike {
|
||||
createdAt: Generated<number>;
|
||||
}
|
||||
|
||||
export const TOURNAMENT_STAFF_ROLES = ["ORGANIZER", "STREAMER"] as const;
|
||||
type TournamentStaffRole = (typeof TOURNAMENT_STAFF_ROLES)[number];
|
||||
|
||||
export interface TournamentStaff {
|
||||
tournamentId: number;
|
||||
userId: number;
|
||||
@@ -901,22 +660,10 @@ export interface TournamentTeamHistory {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const TOURNAMENT_AUDIT_LOG_TYPES = [
|
||||
"MEMBER_ADDED",
|
||||
"MEMBER_REMOVED",
|
||||
"TEAM_REGISTERED",
|
||||
"TEAM_UNREGISTERED",
|
||||
"TEAM_CHECKED_IN",
|
||||
"TEAM_CHECKED_OUT",
|
||||
"TEAM_DROPPED_OUT",
|
||||
"TEAM_DROP_OUT_UNDONE",
|
||||
"UPDATE_IN_GAME_NAME",
|
||||
] as const;
|
||||
|
||||
export interface TournamentAuditLog {
|
||||
id: GeneratedAlways<number>;
|
||||
tournamentId: number;
|
||||
type: (typeof TOURNAMENT_AUDIT_LOG_TYPES)[number];
|
||||
type: TournamentAuditLogType;
|
||||
/** The user who performed the action. */
|
||||
actorUserId: number;
|
||||
/** The affected member, for member-level events. `null` for team-level events. */
|
||||
@@ -927,12 +674,6 @@ export interface TournamentAuditLog {
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
export interface TournamentAuditLogMetadata {
|
||||
bracketIdx?: number;
|
||||
/** The new in-game name, for `UPDATE_IN_GAME_NAME` events. */
|
||||
inGameName?: string;
|
||||
}
|
||||
|
||||
export interface TournamentOrganization {
|
||||
id: GeneratedAlways<number>;
|
||||
name: string;
|
||||
@@ -943,15 +684,6 @@ export interface TournamentOrganization {
|
||||
isEstablished: Generated<DBBoolean>;
|
||||
}
|
||||
|
||||
export const TOURNAMENT_ORGANIZATION_ROLES = [
|
||||
"ADMIN",
|
||||
"MEMBER",
|
||||
"ORGANIZER",
|
||||
"STREAMER",
|
||||
] as const;
|
||||
type TournamentOrganizationRole =
|
||||
(typeof TOURNAMENT_ORGANIZATION_ROLES)[number];
|
||||
|
||||
export interface TournamentOrganizationMember {
|
||||
organizationId: number;
|
||||
userId: number;
|
||||
@@ -1031,79 +763,6 @@ export interface UnvalidatedVideo {
|
||||
youtubeId: string;
|
||||
}
|
||||
|
||||
// missing means "neutral"
|
||||
export type Preference = "AVOID" | "PREFER";
|
||||
export interface UserMapModePreferences {
|
||||
modes: Array<{
|
||||
mode: ModeShort;
|
||||
/** Users opinion on the mode, `undefined` means neutral */
|
||||
preference?: Preference;
|
||||
}>;
|
||||
pool: Array<{
|
||||
mode: ModeShort;
|
||||
stages: StageId[];
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface WeaponPoolEntry {
|
||||
weaponSplId: MainWeaponId;
|
||||
isFavorite: number;
|
||||
}
|
||||
|
||||
export const BUILD_SORT_IDENTIFIERS = [
|
||||
"UPDATED_AT",
|
||||
"TOP_500",
|
||||
"WEAPON_POOL",
|
||||
"WEAPON_IN_GAME_ORDER",
|
||||
"ALPHABETICAL_TITLE",
|
||||
"MODE",
|
||||
"HEADGEAR_ID",
|
||||
"CLOTHES_ID",
|
||||
"SHOES_ID",
|
||||
"PUBLIC_BUILD",
|
||||
"PRIVATE_BUILD",
|
||||
] as const;
|
||||
|
||||
export type BuildSort = (typeof BUILD_SORT_IDENTIFIERS)[number];
|
||||
|
||||
export interface UserPreferences {
|
||||
disableBuildAbilitySorting?: boolean;
|
||||
disallowScrimPickupsFromUntrusted?: boolean;
|
||||
defaultCalendarFilters?: CalendarFilters;
|
||||
defaultScrimsFilters?: ScrimFilters;
|
||||
/**
|
||||
* What time format the user prefers?
|
||||
*
|
||||
* "auto" = use browser default (default value)
|
||||
* "24h" = 24 hour format (e.g. 14:00)
|
||||
* "12h" = 12 hour format (e.g. 2:00 PM)
|
||||
* */
|
||||
clockFormat?: "24h" | "12h" | "auto";
|
||||
/** Is the new widget based user page enabled? (Supporter early preview) */
|
||||
newProfileEnabled?: boolean;
|
||||
/** Is spoiler-free mode enabled? Hides recent tournament results and scores until the user chooses to reveal them. */
|
||||
spoilerFreeMode?: boolean;
|
||||
weaponReportDefaultOpen?: boolean;
|
||||
}
|
||||
|
||||
export const SUBJECT_PRONOUNS = ["he", "she", "they", "it", "any"] as const;
|
||||
export const OBJECT_PRONOUNS = [
|
||||
"him",
|
||||
"her",
|
||||
"them",
|
||||
"its",
|
||||
"all",
|
||||
...SUBJECT_PRONOUNS,
|
||||
] as const;
|
||||
|
||||
export type Pronouns = {
|
||||
subject: (typeof SUBJECT_PRONOUNS)[number];
|
||||
object: (typeof OBJECT_PRONOUNS)[number];
|
||||
};
|
||||
|
||||
/** Card stat types that can be hidden from a user's card (kept here so `User.hiddenCardStats` does not import a feature module; keep in sync with the feature's `UserCardStat["type"]`). */
|
||||
export type HideableUserCardStat = "XP" | "DIV";
|
||||
|
||||
export interface User {
|
||||
/** 1 = permabanned, timestamp = ban active till then */
|
||||
banned: Generated<number | null>;
|
||||
@@ -1168,11 +827,6 @@ export interface User {
|
||||
unverifiedPeakXP: JSONColumnTypeNullable<PeakXP>;
|
||||
}
|
||||
|
||||
/** Represents User joined with PlusTier table */
|
||||
export type UserWithPlusTier = Tables["User"] & {
|
||||
plusTier: PlusTier["tier"] | null;
|
||||
};
|
||||
|
||||
export interface UserResultHighlight {
|
||||
teamId: number;
|
||||
userId: number;
|
||||
@@ -1228,8 +882,6 @@ export interface UserWidget {
|
||||
index: number;
|
||||
widget: JSONColumnType<StoredWidget>;
|
||||
}
|
||||
export type ApiTokenType = "read" | "write";
|
||||
|
||||
export interface ApiToken {
|
||||
id: GeneratedAlways<number>;
|
||||
userId: number;
|
||||
@@ -1291,15 +943,6 @@ export interface ModNote {
|
||||
isDeleted: Generated<DBBoolean>;
|
||||
}
|
||||
|
||||
export const USER_REPORT_CATEGORIES = [
|
||||
"INAPPROPRIATE_CONTENT",
|
||||
"ALTING",
|
||||
"HARASSMENT",
|
||||
"CHEATING",
|
||||
"OTHER",
|
||||
] as const;
|
||||
export type UserReportCategory = (typeof USER_REPORT_CATEGORIES)[number];
|
||||
|
||||
export interface UserReport {
|
||||
id: GeneratedAlways<number>;
|
||||
reportedUserId: number;
|
||||
@@ -1337,9 +980,6 @@ export interface VideoMatchPlayer {
|
||||
weaponSplId: number;
|
||||
}
|
||||
|
||||
/** `WEST` = Tentatek division, `JPN` = Takoroka division. */
|
||||
export type XRankPlacementRegion = "WEST" | "JPN";
|
||||
|
||||
export interface XRankPlacement {
|
||||
badges: string;
|
||||
bannerSplId: number;
|
||||
@@ -1466,14 +1106,6 @@ export interface NotificationUser {
|
||||
seen: Generated<DBBoolean>;
|
||||
}
|
||||
|
||||
export interface NotificationSubscription {
|
||||
endpoint: string;
|
||||
keys: {
|
||||
auth: string;
|
||||
p256dh: string;
|
||||
};
|
||||
}
|
||||
|
||||
/** A subscription of user's browser indicating where push notifications can be sent to. */
|
||||
export interface NotificationUserSubscription {
|
||||
id: GeneratedAlways<number>;
|
||||
@@ -1481,9 +1113,6 @@ export interface NotificationUserSubscription {
|
||||
subscription: JSONColumnType<NotificationSubscription>;
|
||||
}
|
||||
|
||||
export const SPLATOON_ROTATION_TYPES = ["SERIES", "OPEN", "X"] as const;
|
||||
export type SplatoonRotationType = (typeof SPLATOON_ROTATION_TYPES)[number];
|
||||
|
||||
export interface SplatoonRotation {
|
||||
id: GeneratedAlways<number>;
|
||||
type: SplatoonRotationType;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ApiTokenType } from "~/db/tables";
|
||||
import * as ApiRepository from "~/features/api/ApiRepository.server";
|
||||
import type { ApiTokenType } from "~/features/api/api-types";
|
||||
|
||||
type CachedToken = { type: ApiTokenType; userId: number };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Pronouns } from "~/db/tables";
|
||||
import type { Pronouns } from "~/db/tables-json";
|
||||
import type { TierName } from "~/features/mmr/mmr-constants";
|
||||
import type { DataTypes, ValueToArray } from "~/modules/brackets-manager/types";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { db } from "~/db/sql";
|
||||
import type { ApiTokenType } from "~/db/tables";
|
||||
import type { ApiTokenType } from "~/features/api/api-types";
|
||||
|
||||
const API_TOKEN_LENGTH = 20;
|
||||
|
||||
|
||||
1
app/features/api/api-types.ts
Normal file
1
app/features/api/api-types.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type ApiTokenType = "read" | "write";
|
||||
@@ -10,13 +10,10 @@ import { sql } from "kysely";
|
||||
import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/sqlite";
|
||||
import * as R from "remeda";
|
||||
import { db } from "~/db/sql";
|
||||
import type {
|
||||
CalendarEventTag,
|
||||
DB,
|
||||
Tables,
|
||||
TournamentSettings,
|
||||
} from "~/db/tables";
|
||||
import type { DB, Tables } from "~/db/tables";
|
||||
import type { TournamentSettings } from "~/db/tables-json";
|
||||
import { EXCLUDED_TAGS } from "~/features/calendar/calendar-constants";
|
||||
import type { CalendarEventTag } from "~/features/calendar/calendar-types";
|
||||
import * as Progression from "~/features/tournament-bracket/core/Progression";
|
||||
import { getTentativeTier } from "~/features/tournament-organization/core/tentativeTiers.server";
|
||||
import {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { ActionFunction } from "react-router";
|
||||
import { redirect } from "react-router";
|
||||
import type { CalendarEventTag } from "~/db/tables";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import * as BadgeRepository from "~/features/badges/BadgeRepository.server";
|
||||
import * as CalendarRepository from "~/features/calendar/CalendarRepository.server";
|
||||
import type { CalendarEventTag } from "~/features/calendar/calendar-types";
|
||||
import * as ShowcaseTournaments from "~/features/front-page/core/ShowcaseTournaments.server";
|
||||
import { MapPool } from "~/features/map-list-generator/core/map-pool";
|
||||
import { notify } from "~/features/notifications/core/notify.server";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CalendarEventTag } from "~/db/tables";
|
||||
import type { CalendarEventTag } from "~/features/calendar/calendar-types";
|
||||
|
||||
export const tags = {
|
||||
SPECIAL: {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { z } from "zod";
|
||||
import { type CalendarEventTag, TOURNAMENT_STAGE_TYPES } from "~/db/tables";
|
||||
import { TOURNAMENT } from "~/features/tournament/tournament-constants";
|
||||
import type { CalendarEventTag } from "~/features/calendar/calendar-types";
|
||||
import {
|
||||
TOURNAMENT,
|
||||
TOURNAMENT_STAGE_TYPES,
|
||||
} from "~/features/tournament/tournament-constants";
|
||||
import * as Progression from "~/features/tournament-bracket/core/Progression";
|
||||
import * as Swiss from "~/features/tournament-bracket/core/Swiss";
|
||||
import {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { z } from "zod";
|
||||
import type { CalendarEventTag, Tables } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { tags } from "~/features/calendar/calendar-constants";
|
||||
import type { calendarFiltersSearchParamsSchema } from "~/features/calendar/calendar-schemas";
|
||||
import type { ModeShortWithSpecial } from "~/modules/in-game-lists/types";
|
||||
import type { CommonUser } from "~/utils/kysely.server";
|
||||
|
||||
export type CalendarEventTag = keyof typeof tags;
|
||||
|
||||
interface CommonEvent {
|
||||
id: number;
|
||||
name: string;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { X } from "lucide-react";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SendouButton } from "~/components/elements/Button";
|
||||
import type { CalendarEventTag } from "~/db/tables";
|
||||
import type { CalendarEventTag } from "~/features/calendar/calendar-types";
|
||||
import { tags as allTags } from "../calendar-constants";
|
||||
import styles from "./Tags.module.css";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { add, startOfWeek, sub } from "date-fns";
|
||||
import type { LoaderFunctionArgs } from "react-router";
|
||||
import type { UserPreferences } from "~/db/tables";
|
||||
import type { UserPreferences } from "~/db/tables-json";
|
||||
import { getUser } from "~/features/auth/core/user.server";
|
||||
import { DAYS_SHOWN_AT_A_TIME } from "~/features/calendar/calendar-constants";
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
import { LFG_TYPES, type Tables } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
|
||||
export const LFG_TYPES = [
|
||||
"PLAYER_FOR_TEAM",
|
||||
"PLAYER_FOR_COACH",
|
||||
"TEAM_FOR_PLAYER",
|
||||
"TEAM_FOR_COACH",
|
||||
"TEAM_FOR_SCRIM",
|
||||
"COACH_FOR_TEAM",
|
||||
] as const;
|
||||
|
||||
export type LFGType = (typeof LFG_TYPES)[number];
|
||||
|
||||
export const LFG = {
|
||||
MIN_TEXT_LENGTH: 1,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LFG_TYPES, type LFGType } from "~/db/tables";
|
||||
import { LFG_TYPES, type LFGType } from "~/features/lfg/lfg-constants";
|
||||
import { languagesUnified } from "~/modules/i18n/config";
|
||||
import type { MainWeaponId } from "~/modules/in-game-lists/types";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
||||
import { db } from "~/db/sql";
|
||||
import type { UserMapModePreferences } from "~/db/tables";
|
||||
import type { UserMapModePreferences } from "~/db/tables-json";
|
||||
import { dbInsertUsers, dbReset, withUserId } from "~/utils/Test";
|
||||
import * as MatchProfileRepository from "./MatchProfileRepository.server";
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as R from "remeda";
|
||||
import { db } from "~/db/sql";
|
||||
import type { Tables, UserMapModePreferences } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { UserMapModePreferences } from "~/db/tables-json";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import type { WeaponPoolItem } from "~/form/fields/WeaponPoolFormField";
|
||||
import type { UnifiedLanguageCode } from "~/modules/i18n/config";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { sub } from "date-fns";
|
||||
import { db } from "~/db/sql";
|
||||
import type { NotificationSubscription, TablesInsertable } from "~/db/tables";
|
||||
import type { TablesInsertable } from "~/db/tables";
|
||||
import type { NotificationSubscription } from "~/db/tables-json";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import { dateToDatabaseTimestamp } from "../../utils/dates";
|
||||
import { NOTIFICATIONS } from "./notifications-contants";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { TFunction } from "i18next";
|
||||
import pLimit from "p-limit";
|
||||
import { type Urgency, WebPushError } from "web-push";
|
||||
import type { NotificationSubscription } from "~/db/tables-json";
|
||||
import { IS_E2E_TEST_RUN } from "~/utils/e2e";
|
||||
import { APP_ICON_URL } from "~/utils/urls";
|
||||
import type { NotificationSubscription } from "../../../db/tables";
|
||||
import { getFixedTForLanguage } from "../../../modules/i18n/i18next.server";
|
||||
import { logger } from "../../../utils/logger";
|
||||
import * as NotificationRepository from "../NotificationRepository.server";
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { Tables, UserWithPlusTier } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type * as PlusSuggestionRepository from "~/features/plus-suggestions/PlusSuggestionRepository.server";
|
||||
import { isAdmin } from "~/modules/permissions/utils";
|
||||
import { allTruthy } from "~/utils/arrays";
|
||||
import type { UserWithPlusTier } from "~/utils/kysely.server";
|
||||
import * as Seasons from "../mmr/core/Seasons";
|
||||
import { isVotingActive } from "../plus-voting/core";
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { UserWithPlusTier } from "~/db/tables";
|
||||
import { getUser } from "~/features/auth/core/user.server";
|
||||
import { lastCompletedVoting } from "~/features/plus-voting/core";
|
||||
import * as PlusVotingRepository from "~/features/plus-voting/PlusVotingRepository.server";
|
||||
import { isSupporter } from "~/modules/permissions/utils";
|
||||
import invariant from "~/utils/invariant";
|
||||
import type { UserWithPlusTier } from "~/utils/kysely.server";
|
||||
import { roundToNDecimalPlaces } from "~/utils/number";
|
||||
|
||||
export const loader = async () => {
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { ExpressionBuilder, NotNull, Transaction } from "kysely";
|
||||
import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/sqlite";
|
||||
import * as R from "remeda";
|
||||
import { db } from "~/db/sql";
|
||||
import type { DB, ParsedMemento } from "~/db/tables";
|
||||
import type { DB } from "~/db/tables";
|
||||
import type { ParsedMemento } from "~/db/tables-json";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import * as Seasons from "~/features/mmr/core/Seasons";
|
||||
import type { TournamentMapListMap } from "~/modules/tournament-map-list-generator/types";
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { Transaction } from "kysely";
|
||||
import { ordinal } from "openskill";
|
||||
import { db } from "~/db/sql";
|
||||
import type { DB, ParsedMemento, Tables } from "~/db/tables";
|
||||
import type { DB, Tables } from "~/db/tables";
|
||||
import type { ParsedMemento } from "~/db/tables-json";
|
||||
import { identifierToUserIds } from "~/features/mmr/mmr-utils";
|
||||
import { databaseTimestampNow } from "~/utils/dates";
|
||||
import type { MementoSkillDifferences } from "./core/skills.server";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as R from "remeda";
|
||||
import type { ParsedMemento, UserMapModePreferences } from "~/db/tables";
|
||||
import type { ParsedMemento, UserMapModePreferences } from "~/db/tables-json";
|
||||
import * as MapList from "~/features/map-list-generator/core/MapList";
|
||||
import { MapPool } from "~/features/map-list-generator/core/map-pool";
|
||||
import {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ordinal, type Rating } from "openskill";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type {
|
||||
GroupSkillDifference,
|
||||
Tables,
|
||||
UserSkillDifference,
|
||||
} from "~/db/tables";
|
||||
} from "~/db/tables-json";
|
||||
import { MATCHES_COUNT_NEEDED_FOR_LEADERBOARD } from "~/features/leaderboards/leaderboards-constants";
|
||||
import * as Seasons from "~/features/mmr/core/Seasons";
|
||||
import {
|
||||
|
||||
@@ -2,7 +2,8 @@ import { sub } from "date-fns";
|
||||
import { type NotNull, sql, type Transaction } from "kysely";
|
||||
import { jsonArrayFrom, jsonBuildObject } from "kysely/helpers/sqlite";
|
||||
import { db } from "~/db/sql";
|
||||
import type { DB, Tables, UserMapModePreferences } from "~/db/tables";
|
||||
import type { DB, Tables } from "~/db/tables";
|
||||
import type { UserMapModePreferences } from "~/db/tables-json";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import { databaseTimestampNow, dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
import { shortNanoid } from "~/utils/id";
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ComponentProps } from "react";
|
||||
import { createMemoryRouter, RouterProvider } from "react-router";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { render } from "vitest-browser-react";
|
||||
import type { GroupSkillDifference } from "~/db/tables";
|
||||
import type { GroupSkillDifference } from "~/db/tables-json";
|
||||
import type { TieredSkill } from "~/features/mmr/tiered.server";
|
||||
import type {
|
||||
SQGroup,
|
||||
|
||||
@@ -11,7 +11,7 @@ import { SendouPopover } from "~/components/elements/Popover";
|
||||
import { Image, ModeImage, TierImage, WeaponImage } from "~/components/Image";
|
||||
import { NoteAvatar } from "~/components/NoteAvatar";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import type { ParsedMemento } from "~/db/tables";
|
||||
import type { ParsedMemento } from "~/db/tables-json";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import { MATCHES_COUNT_NEEDED_FOR_LEADERBOARD } from "~/features/leaderboards/leaderboards-constants";
|
||||
import { ordinalToRoundedSp } from "~/features/mmr/mmr-utils";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { isWithinInterval, sub } from "date-fns";
|
||||
import * as R from "remeda";
|
||||
import type { DBBoolean, ParsedMemento, Tables } from "~/db/tables";
|
||||
import type { DBBoolean, Tables } from "~/db/tables";
|
||||
import type { ParsedMemento } from "~/db/tables-json";
|
||||
import type { AuthenticatedUser } from "~/features/auth/core/user.server";
|
||||
import * as Seasons from "~/features/mmr/core/Seasons";
|
||||
import { defaultOrdinal } from "~/features/mmr/mmr-utils";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { db } from "~/db/sql";
|
||||
import type { UserMapModePreferences } from "~/db/tables";
|
||||
import type { UserMapModePreferences } from "~/db/tables-json";
|
||||
import type { StageId } from "~/modules/in-game-lists/types";
|
||||
import { dbInsertUsers, dbReset } from "~/utils/Test";
|
||||
import { SENDOUQ_BEST_OF } from "../q-constants";
|
||||
|
||||
@@ -7,7 +7,7 @@ vi.mock("~/features/chat/ChatSystemMessage.server", () => ({
|
||||
}));
|
||||
|
||||
import { db } from "~/db/sql";
|
||||
import type { UserMapModePreferences } from "~/db/tables";
|
||||
import type { UserMapModePreferences } from "~/db/tables-json";
|
||||
import { BANNED_MAPS } from "~/features/match-profile/banned-maps";
|
||||
import { stageIds } from "~/modules/in-game-lists/stage-ids";
|
||||
import invariant from "~/utils/invariant";
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { useLoaderData, useLocation } from "react-router";
|
||||
import { LinkButton } from "~/components/elements/Button";
|
||||
import { ModeImage } from "~/components/Image";
|
||||
import type { Preference, UserMapModePreferences } from "~/db/tables";
|
||||
import type { Preference, UserMapModePreferences } from "~/db/tables-json";
|
||||
import { BANNED_MAPS } from "~/features/match-profile/banned-maps";
|
||||
import { AMOUNT_OF_MAPS_IN_POOL_PER_MODE } from "~/features/match-profile/match-profile-constants";
|
||||
import { SendouForm } from "~/form/SendouForm";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import clsx from "clsx";
|
||||
import { Radio, RadioGroup } from "react-aria-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { Preference } from "~/db/tables";
|
||||
import type { Preference } from "~/db/tables-json";
|
||||
import { preferenceEmojiUrl } from "~/utils/urls";
|
||||
import styles from "./PreferenceRadioGroup.module.css";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import type { UserMapModePreferences } from "~/db/tables";
|
||||
import type { UserMapModePreferences } from "~/db/tables-json";
|
||||
import {
|
||||
checkboxGroup,
|
||||
customField,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const SPLATOON_ROTATION_TYPES = ["SERIES", "OPEN", "X"] as const;
|
||||
|
||||
export type SplatoonRotationType = (typeof SPLATOON_ROTATION_TYPES)[number];
|
||||
@@ -1,7 +1,8 @@
|
||||
import { type Insertable, sql, type Transaction } from "kysely";
|
||||
import { jsonArrayFrom } from "kysely/helpers/sqlite";
|
||||
import { db } from "~/db/sql";
|
||||
import type { CustomTheme, DB, Tables } from "~/db/tables";
|
||||
import type { DB, Tables } from "~/db/tables";
|
||||
import type { CustomTheme } from "~/db/tables-json";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import * as LFGRepository from "~/features/lfg/LFGRepository.server";
|
||||
import { NON_PLAYER_TEAM_ROLES } from "~/features/team/team-constants";
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { ActionFunction } from "react-router";
|
||||
import { redirect } from "react-router";
|
||||
import type { MemberRole, MemberRoleType } from "~/db/tables";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import type {
|
||||
MemberRole,
|
||||
MemberRoleType,
|
||||
} from "~/features/team/team-constants";
|
||||
import { parseFormData } from "~/form/parse.server";
|
||||
import { errorToastIfFalsy, notFoundIfNullish } from "~/utils/remix.server";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
|
||||
@@ -24,6 +24,11 @@ export const TEAM_MEMBER_ROLES = [
|
||||
"CHEERLEADER",
|
||||
] as const;
|
||||
|
||||
export type MemberRole = (typeof TEAM_MEMBER_ROLES)[number];
|
||||
|
||||
/** Classifies how a team member's `customRole` should be treated. */
|
||||
export type MemberRoleType = "PLAYER" | "OTHER";
|
||||
|
||||
/** Roles that are not part of a team's active competitive lineup. Excluded when sourcing a roster (e.g. prefilling tournament registration or a scrim post). */
|
||||
export const NON_PLAYER_TEAM_ROLES: readonly (typeof TEAM_MEMBER_ROLES)[number][] =
|
||||
["CHEERLEADER", "COACH", "SUB"];
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { MemberRole, MemberRoleType, Tables } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type {
|
||||
MemberRole,
|
||||
MemberRoleType,
|
||||
} from "~/features/team/team-constants";
|
||||
import type * as TeamRepository from "./TeamRepository.server";
|
||||
import { NON_PLAYER_TEAM_ROLES, TEAM } from "./team-constants";
|
||||
|
||||
|
||||
36
app/features/theme/theme-constants.ts
Normal file
36
app/features/theme/theme-constants.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/** CSS variables a supporter can override with their own custom theme. */
|
||||
export const CUSTOM_THEME_VARS = [
|
||||
"--_base-h",
|
||||
"--_base-c-0",
|
||||
"--_base-c-1",
|
||||
"--_base-c-2",
|
||||
"--_base-c-3",
|
||||
"--_base-c-4",
|
||||
"--_base-c-5",
|
||||
"--_base-c-6",
|
||||
"--_base-c-7",
|
||||
"--_acc-h",
|
||||
"--_acc-c-0",
|
||||
"--_acc-c-1",
|
||||
"--_acc-c-2",
|
||||
"--_acc-c-3",
|
||||
"--_acc-c-4",
|
||||
"--_acc-c-5",
|
||||
"--_second-h",
|
||||
"--_second-c-0",
|
||||
"--_second-c-1",
|
||||
"--_second-c-2",
|
||||
"--_second-c-3",
|
||||
"--_second-c-4",
|
||||
"--_second-c-5",
|
||||
"--_chat-h",
|
||||
"--_radius-box",
|
||||
"--_radius-field",
|
||||
"--_radius-selector",
|
||||
"--_border-width",
|
||||
"--_size-field",
|
||||
"--_size-selector",
|
||||
"--_size-spacing",
|
||||
] as const;
|
||||
|
||||
export type CustomThemeVar = (typeof CUSTOM_THEME_VARS)[number];
|
||||
2
app/features/top-search/top-search-types.ts
Normal file
2
app/features/top-search/top-search-types.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
/** `WEST` = Tentatek division, `JPN` = Takoroka division. */
|
||||
export type XRankPlacementRegion = "WEST" | "JPN";
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { LoaderFunctionArgs } from "react-router";
|
||||
import { z } from "zod";
|
||||
import { TOURNAMENT_AUDIT_LOG_TYPES } from "~/db/tables";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import * as TournamentAuditLogRepository from "~/features/tournament/TournamentAuditLogRepository.server";
|
||||
import { AUDIT_LOG_PAGE_SIZE } from "~/features/tournament/TournamentAuditLogRepository.server";
|
||||
import { TOURNAMENT_AUDIT_LOG_TYPES } from "~/features/tournament/tournament-constants";
|
||||
import { tournamentFromDBCached } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
import {
|
||||
forbidden,
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Label } from "~/components/Label";
|
||||
import { LocaleTime } from "~/components/LocaleTime";
|
||||
import { Pagination } from "~/components/Pagination";
|
||||
import { Table } from "~/components/Table";
|
||||
import { TOURNAMENT_AUDIT_LOG_TYPES } from "~/db/tables";
|
||||
import { useTournament } from "~/features/tournament/routes/to.$id";
|
||||
import { TOURNAMENT_AUDIT_LOG_TYPES } from "~/features/tournament/tournament-constants";
|
||||
import { useSearchParamPagination } from "~/hooks/useSearchParamPagination";
|
||||
import type { CommonUser } from "~/utils/kysely.server";
|
||||
import { tournamentTeamPage, userPage } from "~/utils/urls";
|
||||
|
||||
@@ -30,7 +30,7 @@ import { SendouDialog } from "~/components/elements/Dialog";
|
||||
import { InfoPopover } from "~/components/InfoPopover";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import { Table } from "~/components/Table";
|
||||
import type { SeedingSnapshot } from "~/db/tables";
|
||||
import type { SeedingSnapshot } from "~/db/tables-json";
|
||||
import { useTournament } from "~/features/tournament/routes/to.$id";
|
||||
import { TOURNAMENT } from "~/features/tournament/tournament-constants";
|
||||
import * as AbDivisions from "~/features/tournament-bracket/core/AbDivisions";
|
||||
|
||||
@@ -4,8 +4,9 @@ import { useTranslation } from "react-i18next";
|
||||
import { Avatar } from "~/components/Avatar";
|
||||
import { Divider } from "~/components/Divider";
|
||||
import { LinkButton, SendouButton } from "~/components/elements/Button";
|
||||
import { type Tables, TOURNAMENT_ORGANIZATION_ROLES } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import { useTournament } from "~/features/tournament/routes/to.$id";
|
||||
import { TOURNAMENT_ORGANIZATION_ROLES } from "~/features/tournament-organization/tournament-organization-constants";
|
||||
import { SendouForm } from "~/form/SendouForm";
|
||||
import { tournamentOrganizationEditPage } from "~/utils/urls";
|
||||
import { adminStaffFormSchema } from "../tournament-admin-staff-schemas";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { TOURNAMENT_STAFF_ROLES } from "~/db/tables";
|
||||
import { TOURNAMENT_STAFF_ROLES } from "~/features/tournament/tournament-constants";
|
||||
import {
|
||||
array,
|
||||
fieldset,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ActionFunction } from "react-router";
|
||||
import { sql } from "~/db/sql";
|
||||
import type { PreparedMaps } from "~/db/tables-json";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import * as ChatSystemMessage from "~/features/chat/ChatSystemMessage.server";
|
||||
import { notify } from "~/features/notifications/core/notify.server";
|
||||
@@ -23,7 +24,6 @@ import {
|
||||
} from "~/utils/remix.server";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
import { idObject } from "~/utils/zod";
|
||||
import type { PreparedMaps } from "../../../db/tables";
|
||||
import * as AbDivisions from "../core/AbDivisions";
|
||||
import { getServerTournamentManager } from "../core/brackets-manager/manager.server";
|
||||
import * as PreparedMapsUtils from "../core/PreparedMaps";
|
||||
|
||||
@@ -2,7 +2,7 @@ import clsx from "clsx";
|
||||
import { differenceInMinutes } from "date-fns";
|
||||
import * as React from "react";
|
||||
import { LocaleTime } from "~/components/LocaleTime";
|
||||
import type { TournamentRoundMaps } from "~/db/tables";
|
||||
import type { TournamentRoundMaps } from "~/db/tables-json";
|
||||
import { useTournament } from "~/features/tournament/routes/to.$id";
|
||||
import { resolveLeagueRoundStartDate } from "~/features/tournament/tournament-utils";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
|
||||
@@ -23,7 +23,7 @@ import { Input } from "~/components/Input";
|
||||
import { Label } from "~/components/Label";
|
||||
import { LocaleTime } from "~/components/LocaleTime";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import type { CustomPickBanFlow, TournamentRoundMaps } from "~/db/tables";
|
||||
import type { CustomPickBanFlow, TournamentRoundMaps } from "~/db/tables-json";
|
||||
import {
|
||||
useTournament,
|
||||
useTournamentPreparedMaps,
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ComponentProps } from "react";
|
||||
import { createMemoryRouter, RouterProvider } from "react-router";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { render } from "vitest-browser-react";
|
||||
import type { CustomPickBanFlow } from "~/db/tables";
|
||||
import type { CustomPickBanFlow } from "~/db/tables-json";
|
||||
import { CustomFlowBuilder } from "./CustomFlowBuilder";
|
||||
|
||||
const defaultProps: ComponentProps<typeof CustomFlowBuilder> = {
|
||||
|
||||
@@ -35,14 +35,13 @@ import {
|
||||
SendouTabPanel,
|
||||
SendouTabs,
|
||||
} from "~/components/elements/Tabs";
|
||||
import type { CustomPickBanFlow, CustomPickBanStep } from "~/db/tables-json";
|
||||
import {
|
||||
ACTION_TYPES,
|
||||
type ActionType,
|
||||
type CustomPickBanFlow,
|
||||
type CustomPickBanStep,
|
||||
WHO_SIDES,
|
||||
type WhoSide,
|
||||
} from "~/db/tables";
|
||||
} from "~/features/tournament-bracket/tournament-bracket-constants";
|
||||
import { useLayoutSize } from "~/hooks/useMainContentWidth";
|
||||
import {
|
||||
type CustomFlowValidationError,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { sub } from "date-fns";
|
||||
import * as R from "remeda";
|
||||
import type { Tables, TournamentStageSettings } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { TournamentStageSettings } from "~/db/tables-json";
|
||||
import { TOURNAMENT } from "~/features/tournament/tournament-constants";
|
||||
import type { TournamentManagerDataSet } from "~/modules/brackets-manager/types";
|
||||
import type { Round } from "~/modules/brackets-model";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { TournamentRoundMaps } from "~/db/tables";
|
||||
import type { TournamentRoundMaps } from "~/db/tables-json";
|
||||
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
|
||||
import {
|
||||
CUSTOM_FLOW_VALIDATION_ERRORS,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import * as R from "remeda";
|
||||
import type { CustomPickBanStep, TournamentRoundMaps } from "~/db/tables-json";
|
||||
import type {
|
||||
ActionType,
|
||||
CustomPickBanStep,
|
||||
TournamentRoundMaps,
|
||||
WhoSide,
|
||||
} from "~/db/tables";
|
||||
} from "~/features/tournament-bracket/tournament-bracket-constants";
|
||||
import { isSetOverByResults } from "~/features/tournament-match/tournament-match-utils";
|
||||
import type {
|
||||
ModeShort,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import type { PreparedMaps as PreparedMapsType } from "~/db/tables";
|
||||
import type { PreparedMaps as PreparedMapsType } from "~/db/tables-json";
|
||||
import * as PreparedMaps from "./PreparedMaps";
|
||||
import { testTournament } from "./tests/test-utils";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as R from "remeda";
|
||||
import type { PreparedMaps } from "~/db/tables";
|
||||
import type { PreparedMaps } from "~/db/tables-json";
|
||||
import { nullFilledArray } from "~/utils/arrays";
|
||||
import invariant from "~/utils/invariant";
|
||||
import type { Bracket } from "./Bracket";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as R from "remeda";
|
||||
import type { Tables, TournamentStageSettings } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { TournamentStageSettings } from "~/db/tables-json";
|
||||
import { TOURNAMENT } from "~/features/tournament/tournament-constants";
|
||||
import {
|
||||
databaseTimestampToDate,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type {
|
||||
Tables,
|
||||
TournamentStage,
|
||||
TournamentStageSettings,
|
||||
} from "~/db/tables";
|
||||
import type { Tables, TournamentStage } from "~/db/tables";
|
||||
import type { TournamentStageSettings } from "~/db/tables-json";
|
||||
import {
|
||||
LEAGUES,
|
||||
TOURNAMENT,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ordinal } from "openskill";
|
||||
import * as R from "remeda";
|
||||
import type { WinLossParticipationArray } from "~/db/tables-json";
|
||||
import { MATCHES_COUNT_NEEDED_FOR_LEADERBOARD } from "~/features/leaderboards/leaderboards-constants";
|
||||
import {
|
||||
identifierToUserIds,
|
||||
@@ -12,7 +13,7 @@ import type { AllMatchResult } from "~/features/tournament-match/TournamentMatch
|
||||
import { matchEndedEarly } from "~/features/tournament-match/tournament-match-utils";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { roundToNDecimalPlaces } from "~/utils/number";
|
||||
import type { Tables, WinLossParticipationArray } from "../../../db/tables";
|
||||
import type { Tables } from "../../../db/tables";
|
||||
import { ensureOneStandingPerUser } from "../tournament-bracket-utils";
|
||||
import type { Standing } from "./Bracket";
|
||||
import type { ParsedBracket } from "./Progression";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** Map list generation logic for "TO pick" as in the map list is defined beforehand by TO and teams don't pick */
|
||||
|
||||
import type { Tables, TournamentRoundMaps } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { TournamentRoundMaps } from "~/db/tables-json";
|
||||
import * as MapList from "~/features/map-list-generator/core/MapList";
|
||||
import { MapPool } from "~/features/map-list-generator/core/map-pool";
|
||||
import type { Round } from "~/modules/brackets-model";
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/** Who picks or bans in a custom pick/ban flow step. */
|
||||
export const WHO_SIDES = [
|
||||
"RANDOM",
|
||||
"RANDOM_OTHER",
|
||||
"ALPHA",
|
||||
"BRAVO",
|
||||
"HIGHER_SEED",
|
||||
"LOWER_SEED",
|
||||
"WINNER",
|
||||
"LOSER",
|
||||
] as const;
|
||||
|
||||
export type WhoSide = (typeof WHO_SIDES)[number];
|
||||
|
||||
/** What happens in a custom pick/ban flow step. */
|
||||
export const ACTION_TYPES = [
|
||||
"ROLL",
|
||||
"PICK",
|
||||
"PICK_NO_MODE_REPEAT",
|
||||
"BAN",
|
||||
"MODE_PICK",
|
||||
"MODE_BAN",
|
||||
] as const;
|
||||
|
||||
export type ActionType = (typeof ACTION_TYPES)[number];
|
||||
@@ -1,5 +1,8 @@
|
||||
import { z } from "zod";
|
||||
import { ACTION_TYPES, WHO_SIDES } from "~/db/tables";
|
||||
import {
|
||||
ACTION_TYPES,
|
||||
WHO_SIDES,
|
||||
} from "~/features/tournament-bracket/tournament-bracket-constants";
|
||||
import {
|
||||
_action,
|
||||
checkboxValueToBoolean,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FormWithConfirm } from "~/components/FormWithConfirm";
|
||||
import { Image, WeaponImage } from "~/components/Image";
|
||||
import { NoteAvatar } from "~/components/NoteAvatar";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import type { Pronouns } from "~/db/tables";
|
||||
import type { Pronouns } from "~/db/tables-json";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import { IS_Q_LOOKING_MOBILE_BREAKPOINT } from "~/features/sendouq/q-constants";
|
||||
import { useTournament } from "~/features/tournament/routes/to.$id";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { LoaderFunctionArgs } from "react-router";
|
||||
import * as R from "remeda";
|
||||
import type { Pronouns } from "~/db/tables";
|
||||
import type { Pronouns } from "~/db/tables-json";
|
||||
import { getUser } from "~/features/auth/core/user.server";
|
||||
import { tournamentFromDBCached } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
import * as UserCardRepository from "~/features/user-card/UserCardRepository.server";
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { sql } from "kysely";
|
||||
import { jsonArrayFrom } from "kysely/helpers/sqlite";
|
||||
import { db } from "~/db/sql";
|
||||
import { TournamentMatchStatus, type TournamentRoundMaps } from "~/db/tables";
|
||||
import type { TournamentRoundMaps } from "~/db/tables-json";
|
||||
import { Status } from "~/modules/brackets-model";
|
||||
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { customAvatarUrl } from "~/utils/kysely.server";
|
||||
@@ -429,7 +430,7 @@ export function findByTournamentTeamId(tournamentTeamId: number) {
|
||||
eb(opponentTwoId, "=", tournamentTeamId),
|
||||
]),
|
||||
)
|
||||
.where("TournamentMatch.status", ">=", TournamentMatchStatus.Completed)
|
||||
.where("TournamentMatch.status", ">=", Status.Completed)
|
||||
.where((eb) =>
|
||||
eb.exists(
|
||||
eb
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { ActionFunction } from "react-router";
|
||||
import { sql } from "~/db/sql";
|
||||
import { TournamentMatchStatus } from "~/db/tables";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import * as ChatSystemMessage from "~/features/chat/ChatSystemMessage.server";
|
||||
import * as ReportedWeaponRepository from "~/features/sendouq-match/ReportedWeaponRepository.server";
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
tournamentWebsocketRoom,
|
||||
} from "~/features/tournament-bracket/tournament-bracket-utils";
|
||||
import * as TournamentMatchRepository from "~/features/tournament-match/TournamentMatchRepository.server";
|
||||
import { Status } from "~/modules/brackets-model";
|
||||
import { dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { logger } from "~/utils/logger";
|
||||
@@ -78,8 +78,7 @@ export const action: ActionFunction = async ({ params, request }) => {
|
||||
);
|
||||
|
||||
errorToastIfFalsy(
|
||||
match.status !== TournamentMatchStatus.Locked &&
|
||||
match.status !== TournamentMatchStatus.Waiting,
|
||||
match.status !== Status.Locked && match.status !== Status.Waiting,
|
||||
"Match is locked, waiting for teams to finish their previous matches",
|
||||
);
|
||||
|
||||
@@ -745,10 +744,7 @@ export const action: ActionFunction = async ({ params, request }) => {
|
||||
);
|
||||
|
||||
// can't lock if match status is not Locked or Waiting (team(s) busy with previous match), let's update their view to reflect that
|
||||
if (
|
||||
match.status !== TournamentMatchStatus.Locked &&
|
||||
match.status !== TournamentMatchStatus.Waiting
|
||||
) {
|
||||
if (match.status !== Status.Locked && match.status !== Status.Waiting) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ import { InfoPopover } from "~/components/InfoPopover";
|
||||
import { Label } from "~/components/Label";
|
||||
import { TAB_KEYS } from "~/components/match-page/MatchTabs";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import { TournamentMatchStatus } from "~/db/tables";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import { useTournament } from "~/features/tournament/routes/to.$id";
|
||||
import type { TournamentDataTeam } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
import { Status } from "~/modules/brackets-model";
|
||||
import type { TournamentMatchLoaderData } from "../loaders/to.$id.matches.$mid.server";
|
||||
import { useMatch } from "../match-page-context";
|
||||
import { OrganizerMatchMapListDialog } from "./OrganizerMatchMapListDialog";
|
||||
@@ -96,8 +96,7 @@ function AdminCastSection({
|
||||
false;
|
||||
|
||||
const canLock =
|
||||
(matchStatus === TournamentMatchStatus.Locked ||
|
||||
matchStatus === TournamentMatchStatus.Waiting) &&
|
||||
(matchStatus === Status.Locked || matchStatus === Status.Waiting) &&
|
||||
!isLocked;
|
||||
const canUnlock = !isMatchStarted && isLocked;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { MatchBannerBottomRow } from "~/components/match-page/MatchBannerBottomR
|
||||
import { MatchBannerStartedAt } from "~/components/match-page/MatchBannerStartedAt";
|
||||
import { MatchBannerTimer } from "~/components/match-page/MatchBannerTimer";
|
||||
import { MatchBannerTopRow } from "~/components/match-page/MatchBannerTopRow";
|
||||
import type { TournamentRoundMaps } from "~/db/tables";
|
||||
import type { TournamentRoundMaps } from "~/db/tables-json";
|
||||
import { useTournament } from "~/features/tournament/routes/to.$id";
|
||||
import {
|
||||
isLeagueRoundLocked,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { TournamentRoundMaps } from "~/db/tables";
|
||||
import type { TournamentRoundMaps } from "~/db/tables-json";
|
||||
import * as TournamentRepository from "~/features/tournament/TournamentRepository.server";
|
||||
import * as PickBan from "~/features/tournament-bracket/core/PickBan";
|
||||
import type { TournamentDataTeam } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Tables, TournamentRoundMaps } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { TournamentRoundMaps } from "~/db/tables-json";
|
||||
import { MapPool } from "~/features/map-list-generator/core/map-pool";
|
||||
import { mapPickingStyleToModes } from "~/features/tournament/tournament-utils";
|
||||
import type { Bracket } from "~/features/tournament-bracket/core/Bracket";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { TAB_KEYS } from "~/components/match-page/MatchTabs";
|
||||
import { resolveRoomPass } from "~/components/match-page/utils";
|
||||
import { TournamentMatchStatus } from "~/db/tables";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import { useTournament } from "~/features/tournament/routes/to.$id";
|
||||
import { isLeagueRoundLocked } from "~/features/tournament/tournament-utils";
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
groupNumberToLetters,
|
||||
tournamentTeamToActiveRosterUserIds,
|
||||
} from "~/features/tournament-bracket/tournament-bracket-utils";
|
||||
import { Status } from "~/modules/brackets-model";
|
||||
import type { TournamentMatchLoaderData } from "./loaders/to.$id.matches.$mid.server";
|
||||
import { matchIsLocked, resolveHostingTeam } from "./tournament-match-utils";
|
||||
|
||||
@@ -103,8 +103,7 @@ export function MatchPageProvider({
|
||||
});
|
||||
|
||||
const waitingForPreviousMatch =
|
||||
data.match.status === TournamentMatchStatus.Locked ||
|
||||
data.match.status === TournamentMatchStatus.Waiting;
|
||||
data.match.status === Status.Locked || data.match.status === Status.Waiting;
|
||||
|
||||
const joinInfo = resolveJoinInfo({ tournament, data, teams });
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { TFunction } from "i18next";
|
||||
import * as R from "remeda";
|
||||
import type { TournamentRoundMaps } from "~/db/tables";
|
||||
import type { TournamentRoundMaps } from "~/db/tables-json";
|
||||
import type { Tournament } from "~/features/tournament-bracket/core/Tournament";
|
||||
import type { TournamentDataTeam } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
|
||||
|
||||
@@ -9,6 +9,16 @@ export const ESTABLISHED_ORG = {
|
||||
LOSE_THRESHOLD: 100,
|
||||
};
|
||||
|
||||
export const TOURNAMENT_ORGANIZATION_ROLES = [
|
||||
"ADMIN",
|
||||
"MEMBER",
|
||||
"ORGANIZER",
|
||||
"STREAMER",
|
||||
] as const;
|
||||
|
||||
export type TournamentOrganizationRole =
|
||||
(typeof TOURNAMENT_ORGANIZATION_ROLES)[number];
|
||||
|
||||
export const TOURNAMENT_ORGANIZATION = {
|
||||
DESCRIPTION_MAX_LENGTH: 1_000,
|
||||
BAN_REASON_MAX_LENGTH: 200,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { z } from "zod";
|
||||
import { TOURNAMENT_ORGANIZATION_ROLES } from "~/db/tables";
|
||||
import { TOURNAMENT_ORGANIZATION } from "~/features/tournament-organization/tournament-organization-constants";
|
||||
import {
|
||||
TOURNAMENT_ORGANIZATION,
|
||||
TOURNAMENT_ORGANIZATION_ROLES,
|
||||
} from "~/features/tournament-organization/tournament-organization-constants";
|
||||
import {
|
||||
array,
|
||||
badges,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
||||
import { db } from "~/db/sql";
|
||||
import type { Tables, TournamentAuditLogMetadata } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { TournamentAuditLogMetadata } from "~/db/tables-json";
|
||||
import { dbInsertUsers, dbReset, withUserId } from "~/utils/Test";
|
||||
import * as TournamentAuditLogRepository from "./TournamentAuditLogRepository.server";
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ import { sub } from "date-fns";
|
||||
import type { Transaction } from "kysely";
|
||||
import { jsonObjectFrom } from "kysely/helpers/sqlite";
|
||||
import { db } from "~/db/sql";
|
||||
import type { DB, Tables, TournamentAuditLogMetadata } from "~/db/tables";
|
||||
import type { DB, Tables } from "~/db/tables";
|
||||
import type { TournamentAuditLogMetadata } from "~/db/tables-json";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import { databaseTimestampNow, dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
import { commonUserSelect } from "~/utils/kysely.server";
|
||||
|
||||
@@ -3,13 +3,12 @@ import { type Insertable, type NotNull, sql, type Transaction } from "kysely";
|
||||
import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/sqlite";
|
||||
import { ordinal } from "openskill";
|
||||
import { db } from "~/db/sql";
|
||||
import type { DB, Tables } from "~/db/tables";
|
||||
import type {
|
||||
CastedMatchesInfo,
|
||||
DB,
|
||||
PreparedMaps,
|
||||
Tables,
|
||||
TournamentSettings,
|
||||
} from "~/db/tables";
|
||||
} from "~/db/tables-json";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import { identifierToUserIds } from "~/features/mmr/mmr-utils";
|
||||
import * as Progression from "~/features/tournament-bracket/core/Progression";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { sql } from "~/db/sql";
|
||||
import type { TournamentRoundMaps } from "~/db/tables";
|
||||
import type { TournamentRoundMaps } from "~/db/tables-json";
|
||||
|
||||
const stm = sql.prepare(/*sql*/ `
|
||||
update "TournamentRound"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Config } from "~/config";
|
||||
import type { StageType } from "~/modules/brackets-model";
|
||||
import { TEAM } from "../team/team-constants";
|
||||
|
||||
export const TOURNAMENT = {
|
||||
@@ -35,6 +36,42 @@ export const TOURNAMENT = {
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const TOURNAMENT_STAGE_TYPES = [
|
||||
"single_elimination",
|
||||
"double_elimination",
|
||||
"round_robin",
|
||||
"swiss",
|
||||
] as const satisfies readonly StageType[];
|
||||
|
||||
// AUTO = style where teams pick their map pool ahead of time and the map lists are automatically made for each round
|
||||
// could also have the traditional style where TO picks the maps later
|
||||
export type TournamentMapPickingStyle =
|
||||
| "TO"
|
||||
| "AUTO_ALL"
|
||||
| "AUTO_SZ"
|
||||
| "AUTO_TC"
|
||||
| "AUTO_RM"
|
||||
| "AUTO_CB";
|
||||
|
||||
export const TOURNAMENT_STAFF_ROLES = ["ORGANIZER", "STREAMER"] as const;
|
||||
|
||||
export type TournamentStaffRole = (typeof TOURNAMENT_STAFF_ROLES)[number];
|
||||
|
||||
export const TOURNAMENT_AUDIT_LOG_TYPES = [
|
||||
"MEMBER_ADDED",
|
||||
"MEMBER_REMOVED",
|
||||
"TEAM_REGISTERED",
|
||||
"TEAM_UNREGISTERED",
|
||||
"TEAM_CHECKED_IN",
|
||||
"TEAM_CHECKED_OUT",
|
||||
"TEAM_DROPPED_OUT",
|
||||
"TEAM_DROP_OUT_UNDONE",
|
||||
"UPDATE_IN_GAME_NAME",
|
||||
] as const;
|
||||
|
||||
export type TournamentAuditLogType =
|
||||
(typeof TOURNAMENT_AUDIT_LOG_TYPES)[number];
|
||||
|
||||
export const LEAGUES =
|
||||
process.env.NODE_ENV === "development" && !Config.prodMode
|
||||
? {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { CastedMatchesInfo } from "~/db/tables";
|
||||
import type { CastedMatchesInfo } from "~/db/tables-json";
|
||||
import * as Seasons from "../mmr/core/Seasons";
|
||||
import type { ParsedBracket } from "../tournament-bracket/core/Progression";
|
||||
import { testTournament } from "../tournament-bracket/core/tests/test-utils";
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { sub } from "date-fns";
|
||||
import * as R from "remeda";
|
||||
import type {
|
||||
CastedMatchesInfo,
|
||||
TournamentStageSettings,
|
||||
} from "~/db/tables-json";
|
||||
import { modesShort, rankedModesShort } from "~/modules/in-game-lists/modes";
|
||||
import type { ModeShort, StageId } from "~/modules/in-game-lists/types";
|
||||
import { weekNumberToDate } from "~/utils/dates";
|
||||
import { SHORT_NANOID_LENGTH } from "~/utils/id";
|
||||
import type {
|
||||
CastedMatchesInfo,
|
||||
Tables,
|
||||
TournamentStageSettings,
|
||||
} from "../../db/tables";
|
||||
import type { Tables } from "../../db/tables";
|
||||
import { assertUnreachable } from "../../utils/types";
|
||||
import { MapPool } from "../map-list-generator/core/map-pool";
|
||||
import { BANNED_MAPS } from "../match-profile/banned-maps";
|
||||
|
||||
@@ -3,13 +3,8 @@ import type { Expression, ExpressionBuilder } from "kysely";
|
||||
import { sql } from "kysely";
|
||||
import { jsonBuildObject, jsonObjectFrom } from "kysely/helpers/sqlite";
|
||||
import { db } from "~/db/sql";
|
||||
import type {
|
||||
CustomTheme,
|
||||
HideableUserCardStat,
|
||||
PeakXP,
|
||||
Tables,
|
||||
XRankPlacementRegion,
|
||||
} from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { CustomTheme, PeakXP } from "~/db/tables-json";
|
||||
import { actorId, actorIdOrNull } from "~/features/auth/core/user.server";
|
||||
import { cachedFullUserLeaderboard } from "~/features/leaderboards/core/leaderboards.server";
|
||||
import { LFG } from "~/features/lfg/lfg-constants";
|
||||
@@ -17,6 +12,7 @@ import * as Seasons from "~/features/mmr/core/Seasons";
|
||||
import { TIERS } from "~/features/mmr/mmr-constants";
|
||||
import type { TieredSkill } from "~/features/mmr/tiered.server";
|
||||
import { userSkills } from "~/features/mmr/tiered.server";
|
||||
import type { XRankPlacementRegion } from "~/features/top-search/top-search-types";
|
||||
import type { StageId } from "~/modules/in-game-lists/types";
|
||||
import { dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
import {
|
||||
@@ -25,6 +21,7 @@ import {
|
||||
} from "~/utils/kysely.server";
|
||||
import { PRESET_COLORS } from "../tier-list-maker/tier-list-maker-constants";
|
||||
import type {
|
||||
HideableUserCardStat,
|
||||
UserCardData,
|
||||
UserCardStat,
|
||||
UserCardStatXPValue,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { type ActionFunction, redirect } from "react-router";
|
||||
import type { HideableUserCardStat } from "~/db/tables";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import * as XRankPlacementRepository from "~/features/top-search/XRankPlacementRepository.server";
|
||||
import { parseFormDataWithImages } from "~/form/parse.server";
|
||||
import { userPage } from "~/utils/urls";
|
||||
import * as UserCardRepository from "../UserCardRepository.server";
|
||||
import { updateUserCardSchema } from "../user-card-schemas";
|
||||
import type { HideableUserCardStat } from "../user-card-types";
|
||||
import { isValidUnverifiedXp } from "../user-card-utils";
|
||||
|
||||
export const action: ActionFunction = async ({ request }) => {
|
||||
|
||||
@@ -23,8 +23,8 @@ import { Image, TierImage } from "~/components/Image";
|
||||
import { LocaleTime } from "~/components/LocaleTime";
|
||||
import { NoteAvatar } from "~/components/NoteAvatar";
|
||||
import { Placement } from "~/components/Placement";
|
||||
import type { XRankPlacementRegion } from "~/db/tables";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import type { XRankPlacementRegion } from "~/features/top-search/top-search-types";
|
||||
import { MutualFriends } from "~/features/user-page/components/MutualFriends";
|
||||
import { ReportUserDialog } from "~/features/user-report/components/ReportUserDialog";
|
||||
import { useLayoutSize } from "~/hooks/useMainContentWidth";
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
useSearchParams,
|
||||
} from "react-router";
|
||||
import { Main } from "~/components/Main";
|
||||
import type { XRankPlacementRegion } from "~/db/tables";
|
||||
import { HowToLinkPopover } from "~/features/top-search/components/HowToLinkPopover";
|
||||
import type { XRankPlacementRegion } from "~/features/top-search/top-search-types";
|
||||
import { type CustomFieldRenderProps, FormField } from "~/form/FormField";
|
||||
import { existingImage } from "~/form/image-field";
|
||||
import { SendouForm, useFormFieldContext } from "~/form/SendouForm";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { CustomTheme, Tables, XRankPlacementRegion } from "~/db/tables";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import type { CustomTheme } from "~/db/tables-json";
|
||||
import type { XRankPlacementRegion } from "~/features/top-search/top-search-types";
|
||||
import type { StageId } from "~/modules/in-game-lists/types";
|
||||
import type { CommonUser } from "~/utils/kysely.server";
|
||||
import type { TieredSkill } from "../mmr/tiered.server";
|
||||
@@ -65,6 +67,9 @@ export type UserCardStat =
|
||||
top: number | null;
|
||||
};
|
||||
|
||||
/** Card stat types that a user can hide from their card. */
|
||||
export type HideableUserCardStat = Extract<UserCardStat["type"], "XP" | "DIV">;
|
||||
|
||||
export interface UserCardStatXPValue {
|
||||
isVerified: boolean;
|
||||
region: XRankPlacementRegion;
|
||||
|
||||
@@ -3,15 +3,10 @@ import { sql } from "kysely";
|
||||
import { jsonArrayFrom } from "kysely/helpers/sqlite";
|
||||
import * as R from "remeda";
|
||||
import { db } from "~/db/sql";
|
||||
import type {
|
||||
BuildSort,
|
||||
CustomTheme,
|
||||
DB,
|
||||
Tables,
|
||||
TablesInsertable,
|
||||
UserPreferences,
|
||||
} from "~/db/tables";
|
||||
import type { DB, Tables, TablesInsertable } from "~/db/tables";
|
||||
import type { CustomTheme, UserPreferences } from "~/db/tables-json";
|
||||
import { actorId } from "~/features/auth/core/user.server";
|
||||
import type { BuildSort } from "~/features/user-page/user-page-constants";
|
||||
import { userRoles } from "~/modules/permissions/mapper.server";
|
||||
import { isSupporter } from "~/modules/permissions/utils";
|
||||
import { databaseTimestampNow, dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { type ActionFunction, redirect } from "react-router";
|
||||
import { z } from "zod";
|
||||
import { BUILD_SORT_IDENTIFIERS } from "~/db/tables";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import * as BuildRepository from "~/features/builds/BuildRepository.server";
|
||||
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
||||
import { BUILD_SORT_IDENTIFIERS } from "~/features/user-page/user-page-constants";
|
||||
import { errorToastIfFalsy, parseRequestPayload } from "~/utils/remix.server";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
import { userBuildsPage } from "~/utils/urls";
|
||||
|
||||
@@ -2,12 +2,14 @@ import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AbilitiesSelector } from "~/components/AbilitiesSelector";
|
||||
import { GearSelect } from "~/components/GearSelect";
|
||||
import type { GearType } from "~/db/tables";
|
||||
import type { CustomFieldRenderProps } from "~/form/FormField";
|
||||
import { FormFieldWrapper } from "~/form/fields/FormFieldWrapper";
|
||||
import { SendouForm, useFormFieldContext } from "~/form/SendouForm";
|
||||
import { rankedModesShort } from "~/modules/in-game-lists/modes";
|
||||
import type { BuildAbilitiesTupleWithUnknown } from "~/modules/in-game-lists/types";
|
||||
import type {
|
||||
BuildAbilitiesTupleWithUnknown,
|
||||
GearType,
|
||||
} from "~/modules/in-game-lists/types";
|
||||
import { newBuildSchema } from "../user-page-schemas";
|
||||
|
||||
interface NewBuildFormProps {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { BuildSort } from "~/db/tables";
|
||||
import type * as BuildRepository from "~/features/builds/BuildRepository.server";
|
||||
import type { BuildSort } from "~/features/user-page/user-page-constants";
|
||||
import { modesShort } from "~/modules/in-game-lists/modes";
|
||||
import type { MainWeaponId } from "~/modules/in-game-lists/types";
|
||||
import { weaponIdToBaseWeaponId } from "~/modules/in-game-lists/weapon-ids";
|
||||
|
||||
@@ -9,8 +9,11 @@ import { SendouMenu, SendouMenuItem } from "~/components/elements/Menu";
|
||||
import { FormMessage } from "~/components/FormMessage";
|
||||
import { Image, WeaponImage } from "~/components/Image";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import { BUILD_SORT_IDENTIFIERS, type BuildSort } from "~/db/tables";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import {
|
||||
BUILD_SORT_IDENTIFIERS,
|
||||
type BuildSort,
|
||||
} from "~/features/user-page/user-page-constants";
|
||||
import { useSearchParamState } from "~/hooks/useSearchParamState";
|
||||
import type { MainWeaponId } from "~/modules/in-game-lists/types";
|
||||
import { mainWeaponIds } from "~/modules/in-game-lists/weapon-ids";
|
||||
|
||||
@@ -21,8 +21,39 @@ export const SPL2_JOIN_ORDER_CUTOFF = 13_589;
|
||||
export const MATCHES_PER_SEASONS_PAGE = 8;
|
||||
export const RESULTS_PER_PAGE = 25;
|
||||
export const HIGHLIGHTS_RESULTS_MAX = 500;
|
||||
export const BUILD_SORT_IDENTIFIERS = [
|
||||
"UPDATED_AT",
|
||||
"TOP_500",
|
||||
"WEAPON_POOL",
|
||||
"WEAPON_IN_GAME_ORDER",
|
||||
"ALPHABETICAL_TITLE",
|
||||
"MODE",
|
||||
"HEADGEAR_ID",
|
||||
"CLOTHES_ID",
|
||||
"SHOES_ID",
|
||||
"PUBLIC_BUILD",
|
||||
"PRIVATE_BUILD",
|
||||
] as const;
|
||||
|
||||
export type BuildSort = (typeof BUILD_SORT_IDENTIFIERS)[number];
|
||||
|
||||
export const DEFAULT_BUILD_SORT = ["WEAPON_POOL", "UPDATED_AT"] as const;
|
||||
|
||||
export const SUBJECT_PRONOUNS = ["he", "she", "they", "it", "any"] as const;
|
||||
|
||||
export type SubjectPronoun = (typeof SUBJECT_PRONOUNS)[number];
|
||||
|
||||
export const OBJECT_PRONOUNS = [
|
||||
"him",
|
||||
"her",
|
||||
"them",
|
||||
"its",
|
||||
"all",
|
||||
...SUBJECT_PRONOUNS,
|
||||
] as const;
|
||||
|
||||
export type ObjectPronoun = (typeof OBJECT_PRONOUNS)[number];
|
||||
|
||||
/**
|
||||
* An array of ISO 3166-1 alpha-2 country codes.
|
||||
* Each entry is a two-letter uppercase string representing a country or territory. Sorted alphabetically.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { z } from "zod";
|
||||
import { OBJECT_PRONOUNS, SUBJECT_PRONOUNS } from "~/db/tables";
|
||||
import { BADGE } from "~/features/badges/badges-constants";
|
||||
import * as Seasons from "~/features/mmr/core/Seasons";
|
||||
import {
|
||||
OBJECT_PRONOUNS,
|
||||
SUBJECT_PRONOUNS,
|
||||
} from "~/features/user-page/user-page-constants";
|
||||
import {
|
||||
badges,
|
||||
checkboxGroup,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UserReportCategory } from "~/db/tables";
|
||||
import type { UserReportCategory } from "~/features/user-report/user-report-constants";
|
||||
import {
|
||||
sendModDiscordWebhook,
|
||||
truncateEmbedValue,
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import type { UserReportCategory } from "~/db/tables";
|
||||
|
||||
export const USER_REPORT = {
|
||||
DESCRIPTION_MAX_LENGTH: 2000,
|
||||
MATCH_ID_MAX_LENGTH: 10,
|
||||
};
|
||||
|
||||
export const USER_REPORT_CATEGORIES = [
|
||||
"INAPPROPRIATE_CONTENT",
|
||||
"ALTING",
|
||||
"HARASSMENT",
|
||||
"CHEATING",
|
||||
"OTHER",
|
||||
] as const;
|
||||
|
||||
export type UserReportCategory = (typeof USER_REPORT_CATEGORIES)[number];
|
||||
|
||||
/** English display names, shown on the staff-only admin tab and in the Discord webhook embed. */
|
||||
export const USER_REPORT_CATEGORY_LABELS: Record<UserReportCategory, string> = {
|
||||
INAPPROPRIATE_CONTENT: "Inappropriate content",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user