Big Commit

This commit is contained in:
zkldi
2021-09-15 20:21:37 +01:00
parent 53375fd746
commit 8a4b9b58f1
48 changed files with 1448 additions and 392 deletions
+1 -1
View File
@@ -75,7 +75,7 @@
"resolve": "1.12.0",
"resolve-url-loader": "3.1.0",
"socicon": "3.0.5",
"tachi-common": "0.2.9"
"tachi-common": "0.2.12"
},
"devDependencies": {
"@types/humanize-duration": "3.25.1",
+4 -4
View File
@@ -77,7 +77,7 @@ specifiers:
serve: 11.2.0
socicon: 3.0.5
style-loader: 1.0.0
tachi-common: 0.2.9
tachi-common: 0.2.12
terser-webpack-plugin: 1.4.1
ts-pnp: 1.1.4
typescript: 4.3.5
@@ -142,7 +142,7 @@ dependencies:
resolve: 1.12.0
resolve-url-loader: 3.1.0
socicon: 3.0.5
tachi-common: 0.2.9
tachi-common: 0.2.12
devDependencies:
'@types/humanize-duration': 3.25.1
@@ -11178,8 +11178,8 @@ packages:
strip-ansi: 6.0.0
dev: true
/tachi-common/0.2.9:
resolution: {integrity: sha512-Z7oEv5OorOSB9VBJALzM3yaKxBUWNQGSJihLz923CNX5Wndi1L84LlLYgYXOZ5JrrnUFsSK374kaeODd3X/nEQ==}
/tachi-common/0.2.12:
resolution: {integrity: sha512-xwbQUZefLUs1sus3nrrys84qJyRMWLl/Mb4UzFYd74n0ZmOejrxpwIb18ILWww4J/IxQurPd4Id+DCP12SEW1w==}
dependencies:
monk: 7.3.4
transitivePeerDependencies:
@@ -169,6 +169,7 @@ function TableFolderTable({
return (
<TachiTable
dataset={dataset}
pageLen={25}
headers={[
["Folder", "Folder", StrSOV(x => x.folder.title)],
["Chart Count", "Charts", NumericSOV(x => x.stats.chartCount)],
@@ -250,8 +251,6 @@ function TableBarChart({
return arr.reverse();
}, [dataMap, mode]);
console.log(dataset);
return (
<Card header="Overview">
<div className="row">
@@ -321,8 +320,6 @@ function OverviewBarChart({
[dataset]
);
console.log(longestTitle);
return (
<ResponsiveBar
indexBy="folder"
@@ -352,7 +349,7 @@ function OverviewBarChart({
minValue={0}
maxValue={100}
valueFormat={v => `${v.toFixed(2)}%`}
margin={{ left: 10 + longestTitle.length * 10, bottom: 50, top: 20, right: 20 }}
margin={{ left: 10 + longestTitle.length * 7, bottom: 50, top: 20, right: 20 }}
layout="horizontal"
axisBottom={{
tickSize: 5,
@@ -8,6 +8,7 @@ import { FormatGame, GetGameConfig, PublicUserDocument } from "tachi-common";
import { GamePT } from "types/react";
import FolderSelectPage from "./FolderSelectPage";
import FolderTablePage from "./FolderTablePage";
import SpecificFolderPage from "./SpecificFolderPage";
type Props = { reqUser: PublicUserDocument } & GamePT;
@@ -47,6 +48,12 @@ export default function FoldersMainPage({ reqUser, game, playtype }: Props) {
>
<FolderSelectPage {...{ reqUser, game, playtype }} />
</Route>
<Route
exact
path="/dashboard/users/:userID/games/:game/:playtype/folders/:folderID"
>
<SpecificFolderPage {...{ reqUser, game, playtype }} />
</Route>
</Switch>
</div>
</div>
@@ -0,0 +1,183 @@
import { ResponsiveBar } from "@nivo/bar";
import { BarChartTooltip } from "components/charts/ChartTooltip";
import Card from "components/layout/page/Card";
import FolderTable from "components/tables/folders/FolderTable";
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 React, { useMemo } from "react";
import { useParams } from "react-router-dom";
import { Game, GetGamePTConfig, PublicUserDocument } from "tachi-common";
import { UGPTFolderReturns } from "types/api-returns";
import { FolderDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { DEFAULT_BAR_PROPS } from "util/charts";
import { ChangeOpacity } from "util/color-opacity";
import { CreateChartIDMap, CreateSongMap } from "util/data";
import { ComposeExpFn, ComposeInverseExpFn } from "util/misc";
import { NumericSOV, StrSOV } from "util/sorts";
interface Props {
reqUser: PublicUserDocument;
game: Game;
playtype: Playtype;
}
export default function SpecificFolderPage({ reqUser, game, playtype }: Props) {
const { folderID } = useParams<{ folderID: string }>();
const { data, isLoading, error } = useApiQuery<UGPTFolderReturns>(
`/users/${reqUser.id}/games/${game}/${playtype}/folders/${folderID}`
);
const folderDataset = useMemo(() => {
if (!data) {
return null;
}
const songMap = CreateSongMap(data.songs);
const pbMap = CreateChartIDMap(data.pbs);
const folderDataset: FolderDataset = [];
for (const chart of data.charts) {
folderDataset.push({
...chart,
__related: {
pb: pbMap.get(chart.chartID) ?? null,
song: songMap.get(chart.songID)!,
},
});
}
folderDataset.sort(StrSOV(x => x.__related.song.title));
return folderDataset;
}, [data]);
if (isLoading || !data || !folderDataset) {
return <Loading />;
}
if (error) {
return <ApiError error={error} />;
}
return (
<div className="row">
<div className="col-12">
<Divider className="mb-4" />
</div>
<div className="col-12">
<FolderInfoHeader {...{ folderDataset, data, game, playtype, reqUser }} />
</div>
<div className="col-12">
<Divider className="my-4" />
</div>
<div className="col-12">
<FolderTable
dataset={folderDataset}
game={game}
playtype={playtype}
reqUser={reqUser}
indexCol={false}
/>
</div>
</div>
);
}
function FolderInfoHeader({
game,
playtype,
reqUser,
folderDataset,
data,
}: Props & { folderDataset: FolderDataset; data: UGPTFolderReturns }) {
const dataset = [];
const expScale = 1;
const expFn = ComposeExpFn(expScale);
const invExpFn = ComposeInverseExpFn(expScale);
for (const data of folderDataset) {
const value = data.__related.pb ? expFn(data.__related.pb.scoreData.percent) : 0;
dataset.push({
chartID: data.chartID,
expValue: expFn(value),
value,
grade: data.__related.pb?.scoreData.grade,
});
}
dataset.sort(NumericSOV(x => x.expValue));
const dataMap = CreateChartIDMap(folderDataset);
const gptConfig = GetGamePTConfig(game, playtype);
return (
<Card header={`${reqUser.username}'s ${data.folder.title} Breakdown`}>
<div style={{ height: 400 }}>
<ResponsiveBar
indexBy="chartID"
tooltip={d => (
<BarChartTooltip
point={d}
renderFn={d => {
const data = dataMap.get(d.indexValue as string)!;
return (
<div className="w-100 text-center">
{data.__related.song.title}
<br />
{data.__related.pb?.scoreData.percent.toFixed(2)}%
</div>
);
}}
/>
)}
key={"value"}
// @ts-expect-error temp
colors={k => ChangeOpacity(gptConfig.gradeColours[k.data.grade], 0.5)}
// @ts-expect-error temp
borderColor={k => gptConfig.gradeColours[k.data.grade]}
borderWidth={1}
padding={0.2}
// @ts-expect-error temp
data={dataset}
minValue={0}
maxValue={expFn(100)}
margin={{ left: 50, top: 20, bottom: 20 }}
axisLeft={{
tickValues: gptConfig.gradeBoundaries.map(e => (e === 0 ? 0 : expFn(e))),
format: x => {
let nearest;
const lgv = invExpFn(x);
for (const [i, gradeBnd] of gptConfig.gradeBoundaries.entries()) {
if (Math.abs(gradeBnd - lgv) < 0.00005) {
nearest = i;
break;
}
}
if (nearest === undefined) {
return null;
}
return gptConfig.grades[nearest];
},
}}
axisBottom={null}
{...DEFAULT_BAR_PROPS}
/>
</div>
</Card>
);
}
+3 -3
View File
@@ -36,14 +36,14 @@ export default function ClassBadge<I extends IDStrings = IDStrings>({
if (data.mouseover && showSetOnHover) {
return (
<QuickTooltip text={`${UppercaseFirst(classSet)}: ${data.mouseover}`}>
<QuickTooltip tooltipContent={`${UppercaseFirst(classSet)}: ${data.mouseover}`}>
{badgeComponent}
</QuickTooltip>
);
} else if (data.mouseover) {
return <QuickTooltip text={data.mouseover}>{badgeComponent}</QuickTooltip>;
return <QuickTooltip tooltipContent={data.mouseover}>{badgeComponent}</QuickTooltip>;
} else if (showSetOnHover) {
<QuickTooltip text={UppercaseFirst(classSet)}>{badgeComponent}</QuickTooltip>;
<QuickTooltip tooltipContent={UppercaseFirst(classSet)}>{badgeComponent}</QuickTooltip>;
}
return badgeComponent;
@@ -63,7 +63,7 @@ function GPTSearchResult({
}}
>
<QuickTooltip
text={`Go to ${user.username}'s ${FormatGame(
tooltipContent={`Go to ${user.username}'s ${FormatGame(
game,
playtype
)} profile, if they have one.`}
@@ -5,10 +5,10 @@ import { JustChildren } from "types/react";
export default function QuickTooltip({
children,
text,
}: { text: string } & { children: JSX.Element }) {
tooltipContent,
}: { tooltipContent: React.ReactChild } & { children: JSX.Element }) {
return (
<OverlayTrigger placement="top" overlay={<Tooltip id={nanoid()}>{text}</Tooltip>}>
<OverlayTrigger placement="top" overlay={<Tooltip id={nanoid()}>{tooltipContent}</Tooltip>}>
{children}
</OverlayTrigger>
);
@@ -1,5 +1,4 @@
import React from "react";
import { OverlayTrigger } from "react-bootstrap";
import { Link } from "react-router-dom";
import QuickTooltip from "../misc/QuickTooltip";
@@ -17,7 +16,7 @@ export default function CardNavButton({
}
return (
<QuickTooltip text={hoverText}>
<QuickTooltip tooltipContent={hoverText}>
<Link className={`fas fa-${type} h3 card-nav-icon`} to={to} />
</QuickTooltip>
);
@@ -4,6 +4,7 @@ import React from "react";
import { ChartDocument, Game, COLOUR_SET } from "tachi-common";
import { ChangeOpacity } from "util/color-opacity";
import { FormatBMSTables } from "util/misc";
import TierlistInfoPart from "./TierlistInfoPart";
export default function BMSDifficultyCell({
chart,
@@ -20,8 +21,9 @@ export default function BMSDifficultyCell({
}}
>
<span>{levelText}</span>
<TierlistInfoPart chart={chart} />
{!chart.isPrimary && (
<QuickTooltip text="This chart is an alternate, old chart.">
<QuickTooltip tooltipContent="This chart is an alternate, old chart.">
<div>
<Icon type="exclamation-triangle" />
</div>
@@ -1,5 +1,6 @@
import QuickTooltip from "components/layout/misc/QuickTooltip";
import Icon from "components/util/Icon";
import Muted from "components/util/Muted";
import React from "react";
import {
ChartDocument,
@@ -9,6 +10,7 @@ import {
GetGamePTConfig,
} from "tachi-common";
import { ChangeOpacity } from "util/color-opacity";
import TierlistInfoPart from "./TierlistInfoPart";
export default function DifficultyCell({
game,
@@ -28,13 +30,18 @@ export default function DifficultyCell({
}}
>
{!alwaysShort && (
<span className="d-none d-lg-block">{FormatDifficulty(chart, game)}</span>
<>
<span className="d-none d-lg-block">{FormatDifficulty(chart, game)}</span>
{Object.keys(chart.tierlistInfo).length > 0 && (
<TierlistInfoPart chart={chart} />
)}
</>
)}
<span className={!alwaysShort ? "d-lg-none" : ""}>
{FormatDifficultyShort(chart, game)}
</span>
{!chart.isPrimary && (
<QuickTooltip text="This chart is an alternate, old chart.">
<QuickTooltip tooltipContent="This chart is an alternate, old chart.">
<div>
<Icon type="exclamation-triangle" />
</div>
@@ -0,0 +1,13 @@
import React from "react";
import { ChangeOpacity } from "util/color-opacity";
export default function IndicatorsCell({ highlight }: { highlight: boolean }) {
return (
<td
style={{
backgroundColor: highlight ? ChangeOpacity("#e6a303", 0.5) : "#383838",
maxWidth: 5,
}}
></td>
);
}
@@ -0,0 +1,48 @@
import QuickTooltip from "components/layout/misc/QuickTooltip";
import Icon from "components/util/Icon";
import Muted from "components/util/Muted";
import React from "react";
import { ChartDocument } from "tachi-common";
import MiniTable from "../components/MiniTable";
// I hate the fact object.keys doesnt infer types.
type K = keyof ChartDocument["tierlistInfo"];
export default function TierlistInfoPart({ chart }: { chart: ChartDocument }) {
const keys = Object.keys(chart.tierlistInfo) as K[];
return (
<QuickTooltip
tooltipContent={
<MiniTable className="table-sm">
<thead>
<tr>
<th>Tierlist</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{keys.map(k => (
<tr key={k}>
<td>{k}</td>
<td>
{chart.tierlistInfo[k]!.text} ({chart.tierlistInfo[k]!.value})
{chart.tierlistInfo[k]!.individualDifference && (
<>
<br />
<Icon type="balance-scale-left" />
</>
)}
</td>
</tr>
))}
</tbody>
</MiniTable>
}
>
<div>
<Muted>{keys.map(k => chart.tierlistInfo[k]!.text).join(" / ")}</Muted>
</div>
</QuickTooltip>
);
}
@@ -7,7 +7,11 @@ import React, { useState } from "react";
import { Modal, OverlayTrigger, Tooltip } from "react-bootstrap";
import { GetValueGetter, ValueGetterOrHybrid } from "util/ztable/search";
function AdditionalTableProps({ value }: { value: string | number | [string, number] | null }) {
function AdditionalTableProps({
value,
}: {
value: string | number | [string, number] | boolean | null;
}) {
let typeName;
if (Array.isArray(value)) {
@@ -16,6 +20,8 @@ function AdditionalTableProps({ value }: { value: string | number | [string, num
typeName = "Text";
} else if (value === null) {
typeName = "Cannot Infer :(";
} else if (typeof value === "boolean") {
typeName = "Presence";
} else {
typeName = "Number";
}
@@ -23,7 +29,13 @@ function AdditionalTableProps({ value }: { value: string | number | [string, num
return (
<>
<td>{typeName}</td>
<td>{Array.isArray(value) ? `${value[0]} (${value[1]})` : value}</td>
<td>
{typeof value === "boolean"
? "Yes/No/True/False"
: Array.isArray(value)
? `${value[0]} (${value[1]})`
: value}
</td>
</>
);
}
@@ -58,6 +58,7 @@ export default function SelectableRating<I extends IDStrings>({
color: "#ffffff",
fontSize: "inherit",
font: "inherit",
textAlign: "center",
}}
>
{gptConfig[key].map(s => (
@@ -1,4 +1,5 @@
import { useState, useEffect } from "react";
import { PBScoreDocument, ScoreDocument } from "tachi-common";
import { PBDataset, ScoreDataset } from "types/tables";
export function useScoreState(sc: ScoreDataset[0]) {
@@ -13,7 +14,7 @@ export function useScoreState(sc: ScoreDataset[0]) {
return { highlight, comment, setHighlight, setComment };
}
export function usePBState(pb: PBDataset[0]) {
export function usePBState(pb: PBScoreDocument) {
const [highlight, setHighlight] = useState(pb.highlight);
useEffect(() => {
@@ -8,7 +8,7 @@ import { APIFetchV1 } from "util/api";
import { SpecificSessionReturns } from "types/api-returns";
import Loading from "components/util/Loading";
import DebugContent from "components/util/DebugContent";
import { CreateChartMap, CreateScoreMap, CreateSongMap } from "util/data";
import { CreateChartMap, CreateChartIDMap, CreateSongMap } from "util/data";
import {
Lamps,
Grades,
@@ -236,7 +236,7 @@ function SessionScoreStatBreakdown({
}) {
const songMap = CreateSongMap(sessionData.songs);
const chartMap = CreateChartMap(sessionData.charts);
const scoreMap = CreateScoreMap(sessionData.scores);
const scoreMap = CreateChartIDMap(sessionData.scores);
const [newLamps, newGrades] = useMemo(() => {
const newLamps: Partial<Record<
@@ -34,14 +34,14 @@ export default function ScoreEditButtons({
<div className="btn-group">
{comment ? (
<>
<QuickTooltip text="Edit your comment on this score.">
<QuickTooltip tooltipContent="Edit your comment on this score.">
<Button variant="outline-secondary" onClick={() => setShow(true)}>
<Icon noPad type="file-signature" />
</Button>
</QuickTooltip>
</>
) : (
<QuickTooltip text="Comment on this score.">
<QuickTooltip tooltipContent="Comment on this score.">
<Button variant="outline-secondary" onClick={() => setShow(true)}>
<Icon noPad type="file-signature" />
</Button>
@@ -49,7 +49,7 @@ export default function ScoreEditButtons({
)}
{highlight ? (
<QuickTooltip text="Unhighlight this score.">
<QuickTooltip tooltipContent="Unhighlight this score.">
<Button
variant="success"
onClick={() =>
@@ -65,7 +65,7 @@ export default function ScoreEditButtons({
</Button>
</QuickTooltip>
) : (
<QuickTooltip text="Highlight this score.">
<QuickTooltip tooltipContent="Highlight this score.">
<Button
variant="outline-secondary"
onClick={() =>
@@ -167,7 +167,7 @@ function CommentModal({
</div>
</Form.Group>
<QuickTooltip text="Remove your comment on this score.">
<QuickTooltip tooltipContent="Remove your comment on this score.">
<Button
variant="outline-danger"
onClick={() =>
@@ -0,0 +1,112 @@
import React from "react";
import { Game, PublicUserDocument } from "tachi-common";
import { FolderDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultFolderSearchParams } from "util/tables/create-search";
import BMSDifficultyCell from "../cells/BMSDifficultyCell";
import DifficultyCell from "../cells/DifficultyCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import RankingCell from "../cells/RankingCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable, { Header } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import BMSScoreCoreCells from "../game-core-cells/BMSScoreCoreCells";
import { FolderIndicatorHeader } from "../headers/IndicatorHeader";
export default function BMSFolderTable({
dataset,
reqUser,
game,
playtype,
}: {
dataset: FolderDataset<"bms:7K" | "bms:14K">;
reqUser: PublicUserDocument;
game: Game;
playtype: Playtype;
}) {
const headers: Header<FolderDataset<"bms:7K" | "bms:14K">[0]>[] = [
[
"Chart",
"Ch.",
NumericSOV(
x =>
x.tierlistInfo["sgl-EC"]?.value ?? x.tierlistInfo["sgl-HC"]?.value ?? x.levelNum
),
],
FolderIndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)],
["Deltas", "Deltas", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)],
["Lamp", "Lamp", NumericSOV(x => x.__related.pb?.scoreData.lampIndex ?? -Infinity)],
["Sieglinde", "sgl.", NumericSOV(x => x.__related.pb?.calculatedData.sieglinde ?? 0)],
[
"Site Ranking",
"Site Rank",
NumericSOV(x => x.__related.pb?.rankingData.rank ?? -Infinity),
],
["Last Raised", "Last Raised", NumericSOV(x => x.__related.pb?.timeAchieved ?? 0)],
];
return (
<TachiTable
dataset={dataset}
headers={headers}
entryName="Charts"
searchFunctions={CreateDefaultFolderSearchParams(game, playtype)}
rowFunction={data => (
<Row data={data} key={data.chartID} reqUser={reqUser} game={game} />
)}
/>
);
}
function Row({
data,
reqUser,
game,
}: {
data: FolderDataset<"bms:7K" | "bms:14K">[0];
reqUser: PublicUserDocument;
game: Game;
}) {
const score = data.__related.pb;
if (!score) {
return (
<tr>
<BMSDifficultyCell chart={data} />
<IndicatorsCell highlight={false} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<td colSpan={6}>Not Played.</td>
</tr>
);
}
// screw the rules of hooks
const scoreState = usePBState(score);
return (
<DropdownRow
dropdown={
<GenericPBDropdown
chart={data}
reqUser={reqUser}
game={game}
playtype={data.playtype}
scoreState={scoreState}
/>
}
>
<BMSDifficultyCell chart={data} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<BMSScoreCoreCells sc={score} />
<RankingCell rankingData={score.rankingData} />
<TimestampCell time={score.timeAchieved} />
</DropdownRow>
);
}
@@ -0,0 +1,45 @@
import React from "react";
import { IDStrings, PublicUserDocument, ScoreCalculatedDataLookup } from "tachi-common";
import { GamePT } from "types/react";
import { FolderDataset } from "types/tables";
import BMSFolderTable from "./BMSFolderTable";
import GenericFolderTable from "./GenericFolderTable";
import IIDXFolderTable from "./IIDXFolderTable";
import MusecaFolderTable from "./MusecaFolderTable";
import SDVXFolderTable from "./SDVXFolderTable";
export default function FolderTable({
dataset,
indexCol = true,
reqUser,
playtype,
showPlaycount,
game,
alg,
}: {
dataset: FolderDataset;
indexCol?: boolean;
showPlaycount?: boolean;
reqUser: PublicUserDocument;
alg?: ScoreCalculatedDataLookup[IDStrings];
} & GamePT) {
// We're just going to ignore all these errors
// and assume we just won't make a mistake.
// ever.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const props = { dataset, indexCol, reqUser, playtype, showPlaycount, alg, game } as any;
if (game === "iidx") {
return <IIDXFolderTable {...props} />;
} else if (game === "bms") {
return <BMSFolderTable {...props} />;
} else if (game === "sdvx" || game === "usc") {
return <SDVXFolderTable {...props} />;
} else if (game === "maimai") {
return <GenericFolderTable {...props} game={game} playtype={playtype} showScore={false} />;
} else if (game === "museca") {
return <MusecaFolderTable {...props} />;
}
return <GenericFolderTable {...props} />;
}
@@ -0,0 +1,126 @@
import React from "react";
import { COLOUR_SET, Game, GetGamePTConfig, PublicUserDocument } from "tachi-common";
import { FolderDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { ChangeOpacity } from "util/color-opacity";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultFolderSearchParams } from "util/tables/create-search";
import DifficultyCell from "../cells/DifficultyCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import LampCell from "../cells/LampCell";
import RankingCell from "../cells/RankingCell";
import RatingCell from "../cells/RatingCell";
import ScoreCell from "../cells/ScoreCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable, { Header } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import { FolderIndicatorHeader } from "../headers/IndicatorHeader";
export default function GenericFolderTable({
dataset,
reqUser,
game,
playtype,
showScore,
}: {
dataset: FolderDataset;
reqUser: PublicUserDocument;
game: Game;
playtype: Playtype;
showScore?: boolean;
}) {
const gptConfig = GetGamePTConfig(game, playtype);
const headers: Header<FolderDataset[0]>[] = [
["Chart", "Ch.", NumericSOV(x => x.levelNum)],
FolderIndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)],
["Lamp", "Lamp", NumericSOV(x => x.__related.pb?.scoreData.lampIndex ?? -Infinity)],
[
gptConfig.defaultScoreRatingAlg,
gptConfig.defaultScoreRatingAlg,
NumericSOV(
x => x.__related.pb?.calculatedData[gptConfig.defaultScoreRatingAlg] ?? -Infinity
),
],
[
"Site Ranking",
"Site Rank",
NumericSOV(x => x.__related.pb?.rankingData.rank ?? -Infinity),
],
["Last Raised", "Last Raised", NumericSOV(x => x.__related.pb?.timeAchieved ?? -Infinity)],
];
return (
<TachiTable
dataset={dataset}
headers={headers}
entryName="Charts"
searchFunctions={CreateDefaultFolderSearchParams(game, playtype)}
rowFunction={data => (
<Row
data={data}
key={data.chartID}
reqUser={reqUser}
showScore={showScore}
game={game}
/>
)}
/>
);
}
function Row({
data,
reqUser,
showScore,
game,
}: {
data: FolderDataset[0];
reqUser: PublicUserDocument;
showScore?: boolean;
game: Game;
}) {
const score = data.__related.pb;
if (!score) {
return (
<tr>
<DifficultyCell game={game} chart={data} />
<IndicatorsCell highlight={false} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<td colSpan={5}>Not Played.</td>
</tr>
);
}
// screw the rules of hooks
const scoreState = usePBState(score);
return (
<DropdownRow
dropdown={
<GenericPBDropdown
chart={data}
reqUser={reqUser}
game={game}
playtype={data.playtype}
scoreState={scoreState}
/>
}
>
<DifficultyCell game={game} chart={data} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<ScoreCell score={score} showScore={showScore} />
<LampCell score={score} />
<RatingCell score={score} />
<RankingCell rankingData={score.rankingData} />
<TimestampCell time={score.timeAchieved} />
</DropdownRow>
);
}
@@ -0,0 +1,135 @@
import useScoreRatingAlg from "components/util/useScoreRatingAlg";
import { nanoid } from "nanoid";
import React, { useState } from "react";
import { Game, PublicUserDocument, ScoreCalculatedDataLookup } from "tachi-common";
import { FolderDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultFolderSearchParams } from "util/tables/create-search";
import DifficultyCell from "../cells/DifficultyCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import RankingCell from "../cells/RankingCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import SelectableRating from "../components/SelectableRating";
import TachiTable, { Header, ZTableTHProps } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import IIDXPBDropdown from "../dropdowns/IIDXPBDropdown";
import IIDXScoreCoreCells from "../game-core-cells/IIDXScoreCoreCells";
import { FolderIndicatorHeader } from "../headers/IndicatorHeader";
export default function IIDXFolderTable({
dataset,
reqUser,
game,
playtype,
}: {
dataset: FolderDataset<"iidx:SP" | "iidx:DP">;
reqUser: PublicUserDocument;
game: Game;
playtype: Playtype;
}) {
const defaultRating = useScoreRatingAlg<"iidx:SP" | "iidx:DP">("iidx", playtype);
const [rating, setRating] = useState<ScoreCalculatedDataLookup["iidx:SP" | "iidx:DP"]>(
defaultRating
);
const headers: Header<FolderDataset<"iidx:SP" | "iidx:DP">[0]>[] = [
[
"Chart",
"Ch.",
NumericSOV(
x => x.tierlistInfo["kt-NC"]?.value ?? x.tierlistInfo["kt-HC"]?.value ?? x.levelNum
),
],
FolderIndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)],
["Deltas", "Deltas", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)],
["Lamp", "Lamp", NumericSOV(x => x.__related.pb?.scoreData.lampIndex ?? -Infinity)],
[
"Rating",
"Rating",
NumericSOV(x => x.__related.pb?.calculatedData[rating] ?? 0),
(thProps: ZTableTHProps) => (
<SelectableRating<"iidx:SP" | "iidx:DP">
key={nanoid()}
game="iidx"
playtype="SP"
rating={rating}
setRating={setRating}
{...thProps}
/>
),
],
[
"Site Ranking",
"Site Rank",
NumericSOV(x => x.__related.pb?.rankingData.rank ?? -Infinity),
],
["Last Raised", "Last Raised", NumericSOV(x => x.__related.pb?.timeAchieved ?? 0)],
];
return (
<TachiTable
dataset={dataset}
headers={headers}
entryName="Charts"
searchFunctions={CreateDefaultFolderSearchParams(game, playtype)}
rowFunction={data => (
<Row rating={rating} data={data} key={data.chartID} reqUser={reqUser} game={game} />
)}
/>
);
}
function Row({
data,
reqUser,
rating,
game,
}: {
data: FolderDataset<"iidx:SP" | "iidx:DP">[0];
reqUser: PublicUserDocument;
rating: ScoreCalculatedDataLookup["iidx:SP" | "iidx:DP"];
game: Game;
}) {
const score = data.__related.pb;
if (!score) {
return (
<tr>
<DifficultyCell game={game} chart={data} />
<IndicatorsCell highlight={false} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<td colSpan={6}>Not Played.</td>
</tr>
);
}
// screw the rules of hooks
const scoreState = usePBState(score);
return (
<DropdownRow
dropdown={
<IIDXPBDropdown
chart={data}
reqUser={reqUser}
game={game}
playtype={data.playtype}
scoreState={scoreState}
/>
}
>
<DifficultyCell game={game} chart={data} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<IIDXScoreCoreCells sc={score} rating={rating} />
<RankingCell rankingData={score.rankingData} />
<TimestampCell time={score.timeAchieved} />
</DropdownRow>
);
}
@@ -0,0 +1,107 @@
import React from "react";
import { Game, PublicUserDocument } from "tachi-common";
import { FolderDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultFolderSearchParams } from "util/tables/create-search";
import BMSDifficultyCell from "../cells/BMSDifficultyCell";
import DifficultyCell from "../cells/DifficultyCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import RankingCell from "../cells/RankingCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable, { Header } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import BMSScoreCoreCells from "../game-core-cells/BMSScoreCoreCells";
import MusecaScoreCoreCells from "../game-core-cells/MusecaScoreCoreCells";
import SDVXScoreCoreCells from "../game-core-cells/SDVXCoreCells";
import { FolderIndicatorHeader } from "../headers/IndicatorHeader";
export default function MusecaFolderTable({
dataset,
reqUser,
game,
playtype,
}: {
dataset: FolderDataset<"museca:Single">;
reqUser: PublicUserDocument;
game: Game;
playtype: Playtype;
}) {
const headers: Header<FolderDataset<"museca:Single">[0]>[] = [
["Chart", "Ch.", NumericSOV(x => x.levelNum)],
FolderIndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)],
["Near - Miss", "Nr. Ms.", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)],
["Lamp", "Lamp", NumericSOV(x => x.__related.pb?.scoreData.lampIndex ?? -Infinity)],
["ktRating", "ktRating", NumericSOV(x => x.__related.pb?.calculatedData.ktRating ?? 0)],
[
"Site Ranking",
"Site Rank",
NumericSOV(x => x.__related.pb?.rankingData.rank ?? -Infinity),
],
["Last Raised", "Last Raised", NumericSOV(x => x.__related.pb?.timeAchieved ?? 0)],
];
return (
<TachiTable
dataset={dataset}
headers={headers}
entryName="Charts"
searchFunctions={CreateDefaultFolderSearchParams(game, playtype)}
rowFunction={data => (
<Row data={data} key={data.chartID} reqUser={reqUser} game={game} />
)}
/>
);
}
function Row({
data,
reqUser,
game,
}: {
data: FolderDataset<"museca:Single">[0];
reqUser: PublicUserDocument;
game: Game;
}) {
const score = data.__related.pb;
if (!score) {
return (
<tr>
<DifficultyCell chart={data} game={game} />
<IndicatorsCell highlight={false} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<td colSpan={6}>Not Played.</td>
</tr>
);
}
// screw the rules of hooks
const scoreState = usePBState(score);
return (
<DropdownRow
dropdown={
<GenericPBDropdown
chart={data}
reqUser={reqUser}
game={game}
playtype={data.playtype}
scoreState={scoreState}
/>
}
>
<DifficultyCell chart={data} game={game} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<MusecaScoreCoreCells sc={score} />
<RankingCell rankingData={score.rankingData} />
<TimestampCell time={score.timeAchieved} />
</DropdownRow>
);
}
@@ -0,0 +1,106 @@
import React from "react";
import { Game, PublicUserDocument } from "tachi-common";
import { FolderDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultFolderSearchParams } from "util/tables/create-search";
import BMSDifficultyCell from "../cells/BMSDifficultyCell";
import DifficultyCell from "../cells/DifficultyCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import RankingCell from "../cells/RankingCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable, { Header } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import BMSScoreCoreCells from "../game-core-cells/BMSScoreCoreCells";
import SDVXScoreCoreCells from "../game-core-cells/SDVXCoreCells";
import { FolderIndicatorHeader } from "../headers/IndicatorHeader";
export default function SDVXFolderTable({
dataset,
reqUser,
game,
playtype,
}: {
dataset: FolderDataset<"sdvx:Single">;
reqUser: PublicUserDocument;
game: Game;
playtype: Playtype;
}) {
const headers: Header<FolderDataset<"sdvx:Single">[0]>[] = [
["Chart", "Ch.", NumericSOV(x => x.levelNum)],
FolderIndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)],
["Near - Miss", "Nr. Ms.", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)],
["Lamp", "Lamp", NumericSOV(x => x.__related.pb?.scoreData.lampIndex ?? -Infinity)],
["VF6", "sgl.", NumericSOV(x => x.__related.pb?.calculatedData.VF6 ?? 0)],
[
"Site Ranking",
"Site Rank",
NumericSOV(x => x.__related.pb?.rankingData.rank ?? -Infinity),
],
["Last Raised", "Last Raised", NumericSOV(x => x.__related.pb?.timeAchieved ?? 0)],
];
return (
<TachiTable
dataset={dataset}
headers={headers}
entryName="Charts"
searchFunctions={CreateDefaultFolderSearchParams(game, playtype)}
rowFunction={data => (
<Row data={data} key={data.chartID} reqUser={reqUser} game={game} />
)}
/>
);
}
function Row({
data,
reqUser,
game,
}: {
data: FolderDataset<"sdvx:Single">[0];
reqUser: PublicUserDocument;
game: Game;
}) {
const score = data.__related.pb;
if (!score) {
return (
<tr>
<DifficultyCell chart={data} game={game} />
<IndicatorsCell highlight={false} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<td colSpan={6}>Not Played.</td>
</tr>
);
}
// screw the rules of hooks
const scoreState = usePBState(score);
return (
<DropdownRow
dropdown={
<GenericPBDropdown
chart={data}
reqUser={reqUser}
game={game}
playtype={data.playtype}
scoreState={scoreState}
/>
}
>
<DifficultyCell chart={data} game={game} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={data.__related.song} chart={data} game={game} />
<SDVXScoreCoreCells sc={score} />
<RankingCell rankingData={score.rankingData} />
<TimestampCell time={score.timeAchieved} />
</DropdownRow>
);
}
@@ -1,11 +1,15 @@
import React from "react";
import { ScoreDocument } from "tachi-common";
import { PBScoreDocument, ScoreDocument } from "tachi-common";
import DeltaCell from "../cells/DeltaCell";
import LampCell from "../cells/LampCell";
import RatingCell from "../cells/RatingCell";
import ScoreCell from "../cells/ScoreCell";
export default function BMSScoreCoreCells({ sc }: { sc: ScoreDocument<"bms:7K" | "bms:14K"> }) {
export default function BMSScoreCoreCells({
sc,
}: {
sc: PBScoreDocument<"bms:7K" | "bms:14K"> | ScoreDocument<"bms:7K" | "bms:14K">;
}) {
return (
<>
<ScoreCell score={sc} />
@@ -1,5 +1,5 @@
import React from "react";
import { ScoreDocument, ScoreCalculatedDataLookup } from "tachi-common";
import { ScoreDocument, ScoreCalculatedDataLookup, PBScoreDocument } from "tachi-common";
import { IsNullish } from "util/misc";
import DeltaCell from "../cells/DeltaCell";
import IIDXLampCell from "../cells/IIDXLampCell";
@@ -9,7 +9,7 @@ export default function IIDXScoreCoreCells({
sc,
rating,
}: {
sc: ScoreDocument<"iidx:SP" | "iidx:DP">;
sc: PBScoreDocument<"iidx:SP" | "iidx:DP"> | ScoreDocument<"iidx:SP" | "iidx:DP">;
rating: ScoreCalculatedDataLookup["iidx:SP" | "iidx:DP"];
}) {
return (
@@ -1,11 +1,15 @@
import React from "react";
import { ScoreDocument } from "tachi-common";
import { PBScoreDocument, ScoreDocument } from "tachi-common";
import LampCell from "../cells/LampCell";
import MillionsScoreCell from "../cells/MillionsScoreCell";
import MusecaJudgementCell from "../cells/MusecaJudgementCell";
import RatingCell from "../cells/RatingCell";
export default function MusecaScoreCoreCells({ sc }: { sc: ScoreDocument<"museca:Single"> }) {
export default function MusecaScoreCoreCells({
sc,
}: {
sc: ScoreDocument<"museca:Single"> | PBScoreDocument<"museca:Single">;
}) {
return (
<>
<MillionsScoreCell score={sc} />
@@ -1,11 +1,15 @@
import React from "react";
import { ScoreDocument } from "tachi-common";
import { PBScoreComposedReference, PBScoreDocument, ScoreDocument } from "tachi-common";
import { IsNullish } from "util/misc";
import LampCell from "../cells/LampCell";
import MillionsScoreCell from "../cells/MillionsScoreCell";
import SDVXJudgementCell from "../cells/SDVXJudgementCell";
export default function SDVXScoreCoreCells({ sc }: { sc: ScoreDocument<"sdvx:Single"> }) {
export default function SDVXScoreCoreCells({
sc,
}: {
sc: ScoreDocument<"sdvx:Single"> | PBScoreDocument<"sdvx:Single">;
}) {
return (
<>
<MillionsScoreCell score={sc} />
@@ -0,0 +1,22 @@
import { nanoid } from "nanoid";
import React from "react";
import { PBScoreDocument, ScoreDocument } from "tachi-common";
import { FolderDataset, PBDataset, ScoreDataset } from "types/tables";
import { NumericSOV } from "util/sorts";
import { Header } from "../components/TachiTable";
const IndicatorHeader: Header<PBDataset[0] | ScoreDataset[0]> = [
"Indicators",
"Id.",
NumericSOV<ScoreDocument | PBScoreDocument>(x => Number(x.highlight)),
() => <td key={nanoid()} style={{ maxWidth: 5, padding: 0 }}></td>,
];
export const FolderIndicatorHeader: Header<FolderDataset[0]> = [
"Indicators",
"Id.",
NumericSOV<FolderDataset[0]>(x => Number(x.__related.pb?.highlight)),
() => <td key={nanoid()} style={{ maxWidth: 5, padding: 0 }}></td>,
];
export default IndicatorHeader;
+32 -34
View File
@@ -1,23 +1,26 @@
import React, { useState } from "react";
import TitleCell from "../cells/TitleCell";
import RankingCell from "../cells/RankingCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import React from "react";
import { ChartDocument, PublicUserDocument } from "tachi-common";
import { PBDataset } from "types/tables";
import { PublicUserDocument, ChartDocument } from "tachi-common";
import TachiTable, { Header } from "../components/TachiTable";
import ScoreCell from "../cells/ScoreCell";
import IndexCell from "../cells/IndexCell";
import DeltaCell from "../cells/DeltaCell";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import DropdownRow from "../components/DropdownRow";
import { FormatBMSTables, IsNullish } from "util/misc";
import LampCell from "../cells/LampCell";
import BMSDifficultyCell from "../cells/BMSDifficultyCell";
import { BMS_TABLES } from "util/constants/bms-tables";
import { FormatBMSTables, IsNullish } from "util/misc";
import { NumericSOV, StrSOV } from "util/sorts";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import { CreateDefaultPBSearchParams } from "util/tables/create-search";
import { ValueGetterOrHybrid } from "util/ztable/search";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import BMSDifficultyCell from "../cells/BMSDifficultyCell";
import DeltaCell from "../cells/DeltaCell";
import IndexCell from "../cells/IndexCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import LampCell from "../cells/LampCell";
import RankingCell from "../cells/RankingCell";
import ScoreCell from "../cells/ScoreCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable, { Header } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import IndicatorHeader from "../headers/IndicatorHeader";
function GetBMSTableVal(chart: ChartDocument<"bms:7K" | "bms:14K">, key: string) {
for (const table of chart.data.tableFolders) {
@@ -43,7 +46,17 @@ export default function BMSPBTable({
playtype: "7K" | "14K";
}) {
const headers: Header<PBDataset<"bms:7K" | "bms:14K">[0]>[] = [
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
[
"Chart",
"Ch.",
NumericSOV(
x =>
x.__related.chart.tierlistInfo["sgl-EC"]?.value ??
x.__related.chart.tierlistInfo["sgl-HC"]?.value ??
x.__related.chart.levelNum
),
],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Deltas", "Deltas", NumericSOV(x => x.scoreData.percent)],
@@ -91,23 +104,7 @@ export default function BMSPBTable({
dataset={dataset}
headers={headers}
entryName="PBs"
searchFunctions={{
artist: x => x.__related.song.artist,
title: x => x.__related.song.title,
difficulty: x => FormatBMSTables(x.__related.chart.data.tableFolders),
...tableSearchFns,
score: x => x.scoreData.score,
percent: x => x.scoreData.percent,
ranking: x => x.rankingData.rank,
lamp: {
valueGetter: x => [x.scoreData.lamp, x.scoreData.lampIndex],
strToNum: HumanFriendlyStrToLampIndex("bms", playtype),
},
grade: {
valueGetter: x => [x.scoreData.grade, x.scoreData.gradeIndex],
strToNum: HumanFriendlyStrToGradeIndex("bms", playtype),
},
}}
searchFunctions={CreateDefaultPBSearchParams("bms", playtype)}
defaultSortMode={indexCol ? "#" : undefined}
rowFunction={pb => (
<Row
@@ -150,6 +147,7 @@ function Row({
>
{indexCol && <IndexCell index={pb.__related.index} />}
<BMSDifficultyCell chart={pb.__related.chart} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={pb.__related.song} chart={pb.__related.chart} game="bms" />
<ScoreCell score={pb} />
<DeltaCell
@@ -1,21 +1,24 @@
import React from "react";
import TitleCell from "../cells/TitleCell";
import RankingCell from "../cells/RankingCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import { PBDataset } from "types/tables";
import { Game, GetGamePTConfig, PublicUserDocument } from "tachi-common";
import TachiTable, { Header } from "../components/TachiTable";
import IndexCell from "../cells/IndexCell";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import DropdownRow from "../components/DropdownRow";
import LampCell from "../cells/LampCell";
import DifficultyCell from "../cells/DifficultyCell";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import ScoreCell from "../cells/ScoreCell";
import RatingCell from "../cells/RatingCell";
import { PBDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { NumericSOV, StrSOV } from "util/sorts";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import { CreateDefaultPBSearchParams } from "util/tables/create-search";
import DifficultyCell from "../cells/DifficultyCell";
import IndexCell from "../cells/IndexCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import LampCell from "../cells/LampCell";
import RankingCell from "../cells/RankingCell";
import RatingCell from "../cells/RatingCell";
import ScoreCell from "../cells/ScoreCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable, { Header } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import IndicatorHeader from "../headers/IndicatorHeader";
export default function GenericPBTable({
dataset,
@@ -38,13 +41,14 @@ export default function GenericPBTable({
const headers: Header<PBDataset[0]>[] = [
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
[
gptConfig.defaultScoreRatingAlg,
gptConfig.defaultScoreRatingAlg,
NumericSOV(x => x.calculatedData[gptConfig.defaultScoreRatingAlg] ?? 0),
NumericSOV(x => x.calculatedData[gptConfig.defaultScoreRatingAlg] ?? -Infinity),
],
["Site Ranking", "Site Rank", NumericSOV(x => x.rankingData.rank)],
["Last Raised", "Last Raised", NumericSOV(x => x.timeAchieved ?? 0)],
@@ -63,23 +67,7 @@ export default function GenericPBTable({
dataset={dataset}
headers={headers}
entryName="PBs"
searchFunctions={{
artist: x => x.__related.song.artist,
title: x => x.__related.song.title,
difficulty: x => x.__related.chart.difficulty,
level: x => x.__related.chart.levelNum,
score: x => x.scoreData.score,
percent: x => x.scoreData.percent,
ranking: x => x.rankingData.rank,
lamp: {
valueGetter: x => [x.scoreData.lamp, x.scoreData.lampIndex],
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
},
grade: {
valueGetter: x => [x.scoreData.grade, x.scoreData.gradeIndex],
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
},
}}
searchFunctions={CreateDefaultPBSearchParams(game, playtype)}
defaultSortMode={indexCol ? "#" : undefined}
rowFunction={pb => (
<Row
@@ -125,6 +113,7 @@ function Row({
>
{indexCol && <IndexCell index={pb.__related.index} />}
<DifficultyCell game={pb.game} chart={pb.__related.chart} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={pb.__related.song} chart={pb.__related.chart} game={pb.game} />
<ScoreCell score={pb} showScore={showScore} />
<LampCell score={pb} />
@@ -1,25 +1,28 @@
import React, { useState } from "react";
import { FormatDifficulty } from "tachi-common/js/utils/util";
import TitleCell from "../cells/TitleCell";
import RankingCell from "../cells/RankingCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import SelectableRating from "../components/SelectableRating";
import { PBDataset } from "types/tables";
import { PublicUserDocument, ScoreCalculatedDataLookup } from "tachi-common";
import TachiTable, { Header, ZTableTHProps } from "../components/TachiTable";
import DifficultyCell from "../cells/DifficultyCell";
import ScoreCell from "../cells/ScoreCell";
import IndexCell from "../cells/IndexCell";
import DeltaCell from "../cells/DeltaCell";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import { nanoid } from "nanoid";
import DropdownRow from "../components/DropdownRow";
import IIDXPBDropdown from "../dropdowns/IIDXPBDropdown";
import IIDXLampCell from "../cells/IIDXLampCell";
import { IsNullish } from "util/misc";
import { usePBState } from "../components/UseScoreState";
import useScoreRatingAlg from "components/util/useScoreRatingAlg";
import { nanoid } from "nanoid";
import React, { useState } from "react";
import { PublicUserDocument, ScoreCalculatedDataLookup } from "tachi-common";
import { FormatDifficulty } from "tachi-common/js/utils/util";
import { PBDataset } from "types/tables";
import { IsNullish } from "util/misc";
import { NumericSOV, StrSOV } from "util/sorts";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import { CreateDefaultPBSearchParams } from "util/tables/create-search";
import DeltaCell from "../cells/DeltaCell";
import DifficultyCell from "../cells/DifficultyCell";
import IIDXLampCell from "../cells/IIDXLampCell";
import IndexCell from "../cells/IndexCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import RankingCell from "../cells/RankingCell";
import ScoreCell from "../cells/ScoreCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import SelectableRating from "../components/SelectableRating";
import TachiTable, { Header, ZTableTHProps } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import IIDXPBDropdown from "../dropdowns/IIDXPBDropdown";
import IndicatorHeader from "../headers/IndicatorHeader";
export default function IIDXPBTable({
dataset,
@@ -43,7 +46,17 @@ export default function IIDXPBTable({
);
const headers: Header<PBDataset<"iidx:SP" | "iidx:DP">[0]>[] = [
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
[
"Chart",
"Ch.",
NumericSOV(
x =>
x.__related.chart.tierlistInfo["kt-NC"]?.value ??
x.__related.chart.tierlistInfo["kt-HC"]?.value ??
x.__related.chart.levelNum
),
],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Deltas", "Deltas", NumericSOV(x => x.scoreData.percent)],
@@ -80,23 +93,7 @@ export default function IIDXPBTable({
dataset={dataset}
headers={headers}
entryName="PBs"
searchFunctions={{
artist: x => x.__related.song.artist,
title: x => x.__related.song.title,
difficulty: x => FormatDifficulty(x.__related.chart, "iidx"),
level: x => x.__related.chart.levelNum,
score: x => x.scoreData.score,
percent: x => x.scoreData.percent,
ranking: x => x.rankingData.rank,
lamp: {
valueGetter: x => [x.scoreData.lamp, x.scoreData.lampIndex],
strToNum: HumanFriendlyStrToLampIndex("iidx", playtype),
},
grade: {
valueGetter: x => [x.scoreData.grade, x.scoreData.gradeIndex],
strToNum: HumanFriendlyStrToGradeIndex("iidx", playtype),
},
}}
searchFunctions={CreateDefaultPBSearchParams("iidx", playtype)}
defaultSortMode={indexCol ? "#" : undefined}
rowFunction={pb => (
<Row
@@ -138,10 +135,10 @@ function Row({
scoreState={scoreState}
/>
}
className={scoreState.highlight ? "highlighted-row" : ""}
>
{indexCol && <IndexCell index={pb.__related.index} />}
<DifficultyCell chart={pb.__related.chart} game={"iidx"} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={pb.__related.song} chart={pb.__related.chart} game="iidx" />
<ScoreCell score={pb} />
<DeltaCell
@@ -1,21 +1,24 @@
import React from "react";
import TitleCell from "../cells/TitleCell";
import RankingCell from "../cells/RankingCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import { PBDataset } from "types/tables";
import { PublicUserDocument } from "tachi-common";
import TachiTable, { Header } from "../components/TachiTable";
import IndexCell from "../cells/IndexCell";
import { PBDataset } from "types/tables";
import { NumericSOV, StrSOV } from "util/sorts";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import DropdownRow from "../components/DropdownRow";
import LampCell from "../cells/LampCell";
import { CreateDefaultPBSearchParams } from "util/tables/create-search";
import DifficultyCell from "../cells/DifficultyCell";
import IndexCell from "../cells/IndexCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import LampCell from "../cells/LampCell";
import MillionsScoreCell from "../cells/MillionsScoreCell";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import RatingCell from "../cells/RatingCell";
import MusecaJudgementCell from "../cells/MusecaJudgementCell";
import RankingCell from "../cells/RankingCell";
import RatingCell from "../cells/RatingCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable, { Header } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import IndicatorHeader from "../headers/IndicatorHeader";
export default function MusecaPBTable({
dataset,
@@ -32,9 +35,10 @@ export default function MusecaPBTable({
}) {
const headers: Header<PBDataset<"museca:Single">[0]>[] = [
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Near - Miss", "Nr-Ms", NumericSOV(x => x.scoreData.percent)],
["Near - Miss", "Nr. Ms.", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
["ktRating", "ktRating", NumericSOV(x => x.calculatedData.ktRating ?? 0)],
["Site Ranking", "Site Rank", NumericSOV(x => x.rankingData.rank)],
@@ -54,23 +58,7 @@ export default function MusecaPBTable({
dataset={dataset}
headers={headers}
entryName="PBs"
searchFunctions={{
artist: x => x.__related.song.artist,
title: x => x.__related.song.title,
difficulty: x => x.__related.chart.difficulty,
level: x => x.__related.chart.levelNum,
score: x => x.scoreData.score,
percent: x => x.scoreData.percent,
ranking: x => x.rankingData.rank,
lamp: {
valueGetter: x => [x.scoreData.lamp, x.scoreData.lampIndex],
strToNum: HumanFriendlyStrToLampIndex("museca", playtype),
},
grade: {
valueGetter: x => [x.scoreData.grade, x.scoreData.gradeIndex],
strToNum: HumanFriendlyStrToGradeIndex("museca", playtype),
},
}}
searchFunctions={CreateDefaultPBSearchParams("museca", playtype)}
defaultSortMode={indexCol ? "#" : undefined}
rowFunction={pb => (
<Row
@@ -113,6 +101,7 @@ function Row({
>
{indexCol && <IndexCell index={pb.__related.index} />}
<DifficultyCell game="museca" chart={pb.__related.chart} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={pb.__related.song} chart={pb.__related.chart} game="museca" />
<MillionsScoreCell score={pb} />
<MusecaJudgementCell score={pb} />
+3 -3
View File
@@ -1,12 +1,12 @@
import React from "react";
import { IDStrings, PublicUserDocument, ScoreCalculatedDataLookup } from "tachi-common";
import { GamePT } from "types/react";
import { PBDataset } from "types/tables";
import { IDStrings, PublicUserDocument, ScoreCalculatedDataLookup } from "tachi-common";
import IIDXPBTable from "./IIDXPBTable";
import BMSPBTable from "./BMSPBTable";
import SDVXPBTable from "./SDVXPBTable";
import GenericPBTable from "./GenericPBTable";
import IIDXPBTable from "./IIDXPBTable";
import MusecaPBTable from "./MusecaPBTable";
import SDVXPBTable from "./SDVXPBTable";
export default function PBTable({
dataset,
@@ -1,21 +1,23 @@
import React from "react";
import TitleCell from "../cells/TitleCell";
import RankingCell from "../cells/RankingCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import { PBDataset } from "types/tables";
import { PublicUserDocument } from "tachi-common";
import TachiTable, { Header } from "../components/TachiTable";
import IndexCell from "../cells/IndexCell";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import DropdownRow from "../components/DropdownRow";
import { PBDataset } from "types/tables";
import { IsNullish } from "util/misc";
import LampCell from "../cells/LampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultPBSearchParams } from "util/tables/create-search";
import DifficultyCell from "../cells/DifficultyCell";
import IndexCell from "../cells/IndexCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import LampCell from "../cells/LampCell";
import MillionsScoreCell from "../cells/MillionsScoreCell";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import RankingCell from "../cells/RankingCell";
import SDVXJudgementCell from "../cells/SDVXJudgementCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable, { Header } from "../components/TachiTable";
import { usePBState } from "../components/UseScoreState";
import GenericPBDropdown from "../dropdowns/GenericPBDropdown";
import IndicatorHeader from "../headers/IndicatorHeader";
export default function SDVXPBTable({
dataset,
@@ -32,9 +34,10 @@ export default function SDVXPBTable({
}) {
const headers: Header<PBDataset<"sdvx:Single">[0]>[] = [
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Near - Miss", "Nr-Ms", NumericSOV(x => x.scoreData.percent)],
["Near - Miss", "Nr. Ms.", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
["VF6", "VF6", NumericSOV(x => x.calculatedData.VF6 ?? 0)],
["Site Ranking", "Site Rank", NumericSOV(x => x.rankingData.rank)],
@@ -54,23 +57,7 @@ export default function SDVXPBTable({
dataset={dataset}
headers={headers}
entryName="PBs"
searchFunctions={{
artist: x => x.__related.song.artist,
title: x => x.__related.song.title,
difficulty: x => x.__related.chart.difficulty,
level: x => x.__related.chart.levelNum,
score: x => x.scoreData.score,
percent: x => x.scoreData.percent,
ranking: x => x.rankingData.rank,
lamp: {
valueGetter: x => [x.scoreData.lamp, x.scoreData.lampIndex],
strToNum: HumanFriendlyStrToLampIndex("sdvx", playtype),
},
grade: {
valueGetter: x => [x.scoreData.grade, x.scoreData.gradeIndex],
strToNum: HumanFriendlyStrToGradeIndex("sdvx", playtype),
},
}}
searchFunctions={CreateDefaultPBSearchParams("sdvx", playtype)}
defaultSortMode={indexCol ? "#" : undefined}
rowFunction={pb => (
<Row
@@ -113,6 +100,7 @@ function Row({
>
{indexCol && <IndexCell index={pb.__related.index} />}
<DifficultyCell game="sdvx" chart={pb.__related.chart} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell song={pb.__related.song} chart={pb.__related.chart} game="sdvx" />
<MillionsScoreCell score={pb} />
<SDVXJudgementCell score={pb} />
@@ -1,25 +1,19 @@
import React, { useState } from "react";
import { FormatDifficulty } from "tachi-common/js/utils/util";
import TitleCell from "../cells/TitleCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import React from "react";
import { integer, Playtypes, PublicUserDocument } from "tachi-common";
import { ScoreDataset } from "types/tables";
import { integer, PublicUserDocument, Playtypes, ScoreDocument } from "tachi-common";
import TachiTable from "../components/TachiTable";
import ScoreCell from "../cells/ScoreCell";
import DeltaCell from "../cells/DeltaCell";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import DropdownRow from "../components/DropdownRow";
import { IsNullish } from "util/misc";
import LampCell from "../cells/LampCell";
import BMSDifficultyCell from "../cells/BMSDifficultyCell";
import { Playtype } from "types/tachi";
import GenericScoreDropdown from "../dropdowns/GenericScoreDropdown";
import SDVXJudgementCell from "../cells/SDVXJudgementCell";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultScoreSearchParams } from "util/tables/create-search";
import BMSDifficultyCell from "../cells/BMSDifficultyCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable, { Header } from "../components/TachiTable";
import { useScoreState } from "../components/UseScoreState";
import RatingCell from "../cells/RatingCell";
import GenericScoreDropdown from "../dropdowns/GenericScoreDropdown";
import BMSScoreCoreCells from "../game-core-cells/BMSScoreCoreCells";
import { CreateDefaultScoreSearch } from "util/tables";
import IndicatorHeader from "../headers/IndicatorHeader";
export default function BMSScoreTable({
reqUser,
@@ -36,18 +30,30 @@ export default function BMSScoreTable({
<TachiTable
dataset={dataset}
pageLen={pageLen}
headers={[
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Deltas", "Deltas", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
["Sieglinde", "sgl.", NumericSOV(x => x.calculatedData.sieglinde ?? 0)],
headers={
[
[
"Chart",
"Ch.",
NumericSOV(
x =>
x.__related.chart.tierlistInfo["sgl-EC"]?.value ??
x.__related.chart.tierlistInfo["sgl-HC"]?.value ??
x.__related.chart.levelNum
),
],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Deltas", "Deltas", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
["Sieglinde", "sgl.", NumericSOV(x => x.calculatedData.sieglinde ?? 0)],
["Timestamp", "Timestamp", NumericSOV(x => x.timeAchieved ?? 0)],
]}
["Timestamp", "Timestamp", NumericSOV(x => x.timeAchieved ?? 0)],
] as Header<ScoreDataset<"bms:7K" | "bms:14K">[0]>[]
}
entryName="Scores"
searchFunctions={CreateDefaultScoreSearch<"bms:7K" | "bms:14K">("bms", playtype)}
searchFunctions={CreateDefaultScoreSearchParams("bms", playtype)}
rowFunction={sc => (
<Row playtype={playtype} key={sc.scoreID} sc={sc} reqUser={reqUser} />
)}
@@ -81,6 +87,7 @@ function Row({
}
>
<BMSDifficultyCell chart={sc.__related.chart} />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell
song={sc.__related.song}
chart={sc.__related.chart}
@@ -1,24 +1,18 @@
import React, { useState } from "react";
import { FormatDifficulty } from "tachi-common/js/utils/util";
import TitleCell from "../cells/TitleCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import React from "react";
import { Game, GetGamePTConfig, integer, PublicUserDocument } from "tachi-common";
import { ScoreDataset } from "types/tables";
import { integer, PublicUserDocument, Playtypes, Game, GetGamePTConfig } from "tachi-common";
import TachiTable from "../components/TachiTable";
import DifficultyCell from "../cells/DifficultyCell";
import ScoreCell from "../cells/ScoreCell";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import DropdownRow from "../components/DropdownRow";
import { IsNullish } from "util/misc";
import LampCell from "../cells/LampCell";
import MillionsScoreCell from "../cells/MillionsScoreCell";
import GenericScoreDropdown from "../dropdowns/GenericScoreDropdown";
import RatingCell from "../cells/RatingCell";
import { Playtype } from "types/tachi";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultScoreSearchParams } from "util/tables/create-search";
import DifficultyCell from "../cells/DifficultyCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import TimestampCell from "../cells/TimestampCell";
import DropdownRow from "../components/DropdownRow";
import TachiTable from "../components/TachiTable";
import { useScoreState } from "../components/UseScoreState";
import GenericScoreDropdown from "../dropdowns/GenericScoreDropdown";
import GenericScoreCoreCells from "../game-core-cells/GenericScoreCoreCells";
import { CreateDefaultScoreSearch } from "util/tables";
import IndicatorHeader from "../headers/IndicatorHeader";
export default function GenericScoreTable({
reqUser,
@@ -43,6 +37,7 @@ export default function GenericScoreTable({
pageLen={pageLen}
headers={[
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
@@ -54,7 +49,7 @@ export default function GenericScoreTable({
["Timestamp", "Timestamp", NumericSOV(x => x.timeAchieved ?? 0)],
]}
entryName="Scores"
searchFunctions={CreateDefaultScoreSearch(game, playtype)}
searchFunctions={CreateDefaultScoreSearchParams(game, playtype)}
rowFunction={sc => (
<Row key={sc.scoreID} sc={sc} reqUser={reqUser} showScore={showScore} />
)}
@@ -88,6 +83,7 @@ function Row({
}
>
<DifficultyCell chart={sc.__related.chart} game={sc.game} />
<IndicatorsCell highlight={scoreState.highlight} />
<GenericScoreCoreCells sc={sc} showScore={showScore} />
<TimestampCell time={sc.timeAchieved} service={sc.service} />
</DropdownRow>
@@ -1,20 +1,22 @@
import React, { useState } from "react";
import TitleCell from "../cells/TitleCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import SelectableRating from "../components/SelectableRating";
import { ScoreDataset } from "types/tables";
import { integer, ScoreCalculatedDataLookup, PublicUserDocument, Playtypes } from "tachi-common";
import TachiTable, { ZTableTHProps } from "../components/TachiTable";
import DifficultyCell from "../cells/DifficultyCell";
import { nanoid } from "nanoid";
import DropdownRow from "../components/DropdownRow";
import IIDXScoreDropdown from "../dropdowns/IIDXScoreDropdown";
import { Playtype } from "types/tachi";
import { useScoreState } from "../components/UseScoreState";
import IIDXScoreCoreCells from "../game-core-cells/IIDXScoreCoreCells";
import { CreateDefaultScoreSearch } from "util/tables";
import useScoreRatingAlg from "components/util/useScoreRatingAlg";
import { nanoid } from "nanoid";
import React, { useState } from "react";
import { integer, Playtypes, PublicUserDocument, ScoreCalculatedDataLookup } from "tachi-common";
import { ScoreDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultScoreSearchParams } from "util/tables/create-search";
import DifficultyCell from "../cells/DifficultyCell";
import IndicatorsCell from "../cells/IndicatorsCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import SelectableRating from "../components/SelectableRating";
import TachiTable, { Header, ZTableTHProps } from "../components/TachiTable";
import { useScoreState } from "../components/UseScoreState";
import IIDXScoreDropdown from "../dropdowns/IIDXScoreDropdown";
import IIDXScoreCoreCells from "../game-core-cells/IIDXScoreCoreCells";
import IndicatorHeader from "../headers/IndicatorHeader";
export default function IIDXScoreTable({
reqUser,
@@ -36,31 +38,43 @@ export default function IIDXScoreTable({
<TachiTable
dataset={dataset}
pageLen={pageLen}
headers={[
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Deltas", "Deltas", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
headers={
[
"Rating",
"Rating",
NumericSOV(x => x.calculatedData[rating] ?? 0),
(thProps: ZTableTHProps) => (
<SelectableRating<"iidx:SP" | "iidx:DP">
key={nanoid()}
game="iidx"
playtype={playtype}
rating={rating}
setRating={setRating}
{...thProps}
/>
),
],
["Timestamp", "Timestamp", NumericSOV(x => x.timeAchieved ?? 0)],
]}
[
"Chart",
"Ch.",
NumericSOV(
x =>
x.__related.chart.tierlistInfo["kt-NC"]?.value ??
x.__related.chart.tierlistInfo["kt-HC"]?.value ??
x.__related.chart.levelNum
),
],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Deltas", "Deltas", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
[
"Rating",
"Rating",
NumericSOV(x => x.calculatedData[rating] ?? 0),
(thProps: ZTableTHProps) => (
<SelectableRating<"iidx:SP" | "iidx:DP">
key={nanoid()}
game="iidx"
playtype={playtype}
rating={rating}
setRating={setRating}
{...thProps}
/>
),
],
["Timestamp", "Timestamp", NumericSOV(x => x.timeAchieved ?? 0)],
] as Header<ScoreDataset<"iidx:SP" | "iidx:DP">[0]>[]
}
entryName="Scores"
searchFunctions={CreateDefaultScoreSearch<"iidx:SP" | "iidx:DP">("iidx", playtype)}
searchFunctions={CreateDefaultScoreSearchParams("iidx", playtype)}
rowFunction={sc => (
<Row
key={sc.scoreID}
@@ -102,6 +116,7 @@ function Row({
}
>
<DifficultyCell chart={sc.__related.chart} game="iidx" />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell
song={sc.__related.song}
comment={sc.comment}
@@ -1,24 +1,18 @@
import React, { useEffect, useState } from "react";
import { FormatDifficulty } from "tachi-common/js/utils/util";
import TitleCell from "../cells/TitleCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import React from "react";
import { integer, Playtypes, PublicUserDocument } from "tachi-common";
import { ScoreDataset } from "types/tables";
import { integer, PublicUserDocument, Playtypes, ScoreDocument } from "tachi-common";
import TachiTable from "../components/TachiTable";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultScoreSearchParams } from "util/tables/create-search";
import DifficultyCell from "../cells/DifficultyCell";
import ScoreCell from "../cells/ScoreCell";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import IndicatorsCell from "../cells/IndicatorsCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import { IsNullish } from "util/misc";
import LampCell from "../cells/LampCell";
import MillionsScoreCell from "../cells/MillionsScoreCell";
import GenericScoreDropdown from "../dropdowns/GenericScoreDropdown";
import RatingCell from "../cells/RatingCell";
import MusecaJudgementCell from "../cells/MusecaJudgementCell";
import TachiTable, { Header } from "../components/TachiTable";
import { useScoreState } from "../components/UseScoreState";
import GenericScoreDropdown from "../dropdowns/GenericScoreDropdown";
import MusecaScoreCoreCells from "../game-core-cells/MusecaScoreCoreCells";
import { CreateDefaultScoreSearch } from "util/tables";
import IndicatorHeader from "../headers/IndicatorHeader";
export default function MusecaScoreTable({
reqUser,
@@ -34,18 +28,21 @@ export default function MusecaScoreTable({
<TachiTable
dataset={dataset}
pageLen={pageLen}
headers={[
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Near-Miss", "Nr-Ms", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
["KtRating", "KtRating", NumericSOV(x => x.calculatedData.ktRating ?? 0)],
headers={
[
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Near-Miss", "Nr. Ms.", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
["KtRating", "KtRating", NumericSOV(x => x.calculatedData.ktRating ?? 0)],
["Timestamp", "Timestamp", NumericSOV(x => x.timeAchieved ?? 0)],
]}
["Timestamp", "Timestamp", NumericSOV(x => x.timeAchieved ?? 0)],
] as Header<ScoreDataset<"museca:Single">[0]>[]
}
entryName="Scores"
searchFunctions={CreateDefaultScoreSearch<"museca:Single">("museca", "Single")}
searchFunctions={CreateDefaultScoreSearchParams("museca", "Single")}
rowFunction={sc => <Row key={sc.scoreID} sc={sc} reqUser={reqUser} />}
/>
);
@@ -75,6 +72,7 @@ function Row({
}
>
<DifficultyCell chart={sc.__related.chart} game="museca" />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell
song={sc.__related.song}
comment={sc.comment}
@@ -1,23 +1,18 @@
import React, { useState } from "react";
import { FormatDifficulty } from "tachi-common/js/utils/util";
import TitleCell from "../cells/TitleCell";
import TimestampCell from "../cells/TimestampCell";
import { NumericSOV, StrSOV } from "util/sorts";
import React from "react";
import { integer, Playtypes, PublicUserDocument } from "tachi-common";
import { ScoreDataset } from "types/tables";
import { integer, PublicUserDocument, Playtypes, ScoreDocument } from "tachi-common";
import TachiTable from "../components/TachiTable";
import { NumericSOV, StrSOV } from "util/sorts";
import { CreateDefaultScoreSearchParams } from "util/tables/create-search";
import DifficultyCell from "../cells/DifficultyCell";
import ScoreCell from "../cells/ScoreCell";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import IndicatorsCell from "../cells/IndicatorsCell";
import TimestampCell from "../cells/TimestampCell";
import TitleCell from "../cells/TitleCell";
import DropdownRow from "../components/DropdownRow";
import { IsNullish } from "util/misc";
import LampCell from "../cells/LampCell";
import MillionsScoreCell from "../cells/MillionsScoreCell";
import GenericScoreDropdown from "../dropdowns/GenericScoreDropdown";
import SDVXJudgementCell from "../cells/SDVXJudgementCell";
import TachiTable, { Header } from "../components/TachiTable";
import { useScoreState } from "../components/UseScoreState";
import GenericScoreDropdown from "../dropdowns/GenericScoreDropdown";
import SDVXScoreCoreCells from "../game-core-cells/SDVXCoreCells";
import { CreateDefaultScoreSearch } from "util/tables";
import IndicatorHeader from "../headers/IndicatorHeader";
export default function SDVXScoreTable({
reqUser,
@@ -33,18 +28,21 @@ export default function SDVXScoreTable({
<TachiTable
dataset={dataset}
pageLen={pageLen}
headers={[
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Near - Miss", "Nr-Ms", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
["VF6", "VF6", NumericSOV(x => x.calculatedData.VF6 ?? 0)],
headers={
[
["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)],
IndicatorHeader,
["Song", "Song", StrSOV(x => x.__related.song.title)],
["Score", "Score", NumericSOV(x => x.scoreData.percent)],
["Near - Miss", "Nr. Ms.", NumericSOV(x => x.scoreData.percent)],
["Lamp", "Lamp", NumericSOV(x => x.scoreData.lampIndex)],
["VF6", "VF6", NumericSOV(x => x.calculatedData.VF6 ?? 0)],
["Timestamp", "Timestamp", NumericSOV(x => x.timeAchieved ?? 0)],
]}
["Timestamp", "Timestamp", NumericSOV(x => x.timeAchieved ?? 0)],
] as Header<ScoreDataset<"sdvx:Single">[0]>[]
}
entryName="Scores"
searchFunctions={CreateDefaultScoreSearch<"sdvx:Single">("sdvx", "Single")}
searchFunctions={CreateDefaultScoreSearchParams("museca", "Single")}
rowFunction={sc => <Row key={sc.scoreID} sc={sc} reqUser={reqUser} />}
/>
);
@@ -68,6 +66,7 @@ function Row({ sc, reqUser }: { sc: ScoreDataset<"sdvx:Single">[0]; reqUser: Pub
}
>
<DifficultyCell chart={sc.__related.chart} game="sdvx" />
<IndicatorsCell highlight={scoreState.highlight} />
<TitleCell
song={sc.__related.song}
comment={sc.comment}
+16 -9
View File
@@ -1,22 +1,22 @@
import {
ChartDocument,
FolderDocument,
Grades,
IDStrings,
IDStringToGame,
integer,
Lamps,
PBScoreDocument,
PublicUserDocument,
ScoreDocument,
UserGameStats,
SessionDocument,
ShowcaseStatChart,
ShowcaseStatFolder,
UserGameStatsSnapshot,
SessionDocument,
IDStringToGame,
ChartDocument,
SongDocument,
PBScoreDocument,
UGSRatingsLookup,
Grades,
Lamps,
TableDocument,
UGSRatingsLookup,
UserGameStats,
UserGameStatsSnapshot,
} from "tachi-common";
export interface UGPTStatsReturn<I extends IDStrings = IDStrings> {
@@ -114,3 +114,10 @@ export interface UGPTTableReturns {
stats: FolderStatsInfo[];
table: TableDocument;
}
export interface UGPTFolderReturns<I extends IDStrings = IDStrings> {
folder: FolderDocument;
songs: SongDocument<IDStringToGame[I]>[];
charts: ChartDocument<I>[];
pbs: PBScoreDocument<I>[];
}
+7
View File
@@ -16,3 +16,10 @@ export type PBDataset<I extends IDStrings = IDStrings> = (PBScoreDocument<I> & {
export type ScoreDataset<I extends IDStrings = IDStrings> = (ScoreDocument<I> & {
__related: { chart: ChartDocument<I>; song: SongDocument<IDStringToGame[I]>; index: integer };
})[];
export type FolderDataset<I extends IDStrings = IDStrings> = (ChartDocument<I> & {
__related: {
pb: PBScoreDocument<I> | null;
song: SongDocument<IDStringToGame[I]>;
};
})[];
+1
View File
@@ -3,6 +3,7 @@ import { TACHI_CHART_THEME } from "./constants/chart-theme";
export const DEFAULT_BAR_PROPS = {
labelTextColor: "black",
labelSkipHeight: 12,
labelSkipWidth: 30,
theme: TACHI_CHART_THEME,
motionConfig: "stiff",
};
+10 -10
View File
@@ -22,6 +22,16 @@ export function CreateSongMap<G extends Game = Game>(songs: SongDocument<G>[]) {
return songMap;
}
export function CreateChartIDMap<T extends { chartID: string }>(arr: T[]): Map<string, T> {
const map = new Map();
for (const t of arr) {
map.set(t.chartID, t);
}
return map;
}
export function CreateChartMap<I extends IDStrings = IDStrings>(charts: ChartDocument<I>[]) {
const chartMap = new Map<string, ChartDocument<I>>();
@@ -32,16 +42,6 @@ export function CreateChartMap<I extends IDStrings = IDStrings>(charts: ChartDoc
return chartMap;
}
export function CreateScoreMap<I extends IDStrings = IDStrings>(scores: ScoreDocument<I>[]) {
const scoreMap = new Map<string, ScoreDocument<I>>();
for (const score of scores) {
scoreMap.set(score.scoreID, score);
}
return scoreMap;
}
export function CreateChartLink(chart: ChartDocument, game: Game) {
if (chart.isPrimary) {
return `/dashboard/games/${game}/${chart.playtype}/songs/${
+12
View File
@@ -115,3 +115,15 @@ export function StepFromToMax(max: integer, step: integer = 50) {
arr.push(max);
return arr;
}
export function ComposeExpFn(pow: number) {
return (x: number) => Math.pow(x, pow);
}
export function ComposeInverseExpFn(pow: number) {
return (x: number) => Math.pow(x, 1 / pow);
}
export function ComposeLogFn(base: number) {
return (x: number) => Math.log(x) / Math.log(base);
}
-27
View File
@@ -1,27 +0,0 @@
import { FormatDifficulty, Game, IDStrings } from "tachi-common";
import { ScoreDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { HumanFriendlyStrToLampIndex, HumanFriendlyStrToGradeIndex } from "./str-to-num";
import { ValueGetterOrHybrid } from "./ztable/search";
export function CreateDefaultScoreSearch<I extends IDStrings = IDStrings>(
game: Game,
playtype: Playtype
): Record<string, ValueGetterOrHybrid<ScoreDataset<I>[0]>> {
return {
artist: x => x.__related.song.artist,
title: x => x.__related.song.title,
difficulty: x => FormatDifficulty(x.__related.chart, game),
level: x => x.__related.chart.levelNum,
score: x => x.scoreData.score,
percent: x => x.scoreData.percent,
lamp: {
valueGetter: x => [x.scoreData.lamp, x.scoreData.lampIndex],
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
},
grade: {
valueGetter: x => [x.scoreData.grade, x.scoreData.gradeIndex],
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
},
};
}
+115
View File
@@ -0,0 +1,115 @@
import { Game, GamePTConfig, GetGamePTConfig, IDStrings } from "tachi-common";
import { FolderDataset, PBDataset, ScoreDataset } from "types/tables";
import { Playtype } from "types/tachi";
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import { ValueGetterOrHybrid } from "util/ztable/search";
export function CreateDefaultScoreSearchParams<I extends IDStrings = IDStrings>(
game: Game,
playtype: Playtype
) {
const gptConfig = GetGamePTConfig(game, playtype);
const searchFunctions: Record<string, ValueGetterOrHybrid<ScoreDataset<I>[0]>> = {
artist: x => x.__related.song.artist,
title: x => x.__related.song.title,
difficulty: x => x.__related.chart.difficulty,
level: x => x.__related.chart.levelNum,
score: x => x.scoreData.score,
percent: x => x.scoreData.percent,
highlight: x => !!x.highlight,
lamp: {
valueGetter: x => [x.scoreData.lamp, x.scoreData.lampIndex],
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
},
grade: {
valueGetter: x => [x.scoreData.grade, x.scoreData.gradeIndex],
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
},
...CreateCalcDataSearchFns(gptConfig),
};
return searchFunctions;
}
export function CreateDefaultPBSearchParams<I extends IDStrings = IDStrings>(
game: Game,
playtype: Playtype
) {
const gptConfig = GetGamePTConfig(game, playtype);
const searchFunctions: Record<string, ValueGetterOrHybrid<PBDataset<I>[0]>> = {
artist: x => x.__related.song.artist,
title: x => x.__related.song.title,
difficulty: x => x.__related.chart.difficulty,
level: x => x.__related.chart.levelNum,
score: x => x.scoreData.score,
percent: x => x.scoreData.percent,
ranking: x => x.rankingData.rank,
highlight: x => !!x.highlight,
lamp: {
valueGetter: x => [x.scoreData.lamp, x.scoreData.lampIndex],
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
},
grade: {
valueGetter: x => [x.scoreData.grade, x.scoreData.gradeIndex],
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
},
...CreateCalcDataSearchFns(gptConfig),
};
return searchFunctions;
}
export function CreateDefaultFolderSearchParams<I extends IDStrings = IDStrings>(
game: Game,
playtype: Playtype
) {
const gptConfig = GetGamePTConfig(game, playtype);
const searchFunctions: Record<string, ValueGetterOrHybrid<FolderDataset<I>[0]>> = {
artist: x => x.__related.song.artist,
title: x => x.__related.song.title,
difficulty: x => x.difficulty,
level: x => x.levelNum,
score: x => x.__related.pb?.scoreData.score ?? -Infinity,
percent: x => x.__related.pb?.scoreData.percent ?? -Infinity,
ranking: x => x.__related.pb?.rankingData.rank ?? -Infinity,
highlight: x => !!x.__related.pb?.highlight,
played: x => !!x.__related.pb,
lamp: {
valueGetter: x =>
x.__related.pb
? [x.__related.pb.scoreData.lamp, x.__related.pb.scoreData.lampIndex]
: null,
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
},
grade: {
valueGetter: x =>
x.__related.pb
? [x.__related.pb.scoreData.grade, x.__related.pb.scoreData.gradeIndex]
: null,
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
},
...CreateFolderCalcDataSearchFns(gptConfig),
};
return searchFunctions;
}
function CreateFolderCalcDataSearchFns(gptConfig: GamePTConfig) {
return Object.fromEntries(
gptConfig.scoreRatingAlgs.map(e => [
e.toLowerCase(),
(x: FolderDataset[0]) => x.__related.pb?.calculatedData[e] ?? null,
])
);
}
function CreateCalcDataSearchFns(gptConfig: GamePTConfig) {
return Object.fromEntries(
gptConfig.scoreRatingAlgs.map(
e => [e.toLowerCase(), (x: PBDataset[0]) => x.calculatedData[e]] ?? null
)
);
}
+33 -3
View File
@@ -38,7 +38,7 @@ export function ParseDirectives(search: string): Directive[] {
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_wholeMatch, key, mode, value] = ps;
const [wholeMatch, key, mode, value] = ps;
let v = value;
@@ -101,6 +101,14 @@ const Matchers: Record<DirectiveModes, MatchFn> = {
const NeutralMatch = (sv: string, dv: string) => dv.toLowerCase().includes(sv.toLowerCase());
const BooleanMatch = (sv: string, dv: boolean) => {
if (dv) {
return ["yes", "true"].some(x => x.startsWith(sv.toLowerCase()));
}
return ["no", "false"].some(x => x.startsWith(sv.toLowerCase()));
};
function GetStrData(dataValue: string | number | [string, number]) {
if (typeof dataValue === "string") {
return dataValue;
@@ -128,13 +136,23 @@ function GetData(dataValue: string | number | [string, number]) {
*/
function DirectiveMatch(
directive: Directive,
dataValue: string | number | [string, number] | null,
dataValue: string | number | boolean | [string, number] | null,
directiveNumValue?: number
) {
if (dataValue === undefined) {
console.warn(`Unexpected undefined for dataValue ${directive}. Casting to null.`);
// eslint-disable-next-line no-param-reassign
dataValue = null;
}
if (dataValue === null) {
return false;
}
if (typeof dataValue === "boolean") {
return BooleanMatch(directive.value, dataValue);
}
if (!directive.mode) {
return NeutralMatch(directive.value, GetStrData(dataValue));
}
@@ -164,7 +182,7 @@ function DirectiveMatch(
* a number for >= style comparisons. The value getter must return a tri-tuple of its string value,
* its numerical value, and a function to convert the users string request to a number.
*/
export type ValueGetter<D> = (data: D) => string | number | null;
export type ValueGetter<D> = (data: D) => string | number | boolean | null;
export type ValueGetterOrHybrid<D> =
| {
@@ -193,10 +211,22 @@ export function ComposeSearchFunction<D>(
return allGetters.some(vgOrHybrid => {
const v = GetValueGetter(vgOrHybrid)(data);
if (v === undefined) {
console.warn(
`Unexpected undefined in directive ${vgOrHybrid}, casting to null.`
);
return null;
}
if (v === null) {
return null;
}
// These don't make sense for a neutral.
if (typeof v === "boolean") {
return null;
}
return NeutralMatch(search, GetStrData(v));
});
}