Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
286 lines
12 KiB
HTML
286 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Home – xXx Sphere</title>
|
||
<link rel="stylesheet" href="/css/variables.css">
|
||
<link rel="stylesheet" href="/css/style.css">
|
||
<style>
|
||
.game-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||
gap: 1.25rem;
|
||
margin-top: 1.5rem;
|
||
}
|
||
.game-card {
|
||
background: var(--color-secondary);
|
||
border: 1px solid var(--color-secondary);
|
||
border-radius: 12px;
|
||
padding: 1.5rem 1.25rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.75rem;
|
||
}
|
||
.game-card-icon { font-size: 2rem; line-height: 1; }
|
||
.game-card-title { font-size: 1.1rem; font-weight: 700; margin: 0; }
|
||
.game-card-desc { font-size: 0.88rem; color: var(--color-muted); line-height: 1.6; flex: 1; }
|
||
.game-card-btn { margin-top: 0.25rem; width: auto; align-self: flex-start; padding: 0.5rem 1.25rem; }
|
||
.welcome { font-size: 0.95rem; color: var(--color-muted); margin: 0.25rem 0 0; }
|
||
|
||
.section-label {
|
||
font-size: 0.8rem; font-weight: 600; color: var(--color-muted);
|
||
text-transform: uppercase; letter-spacing: 0.05em;
|
||
margin: 2rem 0 0.75rem;
|
||
padding-bottom: 0.5rem;
|
||
border-bottom: 1px solid var(--color-secondary);
|
||
}
|
||
.visitors-strip {
|
||
display: flex; flex-wrap: wrap; gap: 0.75rem;
|
||
}
|
||
.visitor-card {
|
||
display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
|
||
text-decoration: none; color: var(--color-text);
|
||
width: 72px;
|
||
}
|
||
.visitor-card:hover .visitor-avatar { border-color: var(--color-primary); }
|
||
.visitor-avatar {
|
||
width: 56px; height: 56px; border-radius: 50%;
|
||
background: var(--color-secondary);
|
||
border: 2px solid var(--color-secondary);
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 1.4rem; overflow: hidden; flex-shrink: 0;
|
||
transition: border-color 0.15s;
|
||
}
|
||
.visitor-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
|
||
.visitor-name {
|
||
font-size: 0.75rem; text-align: center;
|
||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||
width: 100%;
|
||
}
|
||
.visitor-time { font-size: 0.68rem; color: var(--color-muted); text-align: center; }
|
||
|
||
/* ── Dating: Likes & Matches ── */
|
||
.dating-strip { display: flex; flex-wrap: wrap; gap: 0.75rem; }
|
||
.dating-card {
|
||
display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
|
||
text-decoration: none; color: var(--color-text); width: 72px;
|
||
}
|
||
.dating-card:hover .dating-avatar { border-color: var(--color-primary); }
|
||
.dating-avatar {
|
||
width: 56px; height: 56px; border-radius: 50%;
|
||
background: var(--color-secondary);
|
||
border: 2px solid var(--color-secondary);
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 1.4rem; overflow: hidden; flex-shrink: 0;
|
||
transition: border-color 0.15s;
|
||
}
|
||
.dating-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
|
||
.dating-name {
|
||
font-size: 0.75rem; text-align: center;
|
||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%;
|
||
}
|
||
/* Verschwommene Karte für nicht-Premium */
|
||
.dating-card-locked {
|
||
width: 72px; display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
|
||
cursor: default;
|
||
}
|
||
.dating-avatar-blurred {
|
||
width: 56px; height: 56px; border-radius: 50%;
|
||
background: var(--color-secondary);
|
||
border: 2px solid var(--color-secondary);
|
||
overflow: hidden; flex-shrink: 0; position: relative;
|
||
}
|
||
.dating-avatar-blurred img {
|
||
width: 100%; height: 100%; object-fit: cover; border-radius: 50%;
|
||
filter: blur(6px); transform: scale(1.1);
|
||
}
|
||
.dating-avatar-blurred .lock-icon {
|
||
position: absolute; inset: 0;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 1.1rem;
|
||
}
|
||
.premium-hint {
|
||
font-size: 0.65rem; color: var(--color-primary); text-align: center; font-weight: 600;
|
||
}
|
||
.match-badge {
|
||
font-size: 0.65rem; color: var(--color-primary); text-align: center; font-weight: 600;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="app">
|
||
<div class="main">
|
||
<div class="content">
|
||
<h1 style="margin:0 0 0.15rem;">Home</h1>
|
||
<p class="welcome" id="greeting"></p>
|
||
|
||
<div class="game-grid">
|
||
<div class="game-card">
|
||
<div class="game-card-icon">♡</div>
|
||
<h2 class="game-card-title">Vanilla Game</h2>
|
||
<p class="game-card-desc">
|
||
Entdecke spielerische Rollenspiele und Aufgaben in einem entspannten Rahmen.
|
||
Ideal für den Einstieg – ohne Regeln, nur Spaß zu zweit oder in der Gruppe.
|
||
</p>
|
||
<a href="/games/vanilla/sessionvanilla.html"><button class="game-card-btn">Neue Session starten</button></a>
|
||
</div>
|
||
|
||
<div class="game-card">
|
||
<div class="game-card-icon">◆</div>
|
||
<h2 class="game-card-title">BDSM Game</h2>
|
||
<p class="game-card-desc">
|
||
Tauche ein in strukturierte Sessions mit Aufgaben, Toys und klaren Rollen.
|
||
Definiere Grenzen, vergib Aufgaben und erlebe intensive Momente mit deinem Partner.
|
||
</p>
|
||
<a href="/games/bdsm/neubdsm.html"><button class="game-card-btn">Neue Session starten</button></a>
|
||
</div>
|
||
|
||
<div class="game-card">
|
||
<div class="game-card-icon">⊗</div>
|
||
<h2 class="game-card-title">Chastity Game</h2>
|
||
<p class="game-card-desc">
|
||
Erlebe Keuschheit auf eine neue Art: Kartenbasierte Locks, Keyholder-System,
|
||
Community-Abstimmungen und tägliche Verifizierungen machen jedes Lock einzigartig.
|
||
</p>
|
||
<a href="/games/chastity/neulock.html"><button class="game-card-btn">Neues Lock erstellen</button></a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Profilbesucher -->
|
||
<div id="visitorsSection" style="display:none;">
|
||
<div class="section-label">Profilbesucher</div>
|
||
<div class="visitors-strip" id="visitorsStrip"></div>
|
||
</div>
|
||
|
||
<!-- Wer hat mich geliked (Dating) -->
|
||
<div id="likesSection" style="display:none;">
|
||
<div class="section-label">Dating – Wer mag mich ♥</div>
|
||
<div class="dating-strip" id="likesStrip"></div>
|
||
</div>
|
||
|
||
<!-- Matches -->
|
||
<div id="matchesSection" style="display:none;">
|
||
<div class="section-label">Dating – Matches 🎉</div>
|
||
<div class="dating-strip" id="matchesStrip"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/js/icons.js"></script>
|
||
<script src="/js/sidebar.js"></script>
|
||
<script>
|
||
fetch('/login/me')
|
||
.then(r => {
|
||
if (r.status === 401) { window.location.href = '/login.html'; return null; }
|
||
return r.json();
|
||
})
|
||
.then(user => {
|
||
if (user) {
|
||
document.getElementById('greeting').textContent = 'Willkommen zurück, ' + user.name + '!';
|
||
loadVisitors();
|
||
if (user.datingAktiv) {
|
||
loadWhoLikesMe();
|
||
loadMatches();
|
||
}
|
||
}
|
||
})
|
||
.catch(() => { window.location.href = '/login.html'; });
|
||
|
||
function relativeTime(isoString) {
|
||
const diff = Math.floor((Date.now() - new Date(isoString)) / 1000);
|
||
if (diff < 60) return 'gerade eben';
|
||
if (diff < 3600) return 'vor ' + Math.floor(diff / 60) + ' Min.';
|
||
if (diff < 86400) return 'vor ' + Math.floor(diff / 3600) + ' Std.';
|
||
return 'vor ' + Math.floor(diff / 86400) + ' Tag' + (Math.floor(diff / 86400) === 1 ? '' : 'en');
|
||
}
|
||
|
||
async function loadWhoLikesMe() {
|
||
try {
|
||
const res = await fetch('/dating/who-likes-me');
|
||
if (!res.ok) return;
|
||
const data = await res.json();
|
||
if (data.total === 0) return;
|
||
|
||
const strip = document.getElementById('likesStrip');
|
||
strip.innerHTML = data.likers.map(l => {
|
||
const pic = l.profilePicture
|
||
? `<img src="data:image/png;base64,${l.profilePicture}" alt="">`
|
||
: '◉';
|
||
|
||
if (data.premium && l.userId) {
|
||
return `
|
||
<a class="dating-card" href="/community/benutzer.html?userId=${l.userId}">
|
||
<div class="dating-avatar">${pic}</div>
|
||
<span class="dating-name">${esc(l.name)}</span>
|
||
</a>`;
|
||
} else {
|
||
return `
|
||
<div class="dating-card-locked">
|
||
<div class="dating-avatar-blurred">
|
||
${pic}
|
||
<span class="lock-icon">🔒</span>
|
||
</div>
|
||
<span class="premium-hint">Premium</span>
|
||
</div>`;
|
||
}
|
||
}).join('');
|
||
|
||
document.getElementById('likesSection').style.display = '';
|
||
} catch (_) {}
|
||
}
|
||
|
||
async function loadMatches() {
|
||
try {
|
||
const res = await fetch('/dating/matches');
|
||
if (!res.ok) return;
|
||
const matches = await res.json();
|
||
if (!matches.length) return;
|
||
|
||
const strip = document.getElementById('matchesStrip');
|
||
strip.innerHTML = matches.map(m => `
|
||
<a class="dating-card" href="/community/benutzer.html?userId=${m.userId}">
|
||
<div class="dating-avatar">
|
||
${m.profilePicture
|
||
? `<img src="data:image/png;base64,${m.profilePicture}" alt="${esc(m.name)}">`
|
||
: '◉'}
|
||
</div>
|
||
<span class="dating-name">${esc(m.name)}</span>
|
||
<span class="match-badge">♥ Match</span>
|
||
</a>
|
||
`).join('');
|
||
document.getElementById('matchesSection').style.display = '';
|
||
} catch (_) {}
|
||
}
|
||
|
||
function esc(s) {
|
||
if (!s) return '';
|
||
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||
}
|
||
|
||
async function loadVisitors() {
|
||
try {
|
||
const res = await fetch('/social/profile-visits/my-visitors');
|
||
if (!res.ok) return;
|
||
const visitors = await res.json();
|
||
if (!visitors.length) return;
|
||
|
||
const strip = document.getElementById('visitorsStrip');
|
||
strip.innerHTML = visitors.map(v => `
|
||
<a class="visitor-card" href="/community/benutzer.html?userId=${v.userId}">
|
||
<div class="visitor-avatar">
|
||
${v.profilePicture
|
||
? `<img src="data:image/png;base64,${v.profilePicture}" alt="${v.name}">`
|
||
: '◉'}
|
||
</div>
|
||
<span class="visitor-name">${v.name}</span>
|
||
<span class="visitor-time">${relativeTime(v.visitedAt)}</span>
|
||
</a>
|
||
`).join('');
|
||
document.getElementById('visitorsSection').style.display = '';
|
||
} catch (_) {}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|