Files
server/assets/public/widget.html
2025-12-21 02:37:08 +01:00

175 lines
5.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spacebar Widget</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Montserrat', sans-serif;
background-color: rgb(10, 10, 10);
color: white;
font-size: 1.1rem;
height: 100vh;
}
* {
padding: 0;
margin: 0;
}
p {
margin-top: 10px;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
}
#wordmark {
width: min(250px, 50%);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
border-bottom: 1px solid #222;
}
.content {
padding: 8px;
}
#online-count {
font-size: 1.2rem;
font-weight: 999;
}
a, a:visited {
color: #0185ff;
}
</style>
<script>
const guildId = new URLSearchParams(window.location.search).get("id");
document.addEventListener("DOMContentLoaded", function() {
const vcList = document.getElementById("vc-list");
const memberList = document.getElementById("member-list");
const onlineCount = document.getElementById("online-count");
function fetchData() {
const refreshInterval = 1000;
fetch(`/api/guilds/${guildId}/widget.json`)
.then(response => {
if (response.ok) {
const cacheDuration = response.headers.get("Cache-Control")?.match(/max-age=(\d+)/);
if (cacheDuration && cacheDuration[1]) {
const interval = parseInt(cacheDuration[1], 10) * 1000;
if (interval > refreshInterval) {
setTimeout(fetchData, interval);
}
}
}
return response.json();
})
.then(data => {
if (!data) return;
// Update online count
onlineCount.textContent = data.presence_count + "/" + data.member_count;
// Update voice channels
vcList.innerHTML = "";
data.channels.forEach(channel => {
const channelDiv = document.createElement("div");
const channelName = document.createElement("h3");
channelName.textContent = "🔊 " + channel.name;
channelDiv.appendChild(channelName);
const userList = document.createElement("ul");
channel.users?.forEach(user => {
const userItem = document.createElement("li");
userItem.textContent = user.username;
userList.appendChild(userItem);
});
channelDiv.appendChild(userList);
vcList.appendChild(channelDiv);
});
// Update online members
memberList.innerHTML = "";
data.members.forEach(member => {
const memberItem = document.createElement("div");
const img = document.createElement("img");
img.src = member.avatar_url;
img.alt = "";
img.ariaHidden = "true";
img.width = 32;
img.height = 32;
img.style.borderRadius = "50%";
img.style.marginRight = "8px";
img.style.contentVisibility = "auto";
img.loading = "lazy";
img.decoding = "async";
img.onerror = function() {
console.log("Failed to load avatar for user " + member.username + ", using default.");
const domain = new URL(window.location.href).origin;
// noinspection JSUnresolvedReference - BigInt is a valid global
img.src = `${domain}/embed/avatar/${BigInt(member.id) % 6n}.png`;
};
memberItem.appendChild(img);
const nameSpan = document.createElement("span");
nameSpan.textContent = member.username;
memberItem.appendChild(nameSpan);
memberItem.style.display = "flex";
memberItem.style.alignItems = "center";
memberItem.style.marginBottom = "6px";
memberList.appendChild(memberItem);
});
setTimeout(fetchData, 300000); // schedule next fetch in 5 minutes
})
.catch(error => {
console.error("Error fetching widget data:", error);
setTimeout(fetchData, Math.floor(Math.random() * 4000) + 1000);
});
}
// Initial fetch
fetchData();
});
</script>
</head>
<body>
<div class="container">
<div class="header">
<img alt="Spacebar Logo"
id="wordmark"
src="https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/branding/svg/Spacebar__Logo-Blue.svg" />
<span>
<b id="online-count">Unknown</b>
users online
</span>
</div>
<div class="content">
<div id="vc-list"></div>
<p>Online users</p>
<div id="member-list"></div>
</div>
</div>
</body>
</html>