diff --git a/client/src/app/pages/dashboard/users/games/_game/_playtype/OverviewPage.tsx b/client/src/app/pages/dashboard/users/games/_game/_playtype/OverviewPage.tsx index d1580f416..d52c6f9bf 100644 --- a/client/src/app/pages/dashboard/users/games/_game/_playtype/OverviewPage.tsx +++ b/client/src/app/pages/dashboard/users/games/_game/_playtype/OverviewPage.tsx @@ -126,6 +126,8 @@ function UserHistory({
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/tools/ToolsPage.tsx new file mode 100644 index 000000000..db0a0ab17 --- /dev/null +++ b/client/src/app/pages/dashboard/users/games/_game/_playtype/tools/ToolsPage.tsx @@ -0,0 +1,80 @@ +import useSetSubheader from "components/layout/header/useSetSubheader"; +import Card from "components/layout/page/Card"; +import { GetGPTTools } from "components/tools/Tools"; +import Divider from "components/util/Divider"; +import LinkButton from "components/util/LinkButton"; +import React 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"; + +export default function ToolsPage({ reqUser, game, playtype }: UGPT) { + const gameConfig = GetGameConfig(game); + useSetSubheader( + ["Users", reqUser.username, "Games", gameConfig.name, playtype, "Tools"], + [reqUser, game, playtype], + `${reqUser.username}'s ${FormatGame(game, playtype)} Tools` + ); + + 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) => ( + + + + < Back to all tools... + + } + > + {tool.description} + + + + {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 13744f40d..6db0c6657 100644 --- a/client/src/app/routes/UserRoutes.tsx +++ b/client/src/app/routes/UserRoutes.tsx @@ -31,6 +31,7 @@ import { FormatGame, Game, GetGameConfig, UserDocument, UserGameStats } from "ta 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 UserPage from "../pages/dashboard/users/UserPage"; @@ -64,9 +65,9 @@ export default function UserRoutes() { return null; } - // redirect to the users actual name. - if (userID.match(/^[0-9]+$/u)) { - const split = history.location.pathname.match(/^(\/u)\/([0-9]+)(.*)$/u); + // redirect to the users actual name if using a user ID or "me" + if (userID.match(/^([0-9]+|me)$/u)) { + const split = history.location.pathname.match(/^(\/u)\/([0-9]+|me)(.*)$/u); if (!split) { return ( @@ -229,6 +230,8 @@ function UserGamePlaytypeRoutes({ reqUser, game }: { reqUser: UserDocument; game header={`${reqUser.username}'s ${FormatGame(game, playtype)} Profile`} footer={ @@ -261,6 +264,9 @@ function UserGamePlaytypeRoutes({ reqUser, game }: { reqUser: UserDocument; game + + + diff --git a/client/src/components/tools/BMSCustomTablesTool.tsx b/client/src/components/tools/BMSCustomTablesTool.tsx new file mode 100644 index 000000000..ed03c899e --- /dev/null +++ b/client/src/components/tools/BMSCustomTablesTool.tsx @@ -0,0 +1,74 @@ +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"; +import useApiQuery from "components/util/query/useApiQuery"; +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"; + +function Component({ game, playtype, reqUser }: UGPT) { + const { data, error } = useApiQuery< + Array<{ + forSpecificUser?: boolean; + urlName: string; + tableName: string; + symbol: string; + description: string; + }> + >(`/games/${game}/${playtype}/custom-tables`); + + if (error) { + return ; + } + + if (!data) { + return ; + } + + return ( + + + Just paste these URLs into your BMS client of choice. For LR2oraja, this is on the + setup screen. + + + {data.map((tbl) => { + const TABLE_URL = ToAPIURL( + `${ + tbl.forSpecificUser ? `/users/${reqUser.username}` : "" + }/games/bms/${playtype}/custom-tables/${tbl.urlName}` + ); + + return ( + + + {tbl.description} +
+
+ URL: {TABLE_URL} + { + CopyToClipboard(TABLE_URL); + }} + /> +
+ + ); + })} +
+ ); +} + +export const BMSCustomTablesTool: GPTTool = { + name: `${TachiConfig.name} BMS Tables`, + urlPath: "custom-tables", + description: `${TachiConfig.name} has its own BMS tables that you can use in-game!`, + component: Component, +}; diff --git a/client/src/components/tools/IIDXEamusementExportTool.tsx b/client/src/components/tools/IIDXEamusementExportTool.tsx new file mode 100644 index 000000000..6f952d511 --- /dev/null +++ b/client/src/components/tools/IIDXEamusementExportTool.tsx @@ -0,0 +1,68 @@ +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"; +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"; + +function Component({ game, playtype, reqUser }: UGPT) { + const { data, error } = useApiQuery( + `/users/${reqUser.id}/games/${game}/${playtype}/eamusement-csv` + ); + + if (error) { + return ; + } + + if (!data) { + return ; + } + + return ( + + + + Hey! Please be aware that this format is lossy. You should use + this to integrate with other tools that only support eamusement CSVs. +
+ Tachi offers more fully fledged score exporting if you're looking to actually + get your scores elsewhere. +
+ + + +