mirror of
https://github.com/zkldi/Tachi.git
synced 2026-09-22 23:18:05 +03:00
fix frontend issues with ongeki having too much data (#1607)
* fix frontend issues with ongeki having too much data * probably better * I'm so done
This commit is contained in:
@@ -38,6 +38,13 @@ import { type ConfEnumScoreMetric } from "tachi-common/types/metrics";
|
||||
|
||||
type SetScores = (scores: ScoreDocument[]) => void;
|
||||
|
||||
const Plural = (str: string) => str + (str.trimEnd().endsWith("s") ? "" : "s");
|
||||
|
||||
const FormatEnumTitle = (str: string) =>
|
||||
Plural(UppercaseFirst(str))
|
||||
.split(/(?=[A-Z])/u)
|
||||
.join("\u00a0"); // nbsp
|
||||
|
||||
export default function SessionRaiseBreakdown({
|
||||
sessionData,
|
||||
setScores,
|
||||
@@ -48,7 +55,9 @@ export default function SessionRaiseBreakdown({
|
||||
setScores?: SetScores;
|
||||
}) {
|
||||
const game = sessionData.session.game;
|
||||
const lampName = game === "ongeki" || game === "chunithm" ? "noteLamp" : "lamp";
|
||||
const gameConfig = GetGameConfig(game);
|
||||
const enumMetrics = GetScoreMetrics(gameConfig, "ENUM");
|
||||
const gptImpl = GPT_CLIENT_IMPLEMENTATIONS[game];
|
||||
|
||||
const { user } = useContext(UserContext);
|
||||
|
||||
@@ -82,15 +91,21 @@ export default function SessionRaiseBreakdown({
|
||||
<div className="col-12 col-lg-6 offset-lg-3">
|
||||
<div className="d-none d-lg-flex justify-content-center">
|
||||
<div className="btn-group">
|
||||
<SelectButton id={lampName} setValue={setView} value={view}>
|
||||
<Icon type="lightbulb" /> Lamps Only
|
||||
</SelectButton>
|
||||
<SelectButton id={null} setValue={setView} value={view}>
|
||||
<Icon type="bolt" /> All
|
||||
</SelectButton>
|
||||
<SelectButton id="grade" setValue={setView} value={view}>
|
||||
<Icon type="sort-alpha-up" /> Grades Only
|
||||
</SelectButton>
|
||||
{enumMetrics.map((metric) => (
|
||||
<SelectButton
|
||||
id={metric}
|
||||
key={metric}
|
||||
setValue={setView}
|
||||
value={view}
|
||||
>
|
||||
{/* @ts-expect-error ctrl+f `enumIcons[` - standard procedure */}
|
||||
<Icon type={gptImpl.enumIcons[metric] ?? "lightbulb"} />{" "}
|
||||
{FormatEnumTitle(metric)}
|
||||
</SelectButton>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -197,10 +212,11 @@ function SessionScoreStatBreakdown({
|
||||
{enumMetrics.map((metric) => (
|
||||
<div key={metric} style={{ flex: 1 }}>
|
||||
<MiniTable
|
||||
className={enumMetrics.length > 2 ? "no-max-width" : ""}
|
||||
colSpan={[1, 100]}
|
||||
headers={[
|
||||
`${UppercaseFirst(metric)}s`,
|
||||
`New ${UppercaseFirst(metric)}s`,
|
||||
FormatEnumTitle(metric),
|
||||
`New ${FormatEnumTitle(metric)}`,
|
||||
]}
|
||||
>
|
||||
<ElementStatTable
|
||||
@@ -222,7 +238,7 @@ function SessionScoreStatBreakdown({
|
||||
<div className="col-12">
|
||||
<MiniTable
|
||||
colSpan={[1, 100]}
|
||||
headers={[`${UppercaseFirst(view)}s`, `New ${UppercaseFirst(view)}s`]}
|
||||
headers={[`${FormatEnumTitle(view)}`, `New ${FormatEnumTitle(view)}`]}
|
||||
>
|
||||
<ElementStatTable
|
||||
chartMap={chartMap}
|
||||
|
||||
@@ -49,7 +49,7 @@ export default function DifficultyCell({
|
||||
|
||||
const gptImpl = GPT_CLIENT_IMPLEMENTATIONS[game];
|
||||
|
||||
if (game === "iidx-sp" || game === "iidx-dp" || game === "maimaidx") {
|
||||
if (["iidx-dp", "iidx-sp", "maimaidx", "ongeki"].includes(game)) {
|
||||
alwaysShort = true;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,11 @@ export function DisplayLevelNum({
|
||||
return null;
|
||||
}
|
||||
|
||||
if (["chunithm", "maimai", "maimaidx", "ongeki", "wacca"].includes(game)) {
|
||||
if (game === "ongeki" && level === "0") {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (["arcaea", "chunithm", "maimai", "maimaidx", "ongeki", "wacca"].includes(game)) {
|
||||
return (
|
||||
<Muted>
|
||||
{prefix}
|
||||
|
||||
@@ -8,6 +8,14 @@ import { type PBScoreDocument, type ScoreDocument } from "tachi-common";
|
||||
import MiniTable from "../components/MiniTable";
|
||||
import LampCell from "./LampCell";
|
||||
|
||||
const CoreCell = ({ ratingValue }: { ratingValue: number }) => (
|
||||
<td>
|
||||
<div className="underline-on-hover">
|
||||
{FormatScoreRating("ongeki", "scoreRating", ratingValue)}
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
|
||||
export default function OngekiScoreRatingCell({
|
||||
score,
|
||||
}: {
|
||||
@@ -33,7 +41,12 @@ export default function OngekiScoreRatingCell({
|
||||
? 0.1
|
||||
: 0;
|
||||
const bellRating = bellLamp === "FULL BELL" ? 0.05 : 0;
|
||||
const techRating = ratingValue - gradeRating - noteRating - bellRating;
|
||||
const techRating = ratingValue === 0 ? 0 : ratingValue - gradeRating - noteRating - bellRating;
|
||||
|
||||
if (techRating <= 0) {
|
||||
// This value is N/A if techRating isn't positive
|
||||
return <CoreCell ratingValue={ratingValue} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -88,11 +101,7 @@ export default function OngekiScoreRatingCell({
|
||||
}
|
||||
wide
|
||||
>
|
||||
<td>
|
||||
<div className="underline-on-hover">
|
||||
{FormatScoreRating("ongeki", "scoreRating", ratingValue)}
|
||||
</div>
|
||||
</td>
|
||||
<CoreCell ratingValue={ratingValue} />
|
||||
</QuickTooltip>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { FormatTime, MillisToSince } from "#util/time";
|
||||
import { IsNullish } from "#util/misc";
|
||||
import { FormatTime, FormatTimeOfDay, FormatTimeSmall, MillisToSince } from "#util/time";
|
||||
import React from "react";
|
||||
import { type integer } from "tachi-common";
|
||||
import { type integer, type V3Game } from "tachi-common";
|
||||
|
||||
/** Same truncation pattern as TitleCell metadata lines. */
|
||||
const truncLineCls = "d-block w-100 text-truncate";
|
||||
@@ -9,14 +10,26 @@ export default function TimestampCell({
|
||||
service,
|
||||
tableFixedLayoutCompat,
|
||||
time,
|
||||
alwaysShort,
|
||||
game,
|
||||
}: {
|
||||
alwaysShort?: boolean;
|
||||
game?: V3Game;
|
||||
service?: string | null;
|
||||
tableFixedLayoutCompat?: boolean;
|
||||
time: integer | null;
|
||||
}) {
|
||||
if (game === "ongeki" && IsNullish(service)) {
|
||||
alwaysShort = true;
|
||||
}
|
||||
|
||||
const widthStyle = tableFixedLayoutCompat
|
||||
? undefined
|
||||
: { maxWidth: "200px", minWidth: "140px", overflow: "hidden" as const };
|
||||
: {
|
||||
maxWidth: "200px",
|
||||
minWidth: alwaysShort ? "50px" : "140px",
|
||||
overflow: "hidden" as const,
|
||||
};
|
||||
|
||||
return (
|
||||
<td
|
||||
@@ -25,10 +38,18 @@ export default function TimestampCell({
|
||||
>
|
||||
{time ? (
|
||||
<>
|
||||
{MillisToSince(time)}
|
||||
{MillisToSince(time, alwaysShort)}
|
||||
|
||||
<br />
|
||||
<small className="text-body-secondary">{FormatTime(time)}</small>
|
||||
<small className="text-body-secondary">
|
||||
{alwaysShort ? FormatTimeSmall(time) : FormatTime(time)}
|
||||
</small>
|
||||
{alwaysShort && (
|
||||
<>
|
||||
<br />
|
||||
<small className="text-body-secondary">{FormatTimeOfDay(time)}</small>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<span
|
||||
@@ -44,10 +65,11 @@ export default function TimestampCell({
|
||||
<br />
|
||||
<small
|
||||
className={`${truncLineCls} text-body-secondary`}
|
||||
style={{ fontSize: "0.75rem", minWidth: 0 }}
|
||||
style={{ fontSize: "0.75rem", minWidth: 0, whiteSpace: "normal" }}
|
||||
title={`Played On: ${service}`}
|
||||
>
|
||||
Played On: {service}
|
||||
{!alwaysShort && "Played On: "}
|
||||
{service}
|
||||
</small>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -50,6 +50,8 @@ export default function TitleCell({
|
||||
? tooltipParts.filter(Boolean).join(" — ")
|
||||
: undefined;
|
||||
|
||||
const maxWidth = game === "ongeki" ? TITLE_CELL_WIDTH_PX * 0.6 : TITLE_CELL_WIDTH_PX;
|
||||
|
||||
if (game === "popn" && chart) {
|
||||
backgroundImage = `url(${ToCDNURL(
|
||||
`/misc/popn/banners/${(chart as any).data.inGameID}.png`,
|
||||
@@ -71,11 +73,11 @@ export default function TitleCell({
|
||||
className="fading-image-td-left title-cell-wrapper"
|
||||
style={{
|
||||
boxSizing: "border-box",
|
||||
maxWidth: `${TITLE_CELL_WIDTH_PX}px`,
|
||||
maxWidth: `${maxWidth}px`,
|
||||
minWidth: 0,
|
||||
overflow: "hidden",
|
||||
textAlign: "left",
|
||||
width: `${TITLE_CELL_WIDTH_PX}px`,
|
||||
width: `${maxWidth}px`,
|
||||
["--image-url" as string]: backgroundImage,
|
||||
backgroundPosition: center ? "center" : undefined,
|
||||
}}
|
||||
|
||||
@@ -40,8 +40,12 @@ export function ScoreInfo({
|
||||
<tbody>
|
||||
<tr>
|
||||
<ScoreCoreCells chart={chart} game={game} rating={rating} score={score} />
|
||||
{/* @ts-expect-error yeah we know service doesnt necessarily exist */}
|
||||
<TimestampCell service={score?.service} time={score.timeAchieved} />
|
||||
<TimestampCell
|
||||
game={game}
|
||||
/* @ts-expect-error yeah we know service doesnt necessarily exist */
|
||||
service={score?.service}
|
||||
time={score.timeAchieved}
|
||||
/>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -211,7 +211,7 @@ const RowInner = memo(
|
||||
rankingViewMode={rankingViewMode}
|
||||
userID={score.userID}
|
||||
/>
|
||||
<TimestampCell time={score.timeAchieved} />
|
||||
<TimestampCell game={game} time={score.timeAchieved} />
|
||||
</DropdownRow>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -80,7 +80,7 @@ function Row({
|
||||
nested
|
||||
>
|
||||
<ScoreCoreCells chart={chart} game={game} rating={rating as any} score={sc} />
|
||||
<TimestampCell service={sc.service} time={sc.timeAchieved} />
|
||||
<TimestampCell game={game} service={sc.service} time={sc.timeAchieved} />
|
||||
<DropdownIndicatorCell />
|
||||
</DropdownRow>
|
||||
);
|
||||
|
||||
@@ -137,7 +137,7 @@ function Row({
|
||||
rankingViewMode={rankingViewMode}
|
||||
userID={pb.userID}
|
||||
/>
|
||||
<TimestampCell time={pb.timeAchieved} />
|
||||
<TimestampCell game={game} time={pb.timeAchieved} />
|
||||
{showPlaycount && <td>{pb.__playcount ?? 0}</td>}
|
||||
<DropdownIndicatorCell />
|
||||
</DropdownRow>
|
||||
|
||||
@@ -110,7 +110,7 @@ function Row({
|
||||
rankingViewMode={rankingViewMode}
|
||||
userID={pb.userID}
|
||||
/>
|
||||
<TimestampCell time={pb.timeAchieved} />
|
||||
<TimestampCell game={game} time={pb.timeAchieved} />
|
||||
</DropdownRow>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ function Row({
|
||||
song={sc.__related.song}
|
||||
/>
|
||||
<ScoreCoreCells chart={sc.__related.chart} game={game} rating={rating} score={sc} />
|
||||
<TimestampCell service={sc.service} time={sc.timeAchieved} />
|
||||
<TimestampCell game={game} service={sc.service} time={sc.timeAchieved} />
|
||||
<DropdownIndicatorCell />
|
||||
</DropdownRow>
|
||||
);
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
.session-raise-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
@include media-breakpoint-down(md) {
|
||||
@@ -34,6 +36,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
.session-raise-container .no-max-width {
|
||||
max-width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.MuiTabs-scrollButtons.Mui-disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import humaniseDuration from "humanize-duration";
|
||||
import { DateTime } from "luxon";
|
||||
|
||||
export function MillisToSince(ms: number) {
|
||||
return DateTime.fromMillis(ms).toRelative();
|
||||
export function MillisToSince(ms: number, short?: boolean) {
|
||||
return DateTime.fromMillis(ms).toRelative({ style: short ? "narrow" : "long" });
|
||||
}
|
||||
|
||||
export function FormatTime(ms: number) {
|
||||
@@ -33,5 +33,5 @@ export function FormatDurationHours(ms: number) {
|
||||
}
|
||||
|
||||
export function FormatTimeSmall(ms: number) {
|
||||
return DateTime.fromMillis(ms).toLocaleString(DateTime.DATE_SHORT);
|
||||
return DateTime.fromMillis(ms).toISODate();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user