// ── 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 `
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 = '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 = '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)}
Add
`
).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 = '