fix issue with badge loading, profile indef loading if no user, remove unused files, organize the js
All checks were successful
Code quality checks / biome (push) Successful in 15s
All checks were successful
Code quality checks / biome (push) Successful in 15s
This commit is contained in:
parent
634d919239
commit
1020e3ee26
5 changed files with 158 additions and 236 deletions
|
@ -1,5 +1,12 @@
|
|||
const head = document.querySelector("head");
|
||||
const userId = head?.dataset.userId;
|
||||
const activityProgressMap = new Map();
|
||||
|
||||
let instanceUri = head?.dataset.instanceUri;
|
||||
let badgeURL = head?.dataset.badgeUrl;
|
||||
let socket;
|
||||
let badgesLoaded = false;
|
||||
|
||||
function formatTime(ms) {
|
||||
const totalSecs = Math.floor(ms / 1000);
|
||||
const hours = Math.floor(totalSecs / 3600);
|
||||
|
@ -78,57 +85,14 @@ function updateElapsedAndProgress() {
|
|||
}
|
||||
}
|
||||
|
||||
updateElapsedAndProgress();
|
||||
setInterval(updateElapsedAndProgress, 1000);
|
||||
function loadEffectScript(effect) {
|
||||
const existing = document.querySelector(`script[data-effect="${effect}"]`);
|
||||
if (existing) return;
|
||||
|
||||
const head = document.querySelector("head");
|
||||
const userId = head?.dataset.userId;
|
||||
let instanceUri = head?.dataset.instanceUri;
|
||||
let badgeURL = head?.dataset.badgeUrl;
|
||||
|
||||
if (userId && instanceUri) {
|
||||
if (!instanceUri.startsWith("http")) {
|
||||
instanceUri = `https://${instanceUri}`;
|
||||
}
|
||||
|
||||
const wsUri = instanceUri
|
||||
.replace(/^http:/, "ws:")
|
||||
.replace(/^https:/, "wss:")
|
||||
.replace(/\/$/, "");
|
||||
|
||||
const socket = new WebSocket(`${wsUri}/socket`);
|
||||
|
||||
let heartbeatInterval = null;
|
||||
|
||||
socket.addEventListener("open", () => {});
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
const payload = JSON.parse(event.data);
|
||||
|
||||
if (payload.op === 1 && payload.d?.heartbeat_interval) {
|
||||
heartbeatInterval = setInterval(() => {
|
||||
socket.send(JSON.stringify({ op: 3 }));
|
||||
}, payload.d.heartbeat_interval);
|
||||
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
op: 2,
|
||||
d: {
|
||||
subscribe_to_id: userId,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (payload.t === "INIT_STATE" || payload.t === "PRESENCE_UPDATE") {
|
||||
updatePresence(payload.d);
|
||||
requestAnimationFrame(() => updateElapsedAndProgress());
|
||||
}
|
||||
});
|
||||
|
||||
socket.addEventListener("close", () => {
|
||||
if (heartbeatInterval) clearInterval(heartbeatInterval);
|
||||
});
|
||||
const script = document.createElement("script");
|
||||
script.src = `/public/js/${effect}.js`;
|
||||
script.dataset.effect = effect;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
function resolveActivityImage(img, applicationId) {
|
||||
|
@ -280,95 +244,76 @@ function buildActivityHTML(activity) {
|
|||
`;
|
||||
}
|
||||
|
||||
if (badgeURL && badgeURL !== "null" && userId) {
|
||||
if (!badgeURL.startsWith("http")) {
|
||||
badgeURL = `https://${badgeURL}`;
|
||||
}
|
||||
async function loadBadges(userId, options = {}) {
|
||||
const {
|
||||
services = [],
|
||||
seperated = false,
|
||||
cache = true,
|
||||
targetId = "badges",
|
||||
serviceOrder = [],
|
||||
} = options;
|
||||
|
||||
if (!badgeURL.endsWith("/")) {
|
||||
badgeURL += "/";
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
if (services.length) params.set("services", services.join(","));
|
||||
if (seperated) params.set("seperated", "true");
|
||||
if (!cache) params.set("cache", "false");
|
||||
|
||||
async function loadBadges(userId, options = {}) {
|
||||
const {
|
||||
services = [],
|
||||
seperated = false,
|
||||
cache = true,
|
||||
targetId = "badges",
|
||||
serviceOrder = [],
|
||||
} = options;
|
||||
const url = `${badgeURL}${userId}?${params.toString()}`;
|
||||
const target = document.getElementById(targetId);
|
||||
if (!target) return;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
if (services.length) params.set("services", services.join(","));
|
||||
if (seperated) params.set("seperated", "true");
|
||||
if (!cache) params.set("cache", "false");
|
||||
target.classList.add("hidden");
|
||||
|
||||
const url = `${badgeURL}${userId}?${params.toString()}`;
|
||||
const target = document.getElementById(targetId);
|
||||
if (!target) return;
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
target.classList.add("hidden");
|
||||
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
if (!res.ok || !json.badges) {
|
||||
target.textContent = "Failed to load badges.";
|
||||
return;
|
||||
}
|
||||
|
||||
target.innerHTML = "";
|
||||
|
||||
const badgesByService = json.badges;
|
||||
const renderedServices = new Set();
|
||||
|
||||
const renderBadges = (badges) => {
|
||||
for (const badge of badges) {
|
||||
const img = document.createElement("img");
|
||||
img.src = badge.badge;
|
||||
img.alt = badge.tooltip;
|
||||
img.title = badge.tooltip;
|
||||
img.className = "badge";
|
||||
target.appendChild(img);
|
||||
}
|
||||
};
|
||||
|
||||
for (const serviceName of serviceOrder) {
|
||||
const badges = badgesByService[serviceName];
|
||||
if (Array.isArray(badges) && badges.length) {
|
||||
renderBadges(badges);
|
||||
renderedServices.add(serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [serviceName, badges] of Object.entries(badgesByService)) {
|
||||
if (renderedServices.has(serviceName)) continue;
|
||||
if (Array.isArray(badges) && badges.length) {
|
||||
renderBadges(badges);
|
||||
}
|
||||
}
|
||||
|
||||
target.classList.remove("hidden");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
target.innerHTML = "";
|
||||
target.classList.add("hidden");
|
||||
if (!res.ok || !json.badges) {
|
||||
target.textContent = "Failed to load badges.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
loadBadges(userId, {
|
||||
services: [],
|
||||
seperated: true,
|
||||
cache: true,
|
||||
targetId: "badges",
|
||||
serviceOrder: ["discord", "equicord", "reviewdb", "vencord"],
|
||||
});
|
||||
target.innerHTML = "";
|
||||
|
||||
const badgesByService = json.badges;
|
||||
const renderedServices = new Set();
|
||||
|
||||
const renderBadges = (badges) => {
|
||||
for (const badge of badges) {
|
||||
const img = document.createElement("img");
|
||||
img.src = badge.badge;
|
||||
img.alt = badge.tooltip;
|
||||
img.title = badge.tooltip;
|
||||
img.className = "badge";
|
||||
target.appendChild(img);
|
||||
}
|
||||
};
|
||||
|
||||
for (const serviceName of serviceOrder) {
|
||||
const badges = badgesByService[serviceName];
|
||||
if (Array.isArray(badges) && badges.length) {
|
||||
renderBadges(badges);
|
||||
renderedServices.add(serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [serviceName, badges] of Object.entries(badgesByService)) {
|
||||
if (renderedServices.has(serviceName)) continue;
|
||||
if (Array.isArray(badges) && badges.length) {
|
||||
renderBadges(badges);
|
||||
}
|
||||
}
|
||||
|
||||
target.classList.remove("hidden");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
target.innerHTML = "";
|
||||
target.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
async function updatePresence(data) {
|
||||
const cssLink = data.kv?.css;
|
||||
|
||||
if (cssLink) {
|
||||
try {
|
||||
const res = await fetch(`/api/css?url=${encodeURIComponent(cssLink)}`);
|
||||
|
@ -384,10 +329,37 @@ async function updatePresence(data) {
|
|||
}
|
||||
|
||||
const avatarWrapper = document.querySelector(".avatar-wrapper");
|
||||
|
||||
const avatarImg = document.querySelector(".avatar-wrapper .avatar");
|
||||
const avatarImg = avatarWrapper?.querySelector(".avatar");
|
||||
const usernameEl = document.querySelector(".username");
|
||||
|
||||
if (!data.discord_user) {
|
||||
const loadingOverlay = document.getElementById("loading-overlay");
|
||||
if (loadingOverlay) {
|
||||
loadingOverlay.innerHTML = `
|
||||
<div class="error-message">
|
||||
<p>Failed to load user data.</p>
|
||||
</div>
|
||||
`;
|
||||
loadingOverlay.style.opacity = "1";
|
||||
avatarWrapper.classList.add("hidden");
|
||||
avatarImg.classList.add("hidden");
|
||||
usernameEl.classList.add("hidden");
|
||||
document.title = "Error";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!badgesLoaded) {
|
||||
loadBadges(userId, {
|
||||
services: [],
|
||||
seperated: true,
|
||||
cache: true,
|
||||
targetId: "badges",
|
||||
serviceOrder: ["discord", "equicord", "reviewdb", "vencord"],
|
||||
});
|
||||
badgesLoaded = true;
|
||||
}
|
||||
|
||||
if (avatarImg && data.discord_user?.avatar) {
|
||||
const newAvatarUrl = `https://cdn.discordapp.com/avatars/${data.discord_user.id}/${data.discord_user.avatar}`;
|
||||
avatarImg.src = newAvatarUrl;
|
||||
|
@ -582,12 +554,60 @@ async function getAllNoAsset() {
|
|||
}
|
||||
}
|
||||
|
||||
function loadEffectScript(effect) {
|
||||
const existing = document.querySelector(`script[data-effect="${effect}"]`);
|
||||
if (existing) return;
|
||||
if (instanceUri) {
|
||||
if (!instanceUri.startsWith("http")) {
|
||||
instanceUri = `https://${instanceUri}`;
|
||||
}
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.src = `/public/js/${effect}.js`;
|
||||
script.dataset.effect = effect;
|
||||
document.head.appendChild(script);
|
||||
const wsUri = instanceUri
|
||||
.replace(/^http:/, "ws:")
|
||||
.replace(/^https:/, "wss:")
|
||||
.replace(/\/$/, "");
|
||||
|
||||
socket = new WebSocket(`${wsUri}/socket`);
|
||||
}
|
||||
|
||||
if (badgeURL && badgeURL !== "null" && userId) {
|
||||
if (!badgeURL.startsWith("http")) {
|
||||
badgeURL = `https://${badgeURL}`;
|
||||
}
|
||||
|
||||
if (!badgeURL.endsWith("/")) {
|
||||
badgeURL += "/";
|
||||
}
|
||||
}
|
||||
|
||||
if (userId && instanceUri) {
|
||||
let heartbeatInterval = null;
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
const payload = JSON.parse(event.data);
|
||||
|
||||
if (payload.op === 1 && payload.d?.heartbeat_interval) {
|
||||
heartbeatInterval = setInterval(() => {
|
||||
socket.send(JSON.stringify({ op: 3 }));
|
||||
}, payload.d.heartbeat_interval);
|
||||
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
op: 2,
|
||||
d: {
|
||||
subscribe_to_id: userId,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (payload.t === "INIT_STATE" || payload.t === "PRESENCE_UPDATE") {
|
||||
updatePresence(payload.d);
|
||||
requestAnimationFrame(() => updateElapsedAndProgress());
|
||||
}
|
||||
});
|
||||
|
||||
socket.addEventListener("close", () => {
|
||||
if (heartbeatInterval) clearInterval(heartbeatInterval);
|
||||
});
|
||||
}
|
||||
|
||||
updateElapsedAndProgress();
|
||||
setInterval(updateElapsedAndProgress, 1000);
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Error</title>
|
||||
<link rel="stylesheet" href="/public/css/error.css">
|
||||
<meta name="color-scheme" content="dark">
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-container">
|
||||
<div class="error-title">Something went wrong</div>
|
||||
<div class="error-message">
|
||||
<%= message || "An unexpected error occurred." %>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -27,7 +27,7 @@
|
|||
<div class="avatar-status-wrapper">
|
||||
<div class="avatar-wrapper">
|
||||
<img class="avatar hidden" src="">
|
||||
<div class="status-indicator offline"></div>
|
||||
<div class="status-indicator offline hidden"></div>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-info-inner">
|
||||
|
|
72
types/lanyard.d.ts
vendored
72
types/lanyard.d.ts
vendored
|
@ -1,72 +0,0 @@
|
|||
interface DiscordUser {
|
||||
id: string;
|
||||
username: string;
|
||||
avatar: string;
|
||||
discriminator: string;
|
||||
clan?: string | null;
|
||||
avatar_decoration_data?: {
|
||||
sku_id: string;
|
||||
asset: string;
|
||||
expires_at: string | null;
|
||||
};
|
||||
bot: boolean;
|
||||
global_name: string;
|
||||
primary_guild?: string | null;
|
||||
collectibles?: {
|
||||
enabled: boolean;
|
||||
disabled: boolean;
|
||||
};
|
||||
display_name: string;
|
||||
public_flags: number;
|
||||
}
|
||||
|
||||
interface Activity {
|
||||
id: string;
|
||||
name: string;
|
||||
type: number;
|
||||
state: string;
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
interface SpotifyData {
|
||||
track_id: string;
|
||||
album_id: string;
|
||||
album_name: string;
|
||||
artist_name: string;
|
||||
track_name: string;
|
||||
}
|
||||
|
||||
interface Kv {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface LanyardData {
|
||||
kv: Kv;
|
||||
discord_user: DiscordUser;
|
||||
activities: Activity[];
|
||||
discord_status: string;
|
||||
active_on_discord_web: boolean;
|
||||
active_on_discord_desktop: boolean;
|
||||
active_on_discord_mobile: boolean;
|
||||
listening_to_spotify?: boolean;
|
||||
spotify?: SpotifyData;
|
||||
spotify_status: string;
|
||||
active_on_spotify: boolean;
|
||||
active_on_xbox: boolean;
|
||||
active_on_playstation: boolean;
|
||||
}
|
||||
|
||||
type LanyardSuccess = {
|
||||
success: true;
|
||||
data: LanyardData;
|
||||
};
|
||||
|
||||
type LanyardError = {
|
||||
success: false;
|
||||
error: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
};
|
||||
|
||||
type LanyardResponse = LanyardSuccess | LanyardError;
|
9
types/logger.d.ts
vendored
9
types/logger.d.ts
vendored
|
@ -1,9 +0,0 @@
|
|||
type ILogMessagePart = { value: string; color: string };
|
||||
|
||||
type ILogMessageParts = {
|
||||
level: ILogMessagePart;
|
||||
filename: ILogMessagePart;
|
||||
readableTimestamp: ILogMessagePart;
|
||||
message: ILogMessagePart;
|
||||
[key: string]: ILogMessagePart;
|
||||
};
|
Loading…
Add table
Reference in a new issue