diff --git a/app/components/BuildCard.tsx b/app/components/BuildCard.tsx index 3296c1485..fd7025743 100644 --- a/app/components/BuildCard.tsx +++ b/app/components/BuildCard.tsx @@ -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, diff --git a/app/components/CustomThemeSelector.tsx b/app/components/CustomThemeSelector.tsx index 9e2560f4c..8b27dbea5 100644 --- a/app/components/CustomThemeSelector.tsx +++ b/app/components/CustomThemeSelector.tsx @@ -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, diff --git a/app/components/GearSelect.tsx b/app/components/GearSelect.tsx index 1b1ad6763..472f19d46 100644 --- a/app/components/GearSelect.tsx +++ b/app/components/GearSelect.tsx @@ -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"; diff --git a/app/components/match-page/MatchTimeline.tsx b/app/components/match-page/MatchTimeline.tsx index fe8d8e40d..554ea00f9 100644 --- a/app/components/match-page/MatchTimeline.tsx +++ b/app/components/match-page/MatchTimeline.tsx @@ -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, diff --git a/app/db/seed/index.ts b/app/db/seed/index.ts index 2da620ddd..4fd4df26a 100644 --- a/app/db/seed/index.ts +++ b/app/db/seed/index.ts @@ -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, diff --git a/app/db/tables-json.ts b/app/db/tables-json.ts new file mode 100644 index 000000000..6067a8cf3 --- /dev/null +++ b/app/db/tables-json.ts @@ -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, "--_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> + >; + /** 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; + 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; +}; diff --git a/app/db/tables.ts b/app/db/tables.ts index dad4a22ca..3a64f134b 100644 --- a/app/db/tables.ts +++ b/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 = ? ColumnType : ColumnType; -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, "--_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> - >; - /** 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; 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; splId: string; @@ -517,62 +432,6 @@ export interface SplatoonPlayer { peakXp: JSONColumnTypeNullable; } -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; id: GeneratedAlways; @@ -595,21 +454,6 @@ export interface Tournament { vodsSyncCount: Generated; } -export interface SeedingSnapshot { - savedAt: number; - teams: Array<{ - teamId: number; - members: Array<{ userId: number; username: string }>; - }>; -} - -export interface PreparedMaps { - authorId: number; - createdAt: number; - maps: Array; - eliminationTeamCount?: number; -} - export interface SavedCalendarEvent { id: GeneratedAlways; 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; 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; 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; } -// 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; @@ -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; } -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; 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; name: string; @@ -943,15 +684,6 @@ export interface TournamentOrganization { isEstablished: Generated; } -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; @@ -1168,11 +827,6 @@ export interface User { unverifiedPeakXP: JSONColumnTypeNullable; } -/** 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; } -export type ApiTokenType = "read" | "write"; - export interface ApiToken { id: GeneratedAlways; userId: number; @@ -1291,15 +943,6 @@ export interface ModNote { isDeleted: Generated; } -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; 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; } -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; @@ -1481,9 +1113,6 @@ export interface NotificationUserSubscription { subscription: JSONColumnType; } -export const SPLATOON_ROTATION_TYPES = ["SERIES", "OPEN", "X"] as const; -export type SplatoonRotationType = (typeof SPLATOON_ROTATION_TYPES)[number]; - export interface SplatoonRotation { id: GeneratedAlways; type: SplatoonRotationType; diff --git a/app/features/api-public/api-public-utils.server.ts b/app/features/api-public/api-public-utils.server.ts index 36a76b440..aaa01fd28 100644 --- a/app/features/api-public/api-public-utils.server.ts +++ b/app/features/api-public/api-public-utils.server.ts @@ -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 }; diff --git a/app/features/api-public/schema.ts b/app/features/api-public/schema.ts index 3b73bbc79..da5d32d86 100644 --- a/app/features/api-public/schema.ts +++ b/app/features/api-public/schema.ts @@ -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"; diff --git a/app/features/api/ApiRepository.server.ts b/app/features/api/ApiRepository.server.ts index 8051b5454..c76a2244a 100644 --- a/app/features/api/ApiRepository.server.ts +++ b/app/features/api/ApiRepository.server.ts @@ -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; diff --git a/app/features/api/api-types.ts b/app/features/api/api-types.ts new file mode 100644 index 000000000..e6a0f0ce8 --- /dev/null +++ b/app/features/api/api-types.ts @@ -0,0 +1 @@ +export type ApiTokenType = "read" | "write"; diff --git a/app/features/calendar/CalendarRepository.server.ts b/app/features/calendar/CalendarRepository.server.ts index b1906f438..03ccbb65f 100644 --- a/app/features/calendar/CalendarRepository.server.ts +++ b/app/features/calendar/CalendarRepository.server.ts @@ -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 { diff --git a/app/features/calendar/actions/calendar.new.server.ts b/app/features/calendar/actions/calendar.new.server.ts index c3f05edf0..45b4ada97 100644 --- a/app/features/calendar/actions/calendar.new.server.ts +++ b/app/features/calendar/actions/calendar.new.server.ts @@ -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"; diff --git a/app/features/calendar/calendar-constants.ts b/app/features/calendar/calendar-constants.ts index 7a6558ec2..65c74de6f 100644 --- a/app/features/calendar/calendar-constants.ts +++ b/app/features/calendar/calendar-constants.ts @@ -1,4 +1,4 @@ -import type { CalendarEventTag } from "~/db/tables"; +import type { CalendarEventTag } from "~/features/calendar/calendar-types"; export const tags = { SPECIAL: { diff --git a/app/features/calendar/calendar-schemas.ts b/app/features/calendar/calendar-schemas.ts index 9784c8315..177d6e39a 100644 --- a/app/features/calendar/calendar-schemas.ts +++ b/app/features/calendar/calendar-schemas.ts @@ -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 { diff --git a/app/features/calendar/calendar-types.ts b/app/features/calendar/calendar-types.ts index f94176619..6225f2cb9 100644 --- a/app/features/calendar/calendar-types.ts +++ b/app/features/calendar/calendar-types.ts @@ -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; diff --git a/app/features/calendar/components/Tags.tsx b/app/features/calendar/components/Tags.tsx index 5ef374aed..934e0b2f6 100644 --- a/app/features/calendar/components/Tags.tsx +++ b/app/features/calendar/components/Tags.tsx @@ -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"; diff --git a/app/features/calendar/loaders/calendar.server.ts b/app/features/calendar/loaders/calendar.server.ts index 2a32347d8..17415f2a5 100644 --- a/app/features/calendar/loaders/calendar.server.ts +++ b/app/features/calendar/loaders/calendar.server.ts @@ -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 { diff --git a/app/features/lfg/lfg-constants.ts b/app/features/lfg/lfg-constants.ts index 5ccc9d6a1..d5dfd4196 100644 --- a/app/features/lfg/lfg-constants.ts +++ b/app/features/lfg/lfg-constants.ts @@ -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, diff --git a/app/features/lfg/lfg-types.ts b/app/features/lfg/lfg-types.ts index 0f14a463a..06e76314a 100644 --- a/app/features/lfg/lfg-types.ts +++ b/app/features/lfg/lfg-types.ts @@ -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"; diff --git a/app/features/match-profile/MatchProfileRepository.server.test.ts b/app/features/match-profile/MatchProfileRepository.server.test.ts index b071d39e1..ee15bf606 100644 --- a/app/features/match-profile/MatchProfileRepository.server.test.ts +++ b/app/features/match-profile/MatchProfileRepository.server.test.ts @@ -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"; diff --git a/app/features/match-profile/MatchProfileRepository.server.ts b/app/features/match-profile/MatchProfileRepository.server.ts index 89382366f..691506484 100644 --- a/app/features/match-profile/MatchProfileRepository.server.ts +++ b/app/features/match-profile/MatchProfileRepository.server.ts @@ -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"; diff --git a/app/features/notifications/NotificationRepository.server.ts b/app/features/notifications/NotificationRepository.server.ts index fb4ba3fe6..1164b68de 100644 --- a/app/features/notifications/NotificationRepository.server.ts +++ b/app/features/notifications/NotificationRepository.server.ts @@ -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"; diff --git a/app/features/notifications/core/notify.server.ts b/app/features/notifications/core/notify.server.ts index 4d044db96..3228669eb 100644 --- a/app/features/notifications/core/notify.server.ts +++ b/app/features/notifications/core/notify.server.ts @@ -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"; diff --git a/app/features/plus-suggestions/plus-suggestions-utils.ts b/app/features/plus-suggestions/plus-suggestions-utils.ts index f652fa562..8972307a2 100644 --- a/app/features/plus-suggestions/plus-suggestions-utils.ts +++ b/app/features/plus-suggestions/plus-suggestions-utils.ts @@ -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"; diff --git a/app/features/plus-voting/loaders/plus.voting.results.server.ts b/app/features/plus-voting/loaders/plus.voting.results.server.ts index e0949d3a6..51d278bf4 100644 --- a/app/features/plus-voting/loaders/plus.voting.results.server.ts +++ b/app/features/plus-voting/loaders/plus.voting.results.server.ts @@ -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 () => { diff --git a/app/features/sendouq-match/SQMatchRepository.server.ts b/app/features/sendouq-match/SQMatchRepository.server.ts index eb48cd4fa..3d70a827d 100644 --- a/app/features/sendouq-match/SQMatchRepository.server.ts +++ b/app/features/sendouq-match/SQMatchRepository.server.ts @@ -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"; diff --git a/app/features/sendouq-match/SkillRepository.server.ts b/app/features/sendouq-match/SkillRepository.server.ts index 25f9b3f94..b353384f7 100644 --- a/app/features/sendouq-match/SkillRepository.server.ts +++ b/app/features/sendouq-match/SkillRepository.server.ts @@ -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"; diff --git a/app/features/sendouq-match/core/match.server.ts b/app/features/sendouq-match/core/match.server.ts index 44d55b913..a50a72a5e 100644 --- a/app/features/sendouq-match/core/match.server.ts +++ b/app/features/sendouq-match/core/match.server.ts @@ -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 { diff --git a/app/features/sendouq-match/core/skills.server.ts b/app/features/sendouq-match/core/skills.server.ts index 3304d6573..94d6370b2 100644 --- a/app/features/sendouq-match/core/skills.server.ts +++ b/app/features/sendouq-match/core/skills.server.ts @@ -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 { diff --git a/app/features/sendouq/SQGroupRepository.server.ts b/app/features/sendouq/SQGroupRepository.server.ts index 725af15ff..5e6b7ad58 100644 --- a/app/features/sendouq/SQGroupRepository.server.ts +++ b/app/features/sendouq/SQGroupRepository.server.ts @@ -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"; diff --git a/app/features/sendouq/components/GroupCard.browser.test.tsx b/app/features/sendouq/components/GroupCard.browser.test.tsx index c614aa77a..4b58eb9b9 100644 --- a/app/features/sendouq/components/GroupCard.browser.test.tsx +++ b/app/features/sendouq/components/GroupCard.browser.test.tsx @@ -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, diff --git a/app/features/sendouq/components/GroupCard.tsx b/app/features/sendouq/components/GroupCard.tsx index 687494383..136eac151 100644 --- a/app/features/sendouq/components/GroupCard.tsx +++ b/app/features/sendouq/components/GroupCard.tsx @@ -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"; diff --git a/app/features/sendouq/core/SendouQ.server.ts b/app/features/sendouq/core/SendouQ.server.ts index d4ae656e2..c4ddcceff 100644 --- a/app/features/sendouq/core/SendouQ.server.ts +++ b/app/features/sendouq/core/SendouQ.server.ts @@ -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"; diff --git a/app/features/sendouq/core/default-maps.server.test.ts b/app/features/sendouq/core/default-maps.server.test.ts index c9df6e21b..24500d86f 100644 --- a/app/features/sendouq/core/default-maps.server.test.ts +++ b/app/features/sendouq/core/default-maps.server.test.ts @@ -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"; diff --git a/app/features/sendouq/routes/q.looking.test.ts b/app/features/sendouq/routes/q.looking.test.ts index 001513a2c..16366803d 100644 --- a/app/features/sendouq/routes/q.looking.test.ts +++ b/app/features/sendouq/routes/q.looking.test.ts @@ -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"; diff --git a/app/features/settings/components/MatchProfileTab.tsx b/app/features/settings/components/MatchProfileTab.tsx index a5448c50d..81c6319c3 100644 --- a/app/features/settings/components/MatchProfileTab.tsx +++ b/app/features/settings/components/MatchProfileTab.tsx @@ -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"; diff --git a/app/features/settings/components/PreferenceRadioGroup.tsx b/app/features/settings/components/PreferenceRadioGroup.tsx index 833f87c29..3ad262864 100644 --- a/app/features/settings/components/PreferenceRadioGroup.tsx +++ b/app/features/settings/components/PreferenceRadioGroup.tsx @@ -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"; diff --git a/app/features/settings/match-profile-schemas.ts b/app/features/settings/match-profile-schemas.ts index fd93fcf18..134ac2a25 100644 --- a/app/features/settings/match-profile-schemas.ts +++ b/app/features/settings/match-profile-schemas.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import type { UserMapModePreferences } from "~/db/tables"; +import type { UserMapModePreferences } from "~/db/tables-json"; import { checkboxGroup, customField, diff --git a/app/features/splatoon-rotations/splatoon-rotations-constants.ts b/app/features/splatoon-rotations/splatoon-rotations-constants.ts new file mode 100644 index 000000000..a22c1cd03 --- /dev/null +++ b/app/features/splatoon-rotations/splatoon-rotations-constants.ts @@ -0,0 +1,3 @@ +export const SPLATOON_ROTATION_TYPES = ["SERIES", "OPEN", "X"] as const; + +export type SplatoonRotationType = (typeof SPLATOON_ROTATION_TYPES)[number]; diff --git a/app/features/team/TeamRepository.server.ts b/app/features/team/TeamRepository.server.ts index 06afbc129..b42a1702c 100644 --- a/app/features/team/TeamRepository.server.ts +++ b/app/features/team/TeamRepository.server.ts @@ -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"; diff --git a/app/features/team/actions/t.$customUrl.roster.server.ts b/app/features/team/actions/t.$customUrl.roster.server.ts index 7473d21a8..d7b89feae 100644 --- a/app/features/team/actions/t.$customUrl.roster.server.ts +++ b/app/features/team/actions/t.$customUrl.roster.server.ts @@ -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"; diff --git a/app/features/team/team-constants.ts b/app/features/team/team-constants.ts index 8ddf8bd1d..f5cd2eca8 100644 --- a/app/features/team/team-constants.ts +++ b/app/features/team/team-constants.ts @@ -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"]; diff --git a/app/features/team/team-utils.ts b/app/features/team/team-utils.ts index 054ebe90f..394f52c32 100644 --- a/app/features/team/team-utils.ts +++ b/app/features/team/team-utils.ts @@ -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"; diff --git a/app/features/theme/theme-constants.ts b/app/features/theme/theme-constants.ts new file mode 100644 index 000000000..eb4ff165a --- /dev/null +++ b/app/features/theme/theme-constants.ts @@ -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]; diff --git a/app/features/top-search/top-search-types.ts b/app/features/top-search/top-search-types.ts new file mode 100644 index 000000000..f18f6df22 --- /dev/null +++ b/app/features/top-search/top-search-types.ts @@ -0,0 +1,2 @@ +/** `WEST` = Tentatek division, `JPN` = Takoroka division. */ +export type XRankPlacementRegion = "WEST" | "JPN"; diff --git a/app/features/tournament-admin/loaders/to.$id.admin.audit.server.ts b/app/features/tournament-admin/loaders/to.$id.admin.audit.server.ts index 75d86976f..55cc36bb4 100644 --- a/app/features/tournament-admin/loaders/to.$id.admin.audit.server.ts +++ b/app/features/tournament-admin/loaders/to.$id.admin.audit.server.ts @@ -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, diff --git a/app/features/tournament-admin/routes/to.$id.admin.audit.tsx b/app/features/tournament-admin/routes/to.$id.admin.audit.tsx index d28881a61..655efef58 100644 --- a/app/features/tournament-admin/routes/to.$id.admin.audit.tsx +++ b/app/features/tournament-admin/routes/to.$id.admin.audit.tsx @@ -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"; diff --git a/app/features/tournament-admin/routes/to.$id.admin.seeds.tsx b/app/features/tournament-admin/routes/to.$id.admin.seeds.tsx index 8b30f0174..78647692f 100644 --- a/app/features/tournament-admin/routes/to.$id.admin.seeds.tsx +++ b/app/features/tournament-admin/routes/to.$id.admin.seeds.tsx @@ -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"; diff --git a/app/features/tournament-admin/routes/to.$id.admin.staff.tsx b/app/features/tournament-admin/routes/to.$id.admin.staff.tsx index 082821acc..f015670f5 100644 --- a/app/features/tournament-admin/routes/to.$id.admin.staff.tsx +++ b/app/features/tournament-admin/routes/to.$id.admin.staff.tsx @@ -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"; diff --git a/app/features/tournament-admin/tournament-admin-staff-schemas.ts b/app/features/tournament-admin/tournament-admin-staff-schemas.ts index 91c6345f4..82d3ae1d0 100644 --- a/app/features/tournament-admin/tournament-admin-staff-schemas.ts +++ b/app/features/tournament-admin/tournament-admin-staff-schemas.ts @@ -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, diff --git a/app/features/tournament-bracket/actions/to.$id.brackets.server.ts b/app/features/tournament-bracket/actions/to.$id.brackets.server.ts index 8d4b32356..328c2a1b2 100644 --- a/app/features/tournament-bracket/actions/to.$id.brackets.server.ts +++ b/app/features/tournament-bracket/actions/to.$id.brackets.server.ts @@ -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"; diff --git a/app/features/tournament-bracket/components/Bracket/RoundHeader.tsx b/app/features/tournament-bracket/components/Bracket/RoundHeader.tsx index e4607df00..596ab9f23 100644 --- a/app/features/tournament-bracket/components/Bracket/RoundHeader.tsx +++ b/app/features/tournament-bracket/components/Bracket/RoundHeader.tsx @@ -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"; diff --git a/app/features/tournament-bracket/components/BracketMapListDialog.tsx b/app/features/tournament-bracket/components/BracketMapListDialog.tsx index 34ba9da6a..300783f76 100644 --- a/app/features/tournament-bracket/components/BracketMapListDialog.tsx +++ b/app/features/tournament-bracket/components/BracketMapListDialog.tsx @@ -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, diff --git a/app/features/tournament-bracket/components/CustomFlowBuilder.browser.test.tsx b/app/features/tournament-bracket/components/CustomFlowBuilder.browser.test.tsx index a7e222ba2..022f4e16b 100644 --- a/app/features/tournament-bracket/components/CustomFlowBuilder.browser.test.tsx +++ b/app/features/tournament-bracket/components/CustomFlowBuilder.browser.test.tsx @@ -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 = { diff --git a/app/features/tournament-bracket/components/CustomFlowBuilder.tsx b/app/features/tournament-bracket/components/CustomFlowBuilder.tsx index 393db8a18..9d5d5ba18 100644 --- a/app/features/tournament-bracket/components/CustomFlowBuilder.tsx +++ b/app/features/tournament-bracket/components/CustomFlowBuilder.tsx @@ -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, diff --git a/app/features/tournament-bracket/core/Bracket/Bracket.ts b/app/features/tournament-bracket/core/Bracket/Bracket.ts index 971783fdd..6ccfb5d17 100644 --- a/app/features/tournament-bracket/core/Bracket/Bracket.ts +++ b/app/features/tournament-bracket/core/Bracket/Bracket.ts @@ -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"; diff --git a/app/features/tournament-bracket/core/PickBan.test.ts b/app/features/tournament-bracket/core/PickBan.test.ts index fd25abd20..6217d6a45 100644 --- a/app/features/tournament-bracket/core/PickBan.test.ts +++ b/app/features/tournament-bracket/core/PickBan.test.ts @@ -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, diff --git a/app/features/tournament-bracket/core/PickBan.ts b/app/features/tournament-bracket/core/PickBan.ts index 29c5cf642..011a04030 100644 --- a/app/features/tournament-bracket/core/PickBan.ts +++ b/app/features/tournament-bracket/core/PickBan.ts @@ -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, diff --git a/app/features/tournament-bracket/core/PreparedMaps.test.ts b/app/features/tournament-bracket/core/PreparedMaps.test.ts index 76b819c3f..a9d8703ab 100644 --- a/app/features/tournament-bracket/core/PreparedMaps.test.ts +++ b/app/features/tournament-bracket/core/PreparedMaps.test.ts @@ -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"; diff --git a/app/features/tournament-bracket/core/PreparedMaps.ts b/app/features/tournament-bracket/core/PreparedMaps.ts index 12f432ae6..c67c0034f 100644 --- a/app/features/tournament-bracket/core/PreparedMaps.ts +++ b/app/features/tournament-bracket/core/PreparedMaps.ts @@ -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"; diff --git a/app/features/tournament-bracket/core/Progression.ts b/app/features/tournament-bracket/core/Progression.ts index 0ece5d0f4..2cde3fdc9 100644 --- a/app/features/tournament-bracket/core/Progression.ts +++ b/app/features/tournament-bracket/core/Progression.ts @@ -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, diff --git a/app/features/tournament-bracket/core/Tournament.ts b/app/features/tournament-bracket/core/Tournament.ts index dead58fa1..26dbc8532 100644 --- a/app/features/tournament-bracket/core/Tournament.ts +++ b/app/features/tournament-bracket/core/Tournament.ts @@ -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, diff --git a/app/features/tournament-bracket/core/summarizer.server.ts b/app/features/tournament-bracket/core/summarizer.server.ts index 46dd1a3f1..99864699c 100644 --- a/app/features/tournament-bracket/core/summarizer.server.ts +++ b/app/features/tournament-bracket/core/summarizer.server.ts @@ -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"; diff --git a/app/features/tournament-bracket/core/toMapList.ts b/app/features/tournament-bracket/core/toMapList.ts index ab8a32b2e..711fb2c34 100644 --- a/app/features/tournament-bracket/core/toMapList.ts +++ b/app/features/tournament-bracket/core/toMapList.ts @@ -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"; diff --git a/app/features/tournament-bracket/tournament-bracket-constants.ts b/app/features/tournament-bracket/tournament-bracket-constants.ts new file mode 100644 index 000000000..135a40f76 --- /dev/null +++ b/app/features/tournament-bracket/tournament-bracket-constants.ts @@ -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]; diff --git a/app/features/tournament-bracket/tournament-bracket-schemas.server.ts b/app/features/tournament-bracket/tournament-bracket-schemas.server.ts index fb77cf096..e659a717f 100644 --- a/app/features/tournament-bracket/tournament-bracket-schemas.server.ts +++ b/app/features/tournament-bracket/tournament-bracket-schemas.server.ts @@ -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, diff --git a/app/features/tournament-lfg/components/LFGGroupCard.tsx b/app/features/tournament-lfg/components/LFGGroupCard.tsx index 6692eef52..6b86265f8 100644 --- a/app/features/tournament-lfg/components/LFGGroupCard.tsx +++ b/app/features/tournament-lfg/components/LFGGroupCard.tsx @@ -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"; diff --git a/app/features/tournament-lfg/loaders/to.$id.looking.server.ts b/app/features/tournament-lfg/loaders/to.$id.looking.server.ts index 641acd0ae..9c43780ef 100644 --- a/app/features/tournament-lfg/loaders/to.$id.looking.server.ts +++ b/app/features/tournament-lfg/loaders/to.$id.looking.server.ts @@ -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"; diff --git a/app/features/tournament-match/TournamentMatchRepository.server.ts b/app/features/tournament-match/TournamentMatchRepository.server.ts index 9c4fbae45..6565a9754 100644 --- a/app/features/tournament-match/TournamentMatchRepository.server.ts +++ b/app/features/tournament-match/TournamentMatchRepository.server.ts @@ -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 diff --git a/app/features/tournament-match/actions/to.$id.matches.$mid.server.ts b/app/features/tournament-match/actions/to.$id.matches.$mid.server.ts index 944836ed4..20d6b28b7 100644 --- a/app/features/tournament-match/actions/to.$id.matches.$mid.server.ts +++ b/app/features/tournament-match/actions/to.$id.matches.$mid.server.ts @@ -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; } diff --git a/app/features/tournament-match/components/TournamentMatchAdminTab.tsx b/app/features/tournament-match/components/TournamentMatchAdminTab.tsx index 9f15ea4c1..7626ab153 100644 --- a/app/features/tournament-match/components/TournamentMatchAdminTab.tsx +++ b/app/features/tournament-match/components/TournamentMatchAdminTab.tsx @@ -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; diff --git a/app/features/tournament-match/components/TournamentMatchBanner.tsx b/app/features/tournament-match/components/TournamentMatchBanner.tsx index d49c3f913..99238fd95 100644 --- a/app/features/tournament-match/components/TournamentMatchBanner.tsx +++ b/app/features/tournament-match/components/TournamentMatchBanner.tsx @@ -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, diff --git a/app/features/tournament-match/core/executeRoll.server.ts b/app/features/tournament-match/core/executeRoll.server.ts index 3c98872b6..b93dc9023 100644 --- a/app/features/tournament-match/core/executeRoll.server.ts +++ b/app/features/tournament-match/core/executeRoll.server.ts @@ -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"; diff --git a/app/features/tournament-match/core/mapList.server.ts b/app/features/tournament-match/core/mapList.server.ts index 7e1b04fa2..8046453e1 100644 --- a/app/features/tournament-match/core/mapList.server.ts +++ b/app/features/tournament-match/core/mapList.server.ts @@ -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"; diff --git a/app/features/tournament-match/match-page-context.tsx b/app/features/tournament-match/match-page-context.tsx index a5d931a41..f39c9cb74 100644 --- a/app/features/tournament-match/match-page-context.tsx +++ b/app/features/tournament-match/match-page-context.tsx @@ -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 }); diff --git a/app/features/tournament-match/tournament-match-utils.ts b/app/features/tournament-match/tournament-match-utils.ts index 7a9e9df17..67eb65272 100644 --- a/app/features/tournament-match/tournament-match-utils.ts +++ b/app/features/tournament-match/tournament-match-utils.ts @@ -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"; diff --git a/app/features/tournament-organization/tournament-organization-constants.ts b/app/features/tournament-organization/tournament-organization-constants.ts index 415ccc208..06c995464 100644 --- a/app/features/tournament-organization/tournament-organization-constants.ts +++ b/app/features/tournament-organization/tournament-organization-constants.ts @@ -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, diff --git a/app/features/tournament-organization/tournament-organization-schemas.ts b/app/features/tournament-organization/tournament-organization-schemas.ts index f65eb7e8d..ce543bca4 100644 --- a/app/features/tournament-organization/tournament-organization-schemas.ts +++ b/app/features/tournament-organization/tournament-organization-schemas.ts @@ -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, diff --git a/app/features/tournament/TournamentAuditLogRepository.server.test.ts b/app/features/tournament/TournamentAuditLogRepository.server.test.ts index 698b70494..b73bad0d0 100644 --- a/app/features/tournament/TournamentAuditLogRepository.server.test.ts +++ b/app/features/tournament/TournamentAuditLogRepository.server.test.ts @@ -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"; diff --git a/app/features/tournament/TournamentAuditLogRepository.server.ts b/app/features/tournament/TournamentAuditLogRepository.server.ts index e96ce2079..2704308bb 100644 --- a/app/features/tournament/TournamentAuditLogRepository.server.ts +++ b/app/features/tournament/TournamentAuditLogRepository.server.ts @@ -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"; diff --git a/app/features/tournament/TournamentRepository.server.ts b/app/features/tournament/TournamentRepository.server.ts index cff891526..3c75d0f1a 100644 --- a/app/features/tournament/TournamentRepository.server.ts +++ b/app/features/tournament/TournamentRepository.server.ts @@ -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"; diff --git a/app/features/tournament/queries/updateRoundMaps.server.ts b/app/features/tournament/queries/updateRoundMaps.server.ts index 92e215579..34ed75d96 100644 --- a/app/features/tournament/queries/updateRoundMaps.server.ts +++ b/app/features/tournament/queries/updateRoundMaps.server.ts @@ -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" diff --git a/app/features/tournament/tournament-constants.ts b/app/features/tournament/tournament-constants.ts index f9a34a512..a1636da46 100644 --- a/app/features/tournament/tournament-constants.ts +++ b/app/features/tournament/tournament-constants.ts @@ -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 ? { diff --git a/app/features/tournament/tournament-utils.test.ts b/app/features/tournament/tournament-utils.test.ts index 0a9f2da7d..791fd68e1 100644 --- a/app/features/tournament/tournament-utils.test.ts +++ b/app/features/tournament/tournament-utils.test.ts @@ -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"; diff --git a/app/features/tournament/tournament-utils.ts b/app/features/tournament/tournament-utils.ts index d205e48f5..d299603e0 100644 --- a/app/features/tournament/tournament-utils.ts +++ b/app/features/tournament/tournament-utils.ts @@ -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"; diff --git a/app/features/user-card/UserCardRepository.server.ts b/app/features/user-card/UserCardRepository.server.ts index 748d4f331..fcf7f857a 100644 --- a/app/features/user-card/UserCardRepository.server.ts +++ b/app/features/user-card/UserCardRepository.server.ts @@ -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, diff --git a/app/features/user-card/actions/user-card.edit.server.ts b/app/features/user-card/actions/user-card.edit.server.ts index 0ce5cd722..8f3ce2553 100644 --- a/app/features/user-card/actions/user-card.edit.server.ts +++ b/app/features/user-card/actions/user-card.edit.server.ts @@ -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 }) => { diff --git a/app/features/user-card/components/UserCard.tsx b/app/features/user-card/components/UserCard.tsx index a48582c4a..5e6d1a172 100644 --- a/app/features/user-card/components/UserCard.tsx +++ b/app/features/user-card/components/UserCard.tsx @@ -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"; diff --git a/app/features/user-card/routes/user-card.edit.tsx b/app/features/user-card/routes/user-card.edit.tsx index 86f8c842a..5f640e230 100644 --- a/app/features/user-card/routes/user-card.edit.tsx +++ b/app/features/user-card/routes/user-card.edit.tsx @@ -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"; diff --git a/app/features/user-card/user-card-types.ts b/app/features/user-card/user-card-types.ts index 232e8b58f..2a4df76a5 100644 --- a/app/features/user-card/user-card-types.ts +++ b/app/features/user-card/user-card-types.ts @@ -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; + export interface UserCardStatXPValue { isVerified: boolean; region: XRankPlacementRegion; diff --git a/app/features/user-page/UserRepository.server.ts b/app/features/user-page/UserRepository.server.ts index c22df6323..94e6b0170 100644 --- a/app/features/user-page/UserRepository.server.ts +++ b/app/features/user-page/UserRepository.server.ts @@ -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"; diff --git a/app/features/user-page/actions/u.$identifier.builds.server.ts b/app/features/user-page/actions/u.$identifier.builds.server.ts index 85e63464e..5d9b3b5ee 100644 --- a/app/features/user-page/actions/u.$identifier.builds.server.ts +++ b/app/features/user-page/actions/u.$identifier.builds.server.ts @@ -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"; diff --git a/app/features/user-page/components/NewBuildForm.tsx b/app/features/user-page/components/NewBuildForm.tsx index 6e4684ae5..56e337f6d 100644 --- a/app/features/user-page/components/NewBuildForm.tsx +++ b/app/features/user-page/components/NewBuildForm.tsx @@ -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 { diff --git a/app/features/user-page/core/build-sorting.server.ts b/app/features/user-page/core/build-sorting.server.ts index b47880c4a..873cfd3d7 100644 --- a/app/features/user-page/core/build-sorting.server.ts +++ b/app/features/user-page/core/build-sorting.server.ts @@ -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"; diff --git a/app/features/user-page/routes/u.$identifier.builds.tsx b/app/features/user-page/routes/u.$identifier.builds.tsx index 6676b1fcf..5b5d32a62 100644 --- a/app/features/user-page/routes/u.$identifier.builds.tsx +++ b/app/features/user-page/routes/u.$identifier.builds.tsx @@ -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"; diff --git a/app/features/user-page/user-page-constants.ts b/app/features/user-page/user-page-constants.ts index 916021bc0..4d8af2e42 100644 --- a/app/features/user-page/user-page-constants.ts +++ b/app/features/user-page/user-page-constants.ts @@ -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. diff --git a/app/features/user-page/user-page-schemas.ts b/app/features/user-page/user-page-schemas.ts index 6add7f675..b810d352f 100644 --- a/app/features/user-page/user-page-schemas.ts +++ b/app/features/user-page/user-page-schemas.ts @@ -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, diff --git a/app/features/user-report/core/discord-webhook.server.ts b/app/features/user-report/core/discord-webhook.server.ts index cacc3f5d6..accbab0ab 100644 --- a/app/features/user-report/core/discord-webhook.server.ts +++ b/app/features/user-report/core/discord-webhook.server.ts @@ -1,4 +1,4 @@ -import type { UserReportCategory } from "~/db/tables"; +import type { UserReportCategory } from "~/features/user-report/user-report-constants"; import { sendModDiscordWebhook, truncateEmbedValue, diff --git a/app/features/user-report/user-report-constants.ts b/app/features/user-report/user-report-constants.ts index cb370fbdb..f974b0fb2 100644 --- a/app/features/user-report/user-report-constants.ts +++ b/app/features/user-report/user-report-constants.ts @@ -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 = { INAPPROPRIATE_CONTENT: "Inappropriate content", diff --git a/app/hooks/intl/useUserIntlPreference.ts b/app/hooks/intl/useUserIntlPreference.ts index 541dcf125..5bbd479c6 100644 --- a/app/hooks/intl/useUserIntlPreference.ts +++ b/app/hooks/intl/useUserIntlPreference.ts @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import type { UserPreferences } from "~/db/tables"; +import type { UserPreferences } from "~/db/tables-json"; import { useUser } from "~/features/auth/core/user"; import { useHydrated } from "../useHydrated"; diff --git a/app/modules/brackets-manager/test/double-elimination.test.ts b/app/modules/brackets-manager/test/double-elimination.test.ts index ab668c710..348c774bd 100644 --- a/app/modules/brackets-manager/test/double-elimination.test.ts +++ b/app/modules/brackets-manager/test/double-elimination.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, test } from "vitest"; -import { TournamentMatchStatus } from "~/db/tables"; import { InMemoryDatabase } from "~/modules/brackets-memory-db"; +import { Status } from "~/modules/brackets-model"; import { BracketsManager } from "../manager"; const storage = new InMemoryDatabase(); @@ -125,7 +125,7 @@ describe("Previous and next match update in double elimination stage", () => { expect(matchSemiLB.opponent2.id).toBe(loserId); expect(matchSemiLB.opponent2.result).toBe("win"); - expect(matchSemiLB.status).toBe(TournamentMatchStatus.Completed); + expect(matchSemiLB.status).toBe(Status.Completed); expect( storage.select("match", 4).opponent2.id, // Propagated winner in LB Final because of the BYE. @@ -136,7 +136,7 @@ describe("Previous and next match update in double elimination stage", () => { matchSemiLB = storage.select("match", 3); expect(matchSemiLB.opponent2.id).toBeNull(); expect(matchSemiLB.opponent2.result).toBeUndefined(); - expect(matchSemiLB.status).toBe(TournamentMatchStatus.Locked); + expect(matchSemiLB.status).toBe(Status.Locked); expect(storage.select("match", 4).opponent2.id).toBeNull(); // Propagated winner is removed. }); @@ -198,12 +198,8 @@ describe("Previous and next match update in double elimination stage", () => { storage.select("match", 6).opponent2.id, // Determined opponent for the grand final (round 2) ).toBe(storage.select("match", 1).opponent2.id); // Winner of LB Final - expect(storage.select("match", 5).status).toBe( - TournamentMatchStatus.Completed, - ); // Grand final (round 1) - expect(storage.select("match", 6).status).toBe( - TournamentMatchStatus.Ready, - ); // Grand final (round 2) + expect(storage.select("match", 5).status).toBe(Status.Completed); // Grand final (round 1) + expect(storage.select("match", 6).status).toBe(Status.Ready); // Grand final (round 2) manager.update.match({ id: 6, // Grand Final round 2 @@ -273,9 +269,7 @@ describe("Previous and next match update in double elimination stage", () => { manager.update.match({ id: 15, opponent1: { result: "win" } }); // LB 1.1 manager.update.match({ id: 19, opponent1: { result: "win" } }); // LB 2.1 - expect(storage.select("match", 8).status).toBe( - TournamentMatchStatus.Completed, - ); // WB 2.1 + expect(storage.select("match", 8).status).toBe(Status.Completed); // WB 2.1 }); test("should send the losers to the right LB matches in round 1", () => { diff --git a/app/modules/brackets-manager/test/single-elimination.test.ts b/app/modules/brackets-manager/test/single-elimination.test.ts index 5ee96a1e9..d31d9145d 100644 --- a/app/modules/brackets-manager/test/single-elimination.test.ts +++ b/app/modules/brackets-manager/test/single-elimination.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, test } from "vitest"; -import { TournamentMatchStatus } from "~/db/tables"; import { InMemoryDatabase } from "~/modules/brackets-memory-db"; +import { Status } from "~/modules/brackets-model"; import { BracketsManager } from "../manager"; const storage = new InMemoryDatabase(); @@ -160,12 +160,8 @@ describe("Previous and next match update", () => { expect(storage.select("match", 3).opponent2.id).toBe( storage.select("match", 1).opponent1.id, ); - expect(storage.select("match", 2).status).toBe( - TournamentMatchStatus.Ready, - ); - expect(storage.select("match", 3).status).toBe( - TournamentMatchStatus.Ready, - ); + expect(storage.select("match", 2).status).toBe(Status.Ready); + expect(storage.select("match", 3).status).toBe(Status.Ready); }); test("should play both the final and consolation final in parallel", () => { @@ -195,12 +191,8 @@ describe("Previous and next match update", () => { opponent2: { score: 9 }, }); - expect(storage.select("match", 2).status).toBe( - TournamentMatchStatus.Running, - ); - expect(storage.select("match", 3).status).toBe( - TournamentMatchStatus.Ready, - ); + expect(storage.select("match", 2).status).toBe(Status.Running); + expect(storage.select("match", 3).status).toBe(Status.Ready); manager.update.match({ id: 3, // Consolation final @@ -208,12 +200,8 @@ describe("Previous and next match update", () => { opponent2: { score: 9 }, }); - expect(storage.select("match", 2).status).toBe( - TournamentMatchStatus.Running, - ); - expect(storage.select("match", 3).status).toBe( - TournamentMatchStatus.Running, - ); + expect(storage.select("match", 2).status).toBe(Status.Running); + expect(storage.select("match", 3).status).toBe(Status.Running); manager.update.match({ id: 3, // Consolation final @@ -221,9 +209,7 @@ describe("Previous and next match update", () => { opponent2: { score: 9 }, }); - expect(storage.select("match", 2).status).toBe( - TournamentMatchStatus.Running, - ); + expect(storage.select("match", 2).status).toBe(Status.Running); manager.update.match({ id: 2, // Final diff --git a/app/modules/brackets-manager/test/update.test.ts b/app/modules/brackets-manager/test/update.test.ts index 5dce89d92..53c01fc90 100644 --- a/app/modules/brackets-manager/test/update.test.ts +++ b/app/modules/brackets-manager/test/update.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, test } from "vitest"; -import { TournamentMatchStatus } from "~/db/tables"; import { InMemoryDatabase } from "~/modules/brackets-memory-db"; +import { Status } from "~/modules/brackets-model"; import { BracketsManager } from "../manager"; const storage = new InMemoryDatabase(); @@ -22,7 +22,7 @@ describe("Update matches", () => { test("should start a match", () => { const before = storage.select("match", 0); - expect(before.status).toBe(TournamentMatchStatus.Ready); + expect(before.status).toBe(Status.Ready); manager.update.match({ id: 0, @@ -31,7 +31,7 @@ describe("Update matches", () => { }); const after = storage.select("match", 0); - expect(after.status).toBe(TournamentMatchStatus.Running); + expect(after.status).toBe(Status.Running); }); test("should update the scores for a match and set it to running", () => { @@ -42,7 +42,7 @@ describe("Update matches", () => { }); const after = storage.select("match", 0); - expect(after.status).toBe(TournamentMatchStatus.Running); + expect(after.status).toBe(Status.Running); expect(after.opponent1.score).toBe(2); // Name should stay. It shouldn't be overwritten. @@ -59,7 +59,7 @@ describe("Update matches", () => { }); const after = storage.select("match", 0); - expect(after.status).toBe(TournamentMatchStatus.Completed); + expect(after.status).toBe(Status.Completed); expect(after.opponent1.result).toBe("win"); expect(after.opponent2.result).toBe("loss"); }); @@ -78,12 +78,12 @@ describe("Update matches", () => { }); const after = storage.select("match", 0); - expect(after.status).toBe(TournamentMatchStatus.Completed); + expect(after.status).toBe(Status.Completed); expect(after.opponent1.result).toBe("loss"); expect(after.opponent2.result).toBe("win"); const nextMatch = storage.select("match", 8); - expect(nextMatch.status).toBe(TournamentMatchStatus.Waiting); + expect(nextMatch.status).toBe(Status.Waiting); expect(nextMatch.opponent1.id).toBe(2); }); @@ -93,24 +93,20 @@ describe("Update matches", () => { opponent1: { result: "win" }, }); - expect(storage.select("match", 8).status).toBe( - TournamentMatchStatus.Waiting, - ); + expect(storage.select("match", 8).status).toBe(Status.Waiting); manager.update.match({ id: 1, opponent1: { result: "win" }, }); - expect(storage.select("match", 8).status).toBe( - TournamentMatchStatus.Ready, - ); + expect(storage.select("match", 8).status).toBe(Status.Ready); }); test("should end the match by setting winner and loser", () => { manager.update.match({ id: 0, - status: TournamentMatchStatus.Running, + status: Status.Running, }); manager.update.match({ @@ -120,7 +116,7 @@ describe("Update matches", () => { }); const after = storage.select("match", 0); - expect(after.status).toBe(TournamentMatchStatus.Completed); + expect(after.status).toBe(Status.Completed); expect(after.opponent1.result).toBe("win"); expect(after.opponent2.result).toBe("loss"); }); @@ -135,7 +131,7 @@ describe("Update matches", () => { manager.reset.matchResults(0); const after = storage.select("match", 0); - expect(after.status).toBe(TournamentMatchStatus.Ready); + expect(after.status).toBe(Status.Ready); expect(after.opponent1.result).toBeFalsy(); expect(after.opponent2.result).toBeFalsy(); }); @@ -150,7 +146,7 @@ describe("Update matches", () => { manager.reset.matchResults(0); const after = storage.select("match", 0); - expect(after.status).toBe(TournamentMatchStatus.Running); + expect(after.status).toBe(Status.Running); expect(after.opponent1.result).toBeFalsy(); expect(after.opponent2.result).toBeFalsy(); }); @@ -164,7 +160,7 @@ describe("Update matches", () => { }); const after = storage.select("match", 1); - expect(after.status).toBe(TournamentMatchStatus.Running); + expect(after.status).toBe(Status.Running); expect(after.opponent1.score).toBe(1); expect(after.opponent2.score).toBeFalsy(); }); @@ -177,7 +173,7 @@ describe("Update matches", () => { }); const after = storage.select("match", 1); - expect(after.status).toBe(TournamentMatchStatus.Completed); + expect(after.status).toBe(Status.Completed); expect(after.opponent1.result).toBe("loss"); expect(after.opponent1.score).toBe(6); diff --git a/app/modules/brackets-model/storage.ts b/app/modules/brackets-model/storage.ts index 02c37ce5f..3de34149f 100644 --- a/app/modules/brackets-model/storage.ts +++ b/app/modules/brackets-model/storage.ts @@ -2,7 +2,7 @@ * Contains the types which are persisted in the chosen storage. *----------------------------------------------------------------*/ -import type { TournamentRoundMaps } from "~/db/tables"; +import type { TournamentRoundMaps } from "~/db/tables-json"; import type { StageSettings } from "./input"; import type { MatchResults } from "./other"; import type { StageType } from "./unions"; diff --git a/app/modules/in-game-lists/types.ts b/app/modules/in-game-lists/types.ts index cff1eca49..d0df91aeb 100644 --- a/app/modules/in-game-lists/types.ts +++ b/app/modules/in-game-lists/types.ts @@ -16,6 +16,8 @@ export type StageId = (typeof stageIds)[number]; export type ModeWithStage = { mode: ModeShort; stageId: StageId }; +export type GearType = "HEAD" | "CLOTHES" | "SHOES"; + export type Ability = (typeof abilities)[number]["name"]; export type AbilityWithUnknown = (typeof abilities)[number]["name"] | "UNKNOWN"; export type AbilityType = (typeof abilities)[number]["type"]; diff --git a/app/modules/permissions/mapper.server.ts b/app/modules/permissions/mapper.server.ts index 08d2f1de8..02c6dbff3 100644 --- a/app/modules/permissions/mapper.server.ts +++ b/app/modules/permissions/mapper.server.ts @@ -1,4 +1,4 @@ -import type { UserWithPlusTier } from "~/db/tables"; +import type { UserWithPlusTier } from "~/utils/kysely.server"; import { userDiscordIdIsAged } from "~/utils/users"; import type { Role } from "./types"; import { isAdmin, isDev, isStaff, isSupporter } from "./utils"; diff --git a/app/root.tsx b/app/root.tsx index 44dde0ee2..ebd6dc836 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -28,7 +28,7 @@ import { useSearchParams, } from "react-router"; import { Config } from "~/config"; -import type { CustomTheme } from "~/db/tables"; +import type { CustomTheme } from "~/db/tables-json"; import * as NotificationRepository from "~/features/notifications/NotificationRepository.server"; import { NOTIFICATIONS } from "~/features/notifications/notifications-contants"; import { resolveSidebarData } from "~/features/sidebar/core/sidebar.server"; diff --git a/app/routines/syncTournamentVods.test.ts b/app/routines/syncTournamentVods.test.ts index 4c772bef0..de1d38978 100644 --- a/app/routines/syncTournamentVods.test.ts +++ b/app/routines/syncTournamentVods.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { db, sql } from "~/db/sql"; -import type { CastedMatchesInfo } from "~/db/tables"; +import type { CastedMatchesInfo } from "~/db/tables-json"; import { dbInsertUsers, dbReset } from "~/utils/Test"; const { mockGetUsersByLogin, mockGetArchiveVideos } = vi.hoisted(() => ({ diff --git a/app/utils/kysely.server.ts b/app/utils/kysely.server.ts index 3c8a2032c..bf540c887 100644 --- a/app/utils/kysely.server.ts +++ b/app/utils/kysely.server.ts @@ -48,6 +48,11 @@ export type CommonUser = Pick< "id" | "username" | "discordId" | "discordAvatar" | "customUrl" > & { customAvatarUrl: string | null }; +/** Represents User joined with PlusTier table */ +export type UserWithPlusTier = Tables["User"] & { + plusTier: Tables["PlusTier"]["tier"] | null; +}; + const userChatNameHueRaw = sql< string | null >`IIF(COALESCE("User"."patronTier", 0) >= 2, "User"."customTheme" ->> '--_chat-h', null)`; diff --git a/app/utils/oklch-gamut.ts b/app/utils/oklch-gamut.ts index 02a54f1e5..e45e6dbcb 100644 --- a/app/utils/oklch-gamut.ts +++ b/app/utils/oklch-gamut.ts @@ -1,5 +1,5 @@ import type { z } from "zod"; -import type { CustomTheme } from "~/db/tables"; +import type { CustomTheme } from "~/db/tables-json"; import type { themeInputSchema } from "~/utils/zod"; export type ThemeInput = z.infer; diff --git a/app/utils/strings.ts b/app/utils/strings.ts index 1d511fb53..39fbbddb4 100644 --- a/app/utils/strings.ts +++ b/app/utils/strings.ts @@ -1,4 +1,4 @@ -import type { GearType } from "~/db/tables"; +import type { GearType } from "~/modules/in-game-lists/types"; import { assertUnreachable } from "./types"; export function inGameNameWithoutDiscriminator(inGameName: string) { diff --git a/app/utils/urls.ts b/app/utils/urls.ts index 4c63e4de7..51e246eda 100644 --- a/app/utils/urls.ts +++ b/app/utils/urls.ts @@ -1,6 +1,7 @@ import slugify from "slugify"; import { Config } from "~/config"; -import type { GearType, Preference, Tables } from "~/db/tables"; +import type { Tables } from "~/db/tables"; +import type { Preference } from "~/db/tables-json"; import type { ArtSource } from "~/features/art/art-types"; import type { AuthErrorCode } from "~/features/auth/core/errors"; import { serializeBuild } from "~/features/build-analyzer/core/serializer"; @@ -14,6 +15,7 @@ import type { AbilityWithUnknown, BrandId, BuildAbilitiesTupleWithUnknown, + GearType, MainWeaponId, ModeShort, ModeShortWithSpecial, diff --git a/e2e/builds.spec.ts b/e2e/builds.spec.ts index c4f5041f5..98c1c68c2 100644 --- a/e2e/builds.spec.ts +++ b/e2e/builds.spec.ts @@ -1,8 +1,8 @@ import type { Locator, Page } from "@playwright/test"; import { NZAP_TEST_DISCORD_ID, NZAP_TEST_ID } from "~/db/seed/constants"; -import type { GearType } from "~/db/tables"; import { ADMIN_DISCORD_ID } from "~/features/admin/admin-constants"; import { newBuildBaseSchema } from "~/features/user-page/user-page-schemas"; +import type { GearType } from "~/modules/in-game-lists/types"; import invariant from "~/utils/invariant"; import { BUILDS_PAGE, userBuildsPage, userNewBuildPage } from "~/utils/urls"; import {