// ── Friends page ────────────────────────────────────────────────────────────── const addFriendModal = new bootstrap.Modal('#addFriendModal'); let _currentTab = 'friends'; // ── Tab switching ───────────────────────────────────────────────────────────── document.querySelectorAll('#friendTabs .nav-link').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('#friendTabs .nav-link').forEach(b => b.classList.remove('active')); btn.classList.add('active'); _currentTab = btn.dataset.tab; document.getElementById('tabFriends').classList.toggle('d-none', _currentTab !== 'friends'); document.getElementById('tabRequests').classList.toggle('d-none', _currentTab !== 'requests'); document.getElementById('tabSent').classList.toggle('d-none', _currentTab !== 'sent'); }); }); // ── Helpers ─────────────────────────────────────────────────────────────────── function initials(detail) { const name = detail?.display_name || detail?.username || '?'; return name.slice(0, 2).toUpperCase(); } function displayName(detail) { return esc(detail?.display_name || detail?.username || '—'); } // ── Friends ─────────────────────────────────────────────────────────────────── async function loadFriends() { try { const data = await apiGet('/social/friends/'); const grid = document.getElementById('friendsGrid'); const count = document.getElementById('friendsCount'); count.textContent = data.length; if (!data.length) { grid.innerHTML = '

No friends yet. Use "Add friend" to get started.

'; return; } // We need to know which side is "us" — resolved via unread-count trick: // actually we can figure it out since from_user or to_user is the current user. // But we don't have current user id here. Let's show both sides gracefully. grid.innerHTML = data.map(f => { // Show the "other" person — API returns both sides; we show both names const fromD = f.from_user_detail; const toD = f.to_user_detail; return `
${initials(fromD)}
${displayName(fromD)}
@${esc(fromD?.username || '—')}
${initials(toD)}
${displayName(toD)}
@${esc(toD?.username || '—')}
`; }).join(''); } catch (e) { document.getElementById('friendsGrid').innerHTML = '
Failed to load friends.
'; } } async function unfriend(id) { if (!confirm('Remove this friendship?')) return; try { await apiDelete(`/social/friends/${id}/`); showToast('Friendship removed.'); loadFriends(); } catch (e) { showToast('Failed to remove friendship.', 'danger'); } } // ── Incoming requests ───────────────────────────────────────────────────────── async function loadRequests() { try { const data = await apiGet('/social/friends/requests/'); const el = document.getElementById('requestsList'); const badge = document.getElementById('requestsCount'); if (data.length > 0) { badge.textContent = data.length; badge.classList.remove('d-none'); } else { badge.classList.add('d-none'); } if (!data.length) { el.innerHTML = '

No pending requests.

'; return; } el.innerHTML = data.map(f => `
${initials(f.from_user_detail)}
${displayName(f.from_user_detail)}
@${esc(f.from_user_detail?.username || '—')}
` ).join(''); } catch (e) { document.getElementById('requestsList').innerHTML = '
Failed to load requests.
'; } } async function acceptRequest(id) { try { await apiPost(`/social/friends/${id}/accept/`, {}); showToast('Friend request accepted!'); document.getElementById(`req-${id}`)?.remove(); loadFriends(); loadRequests(); } catch (e) { showToast('Failed to accept request.', 'danger'); } } async function declineRequest(id) { try { await apiPost(`/social/friends/${id}/decline/`, {}); showToast('Request declined.'); document.getElementById(`req-${id}`)?.remove(); loadRequests(); } catch (e) { showToast('Failed to decline request.', 'danger'); } } // ── Sent requests ───────────────────────────────────────────────────────────── async function loadSentRequests() { try { const data = await apiGet('/social/friends/sent-requests/'); const el = document.getElementById('sentList'); const badge = document.getElementById('sentCount'); if (data.length > 0) { badge.textContent = data.length; badge.classList.remove('d-none'); } else { badge.classList.add('d-none'); } if (!data.length) { el.innerHTML = '

No sent requests.

'; return; } el.innerHTML = data.map(f => `
${initials(f.to_user_detail)}
${displayName(f.to_user_detail)}
@${esc(f.to_user_detail?.username || '—')}
Pending
` ).join(''); } catch (e) { document.getElementById('sentList').innerHTML = '
Failed to load sent requests.
'; } } async function cancelRequest(id) { if (!confirm('Cancel this friend request?')) return; try { await apiPost(`/social/friends/${id}/decline/`, {}); showToast('Request cancelled.'); document.getElementById(`sent-${id}`)?.remove(); loadSentRequests(); } catch (e) { showToast('Failed to cancel request.', 'danger'); } } // ── Add friend modal ────────────────────────────────────────────────────────── document.getElementById('addFriendBtn').addEventListener('click', () => { document.getElementById('friendSearch').value = ''; document.getElementById('friendSearchResults').innerHTML = ''; document.getElementById('addAlert').classList.add('d-none'); addFriendModal.show(); setTimeout(() => document.getElementById('friendSearch').focus(), 300); }); let _friendSearchTimer = null; document.getElementById('friendSearch').addEventListener('input', function () { clearTimeout(_friendSearchTimer); const q = this.value.trim(); const resultsEl = document.getElementById('friendSearchResults'); if (q.length < 2) { resultsEl.innerHTML = ''; return; } _friendSearchTimer = setTimeout(async () => { try { const results = await apiGet(`/social/members/?q=${encodeURIComponent(q)}`); if (!results.length) { resultsEl.innerHTML = '
No users found.
'; return; } resultsEl.innerHTML = results.map(u => `
${esc(u.display_name || u.username)} @${esc(u.username)}
` ).join(''); } catch (e) { /* ignore */ } }, 300); }); async function sendRequest(userId, btn) { const alertEl = document.getElementById('addAlert'); alertEl.classList.add('d-none'); btn.disabled = true; btn.innerHTML = ''; try { await apiPost('/social/friends/', { to_user: userId }); btn.outerHTML = 'Request sent'; showToast('Friend request sent!'); loadSentRequests(); } catch (e) { if (e.status === 409) { btn.outerHTML = 'Already connected'; } else { alertEl.textContent = 'Failed to send request.'; alertEl.classList.remove('d-none'); btn.disabled = false; btn.innerHTML = 'Add'; } } } // ── Boot ────────────────────────────────────────────────────────────────────── loadFriends(); loadRequests(); loadSentRequests();