From bf66b301aedcd5b658568f957ef3041b0161e420 Mon Sep 17 00:00:00 2001 From: creations Date: Sat, 19 Apr 2025 13:37:37 -0400 Subject: [PATCH] add badges and fix clan tags, and readme issue --- .env.example | 3 ++ README.md | 1 + config/environment.ts | 2 ++ public/css/index.css | 45 +++++++++++++++++++++++--- public/js/index.js | 74 +++++++++++++++++++++++++++++++++++++++++++ src/routes/[id].ts | 3 +- src/routes/index.ts | 3 +- src/server.ts | 1 - src/views/index.ejs | 7 ++-- 9 files changed, 129 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index d2b3d15..bf1f90d 100644 --- a/.env.example +++ b/.env.example @@ -5,3 +5,6 @@ PORT=8080 # this is only the default value if non is give in /id LANYARD_USER_ID=id-here LANYARD_INSTANCE=https://lanyard.rest + +# Required if you want to enable badges +BADGE_API_URL=http://localhost:8081 diff --git a/README.md b/README.md index 81bfda2..f9231c3 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ cp .env.example .env | `PORT` | Port to run the server on (default: `8080`) | | `LANYARD_USER_ID` | Your Discord user ID | | `LANYARD_INSTANCE` | Lanyard WebSocket endpoint URL | +| `BADGE_API_URL` | Uses the [badge api](https://git.creations.works/creations/badgeAPI) only required if you want to use badges #### Optional Lanyard KV Vars (per-user customization) diff --git a/config/environment.ts b/config/environment.ts index 239d2c3..1476062 100644 --- a/config/environment.ts +++ b/config/environment.ts @@ -9,3 +9,5 @@ export const lanyardConfig: LanyardConfig = { userId: process.env.LANYARD_USER_ID || "", instance: process.env.LANYARD_INSTANCE || "https://api.lanyard.rest", }; + +export const badgeApi: string | null = process.env.BADGE_API_URL || null; diff --git a/public/css/index.css b/public/css/index.css index 1bcd28d..e3e63d9 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -26,7 +26,7 @@ body { } .hidden { - display: none; + display: none !important; } .activity-header.hidden { @@ -63,6 +63,28 @@ body { border-radius: 50%; } +.badges { + max-width: 700px; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 0.3rem; + flex-wrap: wrap; + margin-top: 0.5rem; + padding: 0.5rem; + background-color: var(--card-bg); + border-radius: 10px; + border: 1px solid var(--border-color); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +.badge { + width: 26px; + height: 26px; + border-radius: 50%; +} + .decoration { position: absolute; top: -18px; @@ -143,7 +165,11 @@ body { align-items: center; justify-content: center; gap: 0.3rem; - padding: 0.2rem 0.3rem; + padding: .4rem 0.5rem; + + text-align: center; + align-items: center; + justify-content: center; } .clan-badge img { @@ -154,7 +180,7 @@ body { } .clan-badge span { - font-size: 1rem; + font-size: .9rem; color: var(--text-color); margin: 0; @@ -409,6 +435,14 @@ ul { align-items: center; } + .badges { + max-width: 100%; + border-radius: 0; + border: none; + background-color: transparent; + margin-top: 0; + } + .avatar-status-wrapper { flex-direction: column; align-items: center; @@ -548,7 +582,7 @@ ul { border-radius: 8px; border: 1px solid var(--border-color); - margin-top: 2rem; + margin-top: 1rem; box-sizing: border-box; overflow: hidden; @@ -626,7 +660,8 @@ ul { @media (max-width: 600px) { .readme { - width: 100%; + max-width: 100%; + min-width: 100%; padding: 1rem; margin-top: 1rem; diff --git a/public/js/index.js b/public/js/index.js index 22e2dc6..b18f604 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -84,6 +84,7 @@ setInterval(updateElapsedAndProgress, 1000); 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")) { @@ -276,6 +277,79 @@ function buildActivityHTML(activity) { `; } +if (badgeURL && badgeURL !== "null" && userId) { + if (!badgeURL.startsWith("http")) { + badgeURL = `https://${badgeURL}`; + } + + if (!badgeURL.endsWith("/")) { + badgeURL += "/"; + } + + async function loadBadges(userId, options = {}) { + const { + services = [], + seperated = false, + cache = true, + targetId = "badges", + } = options; + + const params = new URLSearchParams(); + if (services.length) params.set("services", services.join(",")); + if (seperated) params.set("seperated", "true"); + if (!cache) params.set("cache", "false"); + + const url = `${badgeURL}${userId}?${params.toString()}`; + const target = document.getElementById(targetId); + if (!target) return; + + 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; + } + + const badges = Array.isArray(json.badges) + ? json.badges + : Object.values(json.badges).flat(); + + if (badges.length === 0) { + target.innerHTML = ""; + target.classList.add("hidden"); + return; + } + + target.innerHTML = ""; + 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); + } + + target.classList.remove("hidden"); + } catch (err) { + console.error(err); + target.innerHTML = ""; + target.classList.add("hidden"); + } + } + + loadBadges(userId, { + services: [], + seperated: false, + cache: true, + targetId: "badges", + }); +} + function updatePresence(data) { const avatarWrapper = document.querySelector(".avatar-wrapper"); const statusIndicator = avatarWrapper?.querySelector(".status-indicator"); diff --git a/src/routes/[id].ts b/src/routes/[id].ts index 46e3952..cef0edf 100644 --- a/src/routes/[id].ts +++ b/src/routes/[id].ts @@ -1,5 +1,5 @@ import { getImageColors } from "@/helpers/colors"; -import { lanyardConfig } from "@config/environment"; +import { badgeApi, lanyardConfig } from "@config/environment"; import { renderEjsTemplate } from "@helpers/ejs"; import { getLanyardData, handleReadMe } from "@helpers/lanyard"; @@ -64,6 +64,7 @@ async function handler(request: ExtendedRequest): Promise { allowSnow: presence.kv.snow === "true", allowRain: presence.kv.rain === "true", colors: colors?.colors ?? {}, + badgeApi: badgeApi, }; return await renderEjsTemplate("index", ejsTemplateData); diff --git a/src/routes/index.ts b/src/routes/index.ts index b0d6a61..1359239 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,5 +1,5 @@ import { getImageColors } from "@/helpers/colors"; -import { lanyardConfig } from "@config/environment"; +import { badgeApi, lanyardConfig } from "@config/environment"; import { renderEjsTemplate } from "@helpers/ejs"; import { getLanyardData, handleReadMe } from "@helpers/lanyard"; @@ -63,6 +63,7 @@ async function handler(): Promise { allowSnow: presence.kv.snow === "true", allowRain: presence.kv.rain === "true", colors: colors?.colors ?? {}, + badgeApi: badgeApi, }; return await renderEjsTemplate("index", ejsTemplateData); diff --git a/src/server.ts b/src/server.ts index ebdbed0..8c7c0ba 100644 --- a/src/server.ts +++ b/src/server.ts @@ -236,7 +236,6 @@ class ServerHandler { "unknown"; } - logger.custom( `[${request.method}]`, `(${response.status})`, diff --git a/src/views/index.ejs b/src/views/index.ejs index 0736af4..9ba151d 100644 --- a/src/views/index.ejs +++ b/src/views/index.ejs @@ -1,7 +1,7 @@ - + @@ -50,7 +50,7 @@