Alles mögliche
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
This commit is contained in:
168
bin/main/static/dating/likes.html
Normal file
168
bin/main/static/dating/likes.html
Normal file
@@ -0,0 +1,168 @@
|
||||
<!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/sidebar.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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||||
|
||||
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>
|
||||
Reference in New Issue
Block a user