More Large Commits

This commit is contained in:
zkldi
2021-08-06 02:22:14 +01:00
parent c6b7e51516
commit 7722ca6759
9 changed files with 389 additions and 234 deletions
@@ -166,6 +166,7 @@ function LastSession({ reqUser, game, playtype }: { reqUser: PublicUserDocument
{...data}
game={game}
playtype={playtype}
reqUser={reqUser}
/>
</div>
</>
@@ -183,9 +184,11 @@ function RecentSessionScoreInfo({
sessionData,
game,
playtype,
reqUser,
}: {
session: SessionDocument;
sessionData: SessionReturns;
reqUser: PublicUserDocument;
} & GamePT) {
const highlightedScores = sessionData.scores.find(e => e.highlight);
@@ -250,7 +253,7 @@ function RecentSessionScoreInfo({
</div>
</div>
<div className="col-12 mt-4">
<IIDXScoreTable dataset={dataset as any} pageLen={5} />
<IIDXScoreTable dataset={dataset as any} pageLen={5} reqUser={reqUser} />
</div>
</div>
);
@@ -199,7 +199,7 @@ function ScoresOverview({ reqUser, game, playtype }: { reqUser: PublicUserDocume
style={{ height: 500 }}
{...{ isLoading, dataset: data, error }}
>
<IIDXScoreTable dataset={data!} />
<IIDXScoreTable dataset={data!} reqUser={reqUser} />
</LoadingWrapper>
) : (
<ScoresSearch {...{ reqUser, game, playtype, search }} />
@@ -221,7 +221,7 @@ function ScoresSearch({
return (
<LoadingWrapper style={{ height: 500 }} {...{ isLoading, error, dataset: data }}>
<IIDXScoreTable dataset={data!} />
<IIDXScoreTable dataset={data!} reqUser={reqUser} />
</LoadingWrapper>
);
}
@@ -92,7 +92,6 @@ export default function IIDXPBTable({
<IIDXPBDropdown
chart={pb.__related.chart}
reqUser={reqUser}
pb={pb}
game={"iidx"}
playtype={"SP"}
/>
@@ -4,36 +4,28 @@ import IIDXLampCell from "components/tables/cells/IIDXLampCell";
import ScoreCell from "components/tables/cells/ScoreCell";
import TimestampCell from "components/tables/cells/TimestampCell";
import MiniTable from "components/tables/components/MiniTable";
import AsyncLoader from "components/util/AsyncLoader";
import ExternalLink from "components/util/ExternalLink";
import Icon from "components/util/Icon";
import Loading from "components/util/Loading";
import SelectButton from "components/util/SelectButton";
import SelectNav from "components/util/SelectNav";
import React, { useMemo, useState } from "react";
import { Nav } from "react-bootstrap";
import { useQuery } from "react-query";
import {
ChartDocument,
Lamps,
PBScoreDocument,
PublicUserDocument,
ScoreDocument,
} from "tachi-common";
import { ChartDocument, PBScoreDocument, PublicUserDocument } from "tachi-common";
import { UGPTChartPBComposition } from "types/api-returns";
import { GamePT } from "types/react";
import { APIFetchV1 } from "util/api";
import { IsScore } from "util/asserts";
import DropdownStructure from "./components/DropdownStructure";
import { IIDXGraphsComponent, ModsTable, ScoreInfo } from "./components/IIDXScoreDropdownParts";
import JudgementTable from "./components/JudgementTable";
type LampTypes = "NORMAL" | "EASY" | "HARD" | "EX_HARD";
import PBNote from "./components/PBNote";
export default function IIDXPBDropdown({
pb,
game,
playtype,
reqUser,
chart,
}: {
pb: PBScoreDocument<"iidx:SP" | "iidx:DP">;
reqUser: PublicUserDocument;
chart: ChartDocument<"iidx:SP">;
} & GamePT) {
@@ -67,36 +59,30 @@ export default function IIDXPBDropdown({
}
return (
<div className="p-2">
<div className="row h-100 mb-0 align-items-center">
<div className="col-3 col-xl-2">
<div className="btn-group-vertical">
<SelectButton setValue={setView} value={view} id="pb">
<Icon type="trophy" />
PB Info
</SelectButton>
<SelectButton setValue={setView} value={view} id="scorePB">
<Icon type="star-half-alt" />
Best Score
</SelectButton>
<SelectButton setValue={setView} value={view} id="lampPB">
<Icon type="lightbulb" />
Best Lamp
</SelectButton>
<SelectButton setValue={setView} value={view} id="history" disabled>
<Icon type="history" />
Play History
</SelectButton>
</div>
</div>
<div className="col-9 col-xl-10">
<div className="row mb-0">
{view === "history" ? <></> : <DocumentDisplay pbData={data} view={view} />}
</div>
</div>
</div>
</div>
<DropdownStructure
buttons={
<>
<SelectButton setValue={setView} value={view} id="pb">
<Icon type="trophy" />
PB Info
</SelectButton>
<SelectButton setValue={setView} value={view} id="scorePB">
<Icon type="star-half-alt" />
Best Score
</SelectButton>
<SelectButton setValue={setView} value={view} id="lampPB">
<Icon type="lightbulb" />
Best Lamp
</SelectButton>
<SelectButton setValue={setView} value={view} id="history" disabled>
<Icon type="history" />
Play History
</SelectButton>
</>
}
>
{view === "history" ? <></> : <DocumentDisplay pbData={data} view={view} />}
</DropdownStructure>
);
}
@@ -105,7 +91,7 @@ function DocumentDisplay({
view,
}: {
pbData: UGPTChartPBComposition<"iidx:SP">;
view: "pb" | "scorePB" | "lampPB" | "bpPB" | "history";
view: "pb" | "scorePB" | "lampPB" | "bpPB";
}) {
const idMap = {
scorePB: pbData.pb.composedFrom.scorePB,
@@ -122,134 +108,26 @@ function DocumentDisplay({
return pbData.scores.filter(e => e.scoreID === idMap[view])[0];
}, [view]);
const [lamp, setLamp] = useState(LampToKey(currentDoc.scoreData.lamp));
let gaugeStatus: "none" | "single" | "gsm" = "none";
if (currentDoc.scoreData.hitMeta.gsm) {
gaugeStatus = "gsm";
} else if (currentDoc.scoreData.hitMeta.gaugeHistory) {
gaugeStatus = "single";
}
const shouldDisable = (r: LampTypes) => {
if (gaugeStatus === "gsm") {
return false;
} else if (gaugeStatus === "single") {
return r !== LampToKey(currentDoc.scoreData.lamp);
}
return true;
};
// @ts-expect-error temp
currentDoc.comment = "THIS IS AN EXAMPLE COMMENT.";
return (
<>
<div className="col-9">
<div className="row">
<div className="col-12 d-flex justify-content-center">
<Nav variant="pills">
<SelectNav
id="EASY"
value={lamp}
setValue={setLamp}
disabled={shouldDisable("EASY")}
>
Easy
</SelectNav>
<SelectNav
id="NORMAL"
value={lamp}
setValue={setLamp}
disabled={shouldDisable("NORMAL")}
>
Normal
</SelectNav>
<SelectNav
id="HARD"
value={lamp}
setValue={setLamp}
disabled={shouldDisable("HARD")}
>
Hard
</SelectNav>
<SelectNav
id="EX_HARD"
value={lamp}
setValue={setLamp}
disabled={shouldDisable("EX_HARD")}
>
Ex Hard
</SelectNav>
</Nav>
</div>
<div className="col-12">
{gaugeStatus === "gsm" ? (
<GraphComponent
type={lamp}
values={currentDoc.scoreData.hitMeta.gsm![lamp]}
/>
) : gaugeStatus === "single" ? (
<GraphComponent
type={lamp}
values={currentDoc.scoreData.hitMeta.gaugeHistory!}
/>
) : (
<div
className="d-flex align-items-center justify-content-center"
style={{ height: "200px" }}
>
<span className="text-muted">No gauge data :(</span>
</div>
)}
</div>
{view !== "pb" ? (
<IIDXGraphsComponent score={currentDoc} />
{IsScore(currentDoc) ? (
<>
<div className="col-12">
<table className="table">
<thead>
<tr>
<td colSpan={100}>Score Info</td>
</tr>
</thead>
<tbody>
<tr>
<ScoreCell
score={currentDoc}
game="iidx"
playtype="SP"
/>
<DeltaCell
game="iidx"
playtype="SP"
score={currentDoc.scoreData.score}
percent={currentDoc.scoreData.percent}
grade={currentDoc.scoreData.grade}
/>
<IIDXLampCell sc={currentDoc} />
<TimestampCell time={currentDoc.timeAchieved} />
</tr>
</tbody>
</table>
</div>
{pbData.pb.composedFrom.lampPB === pbData.pb.composedFrom.scorePB && (
<ScoreInfo score={currentDoc} />
{currentDoc.comment && (
<div className="col-12">
<small>
Looks like your best lamp and your best score was the same
score!
</small>
<em>&quot;{currentDoc.comment}&quot;</em>
</div>
)}
</>
) : (
<div className="col-12">
<small>
Your PB is the combination of your best score and your best lamp.
Read more{" "}
<ExternalLink href="https://tachi.readthedocs.io/en/latest/user/pbs-scores/">
{" "}
here.
</ExternalLink>
</small>
<PBNote />
</div>
)}
</div>
@@ -260,74 +138,8 @@ function DocumentDisplay({
playtype="SP"
judgements={currentDoc.scoreData.judgements}
/>
{/* @ts-expect-error temp */}
<ModsTable view={view} doc={currentDoc} />
{IsScore(currentDoc) && <ModsTable score={currentDoc} />}
</div>
</>
);
}
function ModsTable({
view,
doc,
}: {
view: "pb" | "lampPB" | "scorePB" | "bpPB";
doc: PBScoreDocument<"iidx:SP"> | ScoreDocument<"iidx:SP">;
}) {
if (view === "pb") {
return null;
}
const sc = doc as ScoreDocument<"iidx:SP">;
if (!sc.scoreMeta.assist && !sc.scoreMeta.random) {
return null;
}
return (
<MiniTable className="text-center table-sm" headers={["Mods"]} colSpan={2}>
{sc.scoreMeta.random && (
<tr>
<td>Note</td>
<td>{sc.scoreMeta.random}</td>
</tr>
)}
{sc.scoreMeta.assist && (
<tr>
<td>Assist</td>
<td>{sc.scoreMeta.assist}</td>
</tr>
)}
</MiniTable>
);
}
function GraphComponent({ type, values }: { type: LampTypes; values: (number | null)[] }) {
return (
<IIDXLampChart
height="200px"
mobileHeight="175px"
type={type}
data={[
{
id: type,
data: values.map((e, i) => ({ x: i, y: e ?? 0 })),
},
]}
/>
);
}
function LampToKey(lamp: Lamps["iidx:SP"]): LampTypes {
if (lamp === "CLEAR") {
return "NORMAL";
} else if (lamp === "EASY CLEAR") {
return "EASY";
} else if (lamp === "HARD CLEAR") {
return "HARD";
} else if (lamp === "EX HARD CLEAR") {
return "EX_HARD";
}
return "NORMAL";
}
@@ -0,0 +1,107 @@
import Icon from "components/util/Icon";
import Loading from "components/util/Loading";
import SelectButton from "components/util/SelectButton";
import React, { useState } from "react";
import { UGPTChartPBComposition } from "types/api-returns";
import { GamePT } from "types/react";
import { APIFetchV1 } from "util/api";
import DropdownStructure from "./components/DropdownStructure";
import { PublicUserDocument, ChartDocument, ScoreDocument } from "tachi-common";
import { useQuery } from "react-query";
import { IsScore } from "util/asserts";
import { IIDXGraphsComponent, ScoreInfo, ModsTable } from "./components/IIDXScoreDropdownParts";
import JudgementTable from "./components/JudgementTable";
import PBNote from "./components/PBNote";
export default function IIDXScoreDropdown({
thisScore,
game,
playtype,
reqUser,
chart,
}: {
thisScore: ScoreDocument<"iidx:SP" | "iidx:DP">;
reqUser: PublicUserDocument;
chart: ChartDocument<"iidx:SP">;
} & GamePT) {
const [view, setView] = useState<"vsPB" | "moreInfo" | "history">("moreInfo");
const { isLoading, error, data } = useQuery(
`/users/${reqUser.id}/games/${game}/${playtype}/pbs/${chart.chartID}?getComposition=true`,
async () => {
const res = await APIFetchV1<UGPTChartPBComposition<"iidx:SP">>(
`/users/${reqUser.id}/games/${game}/${playtype}/pbs/${chart.chartID}?getComposition=true`
);
if (!res.success) {
throw new Error(res.description);
}
return res.body;
}
);
if (error) {
return <>An error has occured. Nice.</>;
}
if (isLoading || !data) {
return (
<div style={{ height: "200px" }} className="d-flex align-items-center">
<Loading />
</div>
);
}
let content = null;
thisScore.comment = "THIS IS AN EXAMPLE COMMENT.";
if (view === "moreInfo") {
content = (
<>
<div className="col-9">
<div className="row">
<IIDXGraphsComponent score={thisScore} />
{thisScore.comment && (
<div className="col-12">
<em>&quot;{thisScore.comment}&quot;</em>
</div>
)}
</div>
</div>
<div className="col-3 align-self-center">
<JudgementTable
game="iidx"
playtype="SP"
judgements={thisScore.scoreData.judgements}
/>
{IsScore(thisScore) && <ModsTable score={thisScore} />}
</div>
</>
);
}
return (
<DropdownStructure
buttons={
<>
<SelectButton setValue={setView} value={view} id="moreInfo">
<Icon type="chart-bar" />
This Score
</SelectButton>
<SelectButton setValue={setView} value={view} id="vsPB">
<Icon type="balance-scale-right" />
Versus PB
</SelectButton>
<SelectButton setValue={setView} value={view} id="history" disabled>
<Icon type="history" />
Play History
</SelectButton>
</>
}
>
{content}
</DropdownStructure>
);
}
@@ -0,0 +1,23 @@
import React from "react";
import { JustChildren } from "types/react";
export default function DropdownStructure({
buttons,
children,
}: {
buttons: React.ReactNode;
} & JustChildren) {
return (
<div className="p-2">
<div className="row h-100 mb-0 align-items-center">
<div className="col-3 col-xl-2">
<div className="btn-group-vertical">{buttons}</div>
</div>
<div className="col-9 col-xl-10">
<div className="row mb-0">{children}</div>
</div>
</div>
</div>
);
}
@@ -0,0 +1,178 @@
import DeltaCell from "components/tables/cells/DeltaCell";
import IIDXLampCell from "components/tables/cells/IIDXLampCell";
import ScoreCell from "components/tables/cells/ScoreCell";
import TimestampCell from "components/tables/cells/TimestampCell";
import SelectNav from "components/util/SelectNav";
import React, { useState } from "react";
import { ScoreDocument, PBScoreDocument, Lamps } from "tachi-common";
import { Nav } from ".pnpm/react-bootstrap@1.0.1_react-dom@16.12.0+react@17.0.2/node_modules/react-bootstrap";
import IIDXLampChart from "components/charts/IIDXLampChart";
import MiniTable from "components/tables/components/MiniTable";
export function ScoreInfo({ score }: { score: ScoreDocument<"iidx:SP" | "iidx:DP"> }) {
return (
<div className="col-12">
<table className="table">
<thead>
<tr>
<td colSpan={100}>Score Info</td>
</tr>
</thead>
<tbody>
<tr>
<ScoreCell score={score} game="iidx" playtype="SP" />
<DeltaCell
game="iidx"
playtype="SP"
score={score.scoreData.score}
percent={score.scoreData.percent}
grade={score.scoreData.grade}
/>
<IIDXLampCell sc={score} />
<TimestampCell time={score.timeAchieved} />
</tr>
</tbody>
</table>
</div>
);
}
export function ModsTable({ score }: { score: ScoreDocument<"iidx:SP" | "iidx:DP"> }) {
if (!score.scoreMeta.assist && !score.scoreMeta.random) {
return null;
}
return (
<MiniTable className="text-center table-sm" headers={["Mods"]} colSpan={2}>
{score.scoreMeta.random && (
<tr>
<td>Note</td>
<td>
{Array.isArray(score.scoreMeta.random)
? score.scoreMeta.random.join(" | ")
: score.scoreMeta.random}
</td>
</tr>
)}
{score.scoreMeta.assist && (
<tr>
<td>Assist</td>
<td>{score.scoreMeta.assist}</td>
</tr>
)}
</MiniTable>
);
}
type LampTypes = "NORMAL" | "EASY" | "HARD" | "EX_HARD";
export function IIDXGraphsComponent({
score,
}: {
score: ScoreDocument<"iidx:SP" | "iidx:DP"> | PBScoreDocument<"iidx:SP" | "iidx:DP">;
}) {
const [lamp, setLamp] = useState(LampToKey(score.scoreData.lamp));
let gaugeStatus: "none" | "single" | "gsm" = "none";
if (score.scoreData.hitMeta.gsm) {
gaugeStatus = "gsm";
} else if (score.scoreData.hitMeta.gaugeHistory) {
gaugeStatus = "single";
}
const shouldDisable = (r: LampTypes) => {
if (gaugeStatus === "gsm") {
return false;
} else if (gaugeStatus === "single") {
return r !== LampToKey(score.scoreData.lamp);
}
return true;
};
return (
<>
<div className="col-12 d-flex justify-content-center">
<Nav variant="pills">
<SelectNav
id="EASY"
value={lamp}
setValue={setLamp}
disabled={shouldDisable("EASY")}
>
Easy
</SelectNav>
<SelectNav
id="NORMAL"
value={lamp}
setValue={setLamp}
disabled={shouldDisable("NORMAL")}
>
Normal
</SelectNav>
<SelectNav
id="HARD"
value={lamp}
setValue={setLamp}
disabled={shouldDisable("HARD")}
>
Hard
</SelectNav>
<SelectNav
id="EX_HARD"
value={lamp}
setValue={setLamp}
disabled={shouldDisable("EX_HARD")}
>
Ex Hard
</SelectNav>
</Nav>
</div>
<div className="col-12">
{gaugeStatus === "gsm" ? (
<GraphComponent type={lamp} values={score.scoreData.hitMeta.gsm![lamp]} />
) : gaugeStatus === "single" ? (
<GraphComponent type={lamp} values={score.scoreData.hitMeta.gaugeHistory!} />
) : (
<div
className="d-flex align-items-center justify-content-center"
style={{ height: "200px" }}
>
<span className="text-muted">No gauge data :(</span>
</div>
)}
</div>
</>
);
}
function GraphComponent({ type, values }: { type: LampTypes; values: (number | null)[] }) {
return (
<IIDXLampChart
height="200px"
mobileHeight="175px"
type={type}
data={[
{
id: type,
data: values.map((e, i) => ({ x: i, y: e ?? 0 })),
},
]}
/>
);
}
function LampToKey(lamp: Lamps["iidx:SP"]): LampTypes {
if (lamp === "CLEAR") {
return "NORMAL";
} else if (lamp === "EASY CLEAR") {
return "EASY";
} else if (lamp === "HARD CLEAR") {
return "HARD";
} else if (lamp === "EX HARD CLEAR") {
return "EX_HARD";
}
return "NORMAL";
}
@@ -0,0 +1,14 @@
import ExternalLink from "components/util/ExternalLink";
import React from "react";
export default function PBNote() {
return (
<small>
Your PB is the combination of your best score and your best lamp. Read more{" "}
<ExternalLink href="https://tachi.readthedocs.io/en/latest/user/pbs-scores/">
{" "}
here.
</ExternalLink>
</small>
);
}
@@ -6,7 +6,12 @@ import BPICell from "../cells/BPICell";
import { NumericSOV, StrSOV } from "util/sorts";
import SelectableRating from "../components/SelectableRating";
import { ScoreDataset } from "types/tables";
import { GetGamePTConfig, integer, ScoreCalculatedDataLookup } from "tachi-common";
import {
GetGamePTConfig,
integer,
ScoreCalculatedDataLookup,
PublicUserDocument,
} from "tachi-common";
import TachiTable, { ZTableTHProps } from "../components/TachiTable";
import DifficultyCell from "../cells/DifficultyCell";
import ScoreCell from "../cells/ScoreCell";
@@ -14,11 +19,15 @@ import DeltaCell from "../cells/DeltaCell";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import { nanoid } from "nanoid";
import IIDXLampCell from "../cells/IIDXLampCell";
import DropdownRow from "../components/DropdownRow";
import IIDXScoreDropdown from "../pbs/dropdowns/IIDXScoreDropdown";
export default function IIDXScoreTable({
reqUser,
dataset,
pageLen,
}: {
reqUser: PublicUserDocument;
dataset: ScoreDataset<"iidx:SP">;
pageLen?: integer;
}) {
@@ -73,7 +82,17 @@ export default function IIDXScoreTable({
},
}}
rowFunction={sc => (
<tr key={sc.chartID}>
<DropdownRow
dropdown={
<IIDXScoreDropdown
chart={sc.__related.chart}
game="iidx"
playtype={sc.playtype}
reqUser={reqUser}
thisScore={sc}
/>
}
>
<DifficultyCell chart={sc.__related.chart} game={"iidx"} />
<TitleCell song={sc.__related.song} chart={sc.__related.chart} game="iidx" />
<ScoreCell score={sc} game="iidx" playtype="SP" />
@@ -95,7 +114,7 @@ export default function IIDXScoreTable({
</td>
)}
<TimestampCell time={sc.timeAchieved} />
</tr>
</DropdownRow>
)}
/>
);