Show configured player's avatar too
This commit is contained in:
parent
976c331834
commit
d971936445
@ -312,6 +312,14 @@ span:empty {
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
#friendScoresPlayerAvatar {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#friendScoresHeaderImg {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
@ -413,6 +421,7 @@ body.bottom #time {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
#settings .beatLeaderPlayerRow,
|
||||
#settings .debugSongIdRow {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="friendScores" aria-live="polite">
|
||||
<div id="friendScoresHeader"><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>
|
||||
<div id="friendScoresEmpty">No map loaded</div>
|
||||
</div>
|
||||
@ -71,7 +71,10 @@
|
||||
<option value="following">Following (I follow them)</option>
|
||||
<option value="followers">Followers (they follow me)</option>
|
||||
</select></label>
|
||||
<label id="beatLeaderPlayerSetting">BeatLeader player id: <input id="beatLeaderPlayerInput" type="text" placeholder="7656119… or alias"></label>
|
||||
<label id="beatLeaderPlayerSetting">Player id: <span class="beatLeaderPlayerRow">
|
||||
<span class="debugSongIdHint">e.g. <button type="button" id="beatLeaderPlayerExample" title="Fill with pleb's numeric id">pleb</button></span>
|
||||
<input id="beatLeaderPlayerInput" type="text" placeholder="7656119… or alias" spellcheck="false" autocomplete="off">
|
||||
</span></label>
|
||||
<label>Show BSR / map id: <input id="bsrInput" type="checkbox"></label>
|
||||
<label>Position: <select id="positionInput">
|
||||
<option value="[false,false]">Top left</option>
|
||||
|
||||
51
index.js
51
index.js
@ -65,18 +65,26 @@ async function fetchBLLeaderboardsByHash(hash) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
async function resolveBeatLeaderPlayerId(playerId) {
|
||||
async function fetchBeatLeaderPlayer(playerId) {
|
||||
const path = `/player/${encodeURIComponent(playerId)}`;
|
||||
try {
|
||||
const res = await fetch(beatleaderUrl(path));
|
||||
if (!res.ok) return playerId;
|
||||
if (!res.ok) return null;
|
||||
const data = await res.json();
|
||||
const canonicalId = data.id;
|
||||
return canonicalId == null ? playerId : String(canonicalId);
|
||||
const id = data.id == null ? playerId : String(data.id);
|
||||
const avatar = typeof data.avatar === "string" ? data.avatar.trim() || null : null;
|
||||
return {
|
||||
id,
|
||||
avatar
|
||||
};
|
||||
} catch {
|
||||
return playerId;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async function resolveBeatLeaderPlayerId(playerId) {
|
||||
const p = await fetchBeatLeaderPlayer(playerId);
|
||||
return p?.id ?? playerId;
|
||||
}
|
||||
async function fetchLeaderboardScoresById(leaderboardId, maxPages = MAX_LEADERBOARD_SCORE_PAGES) {
|
||||
const scores = [];
|
||||
const pageSize = PAGE_SIZE;
|
||||
@ -238,6 +246,7 @@ var beatSaberPlus = {
|
||||
break;
|
||||
case "handshake":
|
||||
currentPlayerPlatformId = data.playerPlatformId || "";
|
||||
void refreshConfiguredPlayerAvatar();
|
||||
void refreshMapFriendScores();
|
||||
break;
|
||||
default:
|
||||
@ -439,7 +448,30 @@ var friendScoresPanel = must("friendScores");
|
||||
var friendScoresList = must("friendScoresList");
|
||||
var friendScoresEmpty = must("friendScoresEmpty");
|
||||
var friendScoresHeaderText = must("friendScoresHeaderText");
|
||||
var friendScoresPlayerAvatar = must("friendScoresPlayerAvatar");
|
||||
var friendScoresHeaderImg = must("friendScoresHeaderImg");
|
||||
var cachedConfiguredPlayerAvatarKey = "";
|
||||
var cachedConfiguredPlayerAvatarSrc = "images/unknown.svg";
|
||||
async function refreshConfiguredPlayerAvatar() {
|
||||
const key = `${settings.beatLeaderId.trim()}|${currentPlayerPlatformId}`;
|
||||
const pid = getEffectivePlayerId();
|
||||
if (!pid) {
|
||||
cachedConfiguredPlayerAvatarKey = "";
|
||||
cachedConfiguredPlayerAvatarSrc = "images/unknown.svg";
|
||||
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
||||
return;
|
||||
}
|
||||
if (key === cachedConfiguredPlayerAvatarKey) {
|
||||
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
||||
return;
|
||||
}
|
||||
const profile = await fetchBeatLeaderPlayer(pid);
|
||||
const keyAfter = `${settings.beatLeaderId.trim()}|${currentPlayerPlatformId}`;
|
||||
if (keyAfter !== key) return;
|
||||
cachedConfiguredPlayerAvatarKey = key;
|
||||
cachedConfiguredPlayerAvatarSrc = profile?.avatar?.trim() || "images/unknown.svg";
|
||||
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
||||
}
|
||||
function updateScore(score) {
|
||||
if (!settings.score) return;
|
||||
accuracy.textContent = (score.accuracy * 100).toFixed(1);
|
||||
@ -568,6 +600,7 @@ async function refreshMapFriendScores() {
|
||||
}
|
||||
window.onhashchange = () => {
|
||||
loadSettings();
|
||||
void refreshConfiguredPlayerAvatar();
|
||||
const debugEl = document.getElementById("debugSongIdInput");
|
||||
if (debugEl) debugEl.value = settings.debugSongId;
|
||||
if (settings.debugSongId.trim()) void applyDebugSong();
|
||||
@ -687,6 +720,7 @@ async function bootstrap() {
|
||||
}
|
||||
loadSettings();
|
||||
document.head.appendChild(style);
|
||||
void refreshConfiguredPlayerAvatar();
|
||||
if (settings.debugSongId.trim()) void applyDebugSong();
|
||||
else void refreshMapFriendScores();
|
||||
for (const key of [
|
||||
@ -717,8 +751,15 @@ async function bootstrap() {
|
||||
beatLeaderPlayerInput.oninput = () => {
|
||||
settings.beatLeaderId = beatLeaderPlayerInput.value.trim();
|
||||
saveSettings();
|
||||
void refreshConfiguredPlayerAvatar();
|
||||
void refreshMapFriendScores();
|
||||
};
|
||||
must("beatLeaderPlayerExample").onclick = () => {
|
||||
beatLeaderPlayerInput.value = "76561199407393962";
|
||||
beatLeaderPlayerInput.dispatchEvent(new Event("input", {
|
||||
bubbles: true
|
||||
}));
|
||||
};
|
||||
const scale = must("scaleInput");
|
||||
scale.valueAsNumber = settings.scale * 100;
|
||||
scale.oninput = () => {
|
||||
|
||||
@ -2,6 +2,7 @@ import type {
|
||||
BeatLeaderFollower,
|
||||
BeatLeaderLeaderboard,
|
||||
BeatLeaderLeaderboardsByHashResponse,
|
||||
BeatLeaderPlayer,
|
||||
BeatLeaderScore,
|
||||
FriendMode,
|
||||
} from "./types.ts";
|
||||
@ -27,10 +28,6 @@ function beatleaderUrl(path: string): string {
|
||||
return `${BASE_URL}${path}`;
|
||||
}
|
||||
|
||||
interface BeatLeaderPlayerLookup {
|
||||
id?: string | number | null;
|
||||
}
|
||||
|
||||
export async function fetchBLLeaderboardsByHash(hash: string): Promise<BeatLeaderLeaderboard[]> {
|
||||
const path = `/leaderboards/hash/${encodeURIComponent(hash)}`;
|
||||
try {
|
||||
@ -47,19 +44,25 @@ export async function fetchBLLeaderboardsByHash(hash: string): Promise<BeatLeade
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveBeatLeaderPlayerId(playerId: string): Promise<string> {
|
||||
export async function fetchBeatLeaderPlayer(playerId: string): Promise<{ id: string; avatar: string | null } | null> {
|
||||
const path = `/player/${encodeURIComponent(playerId)}`;
|
||||
try {
|
||||
const res = await fetch(beatleaderUrl(path));
|
||||
if (!res.ok) return playerId;
|
||||
const data = await res.json() as BeatLeaderPlayerLookup;
|
||||
const canonicalId = data.id;
|
||||
return canonicalId == null ? playerId : String(canonicalId);
|
||||
if (!res.ok) return null;
|
||||
const data = await res.json() as BeatLeaderPlayer;
|
||||
const id = data.id == null ? playerId : String(data.id);
|
||||
const avatar = typeof data.avatar === "string" ? data.avatar.trim() || null : null;
|
||||
return { id, avatar };
|
||||
} catch {
|
||||
return playerId;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveBeatLeaderPlayerId(playerId: string): Promise<string> {
|
||||
const p = await fetchBeatLeaderPlayer(playerId);
|
||||
return p?.id ?? playerId;
|
||||
}
|
||||
|
||||
async function fetchLeaderboardScoresById(
|
||||
leaderboardId: string,
|
||||
maxPages = MAX_LEADERBOARD_SCORE_PAGES,
|
||||
|
||||
@ -15,6 +15,7 @@ import type { BeatSaverMap } from "./beatsaver.ts";
|
||||
import { fetchBeatSaverMapById, fetchBeatSaverMeta } from "./beatsaver.ts";
|
||||
import {
|
||||
fetchAllMapScoresByHash,
|
||||
fetchBeatLeaderPlayer,
|
||||
fetchBLLeaderboardsByHash,
|
||||
fetchFriends,
|
||||
normalizeAccuracy,
|
||||
@ -87,6 +88,7 @@ const beatSaberPlus = {
|
||||
break;
|
||||
case "handshake":
|
||||
currentPlayerPlatformId = data.playerPlatformId || "";
|
||||
void refreshConfiguredPlayerAvatar();
|
||||
void refreshMapFriendScores();
|
||||
break;
|
||||
default:
|
||||
@ -326,8 +328,33 @@ const friendScoresPanel = must<HTMLElement>("friendScores");
|
||||
const friendScoresList = must<HTMLOListElement>("friendScoresList");
|
||||
const friendScoresEmpty = must<HTMLElement>("friendScoresEmpty");
|
||||
const friendScoresHeaderText = must<HTMLElement>("friendScoresHeaderText");
|
||||
const friendScoresPlayerAvatar = must<HTMLImageElement>("friendScoresPlayerAvatar");
|
||||
const friendScoresHeaderImg = must<HTMLImageElement>("friendScoresHeaderImg");
|
||||
|
||||
let cachedConfiguredPlayerAvatarKey = "";
|
||||
let cachedConfiguredPlayerAvatarSrc = "images/unknown.svg";
|
||||
|
||||
async function refreshConfiguredPlayerAvatar() {
|
||||
const key = `${settings.beatLeaderId.trim()}|${currentPlayerPlatformId}`;
|
||||
const pid = getEffectivePlayerId();
|
||||
if (!pid) {
|
||||
cachedConfiguredPlayerAvatarKey = "";
|
||||
cachedConfiguredPlayerAvatarSrc = "images/unknown.svg";
|
||||
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
||||
return;
|
||||
}
|
||||
if (key === cachedConfiguredPlayerAvatarKey) {
|
||||
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
||||
return;
|
||||
}
|
||||
const profile = await fetchBeatLeaderPlayer(pid);
|
||||
const keyAfter = `${settings.beatLeaderId.trim()}|${currentPlayerPlatformId}`;
|
||||
if (keyAfter !== key) return;
|
||||
cachedConfiguredPlayerAvatarKey = key;
|
||||
cachedConfiguredPlayerAvatarSrc = profile?.avatar?.trim() || "images/unknown.svg";
|
||||
friendScoresPlayerAvatar.src = cachedConfiguredPlayerAvatarSrc;
|
||||
}
|
||||
|
||||
function updateScore(score: Score) {
|
||||
if (!settings.score) return;
|
||||
accuracy.textContent = (score.accuracy * 100).toFixed(1);
|
||||
@ -465,6 +492,7 @@ async function refreshMapFriendScores() {
|
||||
|
||||
window.onhashchange = () => {
|
||||
loadSettings();
|
||||
void refreshConfiguredPlayerAvatar();
|
||||
const debugEl = document.getElementById("debugSongIdInput") as HTMLInputElement | null;
|
||||
if (debugEl) debugEl.value = settings.debugSongId;
|
||||
if (settings.debugSongId.trim()) void applyDebugSong();
|
||||
@ -590,6 +618,7 @@ async function bootstrap() {
|
||||
}
|
||||
loadSettings();
|
||||
document.head.appendChild(style);
|
||||
void refreshConfiguredPlayerAvatar();
|
||||
if (settings.debugSongId.trim()) void applyDebugSong();
|
||||
else void refreshMapFriendScores();
|
||||
|
||||
@ -618,8 +647,13 @@ async function bootstrap() {
|
||||
beatLeaderPlayerInput.oninput = () => {
|
||||
settings.beatLeaderId = beatLeaderPlayerInput.value.trim();
|
||||
saveSettings();
|
||||
void refreshConfiguredPlayerAvatar();
|
||||
void refreshMapFriendScores();
|
||||
};
|
||||
must<HTMLButtonElement>("beatLeaderPlayerExample").onclick = () => {
|
||||
beatLeaderPlayerInput.value = "76561199407393962";
|
||||
beatLeaderPlayerInput.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
};
|
||||
|
||||
const scale = must<HTMLInputElement>("scaleInput");
|
||||
scale.valueAsNumber = settings.scale * 100;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user