mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-03 04:18:16 +03:00
fix: landing page ghost button (#919)
This commit is contained in:
@@ -20,11 +20,12 @@ import { UserContext } from "context/UserContext";
|
||||
import { UserSettingsContext } from "context/UserSettingsContext";
|
||||
import { ColourConfig, TachiConfig } from "lib/config";
|
||||
import React, { useContext, useMemo } from "react";
|
||||
import { Alert } from "react-bootstrap";
|
||||
import { Alert, Stack } from "react-bootstrap";
|
||||
import { Link, Route, Switch } from "react-router-dom";
|
||||
import { COLOUR_SET, GetGameConfig, UserDocument } from "tachi-common";
|
||||
import { UGSWithRankingData, UserRecentSummary } from "types/api-returns";
|
||||
import SessionCalendar from "components/sessions/SessionCalendar";
|
||||
import { WindowContext } from "context/WindowContext";
|
||||
import { GameStatContainer } from "./users/UserGamesPage";
|
||||
import SupportBanner from "./misc/SupportBanner";
|
||||
|
||||
@@ -234,19 +235,28 @@ function UserGameStatsInfo({ user }: { user: UserDocument }) {
|
||||
}
|
||||
|
||||
function DashboardNotLoggedIn() {
|
||||
const {
|
||||
breakpoint: { isMd },
|
||||
} = useContext(WindowContext);
|
||||
return (
|
||||
<div style={{ fontSize: "1.3rem" }}>
|
||||
<h1 className="mb-4">Welcome to {TachiConfig.name}!</h1>
|
||||
<h4>
|
||||
Looks like you're not logged in. If you've got an account,{" "}
|
||||
<Link to="/login">Login!</Link>
|
||||
</h4>
|
||||
<Divider />
|
||||
<h1 className="my-4">I'm New Around Here, What is this?</h1>
|
||||
<span>
|
||||
<b>{TachiConfig.name}</b> is a Rhythm Game Score Tracker. That means we...
|
||||
</span>
|
||||
<Stack gap={4} className="enable-rfs" style={{ fontSize: "16px" }}>
|
||||
<div>
|
||||
<h1 className="fw-bold">Welcome to {TachiConfig.name}!</h1>
|
||||
<h4 className="fs-3">
|
||||
Looks like you're not logged in. If you've got an account,{" "}
|
||||
<Link className="link-primary text-decoration-underline" to="/login">
|
||||
Login!
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
<Divider />
|
||||
<div>
|
||||
<h1>I'm New Around Here, What is this?</h1>
|
||||
<p>
|
||||
<b>{TachiConfig.name}</b> is a Rhythm Game Score Tracker. That means we...
|
||||
</p>
|
||||
</div>
|
||||
<Divider className="mb-2" />
|
||||
<FeatureContainer
|
||||
tagline="Track Your Scores."
|
||||
description={`${TachiConfig.name} supports a bunch of your favourite games, and integrates with many existing services to make sure no score is lost to the void. Furthermore, it's backed by an Open-Source API, so your scores are always available!`}
|
||||
@@ -260,32 +270,31 @@ function DashboardNotLoggedIn() {
|
||||
description={`${TachiConfig.name} implements the features rhythm gamers already talk about. Break your scores down into sessions, Showcase your best metrics on your profile, study your progress on folders - it's all there, and done for you!`}
|
||||
/>
|
||||
<Divider />
|
||||
<div className="col-12 text-center" style={{ paddingTop: 50, paddingBottom: 50 }}>
|
||||
Interested? You can register right now for <b>free</b>!
|
||||
<br />
|
||||
<LinkButton to="/register" className="mt-4 btn-outline-primary">
|
||||
Register!
|
||||
|
||||
<Stack direction={isMd ? "horizontal" : "vertical"} className="mx-auto gap-4 gap-md-8">
|
||||
<h1 className="fw-bold p-0 m-0 text-center">Interested?</h1>
|
||||
<div className="vr d-none d-md-block" />
|
||||
<hr className="m-0 mb-2 d-md-none" />
|
||||
<LinkButton to="/register" size="lg" className="align-self-center">
|
||||
Create an account for <b>free</b>!
|
||||
</LinkButton>
|
||||
</div>
|
||||
</Stack>
|
||||
<Divider />
|
||||
<div className="col-12 text-center" style={{ paddingTop: 25 }}>
|
||||
<div className="text-center">
|
||||
Nosey? Here's what our users are up to.
|
||||
<br />
|
||||
<div style={{ fontSize: "1.0rem" }}>
|
||||
<div style={{ fontSize: "1rem" }}>
|
||||
<Activity url="/activity" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function FeatureContainer({ tagline, description }: { tagline: string; description: string }) {
|
||||
return (
|
||||
<div className="row my-4 mb-16" style={{ lineHeight: "1.3", fontSize: "1.15rem" }}>
|
||||
<div className="col-12 col-lg-6">
|
||||
<h1 className="display-4">{tagline}</h1>
|
||||
<span>{description}</span>
|
||||
</div>
|
||||
<div style={{ maxWidth: "790px" }}>
|
||||
<h1 className="display-3 fw-light">{tagline}</h1>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import Divider from "components/util/Divider";
|
||||
import ExternalLink from "components/util/ExternalLink";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import React from "react";
|
||||
import { Alert } from "react-bootstrap";
|
||||
import Alert from "react-bootstrap/Alert";
|
||||
|
||||
export default function BarbatosPage() {
|
||||
useSetSubheader(["Import Scores", "Barbatos"]);
|
||||
@@ -23,12 +23,18 @@ export default function BarbatosPage() {
|
||||
Download <code>barbatos.dll</code>.{" "}
|
||||
<ul>
|
||||
<li>
|
||||
<ExternalLink href="https://f.wcal.xyz/2W9K7kXl">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://f.wcal.xyz/2W9K7kXl"
|
||||
>
|
||||
VIVID WAVE
|
||||
</ExternalLink>
|
||||
</li>
|
||||
<li>
|
||||
<ExternalLink href="https://f.wcal.xyz/3VqnDgcC">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://f.wcal.xyz/3VqnDgcC"
|
||||
>
|
||||
EXCEED GEAR
|
||||
</ExternalLink>
|
||||
.
|
||||
@@ -37,8 +43,16 @@ export default function BarbatosPage() {
|
||||
</li>
|
||||
<li>
|
||||
Download your config file{" "}
|
||||
<ExternalLink href="/client-file-flow/CXBarbatos">here</ExternalLink>. <br />
|
||||
<b>This file contains an API Key, which is meant to be kept secret!</b>
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="/client-file-flow/CXBarbatos"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
. <br />
|
||||
<Alert variant="warning" className="mt-2">
|
||||
This file contains an API Key, which is meant to be kept secret!
|
||||
</Alert>
|
||||
</li>
|
||||
<li>
|
||||
Place <code>barbatos.dll</code> and the above config file inside your SDVX
|
||||
|
||||
@@ -4,6 +4,7 @@ import ExternalLink from "components/util/ExternalLink";
|
||||
import Muted from "components/util/Muted";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import React from "react";
|
||||
import Alert from "react-bootstrap/Alert";
|
||||
|
||||
const WIN_BAT = `
|
||||
REM *** Set system-wide "_JAVA_OPTIONS" environment variable to use OpenGL pipeline (improved performance of > 30% potentially. Also use anti-aliasing for non-LR2 fonts, and finally allow Swing framework to utilize AA and GTKLookAndFeel for config window. ***
|
||||
@@ -32,7 +33,10 @@ export default function BeatorajaIRPage({ game }: { game: "bms" | "pms" }) {
|
||||
<ol className="instructions-list">
|
||||
<li>
|
||||
Download the latest version of the {name} IR{" "}
|
||||
<ExternalLink href="https://github.com/TNG-dev/tachi-beatoraja-ir/releases">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://github.com/TNG-dev/tachi-beatoraja-ir/releases"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
.
|
||||
@@ -44,7 +48,10 @@ export default function BeatorajaIRPage({ game }: { game: "bms" | "pms" }) {
|
||||
<b>NOT</b> support BMS scores achieved on beatoraja, and will not accept
|
||||
scores from the client.
|
||||
<br />
|
||||
<ExternalLink href="https://github.com/wcko87/lr2oraja/releases">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://github.com/wcko87/lr2oraja/releases"
|
||||
>
|
||||
LR2oraja
|
||||
</ExternalLink>{" "}
|
||||
is a one-file change for beatoraja that changes the settings to match
|
||||
@@ -68,7 +75,7 @@ export default function BeatorajaIRPage({ game }: { game: "bms" | "pms" }) {
|
||||
<li>
|
||||
Select {TachiConfig.name} IR.
|
||||
<br />
|
||||
<span className="text-warning">
|
||||
<Alert className="mt-2" variant="warning">
|
||||
If the IR isn't showing up, make sure you're launching the game with the{" "}
|
||||
<code>beatoraja-config.bat</code> file. Otherwise, IRs will never load.
|
||||
<br />
|
||||
@@ -80,6 +87,7 @@ export default function BeatorajaIRPage({ game }: { game: "bms" | "pms" }) {
|
||||
<a
|
||||
download="beatoraja-config.bat"
|
||||
href={`data:text/plain;base64,${window.btoa(WIN_BAT)}`}
|
||||
className="text-decoration-underline"
|
||||
>
|
||||
Windows
|
||||
</a>
|
||||
@@ -88,26 +96,32 @@ export default function BeatorajaIRPage({ game }: { game: "bms" | "pms" }) {
|
||||
<a
|
||||
download="beatoraja-config.sh"
|
||||
href={`data:text/plain;base64,${window.btoa(LINUX_SH)}`}
|
||||
className="text-decoration-underline"
|
||||
>
|
||||
Linux
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</Alert>
|
||||
</li>
|
||||
<li>
|
||||
Get an API token for the IR by clicking{" "}
|
||||
<ExternalLink href="/client-file-flow/CXBeatorajaIR">this link</ExternalLink>.
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="/client-file-flow/CXBeatorajaIR"
|
||||
>
|
||||
this link
|
||||
</ExternalLink>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Place the API token in the password field. Put your username in as well! The IR
|
||||
wont load if you don't have a username set.
|
||||
<br />
|
||||
<span className="text-warning">
|
||||
<Alert className="mt-2" variant="warning">
|
||||
<b>DO NOT PUT YOUR PASSWORD IN THE PASSWORD FIELD!</b>
|
||||
<br />
|
||||
For security reasons, you must put the API Key in that field, instead.
|
||||
</span>
|
||||
</Alert>
|
||||
</li>
|
||||
<li>
|
||||
That's it! Launch the game and start playing, your scores will automatically
|
||||
|
||||
@@ -3,6 +3,7 @@ import Divider from "components/util/Divider";
|
||||
import ExternalLink from "components/util/ExternalLink";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import React from "react";
|
||||
import Alert from "react-bootstrap/Alert";
|
||||
|
||||
export default function ChunitachiPage() {
|
||||
useSetSubheader(["Import Scores", "Chunitachi"]);
|
||||
@@ -20,19 +21,33 @@ export default function ChunitachiPage() {
|
||||
<ol className="instructions-list">
|
||||
<li>
|
||||
Download <code>chunitachi.dll</code> from{" "}
|
||||
<ExternalLink href="https://github.com/tomatosoupcan/ChunItachi/releases">
|
||||
Here
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://github.com/tomatosoupcan/ChunItachi/releases"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Download your <code>ChunItachi.ini</code> config file{" "}
|
||||
<ExternalLink href="/client-file-flow/CXChunitachi">here</ExternalLink>. <br />
|
||||
<b>This config file contains an API Key, which should be kept secret!</b>.
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="/client-file-flow/CXChunitachi"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
. <br />
|
||||
<Alert variant="warning" className="mt-2">
|
||||
This config file contains an API Key, which should be kept secret!
|
||||
</Alert>
|
||||
</li>
|
||||
<li>
|
||||
Follow the remaining install instructions on{" "}
|
||||
<ExternalLink href="https://github.com/tomatosoupcan/ChunItachi/blob/main/README.md">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://github.com/tomatosoupcan/ChunItachi/blob/main/README.md"
|
||||
>
|
||||
GitHub
|
||||
</ExternalLink>
|
||||
.
|
||||
|
||||
@@ -3,7 +3,7 @@ import Divider from "components/util/Divider";
|
||||
import ExternalLink from "components/util/ExternalLink";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import React from "react";
|
||||
import { Alert } from "react-bootstrap";
|
||||
import Alert from "react-bootstrap/Alert";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function FervidexPage() {
|
||||
@@ -22,12 +22,26 @@ export default function FervidexPage() {
|
||||
<ol className="instructions-list">
|
||||
<li>
|
||||
Download <code>fervidex.dll</code> from{" "}
|
||||
<ExternalLink href="https://client.fervidex.net/latest.zip">Here</ExternalLink>.
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://client.fervidex.net/latest.zip"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Download your <code>fervidex.json</code> config file{" "}
|
||||
<ExternalLink href="/client-file-flow/CXFervidex">here</ExternalLink>. <br />
|
||||
<b>This config file contains an API Key, which should be kept secret!</b>.
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="/client-file-flow/CXFervidex"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
. <br />
|
||||
<Alert variant="warning" className="mt-2">
|
||||
This config file contains an API Key, which should be kept secret!
|
||||
</Alert>
|
||||
</li>
|
||||
<li>
|
||||
Drop both files into the same folder as your <code>bm2dx.dll</code>.
|
||||
@@ -36,7 +50,10 @@ export default function FervidexPage() {
|
||||
<ul className="instructions-list">
|
||||
<li>
|
||||
You'll also need to download{" "}
|
||||
<ExternalLink href="https://client.fervidex.net/latest-chainloader.zip">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://client.fervidex.net/latest-chainloader.zip"
|
||||
>
|
||||
this
|
||||
</ExternalLink>
|
||||
, and extract it to the same folder.
|
||||
@@ -58,9 +75,8 @@ export default function FervidexPage() {
|
||||
Looking to import existing scores? Import from your network first. If that's not
|
||||
possible, enable "Sync Existing Scores" in{" "}
|
||||
<Link
|
||||
className="gentle-link"
|
||||
className="text-decoration-underline"
|
||||
to={"/u/me/integrations/services/fervidex"}
|
||||
style={{ color: "black", textDecoration: "underline" }}
|
||||
>
|
||||
<b>your settings.</b>
|
||||
</Link>
|
||||
|
||||
@@ -29,7 +29,10 @@ export default function ITGHookPage() {
|
||||
<ol className="instructions-list">
|
||||
<li>
|
||||
Download the latest version of <code>Tachi.lua</code>{" "}
|
||||
<ExternalLink href="https://github.com/TNG-Dev/Simply-Love-Tachi-Module">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://github.com/TNG-Dev/Simply-Love-Tachi-Module"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
.
|
||||
@@ -39,7 +42,13 @@ export default function ITGHookPage() {
|
||||
</li>
|
||||
<li>
|
||||
Get your <code>Tachi.json</code> file by clicking{" "}
|
||||
<ExternalLink href="/client-file-flow/CXITGHook">this link</ExternalLink>.
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="/client-file-flow/CXITGHook"
|
||||
>
|
||||
this link
|
||||
</ExternalLink>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Place this file inside your user profile. It should go next to
|
||||
|
||||
@@ -3,6 +3,7 @@ import Divider from "components/util/Divider";
|
||||
import ExternalLink from "components/util/ExternalLink";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import React from "react";
|
||||
import Alert from "react-bootstrap/Alert";
|
||||
|
||||
export default function KsHookPage() {
|
||||
useSetSubheader(["Import Scores", "Konaste Hook"]);
|
||||
@@ -20,15 +21,26 @@ export default function KsHookPage() {
|
||||
<ol className="instructions-list">
|
||||
<li>
|
||||
Download the latest version of <code>kshook.dll</code> from{" "}
|
||||
<ExternalLink href="https://djtrackers.com/kshook/latest.zip">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://djtrackers.com/kshook/latest.zip"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
Download your config file{" "}
|
||||
<ExternalLink href="/client-file-flow/CXKsHook">here</ExternalLink>. <br />
|
||||
<b>This file contains an API Key, which is meant to be kept secret!</b>
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="/client-file-flow/CXKsHook"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
. <br />
|
||||
<Alert variant="warning" className="mt-2">
|
||||
This file contains an API Key, which is meant to be kept secret!
|
||||
</Alert>
|
||||
</li>
|
||||
<li>Place all of these files inside your SDVX6 (EAC) folder.</li>
|
||||
<li>
|
||||
|
||||
@@ -4,6 +4,7 @@ import ExternalLink from "components/util/ExternalLink";
|
||||
import Muted from "components/util/Muted";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import React from "react";
|
||||
import Alert from "react-bootstrap/Alert";
|
||||
|
||||
export default function SilentHookPage() {
|
||||
useSetSubheader(["Import Scores", "Silent Hook"]);
|
||||
@@ -21,8 +22,11 @@ export default function SilentHookPage() {
|
||||
<ol className="instructions-list">
|
||||
<li>
|
||||
Download <code>silent</code> from{" "}
|
||||
<ExternalLink href="https://zkldi.xyz/stuff/silent-latest.zip">
|
||||
Here
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://zkldi.xyz/stuff/silent-latest.zip"
|
||||
>
|
||||
here
|
||||
</ExternalLink>{" "}
|
||||
and place all the <code>.dll</code> files in the same folder as{" "}
|
||||
<code>popn22.dll</code>.
|
||||
@@ -31,8 +35,16 @@ export default function SilentHookPage() {
|
||||
</li>
|
||||
<li>
|
||||
Download your config file to the same folder{" "}
|
||||
<ExternalLink href="/client-file-flow/CXSilentHook">here</ExternalLink>. <br />
|
||||
<b>This file contains an API Key, which is meant to be kept secret!</b>
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="/client-file-flow/CXSilentHook"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
. <br />
|
||||
<Alert variant="warning" className="mt-2">
|
||||
This file contains an API Key, which is meant to be kept secret!
|
||||
</Alert>
|
||||
</li>
|
||||
<li>
|
||||
Add <code>silent</code> to your startup script as a hook.
|
||||
|
||||
@@ -4,6 +4,7 @@ import Divider from "components/util/Divider";
|
||||
import Muted from "components/util/Muted";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import React from "react";
|
||||
import Alert from "react-bootstrap/Alert";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function USCDBPage() {
|
||||
@@ -12,12 +13,14 @@ export default function USCDBPage() {
|
||||
<>
|
||||
<TISInfo name="USC Database" />
|
||||
<Divider />
|
||||
<Muted>
|
||||
<Alert variant="info">
|
||||
This method is intended for syncing up with existing scores. For new scores, you
|
||||
should set up the <Link to="/import/usc-ir">USC IR</Link>, for automatic score
|
||||
uploading.
|
||||
</Muted>
|
||||
<br />
|
||||
should set up the{" "}
|
||||
<Link className="text-decoration-underline" to="/import/usc-ir">
|
||||
USC IR
|
||||
</Link>
|
||||
, for automatic score uploading.
|
||||
</Alert>
|
||||
<Muted>
|
||||
Note: If you submit a score on a chart that {TachiConfig.name} doesn't recognise,
|
||||
you'll need to wait until atleast 2 other players submit scores for that chart
|
||||
|
||||
@@ -38,8 +38,13 @@ export default function USCIRPage() {
|
||||
<Divider />
|
||||
<li>
|
||||
Grab an API Token from{" "}
|
||||
<ExternalLink href="/client-file-flow/CXUSCIR">here</ExternalLink>, and place it
|
||||
in the API Token field.
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="/client-file-flow/CXUSCIR"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
, and place it in the API Token field.
|
||||
</li>
|
||||
<li>That's it! Your scores will now automatically upload to the server.</li>
|
||||
</ol>
|
||||
|
||||
@@ -50,7 +50,10 @@ export default function WACCASiteImportPage() {
|
||||
<div>
|
||||
<h1 className="text-center mb-4">What Is WaccaMyPageScraper?</h1>
|
||||
<div>
|
||||
<ExternalLink href="https://github.com/XezolesS/WaccaMyPageScraper/">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://github.com/XezolesS/WaccaMyPageScraper/"
|
||||
>
|
||||
WaccaMyPageScraper
|
||||
</ExternalLink>{" "}
|
||||
is a scraper for the official WACCA MyPage service site. Since the site is offline
|
||||
|
||||
@@ -9,9 +9,10 @@ import Muted from "components/util/Muted";
|
||||
import SelectButton from "components/util/SelectButton";
|
||||
import { UserSettingsContext } from "context/UserSettingsContext";
|
||||
import { useFormik } from "formik";
|
||||
import React, { useContext, useState } from "react";
|
||||
import { Alert, Button, Form } from "react-bootstrap";
|
||||
import React, { useContext, useRef, useState } from "react";
|
||||
import { Alert, Button, Form, Stack } from "react-bootstrap";
|
||||
import { UserDocument, UserSettingsDocument } from "tachi-common";
|
||||
import { SetState } from "types/react";
|
||||
|
||||
interface Props {
|
||||
reqUser: UserDocument;
|
||||
@@ -269,10 +270,18 @@ function PreferencesForm({ reqUser }: { reqUser: UserDocument }) {
|
||||
|
||||
function ImageForm({ reqUser }: { reqUser: UserDocument }) {
|
||||
const [pfp, setPfp] = useState<File | undefined>();
|
||||
const pfpInput = useRef(null);
|
||||
const [banner, setBanner] = useState<File | undefined>();
|
||||
const bannerInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleReset = (ref: React.MutableRefObject<HTMLInputElement | null>) => {
|
||||
if (ref.current) {
|
||||
ref.current.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-column">
|
||||
<Stack gap={4}>
|
||||
<Alert variant="danger">
|
||||
Do not set inappropriate stuff as your avatar/banner. If you have to ask, the answer
|
||||
is probably no.
|
||||
@@ -282,11 +291,11 @@ function ImageForm({ reqUser }: { reqUser: UserDocument }) {
|
||||
<input
|
||||
className="form-control"
|
||||
accept="image/png,image/jpeg,image/gif"
|
||||
tabIndex={1}
|
||||
type="file"
|
||||
id="pfp"
|
||||
multiple={false}
|
||||
onChange={(e) => setPfp(e.target.files![0])}
|
||||
ref={pfpInput}
|
||||
/>
|
||||
<div className="d-flex justify-content-center mt-4">
|
||||
<ProfilePicture
|
||||
@@ -294,18 +303,24 @@ function ImageForm({ reqUser }: { reqUser: UserDocument }) {
|
||||
src={pfp ? URL.createObjectURL(pfp) : ToAPIURL(`/users/${reqUser.id}/pfp`)}
|
||||
/>
|
||||
</div>
|
||||
<FileUploadController file={pfp} reqUser={reqUser} type="pfp" />
|
||||
<FileUploadController
|
||||
setFile={setPfp}
|
||||
reset={() => handleReset(pfpInput)}
|
||||
file={pfp}
|
||||
reqUser={reqUser}
|
||||
type="pfp"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label>Profile Banner</Form.Label>
|
||||
<input
|
||||
className="form-control"
|
||||
accept="image/png,image/jpeg,image/gif"
|
||||
tabIndex={2}
|
||||
type="file"
|
||||
id="banner"
|
||||
multiple={false}
|
||||
onChange={(e) => setBanner(e.target.files![0])}
|
||||
ref={bannerInput}
|
||||
/>
|
||||
<div
|
||||
className="d-flex justify-content-center mt-4 rounded"
|
||||
@@ -322,15 +337,21 @@ function ImageForm({ reqUser }: { reqUser: UserDocument }) {
|
||||
})`,
|
||||
}}
|
||||
/>
|
||||
<FileUploadController file={banner} reqUser={reqUser} type="banner" />
|
||||
<FileUploadController
|
||||
setFile={setBanner}
|
||||
reset={() => handleReset(bannerInput)}
|
||||
file={banner}
|
||||
reqUser={reqUser}
|
||||
type="banner"
|
||||
/>
|
||||
</Form.Group>
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function SocialMediaForm({ reqUser }: { reqUser: UserDocument }) {
|
||||
const placeholders = {
|
||||
discord: "Example#0000",
|
||||
discord: "Discord Username",
|
||||
twitter: "Twitter Handle",
|
||||
twitch: "Twitch Username",
|
||||
github: "Github Username",
|
||||
@@ -429,32 +450,57 @@ function FileUploadController({
|
||||
file,
|
||||
type,
|
||||
reqUser,
|
||||
setFile,
|
||||
reset,
|
||||
}: {
|
||||
file?: File;
|
||||
type: "pfp" | "banner";
|
||||
reqUser: UserDocument;
|
||||
setFile: SetState<File | undefined>;
|
||||
reset: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="d-flex mt-8">
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const res = await APIFetchV1(
|
||||
`/users/me/${type}`,
|
||||
{ method: "DELETE" },
|
||||
true,
|
||||
true
|
||||
);
|
||||
<div className="d-flex mt-8 justify-content-end">
|
||||
{file ? (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setFile(undefined);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
onClick={async () => {
|
||||
if (
|
||||
confirm(
|
||||
`Are you sure you want to clear your ${
|
||||
type === "pfp" ? "profile picture?" : "profile banner?"
|
||||
}`
|
||||
)
|
||||
) {
|
||||
const res = await APIFetchV1(
|
||||
`/users/me/${type}`,
|
||||
{ method: "DELETE" },
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
if (res.success) {
|
||||
DelayedPageReload();
|
||||
if (res.success) {
|
||||
DelayedPageReload();
|
||||
}
|
||||
}
|
||||
}}
|
||||
disabled={
|
||||
type === "pfp" ? !reqUser.customPfpLocation : !reqUser.customBannerLocation
|
||||
}
|
||||
}}
|
||||
disabled={!reqUser.customPfpLocation}
|
||||
className="ms-auto"
|
||||
variant="secondary"
|
||||
>
|
||||
Unset
|
||||
</Button>
|
||||
variant="danger"
|
||||
>
|
||||
Clear {type === "pfp" ? "Profile Picture" : "Profile Banner"}
|
||||
</Button>
|
||||
)}
|
||||
{file && (
|
||||
<div className="mx-auto">
|
||||
<SizeWarner bytes={file.size} cap={1024} />
|
||||
@@ -480,7 +526,6 @@ function FileUploadController({
|
||||
window.location.reload();
|
||||
}
|
||||
}}
|
||||
className="ms-auto"
|
||||
variant="success"
|
||||
disabled={file.size > 1024 * 1000}
|
||||
>
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
ClumpedActivitySession,
|
||||
} from "types/tachi";
|
||||
import { InnerQuestSectionGoal } from "components/targets/quests/Quest";
|
||||
import ProfilePicture, { ProfilePictureSmall } from "components/user/ProfilePicture";
|
||||
import ProfilePicture from "components/user/ProfilePicture";
|
||||
import SupporterIcon from "components/util/SupporterIcon";
|
||||
|
||||
// Records activity for a group of users on a GPT. Also used for single users.
|
||||
@@ -292,7 +292,7 @@ function ScoresActivity({
|
||||
<div className="timeline-content-inner" onClick={() => setShow(!show)}>
|
||||
<div className="timeline-content-title">
|
||||
<span className="me-2">
|
||||
<ProfilePictureSmall user={user} toGPT={`${game}/${playtype}`} />
|
||||
<ProfilePicture size="sm" user={user} toGPT={`${game}/${playtype}`} />
|
||||
</span>
|
||||
<Icon
|
||||
type={`chevron-${show ? "down" : "right"}`}
|
||||
@@ -374,7 +374,7 @@ function GoalActivity({
|
||||
<div className="timeline-content-inner" onClick={() => setShow(!show)}>
|
||||
<div className="timeline-content-title">
|
||||
<span className="me-2">
|
||||
<ProfilePictureSmall user={user} toGPT={`${game}/${playtype}`} />
|
||||
<ProfilePicture size="sm" user={user} toGPT={`${game}/${playtype}`} />
|
||||
</span>
|
||||
<Icon
|
||||
type={`chevron-${show ? "down" : "right"}`}
|
||||
@@ -443,7 +443,7 @@ function QuestActivity({
|
||||
<div className="timeline-content-title">
|
||||
<span style={{ fontSize: "1.15rem" }}>
|
||||
<span className="me-2">
|
||||
<ProfilePictureSmall user={user} toGPT={`${game}/${playtype}`} />
|
||||
<ProfilePicture size="sm" user={user} toGPT={`${game}/${playtype}`} />
|
||||
</span>
|
||||
<UGPTLink reqUser={user} game={game} playtype={playtype} /> completed
|
||||
the{" "}
|
||||
@@ -495,7 +495,7 @@ function SessionActivity({
|
||||
<div className="timeline-content-inner" onClick={() => setShow(!show)}>
|
||||
<div className="timeline-content-title">
|
||||
<span className="me-2">
|
||||
<ProfilePictureSmall user={user} toGPT={`${game}/${playtype}`} />
|
||||
<ProfilePicture size="sm" user={user} toGPT={`${game}/${playtype}`} />
|
||||
</span>
|
||||
<Icon
|
||||
type={`chevron-${show ? "down" : "right"}`}
|
||||
@@ -641,7 +641,7 @@ function ClassAchievementActivity({
|
||||
<div className="timeline-content-inner">
|
||||
<div className="timeline-content-title">
|
||||
<span className="me-2">
|
||||
<ProfilePictureSmall
|
||||
<ProfilePicture size="sm"
|
||||
user={user}
|
||||
toGPT={`${data.game}/${data.playtype}`}
|
||||
/>
|
||||
|
||||
@@ -9,7 +9,10 @@ export default function TISInfo({ name }: { name: string }) {
|
||||
<ol className="instructions-list">
|
||||
<li>
|
||||
Download the latest version of the {TachiConfig.name} Import Scripts{" "}
|
||||
<ExternalLink href="https://github.com/TNG-dev/tachi-import-scripts/releases">
|
||||
<ExternalLink
|
||||
className="text-decoration-underline"
|
||||
href="https://github.com/TNG-dev/tachi-import-scripts/releases"
|
||||
>
|
||||
here
|
||||
</ExternalLink>
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AllLUGPTStatsContext } from "context/AllLUGPTStatsContext";
|
||||
import { UserSettingsContext } from "context/UserSettingsContext";
|
||||
import React, { useContext } from "react";
|
||||
import React, { useContext, useEffect } from "react";
|
||||
import { UserDocument, UserGameStats } from "tachi-common";
|
||||
import useApiQuery from "components/util/query/useApiQuery";
|
||||
import Nav from "react-bootstrap/Nav";
|
||||
@@ -30,13 +30,15 @@ export function HeaderMenu({
|
||||
"game_stats",
|
||||
]);
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
if (data) {
|
||||
setUGS(data);
|
||||
}
|
||||
if (data) {
|
||||
setUGS(data);
|
||||
}
|
||||
}, [error, data]);
|
||||
|
||||
return (
|
||||
<Nav className="p-4 d-flex flex-column flex-lg-row align-content-between gap-2 gap-lg-4 overflow-y-auto overflow-y-lg-visible scrollbar-hide h-100">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import LinkButton from "components/util/LinkButton";
|
||||
import { UserDocument } from "tachi-common/types/documents";
|
||||
import { ProfilePictureSmall } from "components/user/ProfilePicture";
|
||||
import ProfilePicture from "components/user/ProfilePicture";
|
||||
import { UserNotificationButton } from "./UserNotificationButton";
|
||||
import { UserProfileDropdown } from "./UserProfileDropdown";
|
||||
import { SearchButton } from "./SearchButton";
|
||||
@@ -28,8 +28,7 @@ export default function UserArea({
|
||||
<UserNotificationButton />
|
||||
<UserProfileDropdown style={dropdownMenuStyle} user={user} />
|
||||
<div className="h-14 w-14 d-flex d-lg-none justify-content-center align-items-center">
|
||||
{/* a temporary solution */}
|
||||
<ProfilePictureSmall user={user} />
|
||||
<ProfilePicture size="sm" user={user} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -10,6 +10,7 @@ import SignOut from "components/util/SignOut";
|
||||
import QuickDropdown from "components/ui/QuickDropdown";
|
||||
import { TextColour } from "types/bootstrap";
|
||||
import DropdownNavLink from "components/ui/DropdownNavLink";
|
||||
import ProfilePicture from "components/user/ProfilePicture";
|
||||
|
||||
function UserProfileDropdownToggle({ user }: { user: UserDocument }) {
|
||||
const [heySplash] = useState(RFA(heySplashes));
|
||||
@@ -25,13 +26,7 @@ function UserProfileDropdownToggle({ user }: { user: UserDocument }) {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<img
|
||||
alt={user.username}
|
||||
height={32}
|
||||
width={32}
|
||||
src={ToAPIURL("/users/me/pfp")}
|
||||
className="object-fit-cover rounded"
|
||||
/>
|
||||
<ProfilePicture size="sm" link={false} user={user} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,12 @@ import SplashImage from "../misc/SplashImage";
|
||||
|
||||
export function SplashScreen({ broke }: { broke: string }) {
|
||||
return (
|
||||
<div id="splash-screen" className="splash-screen">
|
||||
<div
|
||||
id="splash-screen"
|
||||
className="bg-body position-fixed inset-0 d-flex flex-column justify-content-center align-items-center"
|
||||
>
|
||||
<SplashImage />
|
||||
{!broke && <CircularProgress className="splash-screen-spinner" />}
|
||||
{!broke && <CircularProgress />}
|
||||
{broke && <p className="mt-4">{broke}</p>}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,101 +5,47 @@ import { Link } from "react-router-dom";
|
||||
|
||||
export default function ProfilePicture({
|
||||
user,
|
||||
src,
|
||||
src: imgUrl,
|
||||
toGPT = "",
|
||||
link = true,
|
||||
size = "lg",
|
||||
}: {
|
||||
user: UserDocument | string;
|
||||
user: UserDocument;
|
||||
|
||||
/**
|
||||
* Specify an image src instead of infering one from the user's info
|
||||
*/
|
||||
src?: string;
|
||||
|
||||
/**
|
||||
* Whether or not this profile picture should be a link (default = true)
|
||||
*/
|
||||
link?: boolean;
|
||||
|
||||
/**
|
||||
* sm = 32px, lg = 128px
|
||||
*/
|
||||
size?: "sm" | "lg";
|
||||
|
||||
/**
|
||||
* When clicking this this profile, should it take you to a UGPT page?
|
||||
*/
|
||||
toGPT?: string;
|
||||
}) {
|
||||
if (typeof user === "string") {
|
||||
const dimensions = size === "sm" ? 32 : 128;
|
||||
const props = {
|
||||
src: imgUrl ? imgUrl : ToAPIURL(`/users/${user.id}/pfp`),
|
||||
alt: `${user.username}'s Profile Picture`,
|
||||
height: dimensions,
|
||||
width: dimensions,
|
||||
className: "d-inline-block object-fit-cover bg-body-tertiary shadow-sm rounded fs-0",
|
||||
};
|
||||
if (link) {
|
||||
return (
|
||||
<Link to={`/u/${user}/${toGPT}`}>
|
||||
<img
|
||||
src={src ? src : ToAPIURL(`/users/${user}/pfp`)}
|
||||
alt={`${user}'s Profile Picture`}
|
||||
className="rounded"
|
||||
style={{
|
||||
width: "128px",
|
||||
height: "128px",
|
||||
objectFit: "cover",
|
||||
boxShadow: "0px 0px 10px 0px #000000",
|
||||
}}
|
||||
/>
|
||||
<Link to={`/u/${user.username}/${toGPT}`}>
|
||||
<img {...props} />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link to={`/u/${user.username}/${toGPT}`}>
|
||||
<img
|
||||
src={src ? src : ToAPIURL(`/users/${user.id}/pfp`)}
|
||||
alt={`${user.username}'s Profile Picture`}
|
||||
className="rounded"
|
||||
style={{
|
||||
width: "128px",
|
||||
height: "128px",
|
||||
objectFit: "cover",
|
||||
boxShadow: "0px 0px 10px 0px #000000",
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function ProfilePictureSmall({
|
||||
user,
|
||||
src,
|
||||
toGPT = "",
|
||||
}: {
|
||||
user: UserDocument | string;
|
||||
src?: string;
|
||||
|
||||
/**
|
||||
* When clicking this this profile, should it take you to a UGPT page?
|
||||
*/
|
||||
toGPT?: string;
|
||||
}) {
|
||||
if (toGPT) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
toGPT = `games/${toGPT}`;
|
||||
}
|
||||
|
||||
if (typeof user === "string") {
|
||||
return (
|
||||
<Link to={`/u/${user}/${toGPT}`}>
|
||||
<img
|
||||
src={src ? src : ToAPIURL(`/users/${user}/pfp`)}
|
||||
alt={`${user}'s Profile Picture`}
|
||||
className="rounded"
|
||||
style={{
|
||||
width: "32px",
|
||||
height: "32px",
|
||||
objectFit: "cover",
|
||||
boxShadow: "0px 0px 10px 0px #000000",
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link to={`/u/${user.username}/${toGPT}`}>
|
||||
<img
|
||||
src={src ? src : ToAPIURL(`/users/${user.id}/pfp`)}
|
||||
alt={`${user.username}'s Profile Picture`}
|
||||
className="rounded"
|
||||
style={{
|
||||
width: "32px",
|
||||
height: "32px",
|
||||
objectFit: "cover",
|
||||
boxShadow: "0px 0px 10px 0px #000000",
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
return <img {...props} />;
|
||||
}
|
||||
|
||||
@@ -637,11 +637,11 @@ $line-height-base: 1.5;
|
||||
$line-height-sm: 1.25;
|
||||
$line-height-lg: 2;
|
||||
|
||||
$h1-font-size: $font-size-base * 2;
|
||||
$h2-font-size: $font-size-base * 1.75;
|
||||
$h3-font-size: $font-size-base * 1.5;
|
||||
$h4-font-size: $font-size-base * 1.25;
|
||||
$h5-font-size: $font-size-base * 1.125;
|
||||
$h1-font-size: $font-size-base * 2.5;
|
||||
$h2-font-size: $font-size-base * 2;
|
||||
$h3-font-size: $font-size-base * 1.75;
|
||||
$h4-font-size: $font-size-base * 1.5;
|
||||
$h5-font-size: $font-size-base * 1.25;
|
||||
$h6-font-size: $font-size-base;
|
||||
// scss-docs-end font-variables
|
||||
|
||||
@@ -650,7 +650,7 @@ $h6-font-size: $font-size-base;
|
||||
$rfs-rem-value: 13;
|
||||
$rfs-base-value: $font-size-base;
|
||||
$rfs-factor: 25; // setting this <= 1 throws an error ==> bootstrap-scss/vendor/_rfs.scss:34
|
||||
$rfs-breakpoint: 648px;
|
||||
$rfs-breakpoint: 576px;
|
||||
$rfs-class: "enable"; // this allows us to enable it with a class selector only when we want it
|
||||
|
||||
// scss-docs-start font-sizes
|
||||
@@ -715,8 +715,8 @@ $hr-height: null !default; // Deprecated in v5.2.0
|
||||
// fusv-enable
|
||||
|
||||
$hr-border-color: null !default; // Allows for inherited colors
|
||||
$hr-border-width: var(--#{$prefix}border-width) !default;
|
||||
$hr-opacity: 0.25 !default;
|
||||
$hr-border-width: var(--#{$prefix}border-width);
|
||||
$hr-opacity: 0.1;
|
||||
|
||||
$legend-margin-bottom: 0.5rem;
|
||||
$legend-font-size: 1.5rem;
|
||||
|
||||
Reference in New Issue
Block a user