Files
xxx-sphere-web/bin/main/static/community/freunde.html
Mario e2a71ab096
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
Hashtags eingeführt
2026-04-11 01:14:33 +02:00

352 lines
14 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>Freunde xXx Sphere</title>
<link rel="stylesheet" href="/css/variables.css">
<link rel="stylesheet" href="/css/style.css">
<style>
.tabs {
display: flex;
gap: 0;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--color-secondary);
}
.tab-btn {
background: none;
border: none;
border-bottom: 3px solid transparent;
border-radius: 0;
padding: 0.6rem 1.25rem;
font-size: 0.95rem;
font-weight: 600;
color: var(--color-muted);
cursor: pointer;
margin-bottom: -1px;
transition: color 0.15s, border-color 0.15s;
}
.tab-btn:hover { color: var(--color-text); background: none; }
.tab-btn.active { color: var(--color-primary); border-bottom-color: var(--color-primary); }
.tab-panel { display: none; }
.tab-panel.active { display: block; }
.user-list { list-style: none; margin: 0; padding: 0; }
.user-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 0;
border-bottom: 1px solid var(--color-secondary);
}
.user-item:last-child { border-bottom: none; }
.user-avatar {
width: 42px; height: 42px;
border-radius: 50%;
background: var(--color-secondary);
display: flex; align-items: center; justify-content: center;
font-size: 1.2rem;
flex-shrink: 0;
overflow: hidden;
border: 1px solid var(--color-secondary);
}
.user-avatar img { width: 100%; height: 100%; object-fit: cover; }
.user-name { font-weight: 600; flex: 1; }
.user-actions { display: flex; gap: 0.5rem; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }
.user-actions button, .user-actions a.btn {
margin-top: 0;
padding: 0.35rem 0.75rem;
font-size: 0.8rem;
width: auto;
}
.user-profile-link {
display: flex;
align-items: center;
gap: 0.75rem;
flex: 1;
min-width: 0;
text-decoration: none;
color: inherit;
}
.user-profile-link:hover .user-name { color: var(--color-primary); }
.btn-reject {
background: transparent;
border: 1px solid var(--color-secondary);
color: var(--color-muted);
font-size: 0.8rem;
padding: 0.35rem 0.75rem;
border-radius: 6px;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.btn-reject:hover { background: #3d0f1a; border-color: var(--color-primary); color: var(--color-primary); }
.empty-hint { color: var(--color-muted); font-size: 0.9rem; margin-top: 0.5rem; }
/* ── Confirm dialog ── */
.dialog-backdrop {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.6);
z-index: 200;
align-items: center;
justify-content: center;
}
.dialog-backdrop.visible { display: flex; }
.dialog {
background: var(--color-card);
border: 1px solid var(--color-secondary);
border-radius: 12px;
padding: 1.75rem;
width: 100%;
max-width: 340px;
box-shadow: 0 8px 32px rgba(0,0,0,0.6);
}
.dialog h3 { color: var(--color-primary); font-size: 1.05rem; margin-bottom: 0.75rem; }
.dialog p { color: var(--color-muted); font-size: 0.88rem; margin-bottom: 1.25rem; }
.dialog-actions { display: flex; gap: 0.75rem; }
.dialog-actions button { flex: 1; margin-top: 0; }
.tab-badge {
display: inline-block;
background: var(--color-primary);
color: #fff;
font-size: 0.65rem;
font-weight: 700;
border-radius: 9999px;
padding: 0.05rem 0.35rem;
margin-left: 0.35rem;
vertical-align: middle;
}
</style>
</head>
<body class="app">
<div class="main">
<div class="content">
<h1 style="margin-bottom: 1.25rem;">Freunde</h1>
<div class="tabs">
<button class="tab-btn active" data-tab="friends" onclick="switchTab('friends', this)">Freunde</button>
<button class="tab-btn" data-tab="pending" onclick="switchTab('pending', this)" id="pendingTabBtn">
Anfragen<span class="tab-badge" id="pendingBadge" style="display:none;"></span>
</button>
</div>
<!-- Friends tab -->
<div class="tab-panel active" id="tab-friends">
<ul class="user-list" id="friendsList"></ul>
<p class="empty-hint" id="friendsEmpty" style="display:none;">Du hast noch keine Freunde. <a href="/search.html" style="color:var(--color-primary);">Personen suchen</a></p>
</div>
<!-- Pending tab -->
<div class="tab-panel" id="tab-pending">
<ul class="user-list" id="pendingList"></ul>
<p class="empty-hint" id="pendingEmpty" style="display:none;">Keine offenen Anfragen.</p>
</div>
</div>
</div>
<!-- Confirm remove dialog -->
<div class="dialog-backdrop" id="removeDialog">
<div class="dialog">
<h3>Freundschaft beenden</h3>
<p id="removeDialogText">Möchtest du diese Freundschaft wirklich beenden?</p>
<div class="dialog-actions">
<button class="secondary" onclick="closeRemoveDialog()">Abbrechen</button>
<button id="removeConfirmBtn" style="background:#c0392b;" onclick="confirmRemove()">Entfernen</button>
</div>
</div>
</div>
<script src="/js/icons.js"></script>
<script src="/js/nav.js"></script>
<script src="/js/social-sidebar.js"></script>
<script>
function switchTab(name, btn) {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
btn.classList.add('active');
document.getElementById('tab-' + name).classList.add('active');
localStorage.setItem('tab_freunde', name);
}
const _savedFreundeTab = localStorage.getItem('tab_freunde');
if (_savedFreundeTab) {
const _btn = document.querySelector(`.tab-btn[data-tab="${_savedFreundeTab}"]`);
if (_btn) switchTab(_savedFreundeTab, _btn);
}
function esc(s) {
const d = document.createElement('div');
d.textContent = s;
return d.innerHTML;
}
function avatar(u) {
return u.profilePicture
? `<img src="data:image/png;base64,${u.profilePicture}" alt="">`
: '◉';
}
async function loadFriends() {
try {
const res = await fetch('/social/friends');
if (!res.ok) return;
const friends = await res.json();
const list = document.getElementById('friendsList');
list.innerHTML = '';
if (friends.length === 0) {
document.getElementById('friendsEmpty').style.display = '';
return;
}
document.getElementById('friendsEmpty').style.display = 'none';
friends.forEach(f => {
list.insertAdjacentHTML('beforeend', `
<li class="user-item" id="friend-${f.friendshipId}">
<a href="/community/benutzer.html?userId=${f.user.userId}" class="user-profile-link">
<div class="user-avatar">${avatar(f.user)}</div>
<div class="user-name">${esc(f.user.name)}</div>
</a>
<div class="user-actions">
<a href="/community/nachrichten.html?userId=${f.user.userId}" class="btn" style="background:var(--color-secondary); color:var(--color-text);">✉ Nachricht</a>
<button class="btn-reject" onclick="removeFriend('${f.friendshipId}', this)">Entfernen</button>
</div>
</li>`);
});
} catch (e) { console.error(e); }
}
async function loadPending() {
try {
const res = await fetch('/social/friends/pending');
if (!res.ok) return;
const pending = await res.json();
const list = document.getElementById('pendingList');
list.innerHTML = '';
const badge = document.getElementById('pendingBadge');
if (pending.length > 0) {
badge.textContent = pending.length;
badge.style.display = '';
} else {
badge.style.display = 'none';
}
if (pending.length === 0) {
document.getElementById('pendingEmpty').style.display = '';
return;
}
document.getElementById('pendingEmpty').style.display = 'none';
pending.forEach(f => {
list.insertAdjacentHTML('beforeend', `
<li class="user-item" id="pending-${f.friendshipId}">
<a href="/community/benutzer.html?userId=${f.user.userId}" class="user-profile-link">
<div class="user-avatar">${avatar(f.user)}</div>
<div class="user-name">${esc(f.user.name)}</div>
</a>
<div class="user-actions">
<button onclick="accept('${f.friendshipId}', this)">✓ Annehmen</button>
<button class="btn-reject" onclick="reject('${f.friendshipId}', this)">✕ Ablehnen</button>
</div>
</li>`);
});
} catch (e) { console.error(e); }
}
async function accept(friendshipId, btn) {
btn.disabled = true;
btn.textContent = '…';
try {
const res = await fetch('/social/friends/accept', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ friendshipId })
});
if (res.ok) {
document.getElementById('pending-' + friendshipId)?.remove();
await loadFriends();
await loadPending();
} else {
btn.disabled = false;
btn.textContent = '✓ Annehmen';
}
} catch (e) {
btn.disabled = false;
btn.textContent = '✓ Annehmen';
}
}
async function reject(friendshipId, btn) {
btn.disabled = true;
try {
await fetch('/social/friends/reject', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ friendshipId })
});
document.getElementById('pending-' + friendshipId)?.remove();
await loadPending();
} catch (e) {
btn.disabled = false;
}
}
let pendingRemoveFriendshipId = null;
let pendingRemoveBtn = null;
function removeFriend(friendshipId, btn) {
pendingRemoveFriendshipId = friendshipId;
pendingRemoveBtn = btn;
document.getElementById('removeDialogText').textContent =
'Möchtest du ' + (btn.closest('.user-item').querySelector('.user-name')?.textContent || 'diese Person') + ' wirklich aus deiner Freundesliste entfernen?';
document.getElementById('removeDialog').classList.add('visible');
}
function closeRemoveDialog() {
document.getElementById('removeDialog').classList.remove('visible');
pendingRemoveFriendshipId = null;
pendingRemoveBtn = null;
}
async function confirmRemove() {
if (!pendingRemoveFriendshipId) return;
const btn = document.getElementById('removeConfirmBtn');
btn.disabled = true;
btn.textContent = '…';
try {
await fetch('/social/friends/reject', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ friendshipId: pendingRemoveFriendshipId })
});
document.getElementById('friend-' + pendingRemoveFriendshipId)?.remove();
const list = document.getElementById('friendsList');
if (list.children.length === 0) {
document.getElementById('friendsEmpty').style.display = '';
}
closeRemoveDialog();
} catch (e) {
if (pendingRemoveBtn) pendingRemoveBtn.disabled = false;
} finally {
btn.disabled = false;
btn.textContent = 'Entfernen';
}
}
document.getElementById('removeDialog').addEventListener('click', e => {
if (e.target === document.getElementById('removeDialog')) closeRemoveDialog();
});
loadFriends();
loadPending();
</script>
</body>
</html>