Files
xxx-sphere-web/bin/main/static/userhome.html
Mario b81ad25c9f
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
Bugfixes im Dating und im Profil
2026-04-04 15:45:55 +02:00

285 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>
<!-- 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">Likes ❤️</div>
<div class="dating-strip" id="likesStrip"></div>
</div>
<!-- Matches -->
<div id="matchesSection" style="display:none;">
<div class="section-label">Matches 💕</div>
<div class="dating-strip" id="matchesStrip"></div>
</div>
</div>
<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">
</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 deinen Spielpartner*Innen.
</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>
</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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
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>