diff --git a/client/package.json b/client/package.json index 12c5682b6..f191f66ff 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "tachi-client", - "version": "2.0.0-prerelease", + "version": "2.0.1", "private": true, "scripts": { "start": "node scripts/start.js -o", diff --git a/client/src/app/pages/ClientFileFlowPage.tsx b/client/src/app/pages/ClientFileFlowPage.tsx index 3dc273461..80a29335b 100644 --- a/client/src/app/pages/ClientFileFlowPage.tsx +++ b/client/src/app/pages/ClientFileFlowPage.tsx @@ -1,6 +1,7 @@ import ApiError from "components/util/ApiError"; import Divider from "components/util/Divider"; import Loading from "components/util/Loading"; +import OAuthMoreInfo from "components/util/OAuthMoreInfo"; import useApiQuery from "components/util/query/useApiQuery"; import { UserContext } from "context/UserContext"; import { TachiConfig } from "lib/config"; @@ -89,6 +90,10 @@ export default function ClientFileFlowPage() { Create the key. +
+ + +
) : ( <> diff --git a/client/src/app/pages/OAuthRequestAuthPage.tsx b/client/src/app/pages/OAuthRequestAuthPage.tsx new file mode 100644 index 000000000..f1ef08ac7 --- /dev/null +++ b/client/src/app/pages/OAuthRequestAuthPage.tsx @@ -0,0 +1,130 @@ +import CenterLayoutPage from "components/layout/CenterLayoutPage"; +import ApiError from "components/util/ApiError"; +import Divider from "components/util/Divider"; +import Loading from "components/util/Loading"; +import OAuthMoreInfo from "components/util/OAuthMoreInfo"; +import useApiQuery from "components/util/query/useApiQuery"; +import useQueryString from "components/util/useQueryString"; +import React from "react"; +import { Link } from "react-router-dom"; +import { integer, TachiAPIClientDocument } from "tachi-common"; +import { APIFetchV1 } from "util/api"; + +export default function OAuthRequestAuthPage() { + const params = useQueryString(); + + const clientID = params.get("clientID"); + + if (!clientID) { + return ( + + No ClientID. Whoever gave you this link likely messed up somehow.
+ Go Home. +
+ ); + } + + return ( + + + + ); +} + +function OAuthRequestAuthLoader({ clientID }: { clientID: string }) { + const { isLoading, error, data } = useApiQuery>( + `/clients/${clientID}` + ); + + if (error) { + return ; + } + + if (isLoading || !data) { + return ; + } + + if (!data.redirectUri) { + return ( + <> + This client does not support OAuth Flows. +
+ Whoever gave you this link likely messed up somehow.
+ Go Home. + + ); + } + + return ; +} + +function OAuthRequestAuthMain({ + client, +}: { + client: Omit; +}) { + const params = useQueryString(); + + const context = params.get("context"); + + return ( +
+ <> +
+ {client.name} is requesting you authenticate with them. + +
+
+ The key will have the following permissions. +
    + {client.requestedPermissions.map(e => ( +
  • {e}
  • + ))} +
+
+
+ + No thanks. + + +
+
+ + +
+ +
+ ); +} diff --git a/client/src/app/pages/dashboard/import/BarbatosPage.tsx b/client/src/app/pages/dashboard/import/BarbatosPage.tsx index 86f30872d..ca60555a5 100644 --- a/client/src/app/pages/dashboard/import/BarbatosPage.tsx +++ b/client/src/app/pages/dashboard/import/BarbatosPage.tsx @@ -21,10 +21,7 @@ export default function BarbatosPage() {
  1. Download barbatos.dll from{" "} - - here (No download yet. Barbatos is not supported.) - - . + here.
  2. Download your config file{" "} @@ -32,8 +29,8 @@ export default function BarbatosPage() { This file contains an API Key, which is meant to be kept secret!
  3. - Place all of these files inside your SDVX folder, next to your .bat{" "} - file. + Place barbatos.dll and the above config file inside your SDVX + folder, next to your .bat file.
    • diff --git a/client/src/app/pages/dashboard/import/BeatorajaIRPage.tsx b/client/src/app/pages/dashboard/import/BeatorajaIRPage.tsx index f59004722..968cc6009 100644 --- a/client/src/app/pages/dashboard/import/BeatorajaIRPage.tsx +++ b/client/src/app/pages/dashboard/import/BeatorajaIRPage.tsx @@ -36,7 +36,14 @@ export default function BeatorajaIRPage() { Place the IR .jar file in the ir/ folder.
    • Open the game options, and navigate to the IR section.
    • -
    • Select {TachiConfig.name} IR.
    • +
    • + Select {TachiConfig.name} IR. +
      + + If the IR isn't showing up, make sure you're launching the game with the{" "} + beatoraja-config.bat file. Otherwise, IRs will never load. + +
    • Get an API token for the IR by clicking{" "} this link. diff --git a/client/src/app/pages/dashboard/import/ImportPage.tsx b/client/src/app/pages/dashboard/import/ImportPage.tsx index 023067e45..065e75a49 100644 --- a/client/src/app/pages/dashboard/import/ImportPage.tsx +++ b/client/src/app/pages/dashboard/import/ImportPage.tsx @@ -133,13 +133,13 @@ function ImportInfoDisplayer({ game }: { game: Game }) { ); } else if (game === "sdvx") { Content.unshift( - // , + , // +
      + OAuth Flow Link:
      + + {window.location.origin}/oauth/request-auth?clientID={client.clientID} + +
      diff --git a/client/src/app/pages/dashboard/users/games/_game/_playtype/LeaderboardsPage.tsx b/client/src/app/pages/dashboard/users/games/_game/_playtype/LeaderboardsPage.tsx index e681a52e9..137bf1109 100644 --- a/client/src/app/pages/dashboard/users/games/_game/_playtype/LeaderboardsPage.tsx +++ b/client/src/app/pages/dashboard/users/games/_game/_playtype/LeaderboardsPage.tsx @@ -23,6 +23,7 @@ import { GPTLeaderboard, UGPTLeaderboardAdjacent } from "types/api-returns"; import { GamePT, SetState } from "types/react"; import { APIFetchV1, UnsuccessfulAPIFetchResponse } from "util/api"; import { ChangeOpacity } from "util/color-opacity"; +import { UppercaseFirst } from "util/misc"; interface LeaderboardsData { stats: UGPTLeaderboardAdjacent; @@ -161,7 +162,10 @@ function LeaderboardsPageContent({ } > - + <> {bestNearbyUser >= 1 && leaderboard.gameStats diff --git a/client/src/app/routes/AppRoutes.tsx b/client/src/app/routes/AppRoutes.tsx index 2c420b633..cd95bf5f7 100644 --- a/client/src/app/routes/AppRoutes.tsx +++ b/client/src/app/routes/AppRoutes.tsx @@ -1,6 +1,7 @@ import { ErrorPage } from "app/pages/ErrorPage"; import ForgotPasswordPage from "app/pages/ForgotPasswordPage"; import LoginPage from "app/pages/LoginPage"; +import OAuthRequestAuthPage from "app/pages/OAuthRequestAuthPage"; import RegisterPage from "app/pages/RegisterPage"; import ResetPasswordPage from "app/pages/ResetPasswordPage"; import VerifyEmailPage from "app/pages/VerifyEmailPage"; @@ -51,6 +52,10 @@ export function Routes() { + + + + diff --git a/client/src/components/imports/ARCImportPage.tsx b/client/src/components/imports/ARCImportPage.tsx index 7ee75dd88..1d5981803 100644 --- a/client/src/components/imports/ARCImportPage.tsx +++ b/client/src/components/imports/ARCImportPage.tsx @@ -91,7 +91,7 @@ export default function ARCImportPage({ game }: { game: "sdvx" | "iidx" }) { ) : ( - Barbatos (Although it's not supported yet, sorry.) + Barbatos )}{" "} instead. diff --git a/client/src/components/layout/footer/Footer.tsx b/client/src/components/layout/footer/Footer.tsx index 5fe663f80..4dc2b059c 100644 --- a/client/src/components/layout/footer/Footer.tsx +++ b/client/src/components/layout/footer/Footer.tsx @@ -1,10 +1,24 @@ import Divider from "components/util/Divider"; import ExternalLink from "components/util/ExternalLink"; -import React from "react"; +import React, { useEffect, useState } from "react"; import { Link } from "react-router-dom"; +import { ServerStatus } from "types/api-returns"; +import { APIFetchV1 } from "util/api"; import { FORMATTED_VERSION } from "util/constants/version"; export function Footer() { + const [serverVersion, setServerVersion] = useState("Loading..."); + + useEffect(() => { + APIFetchV1("/status").then(r => { + if (!r.success) { + setServerVersion("Error Fetching data!"); + } else { + setServerVersion(r.body.version); + } + }); + }, []); + return ( <> @@ -14,12 +28,20 @@ export function Footer() {
      {/* is there a better way to do this? mt-md-3 is the intent */}
      + Client:{" "} {FORMATTED_VERSION} + , Server:{" "} + + {serverVersion} +
      diff --git a/client/src/components/layout/header/HeaderMenu.tsx b/client/src/components/layout/header/HeaderMenu.tsx index 7313587d6..b7ed9082b 100644 --- a/client/src/components/layout/header/HeaderMenu.tsx +++ b/client/src/components/layout/header/HeaderMenu.tsx @@ -14,6 +14,7 @@ import AllGames from "./AllGames"; import ImportScoresLink from "./ImportScoresLink"; import MenuDropdown from "./MenuDropdown"; import MenuLink from "./MenuLink"; +import SearchBar from "./SearchBar"; export function HeaderMenu() { const { user } = useContext(UserContext); @@ -95,6 +96,13 @@ export function HeaderMenu() {
        +
      • +
        + +
        + +
      • + {user && ugs && ugs.length !== 0 && ( {userProfileLinks} )} diff --git a/client/src/components/util/OAuthMoreInfo.tsx b/client/src/components/util/OAuthMoreInfo.tsx new file mode 100644 index 000000000..e93e0f37b --- /dev/null +++ b/client/src/components/util/OAuthMoreInfo.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { PublicUserDocument, TachiAPIClientDocument } from "tachi-common"; +import ApiError from "./ApiError"; +import Loading from "./Loading"; +import Muted from "./Muted"; +import useApiQuery from "./query/useApiQuery"; + +export default function OAuthMoreInfo({ + client, +}: { + client: Omit; +}) { + const { data, isLoading, error } = useApiQuery(`/users/${client.author}`); + + if (error) { + return ; + } + + if (isLoading || !data) { + return ; + } + + return API Client Author: {data.username}.; +} diff --git a/client/src/types/api-returns.ts b/client/src/types/api-returns.ts index c3ecf0db7..071f38d6e 100644 --- a/client/src/types/api-returns.ts +++ b/client/src/types/api-returns.ts @@ -1,4 +1,5 @@ import { + APIPermissions, ChartDocument, ClassAchievementDocument, FolderDocument, @@ -162,3 +163,11 @@ export interface UserRecentSummary { recentSessions: SessionDocument[]; recentFolders: FolderDocument[]; } + +export interface ServerStatus { + serverTime: number; + startTime: number; + version: string; + whoami: integer | null; + permissions: APIPermissions[]; +} diff --git a/client/src/util/constants/version.ts b/client/src/util/constants/version.ts index 847109716..477d5a065 100644 --- a/client/src/util/constants/version.ts +++ b/client/src/util/constants/version.ts @@ -1,8 +1,8 @@ const MAJOR = 2; const MINOR = 0; -const PATCH = 0; +const PATCH = 1; -// As is with all front-facing zkldi projects, the version names for tachi-server +// As is with all front-facing zkldi projects, the version names for tachi-client // are from an album I like. In this case, the album is The Cure - Disintegration. const THE_CURE_DISINTEGRATION = [ diff --git a/client/src/util/splashes.ts b/client/src/util/splashes.ts index 811dd1598..2bd06133c 100644 --- a/client/src/util/splashes.ts +++ b/client/src/util/splashes.ts @@ -97,6 +97,7 @@ export const loggedInSplashes = [ "I'm thinking this one's brown.", "My god is the sun!", "...Like Clockwork", + "brooooyyddg", ]; // Stuff that's bad for the kids, you know?