make the score improvement text smaller and show a previous pp inaccuracy warning
All checks were successful
Deploy Website / docker (ubuntu-latest) (push) Successful in 2m21s
All checks were successful
Deploy Website / docker (ubuntu-latest) (push) Successful in 2m21s
This commit is contained in:
parent
8090361615
commit
08295d7b04
@ -1,5 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { formatNumberWithCommas, formatPp } from "@ssr/common/utils/number-utils";
|
import { formatNumberWithCommas, formatPp } from "@ssr/common/utils/number-utils";
|
||||||
|
import { clsx } from "clsx";
|
||||||
|
|
||||||
type ChangeProps = {
|
type ChangeProps = {
|
||||||
/**
|
/**
|
||||||
@ -13,6 +14,11 @@ type ChangeProps = {
|
|||||||
*/
|
*/
|
||||||
formatValue?: (value: number) => string;
|
formatValue?: (value: number) => string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The additional class names
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the number is a pp number
|
* Whether the number is a pp number
|
||||||
*/
|
*/
|
||||||
@ -24,7 +30,7 @@ type ChangeProps = {
|
|||||||
showColors?: boolean;
|
showColors?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Change({ change, formatValue, isPp, showColors }: ChangeProps) {
|
export function Change({ change, formatValue, className, isPp, showColors }: ChangeProps) {
|
||||||
if (change === 0 || (change && change > 0 && change < 0.01) || change === undefined) {
|
if (change === 0 || (change && change > 0 && change < 0.01) || change === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -38,7 +44,7 @@ export function Change({ change, formatValue, isPp, showColors }: ChangeProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p className={`text-sm ${showColors && (change > 0 ? "text-green-400" : "text-red-400")}`}>
|
<p className={clsx("text-sm", showColors && (change > 0 ? "text-green-400" : "text-red-400"), className)}>
|
||||||
{change > 0 ? "+" : ""}
|
{change > 0 ? "+" : ""}
|
||||||
{`${formatValue(change)}${isPp ? "pp" : ""}`}
|
{`${formatValue(change)}${isPp ? "pp" : ""}`}
|
||||||
</p>
|
</p>
|
||||||
|
@ -26,7 +26,11 @@ export function HandAccuracyBadge({ score, hand }: HandAccuracyProps) {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
{scoreImprovement && previousHandAccuracy && (
|
{scoreImprovement && previousHandAccuracy && (
|
||||||
<Tooltip display={`Previous ${formattedHand} Hand Accuracy: ${previousHandAccuracy.toFixed(2)}`}>
|
<Tooltip display={`Previous ${formattedHand} Hand Accuracy: ${previousHandAccuracy.toFixed(2)}`}>
|
||||||
<Change change={scoreImprovement.handAccuracy[hand]} formatValue={num => num.toFixed(2)} />
|
<Change
|
||||||
|
className="text-xs"
|
||||||
|
change={scoreImprovement.handAccuracy[hand]}
|
||||||
|
formatValue={num => num.toFixed(2)}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,7 +58,7 @@ export function ScoreAccuracyBadge({ score, leaderboard }: ScoreAccuracyProps) {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
{scoreImprovement && previousAccuracy && (
|
{scoreImprovement && previousAccuracy && (
|
||||||
<Tooltip display={`Previous Accuracy: ${previousAccuracy.toFixed(2)}%`}>
|
<Tooltip display={`Previous Accuracy: ${previousAccuracy.toFixed(2)}%`}>
|
||||||
<Change change={scoreImprovement.accuracy} formatValue={num => `${num.toFixed(2)}%`} />
|
<Change className="text-xs" change={scoreImprovement.accuracy} formatValue={num => `${num.toFixed(2)}%`} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,6 +27,8 @@ export default function ScoreMissesBadge({ score, hideXMark }: ScoreMissesBadgeP
|
|||||||
wallsHit: (scoreImprovement.misses.wallsHit - misses.wallsHit) * -1,
|
wallsHit: (scoreImprovement.misses.wallsHit - misses.wallsHit) * -1,
|
||||||
};
|
};
|
||||||
const previousScoreFc = previousScoreMisses?.misses == 0;
|
const previousScoreFc = previousScoreMisses?.misses == 0;
|
||||||
|
const isMissImprovement =
|
||||||
|
previousScoreMisses && scoreImprovement && previousScoreMisses.misses > scoreImprovement.misses.misses;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col justify-center items-center">
|
<div className="flex flex-col justify-center items-center">
|
||||||
@ -42,7 +44,7 @@ export default function ScoreMissesBadge({ score, hideXMark }: ScoreMissesBadgeP
|
|||||||
{!hideXMark && <XMarkIcon className={clsx("w-5 h-5", score.fullCombo ? "hidden" : "text-red-400")} />}
|
{!hideXMark && <XMarkIcon className={clsx("w-5 h-5", score.fullCombo ? "hidden" : "text-red-400")} />}
|
||||||
</div>
|
</div>
|
||||||
</ScoreMissesTooltip>
|
</ScoreMissesTooltip>
|
||||||
{additionalData && previousScoreMisses && scoreImprovement && misses && (
|
{additionalData && previousScoreMisses && scoreImprovement && misses && isMissImprovement && (
|
||||||
<div className="flex gap-2 items-center justify-center">
|
<div className="flex gap-2 items-center justify-center">
|
||||||
<ScoreMissesTooltip
|
<ScoreMissesTooltip
|
||||||
missedNotes={previousScoreMisses.missedNotes}
|
missedNotes={previousScoreMisses.missedNotes}
|
||||||
@ -51,7 +53,7 @@ export default function ScoreMissesBadge({ score, hideXMark }: ScoreMissesBadgeP
|
|||||||
wallsHit={previousScoreMisses.wallsHit}
|
wallsHit={previousScoreMisses.wallsHit}
|
||||||
fullCombo={previousScoreFc}
|
fullCombo={previousScoreFc}
|
||||||
>
|
>
|
||||||
<div className="flex gap-1 items-center">
|
<div className="flex gap-1 items-center text-xs">
|
||||||
{previousScoreFc ? (
|
{previousScoreFc ? (
|
||||||
<p className="text-green-400">FC</p>
|
<p className="text-green-400">FC</p>
|
||||||
) : (
|
) : (
|
||||||
@ -67,7 +69,7 @@ export default function ScoreMissesBadge({ score, hideXMark }: ScoreMissesBadgeP
|
|||||||
wallsHit={misses.wallsHit}
|
wallsHit={misses.wallsHit}
|
||||||
fullCombo={additionalData.fullCombo}
|
fullCombo={additionalData.fullCombo}
|
||||||
>
|
>
|
||||||
<div className="flex gap-1 items-center">
|
<div className="flex gap-1 items-center text-xs">
|
||||||
{additionalData.fullCombo ? <p className="text-green-400">FC</p> : formatNumberWithCommas(misses.misses)}
|
{additionalData.fullCombo ? <p className="text-green-400">FC</p> : formatNumberWithCommas(misses.misses)}
|
||||||
</div>
|
</div>
|
||||||
</ScoreMissesTooltip>
|
</ScoreMissesTooltip>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { ScoreBadgeProps } from "@/components/score/badges/badge-props";
|
import { ScoreBadgeProps } from "@/components/score/badges/badge-props";
|
||||||
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
import ScoreSaberLeaderboard from "@ssr/common/leaderboard/impl/scoresaber-leaderboard";
|
||||||
import Tooltip from "@/components/tooltip";
|
import Tooltip from "@/components/tooltip";
|
||||||
import { formatPp } from "@ssr/common/utils/number-utils";
|
import { ensurePositiveNumber, formatPp } from "@ssr/common/utils/number-utils";
|
||||||
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
|
import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
|
||||||
import { Change } from "@/common/change";
|
import { Change } from "@/common/change";
|
||||||
|
import { Warning } from "@/components/warning";
|
||||||
|
|
||||||
type ScorePpProps = ScoreBadgeProps & {
|
type ScorePpProps = ScoreBadgeProps & {
|
||||||
/**
|
/**
|
||||||
@ -22,26 +23,42 @@ export function ScorePpBadge({ score, leaderboard }: ScorePpProps) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const weightedPp = pp * weight;
|
const weightedPp = pp * weight;
|
||||||
const previousPp = fcAccuracy ? scoresaberService.getPp(leaderboard.stars, fcAccuracy).toFixed(0) : undefined;
|
const fcPp =
|
||||||
const isSamePp = previousPp === pp.toFixed(0);
|
!score.fullCombo && fcAccuracy ? scoresaberService.getPp(leaderboard.stars, fcAccuracy).toFixed(0) : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
display={
|
display={
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Performance Points</p>
|
<p className="font-semibold">Performance Points</p>
|
||||||
<p>Raw: {formatPp(pp)}pp</p>
|
<p>Raw: {formatPp(pp)}pp</p>
|
||||||
<p>
|
<p>
|
||||||
Weighted: {formatPp(weightedPp)}pp ({(100 * weight).toFixed(2)}%)
|
Weighted: {formatPp(weightedPp)}pp ({(100 * weight).toFixed(2)}%)
|
||||||
</p>
|
</p>
|
||||||
{previousPp && !isSamePp && <p>Full Combo: {previousPp}pp</p>}
|
{fcPp && <p>Full Combo: {fcPp}pp</p>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{previousAccuracy && (
|
||||||
|
<Warning>
|
||||||
|
<p className="text-red-700">
|
||||||
|
The previous pp may not be 100% accurate due to ScoreSaber API limitations.
|
||||||
|
</p>
|
||||||
|
</Warning>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col items-center justify-center cursor-default">
|
<div className="flex flex-col items-center justify-center cursor-default">
|
||||||
<p>{formatPp(pp)}pp</p>
|
<p>{formatPp(pp)}pp</p>
|
||||||
{previousAccuracy && <Change change={previousAccuracy} isPp />}
|
{previousAccuracy && (
|
||||||
|
<Change
|
||||||
|
className="text-xs"
|
||||||
|
change={ensurePositiveNumber(pp - scoresaberService.getPp(leaderboard.stars, previousAccuracy))}
|
||||||
|
isPp
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
|
@ -8,7 +8,7 @@ export function ScoreScoreBadge({ score }: ScoreBadgeProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center">
|
<div className="flex flex-col items-center justify-center">
|
||||||
<p>{formatNumberWithCommas(Number(score.score.toFixed(0)))}</p>
|
<p>{formatNumberWithCommas(Number(score.score.toFixed(0)))}</p>
|
||||||
{scoreImprovement && <Change change={scoreImprovement.score} />}
|
{scoreImprovement && <Change className="text-xs" change={scoreImprovement.score} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,9 @@ export default function Tooltip({ children, display, asChild = true, side = "top
|
|||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side={side}>{display}</TooltipContent>
|
<TooltipContent className="max-w-[350px]" side={side}>
|
||||||
|
{display}
|
||||||
|
</TooltipContent>
|
||||||
</ShadCnTooltip>
|
</ShadCnTooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
23
projects/website/src/components/warning.tsx
Normal file
23
projects/website/src/components/warning.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ExclamationTriangleIcon } from "@heroicons/react/24/solid";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
type WarningProps = {
|
||||||
|
/**
|
||||||
|
* The size of the warning icon.
|
||||||
|
*/
|
||||||
|
size?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The children to display.
|
||||||
|
*/
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Warning({ size = 32, children }: WarningProps) {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-2 items-center justify-center">
|
||||||
|
<ExclamationTriangleIcon width={size} height={size} className={`w-[${size}px] h-[${size}px]`} />
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
Reference in New Issue
Block a user