Add pb display
This commit is contained in:
parent
86830adc47
commit
3410e3324e
42
index.css
42
index.css
@ -317,6 +317,48 @@ span:empty {
|
|||||||
max-width: 44rem;
|
max-width: 44rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.personal-best-row {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.45rem;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
opacity: 0.92;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#friendScores.has-personal-best .personal-best-row {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-best-label {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
opacity: 0.75;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-best-acc {
|
||||||
|
font-feature-settings: "tnum";
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-best-delta {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-feature-settings: "tnum";
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
min-width: 4.2ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-best-delta.personal-best-delta--ahead {
|
||||||
|
color: #3ddc97;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-best-delta.personal-best-delta--behind {
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
#friendScoresHeader {
|
#friendScoresHeader {
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|||||||
@ -39,6 +39,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="friendScores" aria-live="polite">
|
<div id="friendScores" aria-live="polite">
|
||||||
|
<div id="personalBestRow" class="personal-best-row">
|
||||||
|
<span class="personal-best-label">PB</span>
|
||||||
|
<span id="personalBestAcc" class="personal-best-acc"></span>
|
||||||
|
<span id="personalBestDelta" class="personal-best-delta"></span>
|
||||||
|
</div>
|
||||||
<div id="friendScoresHeader"><img id="friendScoresPlayerAvatar" src="images/unknown.svg" alt=""> <span id="friendScoresHeaderText">frenz</span> <img id="friendScoresHeaderImg" src="assets/peepohigh.webp" alt=""></div>
|
<div id="friendScoresHeader"><img id="friendScoresPlayerAvatar" src="images/unknown.svg" alt=""> <span id="friendScoresHeaderText">frenz</span> <img id="friendScoresHeaderImg" src="assets/peepohigh.webp" alt=""></div>
|
||||||
<ol id="friendScoresList"></ol>
|
<ol id="friendScoresList"></ol>
|
||||||
<div id="friendScoresEmpty">No map loaded</div>
|
<div id="friendScoresEmpty">No map loaded</div>
|
||||||
|
|||||||
61
index.js
61
index.js
@ -246,6 +246,9 @@ var beatSaberPlus = {
|
|||||||
switch (data._event) {
|
switch (data._event) {
|
||||||
case "gameState":
|
case "gameState":
|
||||||
document.body.dataset.gameState = data.gameStateChanged;
|
document.body.dataset.gameState = data.gameStateChanged;
|
||||||
|
if (data.gameStateChanged === "Menu") {
|
||||||
|
resetPersonalBestDeltaPlaceholder();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "mapInfo":
|
case "mapInfo":
|
||||||
void updateMapInfo(data.mapInfoChanged);
|
void updateMapInfo(data.mapInfoChanged);
|
||||||
@ -475,6 +478,9 @@ var friendScoresEmpty = must("friendScoresEmpty");
|
|||||||
var friendScoresHeaderText = must("friendScoresHeaderText");
|
var friendScoresHeaderText = must("friendScoresHeaderText");
|
||||||
var friendScoresPlayerAvatar = must("friendScoresPlayerAvatar");
|
var friendScoresPlayerAvatar = must("friendScoresPlayerAvatar");
|
||||||
var friendScoresHeaderImg = must("friendScoresHeaderImg");
|
var friendScoresHeaderImg = must("friendScoresHeaderImg");
|
||||||
|
var personalBestAcc = must("personalBestAcc");
|
||||||
|
var personalBestDelta = must("personalBestDelta");
|
||||||
|
var currentPbAccuracyPercent = null;
|
||||||
var cachedConfiguredPlayerAvatarKey = "";
|
var cachedConfiguredPlayerAvatarKey = "";
|
||||||
var cachedConfiguredPlayerAvatarSrc = "images/unknown.svg";
|
var cachedConfiguredPlayerAvatarSrc = "images/unknown.svg";
|
||||||
async function refreshConfiguredPlayerAvatar() {
|
async function refreshConfiguredPlayerAvatar() {
|
||||||
@ -497,12 +503,43 @@ async function refreshConfiguredPlayerAvatar() {
|
|||||||
cachedConfiguredPlayerAvatarSrc = profile?.avatar?.trim() || "images/unknown.svg";
|
cachedConfiguredPlayerAvatarSrc = profile?.avatar?.trim() || "images/unknown.svg";
|
||||||
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
||||||
}
|
}
|
||||||
|
function applyPersonalBestRow(pbPercent) {
|
||||||
|
currentPbAccuracyPercent = pbPercent;
|
||||||
|
friendScoresPanel.classList.toggle("has-personal-best", pbPercent !== null);
|
||||||
|
if (pbPercent === null) {
|
||||||
|
personalBestAcc.textContent = "";
|
||||||
|
personalBestDelta.textContent = "";
|
||||||
|
personalBestDelta.classList.remove("personal-best-delta--ahead", "personal-best-delta--behind");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
personalBestAcc.textContent = `${pbPercent.toFixed(2)}%`;
|
||||||
|
resetPersonalBestDeltaPlaceholder();
|
||||||
|
}
|
||||||
|
function resetPersonalBestDeltaPlaceholder() {
|
||||||
|
if (currentPbAccuracyPercent === null) return;
|
||||||
|
personalBestDelta.textContent = "\u2014";
|
||||||
|
personalBestDelta.classList.remove("personal-best-delta--ahead", "personal-best-delta--behind");
|
||||||
|
}
|
||||||
|
function updatePersonalBestDelta(liveAccuracyPercent) {
|
||||||
|
if (currentPbAccuracyPercent === null) return;
|
||||||
|
const delta = liveAccuracyPercent - currentPbAccuracyPercent;
|
||||||
|
personalBestDelta.textContent = `${delta >= 0 ? "+" : ""}${delta.toFixed(2)}%`;
|
||||||
|
personalBestDelta.classList.toggle("personal-best-delta--ahead", delta > 5e-4);
|
||||||
|
personalBestDelta.classList.toggle("personal-best-delta--behind", delta < -5e-4);
|
||||||
|
if (Math.abs(delta) <= 5e-4) {
|
||||||
|
personalBestDelta.classList.remove("personal-best-delta--ahead", "personal-best-delta--behind");
|
||||||
|
}
|
||||||
|
}
|
||||||
function updateScore(score) {
|
function updateScore(score) {
|
||||||
if (!settings.score) return;
|
if (settings.score) {
|
||||||
accuracy.textContent = (score.accuracy * 100).toFixed(1);
|
accuracy.textContent = (score.accuracy * 100).toFixed(1);
|
||||||
mistakes.textContent = score.missCount ? String(score.missCount) : "";
|
mistakes.textContent = score.missCount ? String(score.missCount) : "";
|
||||||
accuracy.classList.toggle("failed", score.currentHealth === 0);
|
accuracy.classList.toggle("failed", score.currentHealth === 0);
|
||||||
}
|
}
|
||||||
|
if (settings.friends) {
|
||||||
|
updatePersonalBestDelta(score.accuracy * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
function avatarFromScore(score) {
|
function avatarFromScore(score) {
|
||||||
if (typeof score.player === "object" && score.player?.avatar) {
|
if (typeof score.player === "object" && score.player?.avatar) {
|
||||||
return score.player.avatar;
|
return score.player.avatar;
|
||||||
@ -511,6 +548,7 @@ function avatarFromScore(score) {
|
|||||||
return url || null;
|
return url || null;
|
||||||
}
|
}
|
||||||
function clearFriendScores(message) {
|
function clearFriendScores(message) {
|
||||||
|
applyPersonalBestRow(null);
|
||||||
friendScoresList.replaceChildren();
|
friendScoresList.replaceChildren();
|
||||||
friendScoresEmpty.textContent = message;
|
friendScoresEmpty.textContent = message;
|
||||||
friendScoresHeaderText.textContent = "frenz?";
|
friendScoresHeaderText.textContent = "frenz?";
|
||||||
@ -548,6 +586,7 @@ function friendsRelationListKey(playerId) {
|
|||||||
}
|
}
|
||||||
function beginFriendScoresForNewMapContext() {
|
function beginFriendScoresForNewMapContext() {
|
||||||
friendScoreRequestId += 1;
|
friendScoreRequestId += 1;
|
||||||
|
applyPersonalBestRow(null);
|
||||||
if (!settings.friends) return;
|
if (!settings.friends) return;
|
||||||
if (!currentMapHash) {
|
if (!currentMapHash) {
|
||||||
clearFriendScores("No map loaded");
|
clearFriendScores("No map loaded");
|
||||||
@ -578,6 +617,7 @@ async function refreshMapFriendScores() {
|
|||||||
clearFriendScores("Waiting for BeatLeader player id");
|
clearFriendScores("Waiting for BeatLeader player id");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
applyPersonalBestRow(null);
|
||||||
friendScoresList.replaceChildren();
|
friendScoresList.replaceChildren();
|
||||||
friendScoresPanel.classList.remove("has-items");
|
friendScoresPanel.classList.remove("has-items");
|
||||||
friendScoresPanel.classList.add("is-loading");
|
friendScoresPanel.classList.add("is-loading");
|
||||||
@ -594,10 +634,12 @@ async function refreshMapFriendScores() {
|
|||||||
friendsRelationCache = fetched;
|
friendsRelationCache = fetched;
|
||||||
return fetched;
|
return fetched;
|
||||||
})();
|
})();
|
||||||
const [leaderboards, friends] = await Promise.all([
|
const [leaderboards, friends, selfProfile] = await Promise.all([
|
||||||
fetchBLLeaderboardsByHash(hash),
|
fetchBLLeaderboardsByHash(hash),
|
||||||
friendsPromise
|
friendsPromise,
|
||||||
|
fetchBeatLeaderPlayer(playerId)
|
||||||
]);
|
]);
|
||||||
|
const myBeatLeaderId = selfProfile?.id ?? playerId;
|
||||||
if (requestId !== friendScoreRequestId) return;
|
if (requestId !== friendScoreRequestId) return;
|
||||||
if (leaderboards.length === 0) {
|
if (leaderboards.length === 0) {
|
||||||
clearFriendScores("No BeatLeader leaderboards found");
|
clearFriendScores("No BeatLeader leaderboards found");
|
||||||
@ -620,6 +662,15 @@ async function refreshMapFriendScores() {
|
|||||||
}
|
}
|
||||||
const scores = await fetchAllMapScoresByHash(hash, forPlayMode);
|
const scores = await fetchAllMapScoresByHash(hash, forPlayMode);
|
||||||
if (requestId !== friendScoreRequestId) return;
|
if (requestId !== friendScoreRequestId) return;
|
||||||
|
let playerPbAcc = null;
|
||||||
|
for (const score of scores) {
|
||||||
|
const scorePlayerId = score.playerId ?? (typeof score.player === "object" ? score.player?.id : null);
|
||||||
|
const playerKey = scorePlayerId == null ? "" : String(scorePlayerId);
|
||||||
|
if (!playerKey || playerKey !== String(myBeatLeaderId)) continue;
|
||||||
|
const acc = normalizeAccuracy(score.accuracy ?? score.acc);
|
||||||
|
if (acc === null) continue;
|
||||||
|
if (playerPbAcc === null || acc > playerPbAcc) playerPbAcc = acc;
|
||||||
|
}
|
||||||
const bestByPlayer = /* @__PURE__ */ new Map();
|
const bestByPlayer = /* @__PURE__ */ new Map();
|
||||||
for (const score of scores) {
|
for (const score of scores) {
|
||||||
const scorePlayerId = score.playerId ?? (typeof score.player === "object" ? score.player?.id : null);
|
const scorePlayerId = score.playerId ?? (typeof score.player === "object" ? score.player?.id : null);
|
||||||
@ -640,6 +691,10 @@ async function refreshMapFriendScores() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (playerPbAcc !== null) {
|
||||||
|
bestByPlayer.delete(String(myBeatLeaderId));
|
||||||
|
}
|
||||||
|
applyPersonalBestRow(playerPbAcc);
|
||||||
const sorted = Array.from(bestByPlayer.values()).sort((a, b) => b.acc - a.acc);
|
const sorted = Array.from(bestByPlayer.values()).sort((a, b) => b.acc - a.acc);
|
||||||
renderFriendScores(sorted);
|
renderFriendScores(sorted);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@ -72,6 +72,9 @@ const beatSaberPlus = {
|
|||||||
switch (data._event) {
|
switch (data._event) {
|
||||||
case "gameState":
|
case "gameState":
|
||||||
document.body.dataset.gameState = data.gameStateChanged;
|
document.body.dataset.gameState = data.gameStateChanged;
|
||||||
|
if (data.gameStateChanged === "Menu") {
|
||||||
|
resetPersonalBestDeltaPlaceholder();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "mapInfo":
|
case "mapInfo":
|
||||||
void updateMapInfo(data.mapInfoChanged);
|
void updateMapInfo(data.mapInfoChanged);
|
||||||
@ -342,6 +345,11 @@ const friendScoresEmpty = must<HTMLElement>("friendScoresEmpty");
|
|||||||
const friendScoresHeaderText = must<HTMLElement>("friendScoresHeaderText");
|
const friendScoresHeaderText = must<HTMLElement>("friendScoresHeaderText");
|
||||||
const friendScoresPlayerAvatar = must<HTMLImageElement>("friendScoresPlayerAvatar");
|
const friendScoresPlayerAvatar = must<HTMLImageElement>("friendScoresPlayerAvatar");
|
||||||
const friendScoresHeaderImg = must<HTMLImageElement>("friendScoresHeaderImg");
|
const friendScoresHeaderImg = must<HTMLImageElement>("friendScoresHeaderImg");
|
||||||
|
const personalBestAcc = must<HTMLElement>("personalBestAcc");
|
||||||
|
const personalBestDelta = must<HTMLElement>("personalBestDelta");
|
||||||
|
|
||||||
|
/** BeatLeader PB (percent) for the current map + difficulty; null if none or friends panel inactive. */
|
||||||
|
let currentPbAccuracyPercent: number | null = null;
|
||||||
|
|
||||||
let cachedConfiguredPlayerAvatarKey = "";
|
let cachedConfiguredPlayerAvatarKey = "";
|
||||||
let cachedConfiguredPlayerAvatarSrc = "images/unknown.svg";
|
let cachedConfiguredPlayerAvatarSrc = "images/unknown.svg";
|
||||||
@ -367,12 +375,46 @@ async function refreshConfiguredPlayerAvatar() {
|
|||||||
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyPersonalBestRow(pbPercent: number | null) {
|
||||||
|
currentPbAccuracyPercent = pbPercent;
|
||||||
|
friendScoresPanel.classList.toggle("has-personal-best", pbPercent !== null);
|
||||||
|
if (pbPercent === null) {
|
||||||
|
personalBestAcc.textContent = "";
|
||||||
|
personalBestDelta.textContent = "";
|
||||||
|
personalBestDelta.classList.remove("personal-best-delta--ahead", "personal-best-delta--behind");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
personalBestAcc.textContent = `${pbPercent.toFixed(2)}%`;
|
||||||
|
resetPersonalBestDeltaPlaceholder();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetPersonalBestDeltaPlaceholder() {
|
||||||
|
if (currentPbAccuracyPercent === null) return;
|
||||||
|
personalBestDelta.textContent = "—";
|
||||||
|
personalBestDelta.classList.remove("personal-best-delta--ahead", "personal-best-delta--behind");
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePersonalBestDelta(liveAccuracyPercent: number) {
|
||||||
|
if (currentPbAccuracyPercent === null) return;
|
||||||
|
const delta = liveAccuracyPercent - currentPbAccuracyPercent;
|
||||||
|
personalBestDelta.textContent = `${delta >= 0 ? "+" : ""}${delta.toFixed(2)}%`;
|
||||||
|
personalBestDelta.classList.toggle("personal-best-delta--ahead", delta > 0.0005);
|
||||||
|
personalBestDelta.classList.toggle("personal-best-delta--behind", delta < -0.0005);
|
||||||
|
if (Math.abs(delta) <= 0.0005) {
|
||||||
|
personalBestDelta.classList.remove("personal-best-delta--ahead", "personal-best-delta--behind");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateScore(score: Score) {
|
function updateScore(score: Score) {
|
||||||
if (!settings.score) return;
|
if (settings.score) {
|
||||||
accuracy.textContent = (score.accuracy * 100).toFixed(1);
|
accuracy.textContent = (score.accuracy * 100).toFixed(1);
|
||||||
mistakes.textContent = score.missCount ? String(score.missCount) : "";
|
mistakes.textContent = score.missCount ? String(score.missCount) : "";
|
||||||
accuracy.classList.toggle("failed", score.currentHealth === 0);
|
accuracy.classList.toggle("failed", score.currentHealth === 0);
|
||||||
}
|
}
|
||||||
|
if (settings.friends) {
|
||||||
|
updatePersonalBestDelta(score.accuracy * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function avatarFromScore(score: BeatLeaderScore): string | null {
|
function avatarFromScore(score: BeatLeaderScore): string | null {
|
||||||
if (typeof score.player === "object" && score.player?.avatar) {
|
if (typeof score.player === "object" && score.player?.avatar) {
|
||||||
@ -383,6 +425,7 @@ function avatarFromScore(score: BeatLeaderScore): string | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clearFriendScores(message: string) {
|
function clearFriendScores(message: string) {
|
||||||
|
applyPersonalBestRow(null);
|
||||||
friendScoresList.replaceChildren();
|
friendScoresList.replaceChildren();
|
||||||
friendScoresEmpty.textContent = message;
|
friendScoresEmpty.textContent = message;
|
||||||
friendScoresHeaderText.textContent = "frenz?";
|
friendScoresHeaderText.textContent = "frenz?";
|
||||||
@ -427,6 +470,7 @@ function friendsRelationListKey(playerId: string): string {
|
|||||||
*/
|
*/
|
||||||
function beginFriendScoresForNewMapContext() {
|
function beginFriendScoresForNewMapContext() {
|
||||||
friendScoreRequestId += 1;
|
friendScoreRequestId += 1;
|
||||||
|
applyPersonalBestRow(null);
|
||||||
if (!settings.friends) return;
|
if (!settings.friends) return;
|
||||||
if (!currentMapHash) {
|
if (!currentMapHash) {
|
||||||
clearFriendScores("No map loaded");
|
clearFriendScores("No map loaded");
|
||||||
@ -458,6 +502,7 @@ async function refreshMapFriendScores() {
|
|||||||
clearFriendScores("Waiting for BeatLeader player id");
|
clearFriendScores("Waiting for BeatLeader player id");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
applyPersonalBestRow(null);
|
||||||
friendScoresList.replaceChildren();
|
friendScoresList.replaceChildren();
|
||||||
friendScoresPanel.classList.remove("has-items");
|
friendScoresPanel.classList.remove("has-items");
|
||||||
friendScoresPanel.classList.add("is-loading");
|
friendScoresPanel.classList.add("is-loading");
|
||||||
@ -474,10 +519,12 @@ async function refreshMapFriendScores() {
|
|||||||
friendsRelationCache = fetched;
|
friendsRelationCache = fetched;
|
||||||
return fetched;
|
return fetched;
|
||||||
})();
|
})();
|
||||||
const [leaderboards, friends] = await Promise.all([
|
const [leaderboards, friends, selfProfile] = await Promise.all([
|
||||||
fetchBLLeaderboardsByHash(hash),
|
fetchBLLeaderboardsByHash(hash),
|
||||||
friendsPromise,
|
friendsPromise,
|
||||||
|
fetchBeatLeaderPlayer(playerId),
|
||||||
]);
|
]);
|
||||||
|
const myBeatLeaderId = selfProfile?.id ?? playerId;
|
||||||
if (requestId !== friendScoreRequestId) return;
|
if (requestId !== friendScoreRequestId) return;
|
||||||
if (leaderboards.length === 0) {
|
if (leaderboards.length === 0) {
|
||||||
clearFriendScores("No BeatLeader leaderboards found");
|
clearFriendScores("No BeatLeader leaderboards found");
|
||||||
@ -501,6 +548,15 @@ async function refreshMapFriendScores() {
|
|||||||
}
|
}
|
||||||
const scores = await fetchAllMapScoresByHash(hash, forPlayMode);
|
const scores = await fetchAllMapScoresByHash(hash, forPlayMode);
|
||||||
if (requestId !== friendScoreRequestId) return;
|
if (requestId !== friendScoreRequestId) return;
|
||||||
|
let playerPbAcc: number | null = null;
|
||||||
|
for (const score of scores) {
|
||||||
|
const scorePlayerId = score.playerId ?? (typeof score.player === "object" ? score.player?.id : null);
|
||||||
|
const playerKey = scorePlayerId == null ? "" : String(scorePlayerId);
|
||||||
|
if (!playerKey || playerKey !== String(myBeatLeaderId)) continue;
|
||||||
|
const acc = normalizeAccuracy(score.accuracy ?? score.acc);
|
||||||
|
if (acc === null) continue;
|
||||||
|
if (playerPbAcc === null || acc > playerPbAcc) playerPbAcc = acc;
|
||||||
|
}
|
||||||
const bestByPlayer = new Map<string, { name: string; acc: number; avatar: string | null }>();
|
const bestByPlayer = new Map<string, { name: string; acc: number; avatar: string | null }>();
|
||||||
for (const score of scores) {
|
for (const score of scores) {
|
||||||
const scorePlayerId = score.playerId ?? (typeof score.player === "object" ? score.player?.id : null);
|
const scorePlayerId = score.playerId ?? (typeof score.player === "object" ? score.player?.id : null);
|
||||||
@ -523,6 +579,10 @@ async function refreshMapFriendScores() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (playerPbAcc !== null) {
|
||||||
|
bestByPlayer.delete(String(myBeatLeaderId));
|
||||||
|
}
|
||||||
|
applyPersonalBestRow(playerPbAcc);
|
||||||
const sorted = Array.from(bestByPlayer.values()).sort((a, b) => b.acc - a.acc);
|
const sorted = Array.from(bestByPlayer.values()).sort((a, b) => b.acc - a.acc);
|
||||||
renderFriendScores(sorted);
|
renderFriendScores(sorted);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user