diff --git a/.env.example b/.env.example index 85b197214..84953e12e 100644 --- a/.env.example +++ b/.env.example @@ -7,3 +7,6 @@ DISCORD_CLIENT_ID= DISCORD_CLIENT_SECRET= COOKIE_SECRET= FRONT_PAGE_URL=http://localhost:3000/ +LANISTA_TOKEN=lanista +LANISTA_URL= +LANISTA_URL_TOKEN= \ No newline at end of file diff --git a/app/components/Avatar.tsx b/app/components/Avatar.tsx index 7f2003433..055bfcc28 100644 --- a/app/components/Avatar.tsx +++ b/app/components/Avatar.tsx @@ -3,20 +3,21 @@ import { MyCSSProperties } from "~/utils"; export function Avatar({ user, - tiny = false, + size, }: { user: { discordId: string; discordAvatar: string | null }; - tiny?: boolean; + size?: "tiny" | "mini"; }) { const style: MyCSSProperties = { - "--_avatar-size": tiny ? "2rem" : undefined, + "--_avatar-size": + size === "tiny" ? "2rem" : size === "mini" ? "1.5rem" : undefined, }; return ( -
+
{user.discordAvatar && ( diff --git a/app/components/icons/Clock.tsx b/app/components/icons/Clock.tsx new file mode 100644 index 000000000..61fa46133 --- /dev/null +++ b/app/components/icons/Clock.tsx @@ -0,0 +1,16 @@ +export function ClockIcon({ className }: { className?: string }) { + return ( + + + + ); +} diff --git a/app/components/play/DetailedPlayers.tsx b/app/components/play/DetailedPlayers.tsx new file mode 100644 index 000000000..cabced2d9 --- /dev/null +++ b/app/components/play/DetailedPlayers.tsx @@ -0,0 +1,50 @@ +import { LFGMatchLoaderData } from "~/routes/play/match.$id"; +import { Unpacked } from "~/utils"; +import { WeaponImage } from "../WeaponImage"; +import SplatnetIcon from "./SplatnetIcon"; + +export function DetailedPlayers({ + players, + bravo, +}: { + players: Unpacked< + NonNullable["detail"]>["teams"] + >["players"]; + bravo?: boolean; +}) { + return ( +
+ {players + .sort((a, b) => b.assists + b.kills - (a.assists + a.kills)) + .map((player) => ( +
+ +
+ {player.name} +
+ {player.paint}p +
+
+
+ + + +
+
+ ))} +
+ ); +} diff --git a/app/components/play/GroupMembers.tsx b/app/components/play/GroupMembers.tsx index 3832fd73c..4bd04770e 100644 --- a/app/components/play/GroupMembers.tsx +++ b/app/components/play/GroupMembers.tsx @@ -24,7 +24,10 @@ function Contents({ members }: { members: LookingLoaderDataGroup["members"] }) { {new Array(4).fill(null).map((_, i) => { return (
- + ???
); @@ -44,7 +47,7 @@ function Contents({ members }: { members: LookingLoaderDataGroup["members"] }) { rel="noopener noreferrer" className="play__card__member-link" > - + {member.discordName}{" "} {member.captain && ( diff --git a/app/components/play/MatchTeams.tsx b/app/components/play/MatchTeams.tsx new file mode 100644 index 000000000..9b015142b --- /dev/null +++ b/app/components/play/MatchTeams.tsx @@ -0,0 +1,142 @@ +import clsx from "clsx"; +import { useLoaderData } from "remix"; +import { LFGMatchLoaderData } from "~/routes/play/match.$id"; +import { weaponsInGameOrder } from "~/utils/sorters"; +import { + oldSendouInkPlayerProfile, + oldSendouInkUserProfile, +} from "~/utils/urls"; +import { Avatar } from "../Avatar"; +import { WeaponImage } from "../WeaponImage"; + +// TODO: make the whole thing one grid to align stuff better +export function MatchTeams() { + const data = useLoaderData(); + + const detailedGroups = (() => { + if (!data.mapList.some((map) => map.detail)) return; + + const result: { + [groupId: string]: { + weapons: Set; + name: string; + principalId: string; + }[]; + } = { [data.groups[0].id]: [], [data.groups[1].id]: [] }; + + for (const stage of data.mapList) { + if (typeof stage.winner !== "number") break; + + for (const team of stage.detail?.teams ?? []) { + const groupId = + data.groups[team.isWinner ? stage.winner : Number(!stage.winner)].id; + + for (const player of team.players) { + const playerObj = result[groupId].find( + (p) => p.principalId === player.principalId + ); + if (playerObj) playerObj.weapons.add(player.weapon); + else { + result[groupId].push({ + name: player.name, + principalId: player.principalId, + weapons: new Set([player.weapon]), + }); + } + } + } + } + + return result; + })(); + + return ( +
+ {data.groups.map((g, i) => { + return ( +
+ {detailedGroups ? ( + <> + {detailedGroups[g.id].map((player) => ( + +
+ + {player.name} + + + {Array.from(player.weapons) + .sort(weaponsInGameOrder) + .map((weapon) => ( + + ))} + +
+
+ ))} + + + ) : ( + g.members.map((user) => ( + +
+ + + {user.discordName} + +
+
+ )) + )} + {data.scores && ( +
+ {data.scores[i]} +
+ )} +
+ ); + })} +
+ ); +} diff --git a/app/components/play/SplatnetIcon/AutoBombRush.tsx b/app/components/play/SplatnetIcon/AutoBombRush.tsx new file mode 100644 index 000000000..27c7503ae --- /dev/null +++ b/app/components/play/SplatnetIcon/AutoBombRush.tsx @@ -0,0 +1,244 @@ +export function AutoBombRush() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/Baller.tsx b/app/components/play/SplatnetIcon/Baller.tsx new file mode 100644 index 000000000..f42983dca --- /dev/null +++ b/app/components/play/SplatnetIcon/Baller.tsx @@ -0,0 +1,38 @@ +export function Baller() { + return ( + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/BooyahBomb.tsx b/app/components/play/SplatnetIcon/BooyahBomb.tsx new file mode 100644 index 000000000..bb5223095 --- /dev/null +++ b/app/components/play/SplatnetIcon/BooyahBomb.tsx @@ -0,0 +1,83 @@ +export function BooyahBomb() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/BubbleBlower.tsx b/app/components/play/SplatnetIcon/BubbleBlower.tsx new file mode 100644 index 000000000..374b7532d --- /dev/null +++ b/app/components/play/SplatnetIcon/BubbleBlower.tsx @@ -0,0 +1,23 @@ +export function BubbleBlower() { + return ( + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/BurstBombRush.tsx b/app/components/play/SplatnetIcon/BurstBombRush.tsx new file mode 100644 index 000000000..fb92fedbd --- /dev/null +++ b/app/components/play/SplatnetIcon/BurstBombRush.tsx @@ -0,0 +1,91 @@ +export function BurstBombRush() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/ColorDefs.tsx b/app/components/play/SplatnetIcon/ColorDefs.tsx new file mode 100644 index 000000000..5a3167e8f --- /dev/null +++ b/app/components/play/SplatnetIcon/ColorDefs.tsx @@ -0,0 +1,1878 @@ +export function ColorDefs() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/CurlingBombRush.tsx b/app/components/play/SplatnetIcon/CurlingBombRush.tsx new file mode 100644 index 000000000..339a90c66 --- /dev/null +++ b/app/components/play/SplatnetIcon/CurlingBombRush.tsx @@ -0,0 +1,82 @@ +export function CurlingBombRush() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/Deaths.tsx b/app/components/play/SplatnetIcon/Deaths.tsx new file mode 100644 index 000000000..0d90abbd2 --- /dev/null +++ b/app/components/play/SplatnetIcon/Deaths.tsx @@ -0,0 +1,29 @@ +export function Deaths() { + return ( + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/InkArmor.tsx b/app/components/play/SplatnetIcon/InkArmor.tsx new file mode 100644 index 000000000..362480901 --- /dev/null +++ b/app/components/play/SplatnetIcon/InkArmor.tsx @@ -0,0 +1,27 @@ +export function InkArmor() { + return ( + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/InkStorm.tsx b/app/components/play/SplatnetIcon/InkStorm.tsx new file mode 100644 index 000000000..f33190405 --- /dev/null +++ b/app/components/play/SplatnetIcon/InkStorm.tsx @@ -0,0 +1,43 @@ +export function InkStorm() { + return ( + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/Inkjet.tsx b/app/components/play/SplatnetIcon/Inkjet.tsx new file mode 100644 index 000000000..6dda57925 --- /dev/null +++ b/app/components/play/SplatnetIcon/Inkjet.tsx @@ -0,0 +1,64 @@ +export function Inkjet() { + return ( + + + + Specials used + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/Kills.tsx b/app/components/play/SplatnetIcon/Kills.tsx new file mode 100644 index 000000000..ceded8954 --- /dev/null +++ b/app/components/play/SplatnetIcon/Kills.tsx @@ -0,0 +1,32 @@ +export function Kills() { + return ( + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/Splashdown.tsx b/app/components/play/SplatnetIcon/Splashdown.tsx new file mode 100644 index 000000000..0af7e2a80 --- /dev/null +++ b/app/components/play/SplatnetIcon/Splashdown.tsx @@ -0,0 +1,55 @@ +export function Splashdown() { + return ( + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/SplatBombRush.tsx b/app/components/play/SplatnetIcon/SplatBombRush.tsx new file mode 100644 index 000000000..9172acc81 --- /dev/null +++ b/app/components/play/SplatnetIcon/SplatBombRush.tsx @@ -0,0 +1,187 @@ +export function SplatBombRush() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/Stingray.tsx b/app/components/play/SplatnetIcon/Stingray.tsx new file mode 100644 index 000000000..553d5fd77 --- /dev/null +++ b/app/components/play/SplatnetIcon/Stingray.tsx @@ -0,0 +1,64 @@ +export function Stingray() { + return ( + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/SuctionBombRush.tsx b/app/components/play/SplatnetIcon/SuctionBombRush.tsx new file mode 100644 index 000000000..02594a91c --- /dev/null +++ b/app/components/play/SplatnetIcon/SuctionBombRush.tsx @@ -0,0 +1,184 @@ +export function SuctionBombRush() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/TentaMissiles.tsx b/app/components/play/SplatnetIcon/TentaMissiles.tsx new file mode 100644 index 000000000..b597d5dee --- /dev/null +++ b/app/components/play/SplatnetIcon/TentaMissiles.tsx @@ -0,0 +1,143 @@ +export function TentaMissiles() { + return ( + + + + Specials used + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/UltraStamp.tsx b/app/components/play/SplatnetIcon/UltraStamp.tsx new file mode 100644 index 000000000..91e458188 --- /dev/null +++ b/app/components/play/SplatnetIcon/UltraStamp.tsx @@ -0,0 +1,179 @@ +export function UltraStamp() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/components/play/SplatnetIcon/index.tsx b/app/components/play/SplatnetIcon/index.tsx new file mode 100644 index 000000000..fc50602cf --- /dev/null +++ b/app/components/play/SplatnetIcon/index.tsx @@ -0,0 +1,192 @@ +import clsx from "clsx"; +import { AutoBombRush } from "./AutoBombRush"; +import { Baller } from "./Baller"; +import { BooyahBomb } from "./BooyahBomb"; +import { BubbleBlower } from "./BubbleBlower"; +import { BurstBombRush } from "./BurstBombRush"; +import { ColorDefs } from "./ColorDefs"; +import { CurlingBombRush } from "./CurlingBombRush"; +import { Deaths } from "./Deaths"; +import { InkArmor } from "./InkArmor"; +import { Inkjet } from "./Inkjet"; +import { InkStorm } from "./InkStorm"; +import { Kills } from "./Kills"; +import { Splashdown } from "./Splashdown"; +import { SplatBombRush } from "./SplatBombRush"; +import { Stingray } from "./Stingray"; +import { SuctionBombRush } from "./SuctionBombRush"; +import { TentaMissiles } from "./TentaMissiles"; +import { UltraStamp } from "./UltraStamp"; + +const iconToId = { + "Sploosh-o-matic": Splashdown, + "Neo Sploosh-o-matic": TentaMissiles, + "Sploosh-o-matic 7": UltraStamp, + "Splattershot Jr.": InkArmor, + "Custom Splattershot Jr.": InkStorm, + "Kensa Splattershot Jr.": BubbleBlower, + "Splash-o-matic": Inkjet, + "Neo Splash-o-matic": SuctionBombRush, + "Aerospray MG": CurlingBombRush, + "Aerospray RG": Baller, + "Aerospray PG": BooyahBomb, + Splattershot: Splashdown, + "Hero Shot Replica": Splashdown, + "Tentatek Splattershot": Inkjet, + "Octo Shot Replica": Inkjet, + "Kensa Splattershot": TentaMissiles, + ".52 Gal": Baller, + ".52 Gal Deco": Stingray, + "Kensa .52 Gal": BooyahBomb, + "N-ZAP '85": InkArmor, + "N-ZAP '89": TentaMissiles, + "N-ZAP '83": InkStorm, + "Splattershot Pro": InkStorm, + "Forge Splattershot Pro": BubbleBlower, + "Kensa Splattershot Pro": BooyahBomb, + ".96 Gal": InkArmor, + ".96 Gal Deco": Splashdown, + "Jet Squelcher": TentaMissiles, + "Custom Jet Squelcher": Stingray, + "Luna Blaster": Baller, + "Luna Blaster Neo": SuctionBombRush, + "Kensa Luna Blaster": InkStorm, + Blaster: Splashdown, + "Hero Blaster Replica": Splashdown, + "Custom Blaster": Inkjet, + "Range Blaster": InkStorm, + "Custom Range Blaster": BubbleBlower, + "Grim Range Blaster": TentaMissiles, + "Clash Blaster": Stingray, + "Clash Blaster Neo": TentaMissiles, + "Rapid Blaster": SplatBombRush, + "Rapid Blaster Deco": Inkjet, + "Kensa Rapid Blaster": Baller, + "Rapid Blaster Pro": InkStorm, + "Rapid Blaster Pro Deco": InkArmor, + "L-3 Nozzlenose": Baller, + "L-3 Nozzlenose D": Inkjet, + "Kensa L-3 Nozzlenose": UltraStamp, + "H-3 Nozzlenose": TentaMissiles, + "H-3 Nozzlenose D": InkArmor, + "Cherry H-3 Nozzlenose": BubbleBlower, + Squeezer: Stingray, + "Foil Squeezer": BubbleBlower, + "Carbon Roller": InkStorm, + "Carbon Roller Deco": AutoBombRush, + "Splat Roller": Splashdown, + "Hero Roller Replica": Splashdown, + "Krak-On Splat Roller": Baller, + "Kensa Splat Roller": BubbleBlower, + "Dynamo Roller": Stingray, + "Gold Dynamo Roller": InkArmor, + "Kensa Dynamo Roller": BooyahBomb, + "Flingza Roller": SplatBombRush, + "Foil Flingza Roller": TentaMissiles, + Inkbrush: Splashdown, + "Inkbrush Nouveau": Baller, + "Permanent Inkbrush": InkArmor, + Octobrush: Inkjet, + "Herobrush Replica": Inkjet, + "Octobrush Nouveau": TentaMissiles, + "Kensa Octobrush": UltraStamp, + "Classic Squiffer": InkArmor, + "New Squiffer": Baller, + "Fresh Squiffer": Inkjet, + "Splat Charger": Stingray, + "Hero Charger Replica": Stingray, + "Firefin Splat Charger": SuctionBombRush, + "Kensa Charger": Baller, + Splatterscope: Stingray, + "Firefin Splatterscope": SuctionBombRush, + "Kensa Splatterscope": Baller, + "E-liter 4K": InkStorm, + "Custom E-liter 4K": BubbleBlower, + "E-liter 4K Scope": InkStorm, + "Custom E-liter 4K Scope": BubbleBlower, + "Bamboozler 14 Mk I": TentaMissiles, + "Bamboozler 14 Mk II": BurstBombRush, + "Bamboozler 14 Mk III": BubbleBlower, + "Goo Tuber": Splashdown, + "Custom Goo Tuber": Inkjet, + Slosher: TentaMissiles, + "Hero Slosher Replica": TentaMissiles, + "Slosher Deco": Baller, + "Soda Slosher": BurstBombRush, + "Tri-Slosher": InkArmor, + "Tri-Slosher Nouveau": InkStorm, + "Sloshing Machine": Stingray, + "Sloshing Machine Neo": SplatBombRush, + "Kensa Sloshing Machine": Splashdown, + Bloblobber: InkStorm, + "Bloblobber Deco": SuctionBombRush, + Explosher: BubbleBlower, + "Custom Explosher": Baller, + "Mini Splatling": TentaMissiles, + "Zink Mini Splatling": InkStorm, + "Kensa Mini Splatling": UltraStamp, + "Heavy Splatling": Stingray, + "Hero Splatling Replica": Stingray, + "Heavy Splatling Deco": BubbleBlower, + "Heavy Splatling Remix": BooyahBomb, + "Hydra Splatling": Splashdown, + "Custom Hydra Splatling": InkArmor, + "Ballpoint Splatling": Inkjet, + "Ballpoint Splatling Nouveau": InkStorm, + "Nautilus 47": Baller, + "Nautilus 79": Inkjet, + "Dapple Dualies": SuctionBombRush, + "Dapple Dualies Nouveau": InkStorm, + "Clear Dapple Dualies": Splashdown, + "Splat Dualies": TentaMissiles, + "Hero Dualie Replicas": TentaMissiles, + "Enperry Splat Dualies": Inkjet, + "Kensa Splat Dualies": Baller, + "Glooga Dualies": Inkjet, + "Glooga Dualies Deco": Baller, + "Kensa Glooga Dualies": InkArmor, + "Dualie Squelchers": TentaMissiles, + "Custom Dualie Squelchers": InkStorm, + "Dark Tetra Dualies": Splashdown, + "Light Tetra Dualies": AutoBombRush, + "Splat Brella": InkStorm, + "Hero Brella Replica": InkStorm, + "Sorella Brella": SplatBombRush, + "Tenta Brella": BubbleBlower, + "Tenta Sorella Brella": CurlingBombRush, + "Tenta Camo Brella": UltraStamp, + "Undercover Brella": Splashdown, + "Undercover Sorella Brella": Baller, + "Kensa Undercover Brella": InkArmor, + kills: Kills, + deaths: Deaths, +} as const; + +const SplatnetIcon = ({ + icon, + count, + smallCount, + bravo, +}: { + icon: keyof typeof iconToId; + count: number; + smallCount?: number; + bravo?: boolean; +}) => { + const Component = iconToId[icon]; + return ( +
+ +
+ x + {count} + {smallCount ? ( + ({smallCount}) + ) : null} +
+ +
+ ); +}; + +export default SplatnetIcon; diff --git a/app/core/lanista.ts b/app/core/lanista.ts new file mode 100644 index 000000000..fcadc8fa6 --- /dev/null +++ b/app/core/lanista.ts @@ -0,0 +1,60 @@ +import { Mode } from "@prisma/client"; +import { fetchTimeout } from "~/utils"; +import { playersWithResults } from "./play/playerInfos/playerInfos.server"; + +const LANISTA_REQUEST_TIMEOUT = 7000; + +/** Request Lanista to send match details to match-details endpoint. */ +export async function requestMatchDetails({ + matchId, + startTime, + endTime, + playerDiscordIds, + playedStages, +}: { + matchId: string; + startTime: Date; + endTime?: Date; + playerDiscordIds: string[]; + playedStages: { stage: string; mode: Mode }[]; +}) { + try { + if (process.env.NODE_ENV === "development") { + return; + } + if (!process.env.LANISTA_URL) { + throw new Error("process.env.LANISTA_URL not set"); + } + + // there is nothing Lanista can do for us in this case + // -> user should link and try again later manually + if (playersWithResults(playerDiscordIds).length === 0) { + return; + } + + const response = await fetchTimeout( + process.env.LANISTA_URL, + LANISTA_REQUEST_TIMEOUT, + { + body: JSON.stringify({ + maplist: playedStages, + requesterId: playersWithResults(playerDiscordIds), + startTime: startTime.toISOString(), + endTime: (endTime ?? new Date()).toISOString(), + matchId, + token: process.env.LANISTA_URL_TOKEN, + }), + method: "post", + headers: [["Content-Type", "application/json"]], + } + ); + + if (!response.ok) { + throw new Error(`error code: ${response.status}`); + } + } catch (e) { + if (e instanceof Error) { + console.error("Sending match to Lanista failed: ", e.message); + } + } +} diff --git a/app/core/play/playerInfos/playerInfos.server.ts b/app/core/play/playerInfos/playerInfos.server.ts index 4942b6393..ca7dbd42c 100644 --- a/app/core/play/playerInfos/playerInfos.server.ts +++ b/app/core/play/playerInfos/playerInfos.server.ts @@ -7,6 +7,7 @@ import rawInfos from "./data.json"; const infos = rawInfos as Partial< Record >; +const idsWithResults = new Set(Object.keys(infos)); export function addInfoFromOldSendouInk( type: "LEAGUE" | "SOLO", @@ -35,6 +36,10 @@ export function addInfoFromOldSendouInk( } } +export function playersWithResults(discordIds: string[]): string[] { + return discordIds.filter((id) => idsWithResults.has(id)); +} + export function userHasTop500Result({ discordId }: { discordId?: string }) { if (!discordId) return false; return Boolean(infos[discordId]?.peakXP); diff --git a/app/core/play/utils.ts b/app/core/play/utils.ts index cc0098a3f..1fd31cae7 100644 --- a/app/core/play/utils.ts +++ b/app/core/play/utils.ts @@ -183,6 +183,7 @@ export function otherGroupsForResponse({ discordAvatar: m.user.discordAvatar, discordId: m.user.discordId, discordName: m.user.discordName, + discordDiscriminator: m.user.discordDiscriminator, id: m.user.id, captain: m.captain, weapons: m.user.weapons, diff --git a/app/models/LFGMatch.server.ts b/app/models/LFGMatch.server.ts index fa45dd474..10c3d98f0 100644 --- a/app/models/LFGMatch.server.ts +++ b/app/models/LFGMatch.server.ts @@ -24,10 +24,12 @@ export function findById(id: string) { duration: true, teams: { select: { + id: true, isWinner: true, score: true, players: { select: { + principalId: true, assists: true, deaths: true, kills: true, @@ -37,6 +39,7 @@ export function findById(id: string) { subAbilities: true, name: true, weapon: true, + gear: true, }, }, }, diff --git a/app/routes/play/looking.tsx b/app/routes/play/looking.tsx index 426b59296..74d348b30 100644 --- a/app/routes/play/looking.tsx +++ b/app/routes/play/looking.tsx @@ -270,6 +270,7 @@ export const loader: LoaderFunction = async ({ context }) => { discordAvatar: m.user.discordAvatar, discordId: m.user.discordId, discordName: m.user.discordName, + discordDiscriminator: m.user.discordDiscriminator, id: m.user.id, captain: m.captain, weapons: m.user.weapons, diff --git a/app/routes/play/match.$id.tsx b/app/routes/play/match.$id.tsx index 5ade89e13..37dc2e5eb 100644 --- a/app/routes/play/match.$id.tsx +++ b/app/routes/play/match.$id.tsx @@ -15,14 +15,17 @@ import { } from "remix"; import invariant from "tiny-invariant"; import { z } from "zod"; -import { Avatar } from "~/components/Avatar"; import { Button } from "~/components/Button"; import { CheckmarkIcon } from "~/components/icons/Checkmark"; +import { ClockIcon } from "~/components/icons/Clock"; import { ModeImage } from "~/components/ModeImage"; +import { DetailedPlayers } from "~/components/play/DetailedPlayers"; import { MapList } from "~/components/play/MapList"; +import { MatchTeams } from "~/components/play/MatchTeams"; import { SubmitButton } from "~/components/SubmitButton"; import { DISCORD_URL, LFG_AMOUNT_OF_STAGES_TO_GENERATE } from "~/constants"; import { isAdmin } from "~/core/common/permissions"; +import { requestMatchDetails } from "~/core/lanista"; import { groupsToWinningAndLosingPlayerIds, scoresAreIdentical, @@ -43,11 +46,7 @@ import { UserLean, validate, } from "~/utils"; -import { - oldSendouInkUserProfile, - sendouQAddPlayersPage, - sendouQFrontPage, -} from "~/utils/urls"; +import { sendouQAddPlayersPage, sendouQFrontPage } from "~/utils/urls"; export const links: LinksFunction = () => { return [{ rel: "stylesheet", href: styles }]; @@ -59,13 +58,13 @@ export const meta: MetaFunction = ({ data: Nullable; }) => { return { - title: data?.isOwnMatch - ? makeTitle( - `vs. ${listToUserReadableString( + title: makeTitle( + data?.isOwnMatch + ? `vs. ${listToUserReadableString( data.groups[1].members.map((u) => u.discordName) )}` - ) - : "Match", + : "Match" + ), }; }; @@ -151,7 +150,19 @@ export const action: ActionFunction = async ({ }), groupIds: match.groups.map((g) => g.id), }); - break; + + await requestMatchDetails({ + matchId: params.id, + startTime: match.createdAt, + playedStages: match.stages + .slice(0, data.winnerIds.length) + .map(({ stage }) => ({ mode: stage.mode, stage: stage.name })), + playerDiscordIds: match.groups + .flatMap((g) => g.members) + .map((m) => m.user.discordId), + }); + + return { ok: "REPORT_SCORE" }; } case "EDIT_REPORTED_SCORE": { validate(isAdmin(user.id), "Not admin"); @@ -222,7 +233,7 @@ export const action: ActionFunction = async ({ return { ok: data._action }; }; -interface LFGMatchLoaderData { +export interface LFGMatchLoaderData { /** Can the user counterpick and report scores? */ isCaptain: boolean; isOwnMatch: boolean; @@ -350,43 +361,7 @@ export default function LFGMatchPage() {
)}
-
- {data.groups.map((g, i) => { - return ( -
- {g.members.map((user) => ( - -
- - - {user.discordName} - -
-
- ))} - {data.scores && ( -
- {data.scores[i]} -
- )} -
- ); - })} -
+
{new Date(data.createdAtTimestamp).toLocaleString("en-us", { year: "numeric", @@ -440,6 +415,32 @@ export default function LFGMatchPage() { {data.mapList .filter((stage) => typeof stage.winner === "number") .map((stage) => { + const pointsScored = stage.detail?.teams.map((t) => t.score); + const [biggerScore, smallerScore] = (pointsScored ?? []).sort( + (a, b) => b - a + ); + const secondsToDisplay = (duration: number) => { + let minutes = 0; + let seconds = duration; + + while (seconds >= 60) { + seconds -= 60; + minutes++; + } + + return `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`; + }; + const leftTeamDetails = stage.detail?.teams.find( + (t) => + (t.isWinner && stage.winner === 0) || + (!t.isWinner && stage.winner === 1) + ); + const rightTeamDetails = stage.detail?.teams.find( + (t) => + (t.isWinner && stage.winner === 1) || + (!t.isWinner && stage.winner === 0) + ); + return (
{stage.name} + {stage.detail?.duration && ( +
+ {" "} + {secondsToDisplay(stage.detail.duration)} +
+ )} + {pointsScored && ( +
+ {pointsScored.some((p) => p === 100) ? ( + "KO" + ) : ( + <> + {stage.winner === 0 ? biggerScore : smallerScore}- + {stage.winner === 1 ? biggerScore : smallerScore} + + )} +
+ )}
+ {leftTeamDetails && rightTeamDetails && ( + <> + +
+ + + )} ); })} diff --git a/app/styles/play-match.css b/app/styles/play-match.css index 734a5cde8..5c5bb36e9 100644 --- a/app/styles/play-match.css +++ b/app/styles/play-match.css @@ -47,7 +47,7 @@ font-size: var(--fonts-sm); font-weight: var(--semi-bold); gap: var(--s-2); - grid-template-columns: 4rem max-content 4rem; + grid-template-columns: 1fr max-content 1fr; margin-block-start: var(--s-4); text-align: center; } @@ -59,6 +59,48 @@ place-items: center; } +.play-match__players-spacer { + display: none; +} + +.play-match__teams-players { + display: flex; + flex-direction: column; + gap: var(--s-2); + grid-column: 1 / 4; +} + +.play-match__player-row { + display: flex; + align-items: center; + padding: var(--s-2); + background-color: var(--bg-darker); + border-radius: var(--rounded); +} + +.play-match__player-row__weapon { + width: 2rem; + margin-inline-end: var(--s-2); +} + +.play-match__player-row__name { + display: flex; + flex-direction: column; + align-items: flex-start; + line-height: 1rem; +} + +.play-match__player-row__paint { + color: var(--text-lighter); + font-size: var(--fonts-xxs); +} + +.play-match__player-row__splat-net-icons { + display: flex; + gap: var(--s-1); + margin-inline-start: auto; +} + .play-match__checkmark.left { margin-left: auto; } @@ -70,6 +112,7 @@ padding: var(--s-1); background-color: var(--bg-darker); border-radius: var(--rounded); + font-weight: var(--bold); padding-inline: var(--s-3); } @@ -77,9 +120,32 @@ width: 1.5rem; } +.play-match__stage-score { + display: flex; + align-items: center; + font-size: var(--fonts-sm); + font-weight: var(--body); +} + +.play-match__clock { + display: inline-flex; + align-items: center; + color: var(--text-lighter); + font-size: var(--fonts-xs); + font-weight: var(--body); + gap: var(--s-1); + margin-inline-start: var(--s-2); +} + +.play-match__clock > svg { + width: 18px; + margin-block-start: -1px; +} + .play-match__team-info { display: grid; - max-width: 14rem; + min-width: 16rem; + max-width: 16rem; flex: 1; gap: var(--s-4); grid-template-columns: 1fr 1fr; @@ -92,6 +158,17 @@ overflow-wrap: anywhere; } +.play-match__weapons { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: var(--s-2); +} + +.play-match__weapon-img { + width: 1.75rem; +} + .play-match__player { display: flex; flex-direction: column; @@ -100,12 +177,29 @@ font-size: var(--fonts-xs); } +.play-match__player.row { + flex-direction: row; + color: var(--text-lighter); + gap: var(--s-1); +} + +.play-match__player-list { + display: flex; + flex-wrap: wrap; + justify-content: center; + column-gap: var(--s-2); + grid-column: 1 / 3; + margin-block-start: auto; +} + .play-match__score { display: grid; + height: 2rem; border-radius: var(--rounded); font-size: var(--fonts-lg); font-weight: var(--bold); grid-column: 1 / 3; + margin-block-start: auto; place-items: center; } @@ -205,3 +299,912 @@ justify-content: center; margin-block-start: var(--s-4); } + +@media screen and (min-width: 768px) { + .play-match__players-spacer { + display: block; + } + + .play-match__teams-players { + grid-column: initial; + } +} + +/* SplatnetIcon.tsx */ + +.splatnet-icon-container { + --main-bg-color: var(--theme); + --white-or-black: white; + + display: flex; + flex-direction: column; + align-items: center; + font-size: var(--fonts-sm); + font-weight: var(--bold); +} + +.splatnet-icon-container.bravo { + --main-bg-color: var(--theme-secondary); +} + +.splatnet-icon-text { + margin-block-start: -5px; +} + +.splatnet-icon-x { + color: var(--text-lighter); + font-size: var(--fonts-xxs); +} + +.splatnet-icon-smallCount { + font-size: var(--fonts-xxs); +} + +.special-icon-svg, +.kill-icon-svg, +.killed-icon-svg { + width: 26px; + height: 26px; +} + +.kill-icon-svg .bg1, +.killed-icon-svg .bg1 { + fill: var(--main-bg-color); +} + +.kill-icon-svg .white, +.killed-icon-svg .white { + fill: var(--white-or-black); +} + +.special-icon0-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon0-svg .area { + fill: none; +} + +.special-icon0-svg .gr1 { + fill: url("#sp0-gr1"); +} + +.special-icon0-svg .gr2 { + fill: url("#sp0-gr2"); +} + +.special-icon0-svg .gr3 { + fill: url("#sp0-gr3"); +} + +.special-icon0-svg .gr4 { + fill: url("#sp0-gr4"); +} + +.special-icon0-svg .gr5 { + fill: url("#sp0-gr5"); +} + +.special-icon0-svg .gr6 { + fill: url("#sp0-gr6"); +} + +.special-icon0-svg .gr7 { + fill: url("#sp0-gr7"); +} + +.special-icon0-svg .gr8 { + fill: url("#sp0-gr8"); +} + +.special-icon0-svg .gr9 { + fill: url("#sp0-gr9"); +} + +.special-icon0-svg .gr10 { + fill: url("#sp0-gr10"); +} + +.special-icon0-svg .gr11 { + fill: url("#sp0-gr11"); +} + +.special-icon0-svg .gr12 { + fill: url("#sp0-gr12"); +} + +.special-icon1-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon1-svg .area { + fill: none; +} + +.special-icon1-svg .white { + fill: #fff; +} + +.special-icon10-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon10-svg .area { + fill: none; +} + +.special-icon10-svg .gr1 { + fill: url("#sp10-gr1"); +} + +.special-icon10-svg .gr2 { + fill: url("#sp10-gr2"); +} + +.special-icon10-svg .gr3 { + fill: url("#sp10-gr3"); +} + +.special-icon10-svg .gr4 { + fill: url("#sp10-gr4"); +} + +.special-icon11-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon11-svg .area { + fill: none; +} + +.special-icon11-svg .gr1-1 { + fill: url("#sp11-gr1-1"); +} + +.special-icon11-svg .mask1 { + mask: url("#mask1"); +} + +.special-icon11-svg .gr1-2 { + fill: url("#sp11-gr1-2"); +} + +.special-icon11-svg .gr1-3 { + fill: url("#sp11-gr1-3"); +} + +.special-icon11-svg .gr1-4 { + fill: url("#sp11-gr1-4"); +} + +.special-icon12-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon12-svg .area { + fill: none; +} + +.special-icon12-svg .gr1 { + fill: url("#sp12-gr1"); +} + +.special-icon17-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon17-svg .area { + fill: none; +} + +.special-icon17-svg .gr1 { + fill: url("#sp17-gr1"); +} + +.special-icon17-svg .gr2 { + fill: url("#sp17-gr2"); +} + +.special-icon17-svg .gr3 { + fill: url("#sp17-gr3"); +} + +.special-icon17-svg .gr4 { + fill: url("#sp17-gr4"); +} + +.special-icon17-svg .gr5 { + fill: url("#sp17-gr5"); +} + +.special-icon17-svg .gr6 { + fill: url("#sp17-gr6"); +} + +.special-icon17-svg .gr7 { + fill: url("#sp17-gr7"); +} + +.special-icon17-svg .gr8 { + fill: url("#sp17-gr8"); +} + +.special-icon17-svg .gr9 { + fill: url("#sp17-gr9"); +} + +.special-icon17-svg .gr10 { + fill: url("#sp17-gr10"); +} + +.special-icon17-svg .gr11 { + fill: url("#sp17-gr11"); +} + +.special-icon17-svg .gr12 { + fill: url("#sp17-gr12"); +} + +.special-icon17-svg .gr13 { + fill: url("#sp17-gr13"); +} + +.special-icon17-svg .gr14 { + fill: url("#sp17-gr14"); +} + +.special-icon17-svg .gr15 { + fill: url("#sp17-gr15"); +} + +.special-icon17-svg .gr16 { + fill: url("#sp17-gr16"); +} + +.special-icon17-svg .gr17 { + fill: url("#sp17-gr17"); +} + +.special-icon17-svg .mask { + mask: url("#mask17"); +} + +.special-icon18-svg .area { + fill: none; +} + +.special-icon18-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon18-svg .gr1 { + fill: url("#sp18-gr1"); +} + +.special-icon18-svg .gr2 { + fill: url("#sp18-gr2"); +} + +.special-icon18-svg .gr3 { + fill: url("#sp18-gr3"); +} + +.special-icon18-svg .gr4 { + fill: url("#sp18-gr4"); +} + +.special-icon18-svg .gr5 { + fill: url("#sp18-gr5"); +} + +.special-icon18-svg .gr6 { + fill: url("#sp18-gr6"); +} + +.special-icon18-svg .gr7 { + fill: url("#sp18-gr7"); +} + +.special-icon18-svg .gr8 { + fill: url("#sp18-gr8"); +} + +.special-icon18-svg .gr9 { + fill: url("#sp18-gr9"); +} + +.special-icon18-svg .gr10 { + fill: url("#sp18-gr10"); +} + +.special-icon18-svg .gr11 { + fill: url("#sp18-gr11"); +} + +.special-icon18-svg .gr12 { + fill: url("#sp18-gr12"); +} + +.special-icon18-svg .gr13 { + fill: url("#sp18-gr13"); +} + +.special-icon18-svg .gr14 { + fill: url("#sp18-gr14"); +} + +.special-icon18-svg .gr15 { + fill: url("#sp18-gr15"); +} + +.special-icon18-svg .gr16 { + fill: url("#sp18-gr16"); +} + +.special-icon18-svg .gr17 { + fill: url("#sp18-gr17"); +} + +.special-icon18-svg .gr18 { + fill: url("#sp18-gr18"); +} + +.special-icon18-svg .gr19 { + fill: url("#sp18-gr19"); +} + +.special-icon18-svg .gr20 { + fill: url("#sp18-gr20"); +} + +.special-icon18-svg .gr21 { + fill: url("#sp18-gr21"); +} + +.special-icon18-svg .gr22 { + fill: url("#sp18-gr22"); +} + +.special-icon18-svg .gr23 { + fill: url("#sp18-gr23"); +} + +.special-icon18-svg .gr24 { + fill: url("#sp18-gr24"); +} + +.special-icon18-svg .gr25 { + fill: url("#sp18-gr25"); +} + +.special-icon18-svg .gr26 { + fill: url("#sp18-gr26"); +} + +.special-icon2-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon2-svg .area { + fill: none; +} + +.special-icon2-svg .gr1 { + fill: url("#sp2-gr-352-9"); +} + +.special-icon2-svg .gr2 { + fill: url("#sp2-gr-18"); +} + +.special-icon2-svg .gr3 { + fill: url("#sp2-gr-18-20"); +} + +.special-icon2-svg .gr4 { + fill: url("#sp2-gr-18-2"); +} + +.special-icon2-svg .gr5 { + fill: url("#sp2-gr-18-3"); +} + +.special-icon2-svg .gr6 { + fill: url("#sp2-gr-352"); +} + +.special-icon2-svg .gr7 { + fill: url("#sp2-gr-18-4"); +} + +.special-icon2-svg .gr8 { + fill: url("#sp2-gr-18-5"); +} + +.special-icon2-svg .gr9 { + fill: url("#sp2-gr-352-2"); +} + +.special-icon2-svg .gr10 { + fill: url("#sp2-gr-18-6"); +} + +.special-icon2-svg .gr11 { + fill: url("#sp2-gr-18-7"); +} + +.special-icon2-svg .gr12 { + fill: url("#sp2-gr-352-3"); +} + +.special-icon2-svg .gr13 { + fill: url("#sp2-gr-18-8"); +} + +.special-icon2-svg .gr14 { + fill: url("#sp2-gr-18-9"); +} + +.special-icon2-svg .gr15 { + fill: url("#sp2-gr-18-10"); +} + +.special-icon2-svg .gr16 { + fill: url("#sp2-gr-352-4"); +} + +.special-icon2-svg .gr17 { + fill: url("#sp2-gr-18-11"); +} + +.special-icon2-svg .gr18 { + fill: url("#sp2-gr-18-12"); +} + +.special-icon2-svg .gr19 { + fill: url("#sp2-gr-352-5"); +} + +.special-icon2-svg .gr20 { + fill: url("#sp2-gr-18-13"); +} + +.special-icon2-svg .gr21 { + fill: url("#sp2-gr-18-14"); +} + +.special-icon2-svg .gr22 { + fill: url("#sp2-gr-352-6"); +} + +.special-icon2-svg .gr23 { + fill: url("#sp2-gr-18-15"); +} + +.special-icon2-svg .gr24 { + fill: url("#sp2-gr-18-16"); +} + +.special-icon2-svg .gr25 { + fill: url("#sp2-gr-18-17"); +} + +.special-icon2-svg .gr26 { + fill: url("#sp2-gr-352-7"); +} + +.special-icon2-svg .gr27 { + fill: url("#sp2-gr-18-18"); +} + +.special-icon2-svg .gr28 { + fill: url("#sp2-gr-18-19"); +} + +.special-icon2-svg .gr29 { + fill: url("#sp2-gr-352-8"); +} + +.special-icon2-svg .gr31 { + fill: url("#sp2-gr-18-21"); +} + +.special-icon3-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon3-svg .area { + fill: none; +} + +.special-icon3-svg .gr2 { + fill: url("#sp3-gr-18"); +} + +.special-icon3-svg .gr3 { + fill: url("#sp3-gr-18-2"); +} + +.special-icon3-svg .gr4 { + fill: url("#sp3-gr-18-3"); +} + +.special-icon3-svg .gr6 { + fill: url("#sp3-gr-18-4"); +} + +.special-icon3-svg .gr7 { + fill: url("#sp3-gr-18-5"); +} + +.special-icon3-svg .gr8 { + fill: url("#sp3-gr-18-6"); +} + +.special-icon3-svg .gr9 { + fill: url("#sp3-gr-18-7"); +} + +.special-icon3-svg .gr10 { + fill: url("#sp3-gr-18-8"); +} + +.special-icon3-svg .gr11 { + fill: url("#sp3-gr-18-9"); +} + +.special-icon4-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon4-svg .area { + fill: none; +} + +.special-icon4-svg .gr3 { + fill: url("#sp4-gr-1"); +} + +.special-icon4-svg .gr4 { + fill: url("#sp4-gr-2"); +} + +.special-icon4-svg .gr5 { + fill: url("#sp4-gr-3"); +} + +.special-icon4-svg .gr6 { + fill: url("#sp4-gr-4"); +} + +.special-icon4-svg .gr7 { + fill: url("#sp4-gr-5"); +} + +.special-icon4-svg .gr8 { + fill: url("#sp4-gr-6"); +} + +.special-icon4-svg .gr9 { + fill: url("#sp4-gr-7"); +} + +.special-icon4-svg .gr10 { + fill: url("#sp4-gr-8"); +} + +.special-icon4-svg .gr11 { + fill: url("#sp4-gr-9"); +} + +.special-icon4-svg .gr12 { + fill: url("#sp4-gr-10"); +} + +.special-icon4-svg .gr13 { + fill: url("#sp4-gr-11"); +} + +.special-icon4-svg .gr14 { + fill: url("#sp4-gr-12"); +} + +.special-icon5-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon5-svg .area { + fill: none; +} + +.special-icon5-svg .gr-2 { + fill: url("#sp5-gr-1"); +} + +.special-icon5-svg .gr-3 { + fill: url("#sp5-gr-352"); +} + +.special-icon5-svg .gr-4 { + fill: url("#sp5-gr-48"); +} + +.special-icon5-svg .gr-6 { + fill: url("#sp5-gr-2"); +} + +.special-icon5-svg .gr-7 { + fill: url("#sp5-gr-352-2"); +} + +.special-icon5-svg .gr-8 { + fill: url("#sp5-gr-48-2"); +} + +.special-icon5-svg .gr-9 { + fill: url("#sp5-gr-3"); +} + +.special-icon5-svg .gr-10 { + fill: url("#sp5-gr-352-3"); +} + +.special-icon5-svg .gr-11 { + fill: url("#sp5-gr-48-3"); +} + +.special-icon6-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon6-svg .area { + fill: none; +} + +.special-icon6-svg .gr1 { + fill: url("#sp6-gr1"); +} + +.special-icon6-svg .gr2 { + fill: url("#sp6-gr2"); +} + +.special-icon6-svg .gr3 { + fill: url("#sp6-gr3"); +} + +.special-icon6-svg .gr4 { + fill: url("#sp6-gr4"); +} + +.special-icon6-svg .gr5 { + fill: url("#sp6-gr5"); +} + +.special-icon6-svg .gr6 { + fill: url("#sp6-gr6"); +} + +.special-icon6-svg .gr7 { + fill: url("#sp6-gr7"); +} + +.special-icon6-svg .gr8 { + fill: url("#sp6-gr8"); +} + +.special-icon6-svg .gr9 { + fill: url("#sp6-gr9"); +} + +.special-icon6-svg .gr10 { + fill: url("#sp6-gr10"); +} + +.special-icon6-svg .gr11 { + fill: url("#sp6-gr11"); +} + +.special-icon6-svg .gr12 { + fill: url("#sp6-gr12"); +} + +.special-icon6-svg .gr13 { + fill: url("#sp6-gr13"); +} + +.special-icon6-svg .gr14 { + fill: url("#sp6-gr14"); +} + +.special-icon6-svg .gr15 { + fill: url("#sp6-gr15"); +} + +.special-icon6-svg .gr16 { + fill: url("#sp6-gr16"); +} + +.special-icon6-svg .gr17 { + fill: url("#sp6-gr17"); +} + +.special-icon6-svg .gr18 { + fill: url("#sp6-gr18"); +} + +.special-icon6-svg .gr19 { + fill: url("#sp6-gr19"); +} + +.special-icon6-svg .gr20 { + fill: url("#sp6-gr20"); +} + +.special-icon6-svg .gr21 { + fill: url("#sp6-gr21"); +} + +.special-icon6-svg .gr22 { + fill: url("#sp6-gr22"); +} + +.special-icon6-svg .gr23 { + fill: url("#sp6-gr23"); +} + +.special-icon6-svg .gr24 { + fill: url("#sp6-gr24"); +} + +.special-icon6-svg .gr25 { + fill: url("#sp6-gr25"); +} + +.special-icon6-svg .gr26 { + fill: url("#sp6-gr26"); +} + +.special-icon6-svg .gr27 { + fill: url("#sp6-gr27"); +} + +.special-icon6-svg .gr28 { + fill: url("#sp6-gr28"); +} + +.special-icon6-svg .gr29 { + fill: url("#sp6-gr29"); +} + +.special-icon6-svg .gr30 { + fill: url("#sp6-gr30"); +} + +.special-icon7-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon7-svg .area { + fill: none; +} + +.special-icon7-svg .gr1 { + fill: url("#sp7-gr1"); +} + +.special-icon7-svg .gr2 { + fill: url("#sp7-gr2"); +} + +.special-icon7-svg .gr3 { + fill: url("#sp7-gr3"); +} + +.special-icon7-svg .gr4 { + fill: url("#sp7-gr4"); +} + +.special-icon7-svg .gr5 { + fill: url("#sp7-gr5"); +} + +.special-icon7-svg .gr6 { + fill: url("#sp7-gr6"); +} + +.special-icon7-svg .gr7 { + fill: url("#sp7-gr7"); +} + +.special-icon7-svg .gr8 { + fill: url("#sp7-gr8"); +} + +.special-icon7-svg .gr9 { + fill: url("#sp7-gr9"); +} + +.special-icon8-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon8-svg .area { + fill: none; +} + +.special-icon8-svg .gr1 { + fill: url("#sp8-gr1"); +} + +.special-icon8-svg .gr2 { + fill: url("#sp8-gr2"); +} + +.special-icon8-svg .gr3 { + fill: url("#sp8-gr3"); +} + +.special-icon8-svg .gr4 { + fill: url("#sp8-gr4"); +} + +.special-icon8-svg .gr6 { + fill: url("#sp8-gr6"); +} + +.special-icon8-svg .gr7 { + fill: url("#sp8-gr7"); +} + +.special-icon8-svg .gr8 { + fill: url("#sp8-gr8"); +} + +.special-icon8-svg .gr9 { + fill: url("#sp8-gr9"); +} + +.special-icon8-svg .gr10 { + fill: url("#sp8-gr10"); +} + +.special-icon8-svg .mask { + mask: url("#mask"); +} + +.special-icon9-svg .bg1 { + fill: var(--main-bg-color); +} + +.special-icon9-svg .area { + fill: none; +} + +.special-icon9-svg .gr1 { + fill: url("#sp9-gr1"); +} + +.special-icon9-svg .gr2 { + fill: url("#sp9-gr2"); +} + +.special-icon9-svg .gr3 { + fill: url("#sp9-gr3"); +} + +.special-icon9-svg .gr4 { + fill: url("#sp9-gr4"); +} + +.special-icon9-svg .gr5 { + fill: url("#sp9-gr5"); +} + +.special-icon9-svg .gr6 { + fill: url("#sp9-gr6"); +} + +.special-icon9-svg .gr7 { + fill: url("#sp9-gr7"); +} diff --git a/app/utils/index.ts b/app/utils/index.ts index fa3d759c2..3e462f992 100644 --- a/app/utils/index.ts +++ b/app/utils/index.ts @@ -5,6 +5,17 @@ import type { EventTargetRecorder } from "server/events"; import { z } from "zod"; import { LoggedInUserSchema } from "~/utils/schemas"; +export function flipObject< + K extends string | number, + T extends string | number +>(obj: Record): Record { + const result = Object.fromEntries( + Object.entries(obj).map((a) => a.reverse()) + ) as Record; + + return result; +} + export function makeTitle(title?: string | string[]) { if (!title) return "sendou.ink"; if (typeof title === "string") return `${title} | sendou.ink`; @@ -44,6 +55,17 @@ export function requireEvents(ctx: unknown) { } } +// https://stackoverflow.com/a/57888548 +export function fetchTimeout(url: string, ms: number, options: RequestInit) { + const controller = new AbortController(); + const promise = fetch(url, { signal: controller.signal, ...options }); + if (options.signal) { + options.signal.addEventListener("abort", () => controller.abort()); + } + const timeout = setTimeout(() => controller.abort(), ms); + return promise.finally(() => clearTimeout(timeout)); +} + /** Asserts condition is truthy. Throws a new `Response` with status code 400 and given message if falsy. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- same format as TS docs: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions export function validate(condition: any, message: string): asserts condition { @@ -181,6 +203,7 @@ export interface MyCSSProperties extends CSSProperties { export interface UserLean { id: string; discordId: string; + discordDiscriminator: string; discordAvatar: string | null; discordName: string; } diff --git a/app/utils/sorters.ts b/app/utils/sorters.ts new file mode 100644 index 000000000..10155d53d --- /dev/null +++ b/app/utils/sorters.ts @@ -0,0 +1,6 @@ +import { weapons } from "~/constants"; + +export function weaponsInGameOrder(a: string, b: string) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any + return weapons.indexOf(a as any) - weapons.indexOf(b as any); +} diff --git a/app/utils/urls.ts b/app/utils/urls.ts index 933694a37..c2f5f7d89 100644 --- a/app/utils/urls.ts +++ b/app/utils/urls.ts @@ -2,6 +2,14 @@ export function oldSendouInkUserProfile({ discordId }: { discordId: string }) { return `https://sendou.ink/u/${discordId}`; } +export function oldSendouInkPlayerProfile({ + principalId, +}: { + principalId: string; +}) { + return `https://sendou.ink/pid/${principalId}`; +} + export function sendouQFrontPage() { return "/play"; } diff --git a/package-lock.json b/package-lock.json index 6fbe511cb..d74a6f9c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,11 @@ "version": "3.0.0", "hasInstallScript": true, "dependencies": { - "@dnd-kit/core": "^5.0.1", - "@dnd-kit/sortable": "^6.0.0", + "@dnd-kit/core": "^5.0.2", + "@dnd-kit/sortable": "^6.0.1", "@dnd-kit/utilities": "^3.1.0", "@headlessui/react": "^1.5.0", - "@popperjs/core": "^2.11.2", + "@popperjs/core": "^2.11.3", "@prisma/client": "^3.10.0", "@remix-run/express": "^1.2.3", "@remix-run/react": "^1.2.3", @@ -47,13 +47,13 @@ "@types/morgan": "^1.9.3", "@types/passport": "^1.0.7", "@types/passport-discord": "^0.1.5", - "@types/react": "^17.0.39", + "@types/react": "^17.0.40", "@types/react-dom": "^17.0.13", "@types/uuid": "^8.3.4", - "@typescript-eslint/eslint-plugin": "^5.13.0", - "@typescript-eslint/parser": "^5.13.0", + "@typescript-eslint/eslint-plugin": "^5.14.0", + "@typescript-eslint/parser": "^5.14.0", "cypress": "^9.5.1", - "eslint": "^8.10.0", + "eslint": "^8.11.0", "eslint-plugin-react": "^7.29.3", "nodemon": "^2.0.15", "npm-run-all": "^4.1.5", @@ -64,7 +64,7 @@ "stylelint-config-prettier": "^9.0.3", "stylelint-config-standard": "^25.0.0", "stylelint-order": "^5.0.0", - "ts-node": "^10.6.0", + "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", "tsm": "^2.2.1", "typescript": "^4.6.2", @@ -222,9 +222,9 @@ "license": "0BSD" }, "node_modules/@dnd-kit/core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-5.0.1.tgz", - "integrity": "sha512-h02GyHb2KqbpqbELA9QW5WVLdnAQODgZs8Nv15PTg/VpEpQxEkLPn0QHqiMSSOOFBx0qJOowwYh6ggfL1ZIJPQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-5.0.2.tgz", + "integrity": "sha512-RMVm4zCEMeanVKAvBY9hA8B3x6nG0+qVH4q/TNHiFDosGU7xpElXeXplkLHgKQMqG9riOnzEoQyTasBVvl/1eA==", "dependencies": { "@dnd-kit/accessibility": "^3.0.0", "@dnd-kit/utilities": "^3.1.0", @@ -240,15 +240,15 @@ "license": "0BSD" }, "node_modules/@dnd-kit/sortable": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-6.0.0.tgz", - "integrity": "sha512-Kf0HO/rbFpd7HxXGEvxXtxBgPI6+IQuxRXDdFz6XKLhylxuDMEomi1netrhS+253KrOZ68V+sjqOmfE2RreLeQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-6.0.1.tgz", + "integrity": "sha512-FGpRHBcflpwWpSP8bMJ4zNcDywDXssZDJBwGYuHd1RqUU/+JX43pEU0u0OHw06LabEZMOLBbyWoIaZJ0abCOEA==", "dependencies": { "@dnd-kit/utilities": "^3.1.0", "tslib": "^2.0.0" }, "peerDependencies": { - "@dnd-kit/core": "^5.0.0", + "@dnd-kit/core": "^5.0.2", "react": ">=16.8.0" } }, @@ -298,16 +298,16 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.0.tgz", - "integrity": "sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.1", "globals": "^13.9.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.0.4", @@ -334,15 +334,6 @@ } } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/@eslint/eslintrc/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -466,9 +457,10 @@ } }, "node_modules/@popperjs/core": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", - "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==", + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.3.tgz", + "integrity": "sha512-8U7hIl7+30XbIrJ0deQMXpXESM1L4yrt6BHok5hzcR0LivivuNkk+tHU1iRVScOwCmQcrOr6kvtIr29MNbQHqQ==", + "hasInstallScript": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -933,9 +925,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "17.0.39", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz", - "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==", + "version": "17.0.40", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz", + "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -1006,14 +998,14 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.13.0.tgz", - "integrity": "sha512-vLktb2Uec81fxm/cfz2Hd6QaWOs8qdmVAZXLdOBX6JFJDhf6oDZpMzZ4/LZ6SFM/5DgDcxIMIvy3F+O9yZBuiQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.14.0.tgz", + "integrity": "sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.13.0", - "@typescript-eslint/type-utils": "5.13.0", - "@typescript-eslint/utils": "5.13.0", + "@typescript-eslint/scope-manager": "5.14.0", + "@typescript-eslint/type-utils": "5.14.0", + "@typescript-eslint/utils": "5.14.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1062,14 +1054,14 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.13.0.tgz", - "integrity": "sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.14.0.tgz", + "integrity": "sha512-aHJN8/FuIy1Zvqk4U/gcO/fxeMKyoSv/rS46UXMXOJKVsLQ+iYPuXNbpbH7cBLcpSbmyyFbwrniLx5+kutu1pw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.13.0", - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/typescript-estree": "5.13.0", + "@typescript-eslint/scope-manager": "5.14.0", + "@typescript-eslint/types": "5.14.0", + "@typescript-eslint/typescript-estree": "5.14.0", "debug": "^4.3.2" }, "engines": { @@ -1112,13 +1104,13 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz", - "integrity": "sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz", + "integrity": "sha512-LazdcMlGnv+xUc5R4qIlqH0OWARyl2kaP8pVCS39qSL3Pd1F7mI10DbdXeARcE62sVQE4fHNvEqMWsypWO+yEw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/visitor-keys": "5.13.0" + "@typescript-eslint/types": "5.14.0", + "@typescript-eslint/visitor-keys": "5.14.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1129,12 +1121,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.13.0.tgz", - "integrity": "sha512-/nz7qFizaBM1SuqAKb7GLkcNn2buRdDgZraXlkhz+vUGiN1NZ9LzkA595tHHeduAiS2MsHqMNhE2zNzGdw43Yg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz", + "integrity": "sha512-d4PTJxsqaUpv8iERTDSQBKUCV7Q5yyXjqXUl3XF7Sd9ogNLuKLkxz82qxokqQ4jXdTPZudWpmNtr/JjbbvUixw==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.13.0", + "@typescript-eslint/utils": "5.14.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -1178,9 +1170,9 @@ "dev": true }, "node_modules/@typescript-eslint/types": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.13.0.tgz", - "integrity": "sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.14.0.tgz", + "integrity": "sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1191,13 +1183,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz", - "integrity": "sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz", + "integrity": "sha512-QGnxvROrCVtLQ1724GLTHBTR0lZVu13izOp9njRvMkCBgWX26PKvmMP8k82nmXBRD3DQcFFq2oj3cKDwr0FaUA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/visitor-keys": "5.13.0", + "@typescript-eslint/types": "5.14.0", + "@typescript-eslint/visitor-keys": "5.14.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1241,15 +1233,15 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.13.0.tgz", - "integrity": "sha512-+9oHlPWYNl6AwwoEt5TQryEHwiKRVjz7Vk6kaBeD3/kwHE5YqTGHtm/JZY8Bo9ITOeKutFaXnBlMgSATMJALUQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.14.0.tgz", + "integrity": "sha512-EHwlII5mvUA0UsKYnVzySb/5EE/t03duUTweVy8Zqt3UQXBrpEVY144OTceFKaOe4xQXZJrkptCf7PjEBeGK4w==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.13.0", - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/typescript-estree": "5.13.0", + "@typescript-eslint/scope-manager": "5.14.0", + "@typescript-eslint/types": "5.14.0", + "@typescript-eslint/typescript-estree": "5.14.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -1265,12 +1257,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz", - "integrity": "sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.14.0.tgz", + "integrity": "sha512-yL0XxfzR94UEkjBqyymMLgCBdojzEuy/eim7N9/RIcTNxpJudAcqsU8eRyfzBbcEzGoPWfdM3AGak3cN08WOIw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.13.0", + "@typescript-eslint/types": "5.14.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -3530,12 +3522,12 @@ } }, "node_modules/eslint": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.10.0.tgz", - "integrity": "sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", + "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.2.0", + "@eslint/eslintrc": "^1.2.1", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -9798,9 +9790,9 @@ } }, "node_modules/ts-node": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.6.0.tgz", - "integrity": "sha512-CJen6+dfOXolxudBQXnVjRVvYTmTWbyz7cn+xq2XTsvnaXbHqr4gXSCNbS2Jj8yTZMuGwUoBESLaOkLascVVvg==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.7.0", @@ -9820,6 +9812,7 @@ "bin": { "ts-node": "dist/bin.js", "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" @@ -10892,9 +10885,9 @@ } }, "@dnd-kit/core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-5.0.1.tgz", - "integrity": "sha512-h02GyHb2KqbpqbELA9QW5WVLdnAQODgZs8Nv15PTg/VpEpQxEkLPn0QHqiMSSOOFBx0qJOowwYh6ggfL1ZIJPQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-5.0.2.tgz", + "integrity": "sha512-RMVm4zCEMeanVKAvBY9hA8B3x6nG0+qVH4q/TNHiFDosGU7xpElXeXplkLHgKQMqG9riOnzEoQyTasBVvl/1eA==", "requires": { "@dnd-kit/accessibility": "^3.0.0", "@dnd-kit/utilities": "^3.1.0", @@ -10907,9 +10900,9 @@ } }, "@dnd-kit/sortable": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-6.0.0.tgz", - "integrity": "sha512-Kf0HO/rbFpd7HxXGEvxXtxBgPI6+IQuxRXDdFz6XKLhylxuDMEomi1netrhS+253KrOZ68V+sjqOmfE2RreLeQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-6.0.1.tgz", + "integrity": "sha512-FGpRHBcflpwWpSP8bMJ4zNcDywDXssZDJBwGYuHd1RqUU/+JX43pEU0u0OHw06LabEZMOLBbyWoIaZJ0abCOEA==", "requires": { "@dnd-kit/utilities": "^3.1.0", "tslib": "^2.0.0" @@ -10954,16 +10947,16 @@ } }, "@eslint/eslintrc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.0.tgz", - "integrity": "sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.1", "globals": "^13.9.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.0.4", @@ -10979,12 +10972,6 @@ "ms": "2.1.2" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -11080,9 +11067,9 @@ } }, "@popperjs/core": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", - "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==" + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.3.tgz", + "integrity": "sha512-8U7hIl7+30XbIrJ0deQMXpXESM1L4yrt6BHok5hzcR0LivivuNkk+tHU1iRVScOwCmQcrOr6kvtIr29MNbQHqQ==" }, "@prisma/client": { "version": "3.10.0", @@ -11466,9 +11453,9 @@ "dev": true }, "@types/react": { - "version": "17.0.39", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz", - "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==", + "version": "17.0.40", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz", + "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==", "dev": true, "requires": { "@types/prop-types": "*", @@ -11533,14 +11520,14 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.13.0.tgz", - "integrity": "sha512-vLktb2Uec81fxm/cfz2Hd6QaWOs8qdmVAZXLdOBX6JFJDhf6oDZpMzZ4/LZ6SFM/5DgDcxIMIvy3F+O9yZBuiQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.14.0.tgz", + "integrity": "sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.13.0", - "@typescript-eslint/type-utils": "5.13.0", - "@typescript-eslint/utils": "5.13.0", + "@typescript-eslint/scope-manager": "5.14.0", + "@typescript-eslint/type-utils": "5.14.0", + "@typescript-eslint/utils": "5.14.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -11567,14 +11554,14 @@ } }, "@typescript-eslint/parser": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.13.0.tgz", - "integrity": "sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.14.0.tgz", + "integrity": "sha512-aHJN8/FuIy1Zvqk4U/gcO/fxeMKyoSv/rS46UXMXOJKVsLQ+iYPuXNbpbH7cBLcpSbmyyFbwrniLx5+kutu1pw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.13.0", - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/typescript-estree": "5.13.0", + "@typescript-eslint/scope-manager": "5.14.0", + "@typescript-eslint/types": "5.14.0", + "@typescript-eslint/typescript-estree": "5.14.0", "debug": "^4.3.2" }, "dependencies": { @@ -11596,22 +11583,22 @@ } }, "@typescript-eslint/scope-manager": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz", - "integrity": "sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz", + "integrity": "sha512-LazdcMlGnv+xUc5R4qIlqH0OWARyl2kaP8pVCS39qSL3Pd1F7mI10DbdXeARcE62sVQE4fHNvEqMWsypWO+yEw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/visitor-keys": "5.13.0" + "@typescript-eslint/types": "5.14.0", + "@typescript-eslint/visitor-keys": "5.14.0" } }, "@typescript-eslint/type-utils": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.13.0.tgz", - "integrity": "sha512-/nz7qFizaBM1SuqAKb7GLkcNn2buRdDgZraXlkhz+vUGiN1NZ9LzkA595tHHeduAiS2MsHqMNhE2zNzGdw43Yg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz", + "integrity": "sha512-d4PTJxsqaUpv8iERTDSQBKUCV7Q5yyXjqXUl3XF7Sd9ogNLuKLkxz82qxokqQ4jXdTPZudWpmNtr/JjbbvUixw==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.13.0", + "@typescript-eslint/utils": "5.14.0", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -11634,19 +11621,19 @@ } }, "@typescript-eslint/types": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.13.0.tgz", - "integrity": "sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.14.0.tgz", + "integrity": "sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz", - "integrity": "sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz", + "integrity": "sha512-QGnxvROrCVtLQ1724GLTHBTR0lZVu13izOp9njRvMkCBgWX26PKvmMP8k82nmXBRD3DQcFFq2oj3cKDwr0FaUA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/visitor-keys": "5.13.0", + "@typescript-eslint/types": "5.14.0", + "@typescript-eslint/visitor-keys": "5.14.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -11672,26 +11659,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.13.0.tgz", - "integrity": "sha512-+9oHlPWYNl6AwwoEt5TQryEHwiKRVjz7Vk6kaBeD3/kwHE5YqTGHtm/JZY8Bo9ITOeKutFaXnBlMgSATMJALUQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.14.0.tgz", + "integrity": "sha512-EHwlII5mvUA0UsKYnVzySb/5EE/t03duUTweVy8Zqt3UQXBrpEVY144OTceFKaOe4xQXZJrkptCf7PjEBeGK4w==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.13.0", - "@typescript-eslint/types": "5.13.0", - "@typescript-eslint/typescript-estree": "5.13.0", + "@typescript-eslint/scope-manager": "5.14.0", + "@typescript-eslint/types": "5.14.0", + "@typescript-eslint/typescript-estree": "5.14.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz", - "integrity": "sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.14.0.tgz", + "integrity": "sha512-yL0XxfzR94UEkjBqyymMLgCBdojzEuy/eim7N9/RIcTNxpJudAcqsU8eRyfzBbcEzGoPWfdM3AGak3cN08WOIw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.13.0", + "@typescript-eslint/types": "5.14.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -13117,12 +13104,12 @@ "dev": true }, "eslint": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.10.0.tgz", - "integrity": "sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", + "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.2.0", + "@eslint/eslintrc": "^1.2.1", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -17225,9 +17212,9 @@ "integrity": "sha512-DdMwQc1bgzRwbKQ3VoiV3aK2isz7CJhOmtO2oA+mp9soGpbN/p2pkBaIrZnJBW3jB293CUARLFmNktcTSHjypQ==" }, "ts-node": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.6.0.tgz", - "integrity": "sha512-CJen6+dfOXolxudBQXnVjRVvYTmTWbyz7cn+xq2XTsvnaXbHqr4gXSCNbS2Jj8yTZMuGwUoBESLaOkLascVVvg==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.7.0", diff --git a/package.json b/package.json index 388914a7a..5d5281cf3 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,11 @@ "tests": "npm run lint:styles && npm run lint:ts && npm run prettier:check && npm run typecheck" }, "dependencies": { - "@dnd-kit/core": "^5.0.1", - "@dnd-kit/sortable": "^6.0.0", + "@dnd-kit/core": "^5.0.2", + "@dnd-kit/sortable": "^6.0.1", "@dnd-kit/utilities": "^3.1.0", "@headlessui/react": "^1.5.0", - "@popperjs/core": "^2.11.2", + "@popperjs/core": "^2.11.3", "@prisma/client": "^3.10.0", "@remix-run/express": "^1.2.3", "@remix-run/react": "^1.2.3", @@ -66,13 +66,13 @@ "@types/morgan": "^1.9.3", "@types/passport": "^1.0.7", "@types/passport-discord": "^0.1.5", - "@types/react": "^17.0.39", + "@types/react": "^17.0.40", "@types/react-dom": "^17.0.13", "@types/uuid": "^8.3.4", - "@typescript-eslint/eslint-plugin": "^5.13.0", - "@typescript-eslint/parser": "^5.13.0", + "@typescript-eslint/eslint-plugin": "^5.14.0", + "@typescript-eslint/parser": "^5.14.0", "cypress": "^9.5.1", - "eslint": "^8.10.0", + "eslint": "^8.11.0", "eslint-plugin-react": "^7.29.3", "nodemon": "^2.0.15", "npm-run-all": "^4.1.5", @@ -83,7 +83,7 @@ "stylelint-config-prettier": "^9.0.3", "stylelint-config-standard": "^25.0.0", "stylelint-order": "^5.0.0", - "ts-node": "^10.6.0", + "ts-node": "^10.7.0", "tsconfig-paths": "^3.13.0", "tsm": "^2.2.1", "typescript": "^4.6.2",