sendou.ink/app/db/types.ts
Kalle e7bbb565be
SendouQ (#1455)
* Tables

* Clocks

* Maplist preference selector

* Fix SSR

* Nav icon

* RankedOrScrim

* Map pool

* Create group

* Redirect logic

* Persist map pool

* Advance from preparing page

* Rename query

* Fix merge

* Fix migration order

* Seed groups

* Find looking groups SQL

* Renders something

* More UI work

* Back to 30min

* Likes/dislikes

* Always return own group

* Fix like order

* 3 tc/rm/cb -> 2

* Show only 3 weapons

* Pass group size

* Handle both liked and liked by same group

* Fix SQL

* Group preference frontend work

* Morphing

* Styling

* Don't show group controls if not manager

* Give/remove manager

* Leave group

* Leave with confirm

* Delete likes when morphing groups

* Clocks consistency

* Remove bad invariant

* Persist settings to local storage

* Fix initial value flashing

* Fix never resolving loading indicator

* REFRESH_GROUP

* Flip animations

* Tweaks

* Auto refresh logic

* Groups of 4 seed

* Reduce throwing

* Load full groups initial

* Create match

* Match UI initial

* Score reporter initial

* Push footer down on match page

* Score reporter knows when set ended

* Score reporting untested

* Show score after report

* Align better

* Look again with same group functionality

* More migrations

* Team on match page

* Show confirmer before reporting score

* Report weapons

* Report weapos again by admin + skill changing

* Handle no tiebreaker given to MapPool

* Remove unranked

* Remove support for "team id skill"

* no-wrap -> nowrap

* Preparing page work

* Use common GroupCard component

* Add some metas

* MemberAdder in looking page

* Fix GroupCard actions

* Fix SZ only map list including other modes

* Add season info

* Prompt login

* Joining team

* Manage group on preparing page

* Manage group on preparing page

* Seed past matches

* Add to seed

* No map list preference when full group + fix expiry

* Fix skill matchesCount calculation

* Tiers initial work

* Some progress on tiers

* Tiering logic

* MMR in group cards

* Name to challenge

* Team MMR

* Big team rank icons

* Adjust todos

* Match score report with confirm

* Allow regular members to report score

* Handle reporting weapons edge cases

* Add tier images

* Improve GroupCard spacing

* Refactor looking page

* Looking mobile UI

* Calculate skill only for current season

* Divide groups visually when reporting weapons

* Fix match page weapons sorting

* Add cache to user skills+tier calculation

* Admin report match score

* Initial leaderboard

* Cached leaderboard

* Weapon category lb's

* Populate SkillTeamUser in SendouQ

* Team leaderboard filtered down

* Add TODOs

* Seasons initlal

* Season weapons initial

* Weapons stylized

* Show rest weapons as +

* Hide peak if same as current

* Load matches SQL initial

* Season matches UI initial

* Take user id in account

* Add weapons

* Paginated matches

* Fix pages count logic

* Scroll top on data change

* Day headers for matches

* Link from user page to user seasons page

* Summarize maps + ui initial

* Map stats

* Player info tabs

* MMR chart

* Chart adjustments

* Handle basing team MMR on player MMR

* Set initial MMR

* Add info about discord to match page

* Season support to tournaments

* Get tournament skills as well for the graph

* WIP

* New team rating logic + misc other

* tiered -> tiered.server

* Update season starting time

* TODOs

* Add rules page

* Hide elements correctly when off-season

* Fix crash when only one player with skill

* How-to video

* Fix StartRank showing when not logged in

* Make user leaderboard the default

* Make Skill season non-nullable

* Add suggested pass to match

* Add rule

* identifierToUserIds helper

* Fix tiers not showing
2023-08-12 22:42:54 +03:00

561 lines
12 KiB
TypeScript

import type { TEAM_MEMBER_ROLES } from "~/features/team";
import type {
Ability,
MainWeaponId,
ModeShort,
RankedModeShort,
StageId,
} from "~/modules/in-game-lists";
import type allTags from "../routes/calendar/tags.json";
export interface User {
id: number;
discordId: string;
/** Discord display name aka global name (non-unique) */
discordName: string;
discordDiscriminator: string;
discordAvatar: string | null;
/** Discord username (unique) */
discordUniqueName: string | null;
showDiscordUniqueName: number;
twitch: string | null;
twitter: string | null;
youtubeId: string | null;
bio: string | null;
css: string | null;
country: string | null;
customUrl: string | null;
stickSens: number | null;
motionSens: number | null;
inGameName: string | null;
patronTier: number | null;
patronSince: number | null;
/** Used to overwrite normal patron giving process and force give the patron status till this date */
patronTill: number | null;
isVideoAdder: number;
isArtist: number;
favoriteBadgeId: number | null;
commissionsOpen: number;
commissionText: string | null;
}
/** User table after joined with PlusTier table */
export interface UserWithPlusTier extends User {
plusTier: PlusTier["tier"] | null;
}
export interface UserWeapon {
userId: number;
weaponSplId: MainWeaponId;
createdAt: number;
order: number;
isFavorite: number;
}
export interface PlusSuggestion {
id: number;
text: string;
authorId: number;
suggestedId: number;
month: number;
year: number;
tier: number;
createdAt: number;
}
export interface PlusVote {
month: number;
year: number;
tier: number;
authorId: number;
votedId: number;
score: number;
validAfter: number;
}
export interface PlusVotingResult {
votedId: number;
tier: number;
score: number;
month: number;
year: number;
wasSuggested: number;
passedVoting: number;
}
export interface PlusTier {
userId: number;
tier: number;
}
export interface Badge {
id: number;
code: string;
displayName: string;
hue?: number;
}
/** View that is union of TournamentBadgeOwner and Patreon badges */
export interface BadgeOwner {
badgeId: number;
userId: number;
}
export interface TournamentBadgeOwner {
badgeId: number;
userId: number;
}
export interface BadgeManager {
badgeId: number;
userId: number;
}
export interface CalendarEvent {
id: number;
name: string;
authorId: number;
tags: string | null;
description: string | null;
discordInviteCode: string | null;
// generated column
discordUrl: string | null;
bracketUrl: string;
participantCount: number | null;
tournamentId: number | null;
}
export type CalendarEventTag = keyof typeof allTags;
export interface CalendarEventDate {
id: number;
eventId: number;
startTime: number;
}
export interface CalendarEventResultTeam {
id: number;
eventId: number;
name: string;
placement: number;
}
export interface CalendarEventResultPlayer {
teamId: number;
userId: number | null;
name: string | null;
}
export interface UserResultHighlight {
teamId: number;
userId: number;
}
export interface CalendarEventBadge {
eventId: number;
badgeId: number;
}
export interface Build {
id: number;
ownerId: number;
title: string;
/** Private builds are only visible on the user builds page to the owner only */
private: number;
description: string | null;
modes: string | null;
headGearSplId: number;
clothesGearSplId: number;
shoesGearSplId: number;
updatedAt: number;
}
export interface BuildWeapon {
buildId: number;
weaponSplId: MainWeaponId;
}
export type GearType = "HEAD" | "CLOTHES" | "SHOES";
export interface BuildAbility {
buildId: number;
gearType: "HEAD" | "CLOTHES" | "SHOES";
ability: Ability;
slotIndex: 0 | 1 | 2 | 3;
abilityPoints: number; // 3 or 10
}
export interface MapPoolMap {
calendarEventId: number | null; // Part of tournament's map pool
tournamentTeamId: number | null; // Part of team's map pool
tieBreakerCalendarEventId: number | null; // Part of the tournament's tiebreaker pool
groupId: number | null; // Part of SendouQ group's map pool
stageId: StageId;
mode: ModeShort;
}
// 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 =
| "AUTO_ALL"
| "AUTO_SZ"
| "AUTO_TC"
| "AUTO_RM"
| "AUTO_CB";
// TODO: later also e.g. RR_TO_DE where we also need an additional field
// describing how many teams advance
export type TournamentFormat = "SE" | "DE";
export interface Tournament {
id: number;
mapPickingStyle: TournamentMapPickingStyle;
format: TournamentFormat;
showMapListGenerator: number;
}
export interface TournamentTeam {
id: number;
name: string;
createdAt: number;
seed: number | null;
tournamentId: number;
inviteCode: string;
prefersNotToHost: number;
}
export interface TournamentTeamCheckIn {
tournamentTeamId: number;
checkedInAt: number;
}
export interface TournamentTeamMember {
tournamentTeamId: number;
userId: number;
isOwner: number;
createdAt: number;
}
/** A stage is an intermediate phase in a tournament.
* Supported stage types are round-robin, single elimination and double elimination. */
export interface TournamentStage {
id: number;
tournamentId: number;
name: string;
type: "round_robin" | "single_elimination" | "double_elimination";
settings: string; // json
number: number;
}
/** A group is a logical structure used to group multiple rounds together.
- In round-robin stages, a group is a pool.
- In elimination stages, a group is a bracket.
- A single elimination stage can have one or two groups:
- The unique bracket.
- If enabled, the Consolation Final.
- A double elimination stage can have two or three groups:
- Upper and lower brackets.
- If enabled, the Grand Final. */
export interface TournamentGroup {
id: number;
stageId: number;
/** In double elimination 1 = Winners, 2 = Losers, 3 = Grand Finals+Bracket Reset */
number: number;
}
/**
* A round is a logical structure used to group multiple matches together.
- In round-robin stages, a round can be viewed as a day or just as a list of matches that can be played at the same time.
- In elimination stages, a round is a round of a bracket, e.g. 8th finals, semi-finals, etc.
*/
export interface TournamentRound {
id: number;
stageId: number;
groupId: number;
number: number;
}
export enum Status {
/** 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,
/** At least one participant started their following match. */
Archived = 5,
}
/** A match between two participants (more participants are not allowed).
* Participants can be teams or individuals. */
export interface TournamentMatch {
id: number;
/** Not used */
childCount: number;
bestOf: 3 | 5 | 7;
roundId: number;
stageId: number;
groupId: number;
number: number;
opponentOne: string; // json
opponentTwo: string; // json
status: Status;
}
export interface TournamentMatchGameResult {
id: number;
matchId: number;
number: number;
stageId: StageId;
mode: ModeShort;
/** serialized TournamentMaplistSource */
source: string;
winnerTeamId: number;
reporterId: number;
createdAt: number;
}
export interface TournamentMatchGameResultParticipant {
matchGameResultId: number;
userId: number;
}
export interface TournamentSub {
userId: number;
tournamentId: number;
canVc: number;
bestWeapons: string;
okWeapons: string | null;
message: string | null;
visibility: "+1" | "+2" | "+3" | "ALL";
createdAt: number;
}
export interface Skill {
id: number;
mu: number;
sigma: number;
ordinal: number;
userId: number | null;
/** e.g. 12-24-44-1024 for "team skills" */
identifier: string | null;
matchesCount: number;
/** Tournament that caused the skill to change */
tournamentId: number | null;
/** Group match that caused the skill to change */
groupMatchId: number | null;
season: number;
}
export interface SkillTeamUser {
userId: number;
skillId: number;
}
export interface MapResult {
mode: ModeShort;
stageId: StageId;
userId: number;
wins: number;
losses: number;
season: number;
}
export interface PlayerResult {
ownerUserId: number;
otherUserId: number;
mapWins: number;
mapLosses: number;
setWins: number;
setLosses: number;
type: "MATE" | "ENEMY";
season: number;
}
export interface TournamentResult {
tournamentId: number;
userId: number;
placement: number;
participantCount: number;
tournamentTeamId: number;
isHighlight: number;
}
export interface TrustRelationship {
trustGiverUserId: number;
trustReceiverUserId: number;
}
export interface UserSubmittedImage {
id: number;
validatedAt: number | null;
url: string;
submitterUserId: number;
}
export interface Team {
id: number;
name: string;
customUrl: string;
inviteCode: string;
css: string | null;
twitter: string | null;
bio: string | null;
avatarImgId: number | null;
bannerImgId: number | null;
createdAt: number;
deletedAt: number | null;
}
export type MemberRole = (typeof TEAM_MEMBER_ROLES)[number];
export interface TeamMember {
teamId: number;
userId: number;
role: MemberRole | null;
isOwner: number;
createdAt: number;
leftAt: number | null;
}
export interface Video {
id: number;
title: string;
type: "SCRIM" | "TOURNAMENT" | "MATCHMAKING" | "CAST";
youtubeDate: number;
eventId: number | null;
youtubeId: string;
submitterUserId: number;
validatedAt: number | null;
}
export interface VideoMatch {
id: number;
videoId: number;
startsAt: number;
stageId: StageId;
mode: ModeShort;
}
export interface VideoMatchPlayer {
videoMatchId: number;
playerUserId: number | null;
playerName: string | null;
weaponSplId: MainWeaponId;
player: number;
}
export interface XRankPlacement {
id: number;
weaponSplId: MainWeaponId;
name: string;
nameDiscriminator: string;
power: number;
rank: number;
title: string;
badges: string; // badge id's separated by comma
bannerSplId: number;
playerId: number;
month: number;
year: number;
mode: RankedModeShort;
region: "WEST" | "JPN";
}
export interface SplatoonPlayer {
id: number;
userId: number;
splId: string;
}
export interface Art {
id: number;
imgId: number;
authorId: number;
isShowcase: number;
description: string | null;
createdAt: number;
}
export interface ArtTag {
id: number;
name: string;
authorId: number;
createdAt: number;
}
export interface TaggedArt {
artId: number;
tagId: number;
}
export interface ArtUserMetadata {
artId: number;
userId: number;
}
export interface Group {
id: number;
teamId: number | null;
createdAt: number;
latestActionAt: number;
mapListPreference:
| "SZ_ONLY"
| "ALL_MODES_ONLY"
| "PREFER_SZ"
| "PREFER_ALL_MODES"
| "NO_PREFERENCE";
inviteCode: string;
status: "PREPARING" | "ACTIVE" | "INACTIVE";
}
export interface GroupMember {
groupId: number;
userId: number;
role: "OWNER" | "MANAGER" | "REGULAR";
createdAt: number;
}
export interface GroupLike {
likerGroupId: number;
targetGroupId: number;
createdAt: number;
}
export interface GroupMatch {
id: number;
alphaGroupId: number;
bravoGroupId: number;
createdAt: number;
reportedAt: number | null;
reportedByUserId: number | null;
}
export interface GroupMatchMap {
id: number;
matchId: number;
index: number;
mode: ModeShort;
stageId: StageId;
/** serialized TournamentMaplistSource */
source: string;
winnerGroupId: number | null;
}
export interface ReportedWeapon {
groupMatchMapId: number | null;
weaponSplId: MainWeaponId;
userId: User["id"];
}