diff --git a/client/src/app/pages/dashboard/games/_game/_playtype/GPTSongsPage.tsx b/client/src/app/pages/dashboard/games/_game/_playtype/GPTSongsPage.tsx index 8b48d2bf1..66c6550e9 100644 --- a/client/src/app/pages/dashboard/games/_game/_playtype/GPTSongsPage.tsx +++ b/client/src/app/pages/dashboard/games/_game/_playtype/GPTSongsPage.tsx @@ -82,7 +82,8 @@ function SearchSongsTable({ game, playtype, search }: { search: string } & GameP <> {search === "" && (
- Displaying the most played charts for {FormatGame(game, playtype)} +

Displaying the most played charts for {FormatGame(game, playtype)}

+
)} { const res = await APIFetchV1<{ - pbs: PBScoreDocument<"iidx:SP">[]; - charts: ChartDocument<"iidx:SP">[]; - songs: SongDocument<"iidx">[]; + pbs: PBScoreDocument[]; + charts: ChartDocument[]; + songs: SongDocument[]; }>(url); if (!res.success) { diff --git a/client/src/app/pages/dashboard/users/games/_game/_playtype/tools/ToolsPage.tsx b/client/src/app/pages/dashboard/users/games/_game/_playtype/utils/UGPTUtilsPage.tsx similarity index 56% rename from client/src/app/pages/dashboard/users/games/_game/_playtype/tools/ToolsPage.tsx rename to client/src/app/pages/dashboard/users/games/_game/_playtype/utils/UGPTUtilsPage.tsx index db0a0ab17..98f8d33c6 100644 --- a/client/src/app/pages/dashboard/users/games/_game/_playtype/tools/ToolsPage.tsx +++ b/client/src/app/pages/dashboard/users/games/_game/_playtype/utils/UGPTUtilsPage.tsx @@ -1,47 +1,67 @@ import useSetSubheader from "components/layout/header/useSetSubheader"; import Card from "components/layout/page/Card"; -import { GetGPTTools } from "components/tools/Tools"; +import { GetGPTUtils, GetGPTUtilsName } from "components/gpt-utils/GPTUtils"; import Divider from "components/util/Divider"; import LinkButton from "components/util/LinkButton"; -import React from "react"; +import { UserContext } from "context/UserContext"; +import React, { useContext } from "react"; import { Col, Row } from "react-bootstrap"; import { Link, Route, Switch } from "react-router-dom"; import { FormatGame, GetGameConfig } from "tachi-common"; import { UGPT } from "types/react"; +import { GPTUtility } from "types/ugpt"; -export default function ToolsPage({ reqUser, game, playtype }: UGPT) { +export default function UGPTUtilsPage({ reqUser, game, playtype }: UGPT) { const gameConfig = GetGameConfig(game); + const { user } = useContext(UserContext); + + const isViewingOwnProfile = user?.id === reqUser.id; + + const utils = GetGPTUtils(game, playtype); + const pageName = GetGPTUtilsName(game, playtype, isViewingOwnProfile); + useSetSubheader( - ["Users", reqUser.username, "Games", gameConfig.name, playtype, "Tools"], + ["Users", reqUser.username, "Games", gameConfig.name, playtype, pageName ?? "Utils"], [reqUser, game, playtype], - `${reqUser.username}'s ${FormatGame(game, playtype)} Tools` + `${reqUser.username}'s ${FormatGame(game, playtype)} ${pageName ?? "Utils"}` ); - const tools = GetGPTTools(game, playtype); - - if (tools.length === 0) { - return ( -
- {FormatGame(game, playtype)} has no tools. How'd you get here! -
- ); - } - return ( - {tools.map((tool) => ( + + {utils.map((util) => ( + + + + View + + + } + > + {util.description} + + + ))} + + + {utils.map((tool) => ( < Back to all tools... @@ -55,25 +75,6 @@ export default function ToolsPage({ reqUser, game, playtype }: UGPT) { {tool.component({ reqUser, game, playtype })} ))} - - - {tools.map((tool) => ( - - - - Use Tool - - - } - > - {tool.description} - - - ))} - ); diff --git a/client/src/app/routes/UserRoutes.tsx b/client/src/app/routes/UserRoutes.tsx index 6db0c6657..b6141db0f 100644 --- a/client/src/app/routes/UserRoutes.tsx +++ b/client/src/app/routes/UserRoutes.tsx @@ -1,19 +1,20 @@ import { APIFetchV1, APIFetchV1Return, ToAPIURL } from "util/api"; import { IsSupportedGame, IsSupportedPlaytype } from "util/asserts"; +import { ErrorPage } from "app/pages/ErrorPage"; import PlaytypeSelect from "app/pages/dashboard/games/_game/PlaytypeSelect"; -import FoldersMainPage from "app/pages/dashboard/users/games/_game/_playtype/folders/FoldersMainPage"; -import LeaderboardsPage from "app/pages/dashboard/users/games/_game/_playtype/LeaderboardsPage"; -import OverviewPage from "app/pages/dashboard/users/games/_game/_playtype/OverviewPage"; -import RivalsMainPage from "app/pages/dashboard/users/games/_game/_playtype/rivals/RivalsMainPage"; -import SessionsPage from "app/pages/dashboard/users/games/_game/_playtype/SessionsPage"; -import SpecificSessionPage from "app/pages/dashboard/users/games/_game/_playtype/SpecificSessionPage"; -import TargetsPage from "app/pages/dashboard/users/games/_game/_playtype/targets/TargetsPage"; -import UGPTSettingsPage from "app/pages/dashboard/users/games/_game/_playtype/UGPTSettingsPage"; import UserGamesPage from "app/pages/dashboard/users/UserGamesPage"; +import UserImportsPage from "app/pages/dashboard/users/UserImportsPage"; import UserIntegrationsPage from "app/pages/dashboard/users/UserIntegrationsPage"; import UserInvitesPage from "app/pages/dashboard/users/UserInvitesPage"; import UserSettingsPage from "app/pages/dashboard/users/UserSettingsPage"; -import { ErrorPage } from "app/pages/ErrorPage"; +import LeaderboardsPage from "app/pages/dashboard/users/games/_game/_playtype/LeaderboardsPage"; +import OverviewPage from "app/pages/dashboard/users/games/_game/_playtype/OverviewPage"; +import SessionsPage from "app/pages/dashboard/users/games/_game/_playtype/SessionsPage"; +import SpecificSessionPage from "app/pages/dashboard/users/games/_game/_playtype/SpecificSessionPage"; +import UGPTSettingsPage from "app/pages/dashboard/users/games/_game/_playtype/UGPTSettingsPage"; +import FoldersMainPage from "app/pages/dashboard/users/games/_game/_playtype/folders/FoldersMainPage"; +import RivalsMainPage from "app/pages/dashboard/users/games/_game/_playtype/rivals/RivalsMainPage"; +import TargetsPage from "app/pages/dashboard/users/games/_game/_playtype/targets/TargetsPage"; import RequireAuthAsUserParam from "components/auth/RequireAuthAsUserParam"; import LayoutHeaderContainer from "components/layout/LayoutHeaderContainer"; import { UGPTBottomNav, UGPTHeaderBody } from "components/user/UGPTHeader"; @@ -21,6 +22,7 @@ import { UserBottomNav, UserHeaderBody } from "components/user/UserHeader"; import Loading from "components/util/Loading"; import useApiQuery from "components/util/query/useApiQuery"; import { BackgroundContext } from "context/BackgroundContext"; +import { TargetsContextProvider } from "context/TargetsContext"; import { UGPTContextProvider } from "context/UGPTContext"; import { UserContext } from "context/UserContext"; import { UserSettingsContext } from "context/UserSettingsContext"; @@ -29,11 +31,9 @@ import { useQuery } from "react-query"; import { Redirect, Route, Switch, useHistory, useParams } from "react-router-dom"; import { FormatGame, Game, GetGameConfig, UserDocument, UserGameStats } from "tachi-common"; import { UGPTStatsReturn } from "types/api-returns"; -import UserImportsPage from "app/pages/dashboard/users/UserImportsPage"; -import { TargetsContextProvider } from "context/TargetsContext"; -import ToolsPage from "app/pages/dashboard/users/games/_game/_playtype/tools/ToolsPage"; -import ScoresPage from "../pages/dashboard/users/games/_game/_playtype/ScoresPage"; +import UGPTUtilsPage from "app/pages/dashboard/users/games/_game/_playtype/utils/UGPTUtilsPage"; import UserPage from "../pages/dashboard/users/UserPage"; +import ScoresPage from "../pages/dashboard/users/games/_game/_playtype/ScoresPage"; export default function UserRoutes() { const params = useParams<{ userID: string }>(); @@ -264,8 +264,8 @@ function UserGamePlaytypeRoutes({ reqUser, game }: { reqUser: UserDocument; game - - + + diff --git a/client/src/components/gpt-utils/GPTUtils.tsx b/client/src/components/gpt-utils/GPTUtils.tsx new file mode 100644 index 000000000..43d782a57 --- /dev/null +++ b/client/src/components/gpt-utils/GPTUtils.tsx @@ -0,0 +1,62 @@ +import { Game, IDStrings, Playtype } from "tachi-common"; +import { GPTUtility } from "types/ugpt"; +import { BMSCustomTablesTool } from "./tools/BMSCustomTablesTool"; +import { IIDXEamusementExportTool } from "./tools/IIDXEamusementExportTool"; +import { IIDXPlaylistsTool } from "./tools/IIDXPlaylistsTool"; +import { JubilityBreakdownInsight } from "./insights/JubilityBreakdownInsight"; + +// What utils does each game support? +const GPT_UTILS: Record> = { + "bms:7K": [BMSCustomTablesTool], + "bms:14K": [BMSCustomTablesTool], + "chunithm:Single": [], + "gitadora:Dora": [], + "gitadora:Gita": [], + "iidx:DP": [IIDXEamusementExportTool, IIDXPlaylistsTool], + "iidx:SP": [IIDXEamusementExportTool, IIDXPlaylistsTool], + "itg:Stamina": [], + "jubeat:Single": [JubilityBreakdownInsight], + "museca:Single": [], + "pms:Controller": [], + "pms:Keyboard": [], + "popn:9B": [], + "sdvx:Single": [], + "usc:Controller": [], + "usc:Keyboard": [], + "wacca:Single": [], +}; + +export function GetGPTUtils(game: Game, playtype: Playtype) { + const idString = `${game}:${playtype}` as IDStrings; + + return GPT_UTILS[idString]; +} + +export function GetGPTUtilsName(game: Game, playtype: Playtype, isViewingOwnProfile: boolean) { + const utils = GetGPTUtils(game, playtype); + + // things for personal use only are called "tools" + // things everyone might want to view about a person are called "insights". + // an example of an insight would be a jubility breakdown + // an example of a tool would be a eamusement csv export thing. + const tools = utils.filter((e) => e.personalUseOnly); + const insights = utils.filter((e) => e.personalUseOnly !== true); + + if (isViewingOwnProfile) { + if (tools.length > 0 && insights.length > 0) { + return "Tools & Insights"; + } else if (insights.length > 0) { + return "Insights"; + } else if (tools.length > 0) { + return "Tools"; + } + + return null; + } + + if (insights.length > 0) { + return "Insights"; + } + + return null; +} diff --git a/client/src/components/gpt-utils/insights/JubilityBreakdownInsight.tsx b/client/src/components/gpt-utils/insights/JubilityBreakdownInsight.tsx new file mode 100644 index 000000000..d3e31c826 --- /dev/null +++ b/client/src/components/gpt-utils/insights/JubilityBreakdownInsight.tsx @@ -0,0 +1,123 @@ +import { CreateChartMap } from "util/data"; +import { NumericSOV } from "util/sorts"; +import Card from "components/layout/page/Card"; +import PBTable from "components/tables/pbs/PBTable"; +import ApiError from "components/util/ApiError"; +import Divider from "components/util/Divider"; +import Loading from "components/util/Loading"; +import useApiQuery from "components/util/query/useApiQuery"; +import usePreferredRanking from "components/util/usePreferredRanking"; +import React from "react"; +import { Col, Row } from "react-bootstrap"; +import { ChartDocument, CreateSongMap, PBScoreDocument, SongDocument } from "tachi-common"; +import { UGPT } from "types/react"; +import { PBDataset } from "types/tables"; +import { GPTUtility } from "types/ugpt"; + +function Component({ game, playtype, reqUser }: UGPT) { + const { data, error } = useApiQuery<{ + songs: Array; + charts: Array; + pickUp: Array; + other: Array; + }>(`/users/${reqUser.id}/games/${game}/${playtype}/jubility`); + + const preferredRanking = usePreferredRanking(); + + if (error) { + return ; + } + + if (!data) { + return ; + } + + const songMap = CreateSongMap(data.songs); + const chartMap = CreateChartMap(data.charts); + + const pickUpDataset: PBDataset = []; + const normalDataset: PBDataset = []; + + const sortedJubilities = [...data.pickUp, ...data.other] + .map((e) => e.calculatedData.jubility) + .sort(NumericSOV((x) => x ?? -Infinity, true)); + + for (const pb of data.pickUp) { + const song = songMap.get(pb.songID); + const chart = chartMap.get(pb.chartID); + + if (!song) { + continue; + } + + if (!chart) { + continue; + } + + pickUpDataset.push({ + ...pb, + __related: { + chart, + song, + index: sortedJubilities.indexOf(pb.calculatedData.jubility), + }, + }); + } + + for (const pb of data.other) { + const song = songMap.get(pb.songID); + const chart = chartMap.get(pb.chartID); + + if (!song) { + continue; + } + + if (!chart) { + continue; + } + + normalDataset.push({ + ...pb, + __related: { + chart, + song, + index: sortedJubilities.indexOf(pb.calculatedData.jubility), + }, + }); + } + + return ( + + + + + + + + + + + + ); +} + +export const JubilityBreakdownInsight: GPTUtility = { + name: "Jubility Breakdown", + urlPath: "jubility", + description: `See what PBs are going into profile jubility!`, + component: Component, +}; diff --git a/client/src/components/tools/BMSCustomTablesTool.tsx b/client/src/components/gpt-utils/tools/BMSCustomTablesTool.tsx similarity index 94% rename from client/src/components/tools/BMSCustomTablesTool.tsx rename to client/src/components/gpt-utils/tools/BMSCustomTablesTool.tsx index ed03c899e..43795f1a8 100644 --- a/client/src/components/tools/BMSCustomTablesTool.tsx +++ b/client/src/components/gpt-utils/tools/BMSCustomTablesTool.tsx @@ -8,7 +8,7 @@ import { TachiConfig } from "lib/config"; import React from "react"; import { Col, Row } from "react-bootstrap"; import { UGPT } from "types/react"; -import { GPTTool } from "types/ugpt"; +import { GPTUtility } from "types/ugpt"; import Icon from "components/util/Icon"; import Divider from "components/util/Divider"; @@ -66,9 +66,10 @@ function Component({ game, playtype, reqUser }: UGPT) { ); } -export const BMSCustomTablesTool: GPTTool = { +export const BMSCustomTablesTool: GPTUtility = { name: `${TachiConfig.name} BMS Tables`, urlPath: "custom-tables", description: `${TachiConfig.name} has its own BMS tables that you can use in-game!`, component: Component, + personalUseOnly: true, }; diff --git a/client/src/components/tools/IIDXEamusementExportTool.tsx b/client/src/components/gpt-utils/tools/IIDXEamusementExportTool.tsx similarity index 92% rename from client/src/components/tools/IIDXEamusementExportTool.tsx rename to client/src/components/gpt-utils/tools/IIDXEamusementExportTool.tsx index 6f952d511..e8997615c 100644 --- a/client/src/components/tools/IIDXEamusementExportTool.tsx +++ b/client/src/components/gpt-utils/tools/IIDXEamusementExportTool.tsx @@ -1,6 +1,5 @@ import { CopyToClipboard } from "util/misc"; import ApiError from "components/util/ApiError"; -import DebugContent from "components/util/DebugContent"; import Divider from "components/util/Divider"; import Loading from "components/util/Loading"; import useApiQuery from "components/util/query/useApiQuery"; @@ -8,7 +7,7 @@ import { TachiConfig } from "lib/config"; import React from "react"; import { Alert, Button, Col, Row } from "react-bootstrap"; import { UGPT } from "types/react"; -import { GPTTool } from "types/ugpt"; +import { GPTUtility } from "types/ugpt"; function Component({ game, playtype, reqUser }: UGPT) { const { data, error } = useApiQuery( @@ -60,9 +59,10 @@ function Component({ game, playtype, reqUser }: UGPT) { ); } -export const IIDXEamusementExportTool: GPTTool = { +export const IIDXEamusementExportTool: GPTUtility = { name: `e-amusement CSV Export`, urlPath: "eam-csv-export", description: `Export your ${TachiConfig.name} scores into e-amusement format.`, component: Component, + personalUseOnly: true, }; diff --git a/client/src/components/tools/IIDXPlaylistsTool.tsx b/client/src/components/gpt-utils/tools/IIDXPlaylistsTool.tsx similarity index 88% rename from client/src/components/tools/IIDXPlaylistsTool.tsx rename to client/src/components/gpt-utils/tools/IIDXPlaylistsTool.tsx index c386b965a..d0f3b9c59 100644 --- a/client/src/components/tools/IIDXPlaylistsTool.tsx +++ b/client/src/components/gpt-utils/tools/IIDXPlaylistsTool.tsx @@ -1,5 +1,4 @@ import { ToAPIURL } from "util/api"; -import { CopyToClipboard } from "util/misc"; import Card from "components/layout/page/Card"; import ApiError from "components/util/ApiError"; import Loading from "components/util/Loading"; @@ -8,9 +7,7 @@ import { TachiConfig } from "lib/config"; import React from "react"; import { Col, Row } from "react-bootstrap"; import { UGPT } from "types/react"; -import { GPTTool } from "types/ugpt"; -import Icon from "components/util/Icon"; -import Divider from "components/util/Divider"; +import { GPTUtility } from "types/ugpt"; function Component({ game, playtype, reqUser }: UGPT) { const { data, error } = useApiQuery< @@ -76,9 +73,10 @@ function Component({ game, playtype, reqUser }: UGPT) { ); } -export const IIDXPlaylistsTool: GPTTool = { +export const IIDXPlaylistsTool: GPTUtility = { name: `${TachiConfig.name} IIDX Playlists`, - urlPath: "custom-tables", + urlPath: "playlists", description: `${TachiConfig.name} has its own IIDX playlists that you can use in-game via playlister!`, component: Component, + personalUseOnly: true, }; diff --git a/client/src/components/tools/Tools.tsx b/client/src/components/tools/Tools.tsx deleted file mode 100644 index 599c6b2e7..000000000 --- a/client/src/components/tools/Tools.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Game, GetIDString, IDStrings, Playtype } from "tachi-common"; -import { GPTTool } from "types/ugpt"; -import { BMSCustomTablesTool } from "./BMSCustomTablesTool"; -import { IIDXEamusementExportTool } from "./IIDXEamusementExportTool"; -import { IIDXPlaylistsTool } from "./IIDXPlaylistsTool"; - -// A tool is something that users can use, like exporting to a specific format -// or having things integrate with the game in a neat way. -// Tools are **NOT** displayed on someone elses profile. For that, you want -// Insights (Fancy score renderers, basically). - -// What tools does each game support? -const GPT_TOOLS: Record> = { - "bms:7K": [BMSCustomTablesTool], - "bms:14K": [BMSCustomTablesTool], - "chunithm:Single": [], - "gitadora:Dora": [], - "gitadora:Gita": [], - "iidx:DP": [IIDXEamusementExportTool, IIDXPlaylistsTool], - "iidx:SP": [IIDXEamusementExportTool, IIDXPlaylistsTool], - "itg:Stamina": [], - "jubeat:Single": [], - "museca:Single": [], - "pms:Controller": [], - "pms:Keyboard": [], - "popn:9B": [], - "sdvx:Single": [], - "usc:Controller": [], - "usc:Keyboard": [], - "wacca:Single": [], -}; - -export function GetGPTTools(game: Game, playtype: Playtype) { - const idString = GetIDString(game, playtype); - - return GPT_TOOLS[idString]; -} diff --git a/client/src/components/user/UGPTHeader.tsx b/client/src/components/user/UGPTHeader.tsx index 255d1509c..4c3aa4697 100644 --- a/client/src/components/user/UGPTHeader.tsx +++ b/client/src/components/user/UGPTHeader.tsx @@ -10,11 +10,10 @@ import React, { useContext } from "react"; import { Button } from "react-bootstrap"; import { Game, Playtype, UserDocument } from "tachi-common"; import { UGPTStatsReturn } from "types/api-returns"; -import { UserContext } from "context/UserContext"; import useLUGPTSettings from "components/util/useLUGPTSettings"; import { UserSettingsContext } from "context/UserSettingsContext"; import FollowUserButton from "components/util/FollowUserButton"; -import { GetGPTTools } from "components/tools/Tools"; +import { GetGPTUtilsName } from "components/gpt-utils/GPTUtils"; import ProfileBadges from "./ProfileBadges"; import ProfilePicture from "./ProfilePicture"; import RankingData from "./UGPTRankingData"; @@ -201,11 +200,18 @@ export function UGPTBottomNav({ Sessions , - - Leaderboard - , ]; + const utilsName = GetGPTUtilsName(game, playtype, isRequestedUser); + + if (utilsName) { + navItems.push( + + {utilsName} + + ); + } + if (isRequestedUser) { navItems.push( @@ -215,17 +221,15 @@ export function UGPTBottomNav({ Goals & Quests ); + } - const tools = GetGPTTools(game, playtype); - - if (tools.length > 0) { - navItems.push( - - Tools - - ); - } + navItems.push( + + Leaderboard + + ); + if (isRequestedUser) { navItems.push( Settings diff --git a/client/src/types/ugpt.ts b/client/src/types/ugpt.ts index b41602d1e..4b676ce84 100644 --- a/client/src/types/ugpt.ts +++ b/client/src/types/ugpt.ts @@ -1,9 +1,10 @@ import React from "react"; import { UGPT } from "./react"; -export interface GPTTool { +export interface GPTUtility { urlPath: string; name: string; description: React.ReactChild; component: (ugpt: UGPT) => JSX.Element; + personalUseOnly?: boolean; } diff --git a/client/src/util/data.ts b/client/src/util/data.ts index 82ea45dbe..c60eb2343 100644 --- a/client/src/util/data.ts +++ b/client/src/util/data.ts @@ -78,12 +78,12 @@ export function CreateScoreIDMap(scores: ScoreD export function CreateChartLink(chart: ChartDocument, game: Game) { if (chart.isPrimary) { - return `/dashboard/games/${game}/${chart.playtype}/songs/${ - chart.songID - }/${encodeURIComponent(chart.difficulty)}`; + return `/games/${game}/${chart.playtype}/songs/${chart.songID}/${encodeURIComponent( + chart.difficulty + )}`; } - return `/dashboard/games/${game}/${chart.playtype}/songs/${chart.songID}/${chart.chartID}`; + return `/games/${game}/${chart.playtype}/songs/${chart.songID}/${chart.chartID}`; } // stolen from server diff --git a/server/src/lib/score-import/framework/user-game-stats/rating.ts b/server/src/lib/score-import/framework/user-game-stats/rating.ts index b2f1b2b44..94491bac5 100644 --- a/server/src/lib/score-import/framework/user-game-stats/rating.ts +++ b/server/src/lib/score-import/framework/user-game-stats/rating.ts @@ -390,12 +390,7 @@ async function GetBestJubilityOnSongs( const CURRENT_JUBEAT_HOT_VERSION: GPTSupportedVersions["jubeat:Single"] = "festo"; -async function CalculateJubility( - game: Game, - playtype: Playtype, - userID: integer, - _logger: KtLogger -): Promise { +export async function GetPBsForJubility(userID: integer) { const hotSongs = await db.songs.jubeat.find( { "data.displayVersion": CURRENT_JUBEAT_HOT_VERSION }, { projection: { id: 1 } } @@ -415,6 +410,17 @@ async function CalculateJubility( GetBestJubilityOnSongs(coldSongIDs, userID, "jubeat", "Single", 30), ]); + return { bestHotScores, bestScores }; +} + +async function CalculateJubility( + game: Game, + playtype: Playtype, + userID: integer, + _logger: KtLogger +): Promise { + const { bestHotScores, bestScores } = await GetPBsForJubility(userID); + let jubility = 0; jubility = jubility + bestHotScores.reduce((a, e) => a + (e.calculatedData.jubility ?? 0), 0); diff --git a/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts b/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts new file mode 100644 index 000000000..97c2cd005 --- /dev/null +++ b/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/jubeat/router.ts @@ -0,0 +1,35 @@ +import { Router } from "express"; +import { GetPBsForJubility } from "lib/score-import/framework/user-game-stats/rating"; +import { GetRelevantSongsAndCharts } from "utils/db"; +import { GetUser } from "utils/req-tachi-data"; + +const router: Router = Router({ mergeParams: true }); + +/** + * Retrieve the PBs that went into this users jubility ranking. + * + * @name GET /api/v1/users/:userID/games/jubeat/Single/jubility + */ +router.get("/Single/jubility", async (req, res) => { + const user = GetUser(req); + + const { bestHotScores, bestScores } = await GetPBsForJubility(user.id); + + const { songs, charts } = await GetRelevantSongsAndCharts( + [...bestHotScores, ...bestScores], + "jubeat" + ); + + return res.status(200).json({ + success: true, + description: `Retrieved scores that went into this users jubility.`, + body: { + songs, + charts, + pickUp: bestHotScores, + other: bestScores, + }, + }); +}); + +export default router; diff --git a/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/router.ts b/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/router.ts index d1081d5ed..4da9a2ced 100644 --- a/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/router.ts +++ b/server/src/server/router/api/v1/users/_userID/games/@gameSpecificRoutes/router.ts @@ -4,11 +4,13 @@ import bmsRouter from "./bms/router"; import iidxRouter from "./iidx/router"; +import jubeatRouter from "./jubeat/router"; import { Router } from "express"; const router: Router = Router({ mergeParams: true }); router.use("/bms", bmsRouter); router.use("/iidx", iidxRouter); +router.use("/jubeat", jubeatRouter); export default router;