Files
2026-06-20 22:15:31 +02:00

299 lines
7.4 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/img/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg" />
<link rel="shortcut icon" href="/img/favicon.ico" />
<title>ArcaneChat Channels</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
}
.header {
text-align: center;
color: white;
margin-bottom: 50px;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
font-weight: 700;
}
.header p {
font-size: 1.1em;
opacity: 0.9;
}
.channels-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 25px;
}
.channel-card {
background: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
display: flex;
flex-direction: column;
}
.channel-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
}
.channel-header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 20px;
}
.channel-avatar {
width: 60px;
height: 60px;
min-width: 60px;
border-radius: 50%;
object-fit: cover;
background: #f0f0f0;
border: 1px solid #e0e0e0;
}
.channel-name {
font-size: 1.3em;
font-weight: 600;
color: #333;
line-height: 1.3;
}
.channel-content {
flex: 1;
margin-bottom: 20px;
}
.channel-description {
display: inline;
font-size: 0.95em;
color: #666;
line-height: 1.5;
margin-right: 6px;
}
.show-more-btn {
background: none;
border: none;
color: #667eea;
font-size: 0.9em;
font-weight: 600;
cursor: pointer;
padding: 4px 0;
transition: color 0.2s ease;
}
.show-more-btn:hover {
color: #764ba2;
text-decoration: underline;
}
.button-group {
display: flex;
gap: 1rem;
align-items: center;
}
.join-button {
display: inline-block;
flex: 1;
padding: 0.8rem 1rem;
transition: all 0.3s ease;
border-radius: 8px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-weight: 600;
cursor: pointer;
text-decoration: none;
text-align: center;
}
.join-button:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.join-button:active {
transform: scale(0.98);
}
.share-button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.8rem 1rem;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
background-color: rgb(229, 231, 235);
text-decoration: none;
color: rgb(55, 65, 81);
font-weight: 500;
}
.share-button:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.2);
}
.share-button:active {
transform: scale(0.98);
}
@media (max-width: 600px) {
.channels-grid {
grid-template-columns: 1fr;
}
.header h1 {
font-size: 2em;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Channel Directory</h1>
<p>Discover ArcaneChat channels</p>
</div>
<div class="channels-grid" id="channelsContainer"></div>
</div>
<script>
const assets = "/assets/channels";
function toOpenpgp4fpr(url) {
return url
.replace(/https:\/\/i\.delta\.chat\/#/, "openpgp4fpr:")
.replace(/&/, "#");
}
async function renderChannels() {
function h(tag, attributes, ...children) {
const element = document.createElement(tag);
if (attributes) {
Object.entries(attributes).forEach((entry) => {
element.setAttribute(entry[0], entry[1]);
});
}
element.append(...children);
return element;
}
const data = (
await (await fetch(`${assets}/channels-others.json`)).json()
).concat(await (await fetch(`${assets}/channels.json`)).json());
const container = document.getElementById("channelsContainer");
const MAX_LENGTH = 160;
data.forEach((channel) => {
const description = channel.settings.description || "";
const isLong = description.length > MAX_LENGTH;
const descEl = h("p", { class: "channel-description" });
const channelContent = [descEl];
if (isLong) {
descEl.textContent =
description.substring(0, MAX_LENGTH).trimEnd() + "...";
const showMoreBtn = h(
"button",
{ class: "show-more-btn" },
"Show more",
);
channelContent.push(showMoreBtn);
let expanded = false;
showMoreBtn.addEventListener("click", () => {
expanded = !expanded;
if (expanded) {
descEl.textContent = description;
showMoreBtn.textContent = "Show Less";
} else {
descEl.textContent =
description.substring(0, MAX_LENGTH).trimEnd() + "... ";
showMoreBtn.textContent = "Show more";
}
});
} else {
descEl.textContent = description;
}
container.appendChild(
h(
"div",
{ class: "channel-card" },
h(
"div",
{ class: "channel-header" },
h("img", {
src: `${assets}/images/${channel.image}`,
class: "channel-avatar",
}),
h("h2", { class: "channel-name" }, channel.settings.name),
),
h("div", { class: "channel-content" }, ...channelContent),
h(
"div",
{ class: "button-group" },
h(
"a",
{ href: toOpenpgp4fpr(channel.invite), class: "join-button" },
"Join Channel",
),
h(
"a",
{
href: channel.invite,
class: "share-button",
target: "_blank",
},
"Share",
),
),
),
);
});
}
document.addEventListener("DOMContentLoaded", renderChannels);
</script>
</body>
</html>