Files
xxx-sphere-web/bin/main/static/dating/likes.html
Mario 2b0ce62d33
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
Menp überarbeitet
2026-04-08 16:52:43 +02:00

169 lines
9.2 KiB
HTML
Raw Permalink 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>Likes xXx Sphere</title>
<link rel="stylesheet" href="/css/variables.css">
<link rel="stylesheet" href="/css/style.css">
<style>
.profiles-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; }
@media (max-width: 500px) { .profiles-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); } }
.profile-card { background: var(--color-card); border: 1px solid var(--color-secondary); border-radius: 12px; overflow: hidden; transition: border-color 0.15s, box-shadow 0.15s; text-decoration: none; color: var(--color-text); display: flex; flex-direction: column; }
.profile-card:hover { border-color: var(--color-primary); box-shadow: 0 4px 18px rgba(0,0,0,0.35); }
.profile-card-img-wrap { position: relative; width: 100%; aspect-ratio: 1; flex-shrink: 0; overflow: hidden; background: var(--color-secondary); display: flex; align-items: center; justify-content: center; font-size: 3rem; }
.profile-card-img-wrap img { width: 100%; height: 100%; object-fit: cover; }
.profile-card-img-wrap.blurred img { filter: blur(12px); transform: scale(1.1); }
.profile-card-img-wrap .lock-overlay { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 2rem; }
.profile-card-body { padding: 0.75rem; display: flex; flex-direction: column; gap: 0.3rem; flex: 1; }
.profile-card-name { font-weight: 700; font-size: 1rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.profile-card-meta { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.meta-chip { padding: 0.1rem 0.45rem; border-radius: 20px; background: var(--color-secondary); font-size: 0.73rem; color: var(--color-muted); }
.meta-chip.accent { color: var(--color-primary); }
.profile-card-desc { font-size: 0.78rem; color: var(--color-muted); line-height: 1.4; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; margin-top: 0.15rem; }
.profile-card-skeleton { background: var(--color-card); border: 1px solid var(--color-secondary); border-radius: 12px; overflow: hidden; display: flex; flex-direction: column; }
.skeleton-img { width: 100%; aspect-ratio: 1; background: linear-gradient(90deg, var(--color-secondary) 25%, var(--color-card) 50%, var(--color-secondary) 75%); background-size: 200% 100%; animation: shimmer 1.2s infinite; }
.skeleton-body { padding: 0.75rem; display: flex; flex-direction: column; gap: 0.5rem; }
.skeleton-line { height: 0.75rem; border-radius: 4px; background: linear-gradient(90deg, var(--color-secondary) 25%, var(--color-card) 50%, var(--color-secondary) 75%); background-size: 200% 100%; animation: shimmer 1.2s infinite; }
@keyframes shimmer { to { background-position: -200% 0; } }
.empty-state { text-align: center; padding: 3rem 1rem; color: var(--color-muted); grid-column: 1 / -1; }
.empty-state .icon { font-size: 2.5rem; margin-bottom: 0.75rem; }
#sentinel { height: 1px; margin-top: 1rem; }
.results-count { font-size: 0.85rem; color: var(--color-muted); display: block; margin-bottom: 0.75rem; }
.premium-banner { background: var(--color-card); border: 1px solid var(--color-primary); border-radius: 10px; padding: 0.85rem 1rem; margin-bottom: 1rem; font-size: 0.88rem; color: var(--color-muted); }
.premium-banner strong { color: var(--color-primary); }
</style>
</head>
<body class="app">
<div class="main">
<div class="content">
<h1 style="margin:0 0 0.15rem;">Likes</h1>
<span class="results-count" id="resultsCount"></span>
<div id="premiumBanner" style="display:none;" class="premium-banner">
<strong>Premium</strong> Mit Premium siehst du, wer dich geliket hat.
</div>
<div class="profiles-grid" id="profilesGrid"></div>
<div id="sentinel"></div>
</div>
</div>
<script src="/js/icons.js"></script>
<script src="/js/nav.js"></script>
<script>
const BATCH = 12;
let allEntries = []; // { userId } nur für Premium; gesamt inkl. locked
let loadedCount = 0;
let loading = false;
let isPremium = false;
function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
function skeleton() {
return `<div class="profile-card-skeleton"><div class="skeleton-img"></div><div class="skeleton-body"><div class="skeleton-line" style="width:70%"></div><div class="skeleton-line" style="width:50%"></div></div></div>`;
}
function appendLockedCard() {
const grid = document.getElementById('profilesGrid');
const card = document.createElement('div');
card.className = 'profile-card';
card.style.cursor = 'default';
card.innerHTML = `
<div class="profile-card-img-wrap blurred">
<span style="font-size:3rem;">👤</span>
<div class="lock-overlay">🔒</div>
</div>
<div class="profile-card-body">
<div class="profile-card-name">???</div>
<div class="profile-card-meta"><span class="meta-chip accent">Premium</span></div>
</div>`;
grid.appendChild(card);
}
function appendProfiles(profiles) {
const grid = document.getElementById('profilesGrid');
profiles.forEach(p => {
const pic = p.profilePictureHq || p.profilePicture;
const img = pic ? `<img src="data:image/png;base64,${pic}" alt="${esc(p.name)}" loading="lazy">` : '👤';
const chips = [
p.alter ? `<span class="meta-chip">${p.alter} J.</span>` : '',
p.geschlecht ? `<span class="meta-chip">${esc(p.geschlecht)}</span>` : '',
p.datingStadt ? `<span class="meta-chip">${esc(p.datingStadt)}</span>` : '',
`<span class="meta-chip accent">❤️ mag dich</span>`,
].filter(Boolean).join('');
const desc = p.beschreibung ? `<div class="profile-card-desc">${esc(p.beschreibung)}</div>` : '';
const card = document.createElement('a');
card.className = 'profile-card';
card.href = `/community/benutzer.html?userId=${p.userId}`;
card.innerHTML = `
<div class="profile-card-img-wrap">${img}</div>
<div class="profile-card-body">
<div class="profile-card-name">${esc(p.name)}</div>
<div class="profile-card-meta">${chips}</div>
${desc}
</div>`;
grid.appendChild(card);
});
}
async function loadNextBatch() {
if (loading || loadedCount >= allEntries.length) return;
loading = true;
const batch = allEntries.slice(loadedCount, loadedCount + BATCH);
const grid = document.getElementById('profilesGrid');
const skelDiv = document.createElement('div');
skelDiv.style.display = 'contents';
skelDiv.innerHTML = Array.from({length: batch.length}, () => skeleton()).join('');
grid.appendChild(skelDiv);
try {
const res = await fetch('/dating/profiles/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(batch.map(e => e.userId))
});
grid.removeChild(skelDiv);
if (res.ok) appendProfiles(await res.json());
} catch { grid.removeChild(skelDiv); }
loadedCount += batch.length;
loading = false;
}
(async () => {
try {
const res = await fetch('/dating/who-likes-me');
if (!res.ok) throw new Error();
const data = await res.json();
isPremium = !!data.premium;
document.getElementById('resultsCount').textContent =
data.total === 0 ? 'Noch keine Likes'
: data.total + (data.total === 1 ? ' Person mag dich' : ' Personen mögen dich');
if (data.total === 0) {
document.getElementById('profilesGrid').innerHTML =
'<div class="empty-state"><div class="icon">❤️</div><p>Noch keine Likes.</p></div>';
return;
}
if (!isPremium) {
document.getElementById('premiumBanner').style.display = '';
// Locked-Karten für alle anzeigen
data.likers.forEach(() => appendLockedCard());
return;
}
allEntries = data.likers.map(l => ({ userId: l.userId }));
loadNextBatch();
} catch {
document.getElementById('profilesGrid').innerHTML =
'<div class="empty-state"><div class="icon">⚠️</div><p>Fehler beim Laden.</p></div>';
}
})();
new IntersectionObserver(
e => { if (e[0].isIntersecting) loadNextBatch(); },
{ rootMargin: '300px' }
).observe(document.getElementById('sentinel'));
</script>
</body>
</html>