mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-13 09:48:27 +03:00
feat: rival ranking in UI
This commit is contained in:
@@ -261,6 +261,7 @@ function ChartLeaderboardTable({
|
||||
|
||||
return (
|
||||
<PBTable
|
||||
defaultRankingViewMode={mode === "rivals" ? "global-no-switch" : "both-if-self"}
|
||||
key={mode}
|
||||
dataset={dataset}
|
||||
game={game}
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
import { SongsReturn } from "types/api-returns";
|
||||
import { GamePT, SetState } from "types/react";
|
||||
import useLUGPTSettings from "components/util/useLUGPTSettings";
|
||||
import { UGPTContextProvider } from "context/UGPTContext";
|
||||
|
||||
export default function GameRoutes() {
|
||||
const { game } = useParams<{ game: string }>();
|
||||
@@ -66,7 +67,9 @@ export default function GameRoutes() {
|
||||
</Route>
|
||||
|
||||
<Route path="/dashboard/games/:game/:playtype">
|
||||
<GamePlaytypeRoutes game={game} />
|
||||
<UGPTContextProvider>
|
||||
<GamePlaytypeRoutes game={game} />
|
||||
</UGPTContextProvider>
|
||||
</Route>
|
||||
|
||||
<Route path="*">
|
||||
|
||||
@@ -1,15 +1,73 @@
|
||||
import useLUGPTSettings from "components/util/useLUGPTSettings";
|
||||
import React from "react";
|
||||
import { integer } from "rg-stats/js/util/types";
|
||||
import { PBScoreDocument } from "tachi-common";
|
||||
|
||||
export type RankingViewMode = "global" | "rival" | "both-if-self" | "global-no-switch";
|
||||
|
||||
export default function RankingCell({
|
||||
rankingData,
|
||||
userID,
|
||||
rankingViewMode,
|
||||
}: {
|
||||
rankingData: PBScoreDocument["rankingData"];
|
||||
userID: integer;
|
||||
rankingViewMode: RankingViewMode;
|
||||
}) {
|
||||
return (
|
||||
<td>
|
||||
<strong>#{rankingData.rank}</strong>
|
||||
<small>/{rankingData.outOf}</small>
|
||||
</td>
|
||||
);
|
||||
const { settings } = useLUGPTSettings();
|
||||
|
||||
switch (rankingViewMode) {
|
||||
case "global":
|
||||
case "global-no-switch":
|
||||
return (
|
||||
<td>
|
||||
<strong>#{rankingData.rank}</strong>
|
||||
<small>/{rankingData.outOf}</small>
|
||||
</td>
|
||||
);
|
||||
case "rival":
|
||||
if (!settings) {
|
||||
return (
|
||||
<td>
|
||||
No Settings, yet tried to view rival stats? not possible. how'd you get
|
||||
here. report this.
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
if (settings?.userID !== userID) {
|
||||
return (
|
||||
<td>
|
||||
<strong>N/A</strong>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<td>
|
||||
<strong>#{rankingData.rivalRank}</strong>
|
||||
<small>/{settings.rivals.length + 1}</small>
|
||||
</td>
|
||||
);
|
||||
|
||||
case "both-if-self":
|
||||
if (settings?.userID === userID && rankingData.rivalRank !== null) {
|
||||
return (
|
||||
<td>
|
||||
<strong>Global #{rankingData.rank}</strong>
|
||||
<small>/{rankingData.outOf}</small>
|
||||
<br />
|
||||
<strong>Rival #{rankingData.rivalRank}</strong>
|
||||
<small>/{settings.rivals.length + 1}</small>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<td>
|
||||
<strong>#{rankingData.rank}</strong>
|
||||
<small>/{rankingData.outOf}</small>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import useLUGPTSettings from "components/util/useLUGPTSettings";
|
||||
import React from "react";
|
||||
import { SetState } from "types/react";
|
||||
import { RankingViewMode } from "../cells/RankingCell";
|
||||
import SortableTH from "./SortableTH";
|
||||
import { ZTableTHProps } from "./TachiTable";
|
||||
|
||||
export default function SelectableRanking({
|
||||
rankingViewMode,
|
||||
setRankingViewMode,
|
||||
changeSort,
|
||||
currentSortMode,
|
||||
reverseSort,
|
||||
}: {
|
||||
rankingViewMode: RankingViewMode;
|
||||
setRankingViewMode: SetState<RankingViewMode>;
|
||||
} & ZTableTHProps) {
|
||||
const { settings } = useLUGPTSettings();
|
||||
|
||||
if (rankingViewMode === "both-if-self" || rankingViewMode === "global-no-switch" || !settings) {
|
||||
return (
|
||||
<SortableTH
|
||||
changeSort={changeSort}
|
||||
currentSortMode={currentSortMode}
|
||||
name="Ranking"
|
||||
reverseSort={reverseSort}
|
||||
shortName="Ranking"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<th>
|
||||
<select
|
||||
onChange={(v) => setRankingViewMode(v.target.value as RankingViewMode)}
|
||||
value={rankingViewMode}
|
||||
style={{
|
||||
backgroundColor: "#131313",
|
||||
border: "none",
|
||||
color: "#ffffff",
|
||||
fontSize: "inherit",
|
||||
font: "inherit",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<option value="global">Global Ranking</option>
|
||||
<option value="rival">Rival Ranking</option>
|
||||
</select>
|
||||
<br />
|
||||
<span onClick={() => changeSort("Site Ranking")}>
|
||||
<i
|
||||
className={`flaticon2-arrow-up icon-sm sort-icon ${
|
||||
currentSortMode === "Site Ranking" && reverseSort ? "active" : ""
|
||||
}`}
|
||||
></i>
|
||||
<i
|
||||
className={`flaticon2-arrow-down icon-sm sort-icon ${
|
||||
currentSortMode === "Site Ranking" && !reverseSort ? "active" : ""
|
||||
}`}
|
||||
></i>
|
||||
</span>
|
||||
</th>
|
||||
);
|
||||
}
|
||||
@@ -6,9 +6,10 @@ import { Game, IDStrings, ScoreCalculatedDataLookup } from "tachi-common";
|
||||
import { FolderDataset } from "types/tables";
|
||||
import { Playtype } from "types/tachi";
|
||||
import Muted from "components/util/Muted";
|
||||
import useLUGPTSettings from "components/util/useLUGPTSettings";
|
||||
import DifficultyCell from "../cells/DifficultyCell";
|
||||
import IndicatorsCell from "../cells/IndicatorsCell";
|
||||
import RankingCell from "../cells/RankingCell";
|
||||
import RankingCell, { RankingViewMode } from "../cells/RankingCell";
|
||||
import TimestampCell from "../cells/TimestampCell";
|
||||
import TitleCell from "../cells/TitleCell";
|
||||
import DropdownRow from "../components/DropdownRow";
|
||||
@@ -19,6 +20,7 @@ import ScoreCoreCells from "../game-core-cells/ScoreCoreCells";
|
||||
import ChartHeader from "../headers/ChartHeader";
|
||||
import { GetGPTCoreHeaders } from "../headers/GameHeaders";
|
||||
import { EmptyHeader, FolderIndicatorHeader } from "../headers/IndicatorHeader";
|
||||
import { CreateRankingHeader } from "../headers/RankingHeader";
|
||||
|
||||
export default function FolderTable<I extends IDStrings = IDStrings>({
|
||||
dataset,
|
||||
@@ -31,7 +33,10 @@ export default function FolderTable<I extends IDStrings = IDStrings>({
|
||||
}) {
|
||||
const defaultRating = useScoreRatingAlg(game, playtype);
|
||||
|
||||
const { settings } = useLUGPTSettings();
|
||||
|
||||
const [rating, setRating] = useState(defaultRating);
|
||||
const [rankingViewMode, setRankingViewMode] = useState<RankingViewMode>("global");
|
||||
|
||||
const headers: Header<FolderDataset[0]>[] = [
|
||||
ChartHeader(game, (k) => k),
|
||||
@@ -45,11 +50,11 @@ export default function FolderTable<I extends IDStrings = IDStrings>({
|
||||
setRating,
|
||||
(x) => x.__related.pb
|
||||
),
|
||||
[
|
||||
"Site Ranking",
|
||||
"Site Rank",
|
||||
NumericSOV((x) => x.__related.pb?.rankingData.rank ?? -Infinity),
|
||||
],
|
||||
CreateRankingHeader(
|
||||
rankingViewMode,
|
||||
setRankingViewMode,
|
||||
(k) => k.__related.pb?.rankingData
|
||||
),
|
||||
["Last Raised", "Last Raised", NumericSOV((x) => x.__related.pb?.timeAchieved ?? 0)],
|
||||
];
|
||||
|
||||
@@ -60,7 +65,13 @@ export default function FolderTable<I extends IDStrings = IDStrings>({
|
||||
entryName="Charts"
|
||||
searchFunctions={CreateDefaultFolderSearchParams(game, playtype)}
|
||||
rowFunction={(data) => (
|
||||
<Row rating={rating} data={data} key={data.chartID} game={game} />
|
||||
<Row
|
||||
rating={rating}
|
||||
data={data}
|
||||
key={data.chartID}
|
||||
game={game}
|
||||
rankingViewMode={rankingViewMode}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
@@ -70,10 +81,12 @@ function Row<I extends IDStrings = IDStrings>({
|
||||
data,
|
||||
rating,
|
||||
game,
|
||||
rankingViewMode,
|
||||
}: {
|
||||
data: FolderDataset<I>[0];
|
||||
game: Game;
|
||||
rating: ScoreCalculatedDataLookup[I];
|
||||
rankingViewMode: RankingViewMode;
|
||||
}) {
|
||||
const score = data.__related.pb;
|
||||
|
||||
@@ -110,7 +123,11 @@ function Row<I extends IDStrings = IDStrings>({
|
||||
<Muted>PB</Muted>
|
||||
</td>
|
||||
<ScoreCoreCells score={score} game={game} rating={rating} chart={data} />
|
||||
<RankingCell rankingData={score.rankingData} />
|
||||
<RankingCell
|
||||
rankingData={score.rankingData}
|
||||
userID={score.userID}
|
||||
rankingViewMode={rankingViewMode}
|
||||
/>
|
||||
<TimestampCell time={score.timeAchieved} />
|
||||
</DropdownRow>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { NumericSOV } from "util/sorts";
|
||||
import React from "react";
|
||||
import { PBScoreDocument } from "tachi-common";
|
||||
import { SetState } from "types/react";
|
||||
import { RankingViewMode } from "../cells/RankingCell";
|
||||
import SelectableRanking from "../components/SelectableRanking";
|
||||
import { Header, ZTableTHProps } from "../components/TachiTable";
|
||||
|
||||
export function CreateRankingHeader<T>(
|
||||
rankingViewMode: RankingViewMode,
|
||||
setRankingViewMode: SetState<RankingViewMode>,
|
||||
kMapToRankingData: (k: T) => PBScoreDocument["rankingData"] | undefined
|
||||
): Header<T> {
|
||||
return [
|
||||
"Site Ranking",
|
||||
"Site Ranking",
|
||||
|
||||
NumericSOV((x) => {
|
||||
const rankingData = kMapToRankingData(x);
|
||||
|
||||
if (!rankingData) {
|
||||
return -Infinity;
|
||||
}
|
||||
|
||||
return rankingViewMode === "rival"
|
||||
? rankingData.rivalRank ?? -Infinity
|
||||
: rankingData.rank;
|
||||
}),
|
||||
(thProps: ZTableTHProps) => (
|
||||
<SelectableRanking
|
||||
rankingViewMode={rankingViewMode}
|
||||
setRankingViewMode={setRankingViewMode}
|
||||
{...thProps}
|
||||
/>
|
||||
),
|
||||
];
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { PBDataset } from "types/tables";
|
||||
import { Playtype } from "types/tachi";
|
||||
import DropdownIndicatorCell from "../cells/DropdownIndicatorCell";
|
||||
import IndexCell from "../cells/IndexCell";
|
||||
import RankingCell from "../cells/RankingCell";
|
||||
import RankingCell, { RankingViewMode } from "../cells/RankingCell";
|
||||
import TimestampCell from "../cells/TimestampCell";
|
||||
import DropdownRow from "../components/DropdownRow";
|
||||
import TachiTable, { Header } from "../components/TachiTable";
|
||||
@@ -18,6 +18,7 @@ import ScoreCoreCells from "../game-core-cells/ScoreCoreCells";
|
||||
import ChartHeader from "../headers/ChartHeader";
|
||||
import { GetGPTCoreHeaders } from "../headers/GameHeaders";
|
||||
import { EmptyHeader } from "../headers/IndicatorHeader";
|
||||
import { CreateRankingHeader } from "../headers/RankingHeader";
|
||||
import PBLeadingRows from "./PBLeadingRows";
|
||||
|
||||
export default function PBTable<I extends IDStrings = IDStrings>({
|
||||
@@ -29,6 +30,7 @@ export default function PBTable<I extends IDStrings = IDStrings>({
|
||||
showChart = true,
|
||||
playtype,
|
||||
alg,
|
||||
defaultRankingViewMode,
|
||||
}: {
|
||||
dataset: PBDataset<I>;
|
||||
indexCol?: boolean;
|
||||
@@ -38,10 +40,14 @@ export default function PBTable<I extends IDStrings = IDStrings>({
|
||||
playtype: Playtype;
|
||||
game: Game;
|
||||
alg?: ScoreCalculatedDataLookup[I];
|
||||
defaultRankingViewMode?: RankingViewMode;
|
||||
}) {
|
||||
const defaultRating = useScoreRatingAlg(game, playtype);
|
||||
|
||||
const [rating, setRating] = useState(alg ?? defaultRating);
|
||||
const [rankingViewMode, setRankingViewMode] = useState<RankingViewMode>(
|
||||
defaultRankingViewMode ?? "global"
|
||||
);
|
||||
|
||||
const headers: Header<PBDataset<I>[0]>[] = [
|
||||
...GetPBLeadingHeaders(
|
||||
@@ -51,7 +57,7 @@ export default function PBTable<I extends IDStrings = IDStrings>({
|
||||
),
|
||||
EmptyHeader,
|
||||
...GetGPTCoreHeaders<PBDataset>(game, playtype, rating, setRating, (x) => x),
|
||||
["Site Ranking", "Site Rank", NumericSOV((x) => x.rankingData.rank)],
|
||||
CreateRankingHeader(rankingViewMode, setRankingViewMode, (k) => k.rankingData),
|
||||
["Last Raised", "Last Raised", NumericSOV((x) => x.timeAchieved ?? 0)],
|
||||
EmptyHeader,
|
||||
];
|
||||
@@ -86,6 +92,7 @@ export default function PBTable<I extends IDStrings = IDStrings>({
|
||||
rating={rating}
|
||||
game={game}
|
||||
playtype={playtype}
|
||||
rankingViewMode={rankingViewMode}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@@ -100,6 +107,7 @@ function Row<I extends IDStrings = IDStrings>({
|
||||
showUser,
|
||||
game,
|
||||
rating,
|
||||
rankingViewMode,
|
||||
}: {
|
||||
pb: PBDataset<I>[0];
|
||||
indexCol: boolean;
|
||||
@@ -110,6 +118,7 @@ function Row<I extends IDStrings = IDStrings>({
|
||||
playtype: Playtype;
|
||||
// ts bug?
|
||||
rating: any; // ScoreCalculatedDataLookup[I];
|
||||
rankingViewMode: RankingViewMode;
|
||||
}) {
|
||||
const scoreState = usePBState(pb);
|
||||
|
||||
@@ -128,7 +137,11 @@ function Row<I extends IDStrings = IDStrings>({
|
||||
{indexCol && <IndexCell index={pb.__related.index} />}
|
||||
<PBLeadingRows {...{ showUser, showChart, pb, scoreState }} />
|
||||
<ScoreCoreCells score={pb} game={game} rating={rating} chart={pb.__related.chart} />
|
||||
<RankingCell rankingData={pb.rankingData} />
|
||||
<RankingCell
|
||||
rankingData={pb.rankingData}
|
||||
userID={pb.userID}
|
||||
rankingViewMode={rankingViewMode}
|
||||
/>
|
||||
<TimestampCell time={pb.timeAchieved} />
|
||||
{showPlaycount && <td>{pb.__playcount ?? 0}</td>}
|
||||
<DropdownIndicatorCell />
|
||||
|
||||
@@ -4,7 +4,7 @@ import React, { useState } from "react";
|
||||
import { ChartDocument, Game, IDStrings, ScoreCalculatedDataLookup } from "tachi-common";
|
||||
import { RivalChartDataset } from "types/tables";
|
||||
import IndexCell from "../cells/IndexCell";
|
||||
import RankingCell from "../cells/RankingCell";
|
||||
import RankingCell, { RankingViewMode } from "../cells/RankingCell";
|
||||
import TimestampCell from "../cells/TimestampCell";
|
||||
import UserCell from "../cells/UserCell";
|
||||
import DropdownRow from "../components/DropdownRow";
|
||||
@@ -13,6 +13,7 @@ import { GraphAndJudgementDataComponent } from "../dropdowns/components/Document
|
||||
import { GPTDropdownSettings } from "../dropdowns/GPTDropdownSettings";
|
||||
import ScoreCoreCells from "../game-core-cells/ScoreCoreCells";
|
||||
import { GetGPTCoreHeaders } from "../headers/GameHeaders";
|
||||
import { CreateRankingHeader } from "../headers/RankingHeader";
|
||||
|
||||
export default function RivalChartTable<I extends IDStrings = IDStrings>({
|
||||
dataset,
|
||||
@@ -28,6 +29,7 @@ export default function RivalChartTable<I extends IDStrings = IDStrings>({
|
||||
const defaultRating = useScoreRatingAlg(game, playtype);
|
||||
|
||||
const [rating, setRating] = useState(defaultRating);
|
||||
const [rankingViewMode, setRankingViewMode] = useState<RankingViewMode>("global");
|
||||
|
||||
const headers: Header<RivalChartDataset[0]>[] = [
|
||||
["#", "#", NumericSOV((x) => x.__related.index)],
|
||||
@@ -39,11 +41,11 @@ export default function RivalChartTable<I extends IDStrings = IDStrings>({
|
||||
setRating,
|
||||
(x) => x.__related.pb
|
||||
),
|
||||
[
|
||||
"Site Ranking",
|
||||
"Site Rank",
|
||||
NumericSOV((x) => x.__related.pb?.rankingData.rank ?? -Infinity),
|
||||
],
|
||||
CreateRankingHeader(
|
||||
rankingViewMode,
|
||||
setRankingViewMode,
|
||||
(k) => k.__related.pb?.rankingData
|
||||
),
|
||||
["Last Raised", "Last Raised", NumericSOV((x) => x.__related.pb?.timeAchieved ?? 0)],
|
||||
];
|
||||
|
||||
@@ -55,7 +57,14 @@ export default function RivalChartTable<I extends IDStrings = IDStrings>({
|
||||
noTopDisplayStr
|
||||
defaultSortMode="#"
|
||||
rowFunction={(data) => (
|
||||
<Row chart={chart} rating={rating} data={data} key={data.id} game={game} />
|
||||
<Row
|
||||
chart={chart}
|
||||
rating={rating}
|
||||
data={data}
|
||||
key={data.id}
|
||||
game={game}
|
||||
rankingViewMode={rankingViewMode}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
@@ -66,11 +75,13 @@ function Row<I extends IDStrings = IDStrings>({
|
||||
rating,
|
||||
game,
|
||||
chart,
|
||||
rankingViewMode,
|
||||
}: {
|
||||
data: RivalChartDataset<I>[0];
|
||||
game: Game;
|
||||
rating: ScoreCalculatedDataLookup[I];
|
||||
chart: ChartDocument;
|
||||
rankingViewMode: RankingViewMode;
|
||||
}) {
|
||||
const pb = data.__related.pb;
|
||||
|
||||
@@ -98,7 +109,11 @@ function Row<I extends IDStrings = IDStrings>({
|
||||
<IndexCell index={data.__related.index} />
|
||||
<UserCell user={data} game={game} playtype={chart.playtype} />
|
||||
<ScoreCoreCells score={pb} game={game} rating={rating} chart={chart} />
|
||||
<RankingCell rankingData={pb.rankingData} />
|
||||
<RankingCell
|
||||
rankingData={pb.rankingData}
|
||||
userID={pb.userID}
|
||||
rankingViewMode={rankingViewMode}
|
||||
/>
|
||||
<TimestampCell time={pb.timeAchieved} />
|
||||
</DropdownRow>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,6 @@ import fetchUGPTData, { UGPTData } from "components/util/query/fetchUGPTData";
|
||||
import React, { createContext, useContext, useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { JustChildren, SetState } from "types/react";
|
||||
import { ErrorPage } from "app/pages/ErrorPage";
|
||||
import { UserContext } from "./UserContext";
|
||||
|
||||
/**
|
||||
@@ -33,7 +32,6 @@ export const UGPTContext = createContext<{
|
||||
setLoggedInData: () => void 0,
|
||||
|
||||
viewingData: null,
|
||||
|
||||
setViewingData: () => void 0,
|
||||
});
|
||||
|
||||
@@ -106,15 +104,6 @@ export function UGPTContextProvider({ children }: JustChildren) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (!viewingData) {
|
||||
return (
|
||||
<ErrorPage
|
||||
statusCode={400}
|
||||
customMessage="This user probably doesn't play this game, or something has gone wrong."
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<UGPTContext.Provider
|
||||
value={{
|
||||
|
||||
@@ -67,6 +67,7 @@ export function CreateDefaultPBSearchParams<I extends IDStrings = IDStrings>(
|
||||
score: (x) => x.scoreData.score,
|
||||
percent: (x) => x.scoreData.percent,
|
||||
ranking: (x) => x.rankingData.rank,
|
||||
rivalRanking: (x) => x.rankingData.rivalRank,
|
||||
highlight: (x) => !!x.highlight,
|
||||
username: (x) => x.__related.user?.username ?? null,
|
||||
lamp: {
|
||||
@@ -98,9 +99,10 @@ export function CreateDefaultFolderSearchParams<I extends IDStrings = IDStrings>
|
||||
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,
|
||||
score: (x) => x.__related.pb?.scoreData.score ?? null,
|
||||
percent: (x) => x.__related.pb?.scoreData.percent ?? null,
|
||||
ranking: (x) => x.__related.pb?.rankingData.rank ?? null,
|
||||
rivalRanking: (x) => x.__related.pb?.rankingData.rivalRank ?? null,
|
||||
highlight: (x) => !!x.__related.pb?.highlight,
|
||||
played: (x) => !!x.__related.pb,
|
||||
lamp: {
|
||||
|
||||
@@ -204,6 +204,16 @@ export function GetValueGetter<D>(v: ValueGetterOrHybrid<D>) {
|
||||
return v.valueGetter;
|
||||
}
|
||||
|
||||
function GetValueInsensitive<T>(record: Record<string, T>, key: string): T | undefined {
|
||||
for (const [recKey, value] of Object.entries(record)) {
|
||||
if (recKey.toLowerCase() === key.toLowerCase()) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function ComposeSearchFunction<D>(
|
||||
valueGetters: Record<string, ValueGetterOrHybrid<D>>
|
||||
): ZTableSearchFn<D> {
|
||||
@@ -237,7 +247,7 @@ export function ComposeSearchFunction<D>(
|
||||
}
|
||||
|
||||
for (const directive of directives) {
|
||||
const vgOrHybrid = valueGetters[directive.key];
|
||||
const vgOrHybrid = GetValueInsensitive(valueGetters, directive.key);
|
||||
|
||||
if (!vgOrHybrid) {
|
||||
continue;
|
||||
|
||||
@@ -89,12 +89,13 @@ export async function UpdateChartRanking(game: Game, playtype: Playtype, chartID
|
||||
|
||||
for (const score of scores) {
|
||||
rank++;
|
||||
seenUserIDs.push(score.userID);
|
||||
|
||||
const thisUsersRivals = allRivals[score.userID];
|
||||
|
||||
let rivalRank: integer | null = null;
|
||||
|
||||
if (thisUsersRivals) {
|
||||
if (thisUsersRivals && thisUsersRivals.length > 0) {
|
||||
rivalRank = thisUsersRivals.filter((e) => seenUserIDs.includes(e)).length + 1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user