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