mirror of
https://github.com/zkldi/Tachi.git
synced 2026-09-22 23:18:05 +03:00
Merge pull request #804 from TNG-dev/zkldi/gpt-insights
This commit is contained in:
@@ -82,7 +82,8 @@ function SearchSongsTable({ game, playtype, search }: { search: string } & GameP
|
||||
<>
|
||||
{search === "" && (
|
||||
<div className="w-100 text-center">
|
||||
Displaying the most played charts for {FormatGame(game, playtype)}
|
||||
<h4>Displaying the most played charts for {FormatGame(game, playtype)}</h4>
|
||||
<Divider />
|
||||
</div>
|
||||
)}
|
||||
<TachiTable
|
||||
|
||||
@@ -144,9 +144,9 @@ function AlgSelector({
|
||||
function useFetchPBs(url: string, reqUser: UserDocument) {
|
||||
const { data, error } = useQuery(url, async () => {
|
||||
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) {
|
||||
|
||||
+38
-37
@@ -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 (
|
||||
<div className="text-center w-100">
|
||||
{FormatGame(game, playtype)} has no tools. How'd you get here!
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Switch>
|
||||
{tools.map((tool) => (
|
||||
<Route exact path="/u/:userID/games/:game/:playtype/utils">
|
||||
{utils.map((util) => (
|
||||
<Col key={util.urlPath} xs={12} className="my-4" lg={6}>
|
||||
<Card
|
||||
header={util.name}
|
||||
footer={
|
||||
<div className="d-flex w-100 justify-content-end">
|
||||
<LinkButton
|
||||
to={`/u/${reqUser.username}/games/${game}/${playtype}/utils/${util.urlPath}`}
|
||||
>
|
||||
View
|
||||
</LinkButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{util.description}
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
</Route>
|
||||
|
||||
{utils.map((tool) => (
|
||||
<Route
|
||||
key={tool.urlPath}
|
||||
exact
|
||||
path={`/u/:userID/games/:game/:playtype/tools/${tool.urlPath}`}
|
||||
path={`/u/:userID/games/:game/:playtype/utils/${tool.urlPath}`}
|
||||
>
|
||||
<Col xs={12} className="mt-4">
|
||||
<Card
|
||||
header={tool.name}
|
||||
footer={
|
||||
<Link
|
||||
to={`/u/${reqUser.username}/games/${game}/${playtype}/tools`}
|
||||
to={`/u/${reqUser.username}/games/${game}/${playtype}/utils`}
|
||||
className="text-muted text-hover-white"
|
||||
>
|
||||
< Back to all tools...
|
||||
@@ -55,25 +75,6 @@ export default function ToolsPage({ reqUser, game, playtype }: UGPT) {
|
||||
<Col xs={12}>{tool.component({ reqUser, game, playtype })}</Col>
|
||||
</Route>
|
||||
))}
|
||||
|
||||
<Route exact path="/u/:userID/games/:game/:playtype/tools/">
|
||||
{tools.map((tool) => (
|
||||
<Col key={tool.urlPath} xs={12} className="my-4" lg={6}>
|
||||
<Card
|
||||
header={tool.name}
|
||||
footer={
|
||||
<div className="d-flex w-100 justify-content-end">
|
||||
<LinkButton to={`tools/${tool.urlPath}`}>
|
||||
Use Tool
|
||||
</LinkButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{tool.description}
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
</Route>
|
||||
</Switch>
|
||||
</Row>
|
||||
);
|
||||
@@ -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
|
||||
<Route exact path="/u/:userID/games/:game/:playtype/leaderboard">
|
||||
<LeaderboardsPage reqUser={reqUser} game={game} playtype={playtype} />
|
||||
</Route>
|
||||
<Route path="/u/:userID/games/:game/:playtype/tools">
|
||||
<ToolsPage reqUser={reqUser} game={game} playtype={playtype} />
|
||||
<Route path="/u/:userID/games/:game/:playtype/utils">
|
||||
<UGPTUtilsPage reqUser={reqUser} game={game} playtype={playtype} />
|
||||
</Route>
|
||||
<RequireAuthAsUserParam>
|
||||
<Route exact path="/u/:userID/games/:game/:playtype/settings">
|
||||
|
||||
@@ -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<IDStrings, Array<GPTUtility>> = {
|
||||
"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;
|
||||
}
|
||||
@@ -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<SongDocument>;
|
||||
charts: Array<ChartDocument>;
|
||||
pickUp: Array<PBScoreDocument>;
|
||||
other: Array<PBScoreDocument>;
|
||||
}>(`/users/${reqUser.id}/games/${game}/${playtype}/jubility`);
|
||||
|
||||
const preferredRanking = usePreferredRanking();
|
||||
|
||||
if (error) {
|
||||
return <ApiError error={error} />;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
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 (
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<Card header="Pick-Up">
|
||||
<PBTable
|
||||
game={game}
|
||||
playtype={playtype}
|
||||
dataset={pickUpDataset}
|
||||
defaultRankingViewMode={preferredRanking}
|
||||
indexCol
|
||||
alg="jubility"
|
||||
/>
|
||||
</Card>
|
||||
<Divider />
|
||||
<Card header="Others">
|
||||
<PBTable
|
||||
game={game}
|
||||
playtype={playtype}
|
||||
dataset={normalDataset}
|
||||
defaultRankingViewMode={preferredRanking}
|
||||
indexCol
|
||||
alg="jubility"
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export const JubilityBreakdownInsight: GPTUtility = {
|
||||
name: "Jubility Breakdown",
|
||||
urlPath: "jubility",
|
||||
description: `See what PBs are going into profile jubility!`,
|
||||
component: Component,
|
||||
};
|
||||
+3
-2
@@ -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,
|
||||
};
|
||||
+3
-3
@@ -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<string>(
|
||||
@@ -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,
|
||||
};
|
||||
+4
-6
@@ -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,
|
||||
};
|
||||
@@ -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<IDStrings, Array<GPTTool>> = {
|
||||
"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];
|
||||
}
|
||||
@@ -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({
|
||||
<NavItem key="sessions" to={`${baseUrl}/sessions`}>
|
||||
Sessions
|
||||
</NavItem>,
|
||||
<NavItem key="leaderboard" to={`${baseUrl}/leaderboard`}>
|
||||
Leaderboard
|
||||
</NavItem>,
|
||||
];
|
||||
|
||||
const utilsName = GetGPTUtilsName(game, playtype, isRequestedUser);
|
||||
|
||||
if (utilsName) {
|
||||
navItems.push(
|
||||
<NavItem key="targets" to={`${baseUrl}/utils`}>
|
||||
{utilsName}
|
||||
</NavItem>
|
||||
);
|
||||
}
|
||||
|
||||
if (isRequestedUser) {
|
||||
navItems.push(
|
||||
<NavItem key="rivals" to={`${baseUrl}/rivals`}>
|
||||
@@ -215,17 +221,15 @@ export function UGPTBottomNav({
|
||||
Goals & Quests
|
||||
</NavItem>
|
||||
);
|
||||
}
|
||||
|
||||
const tools = GetGPTTools(game, playtype);
|
||||
|
||||
if (tools.length > 0) {
|
||||
navItems.push(
|
||||
<NavItem key="targets" to={`${baseUrl}/tools`}>
|
||||
Tools
|
||||
</NavItem>
|
||||
);
|
||||
}
|
||||
navItems.push(
|
||||
<NavItem key="leaderboard" to={`${baseUrl}/leaderboard`}>
|
||||
Leaderboard
|
||||
</NavItem>
|
||||
);
|
||||
|
||||
if (isRequestedUser) {
|
||||
navItems.push(
|
||||
<NavItem key="settings" to={`${baseUrl}/settings`}>
|
||||
Settings
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -78,12 +78,12 @@ export function CreateScoreIDMap<I extends IDStrings = IDStrings>(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
|
||||
|
||||
@@ -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<number> {
|
||||
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<number> {
|
||||
const { bestHotScores, bestScores } = await GetPBsForJubility(userID);
|
||||
|
||||
let jubility = 0;
|
||||
|
||||
jubility = jubility + bestHotScores.reduce((a, e) => a + (e.calculatedData.jubility ?? 0), 0);
|
||||
|
||||
+35
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user