render.js

 1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5export function getSortedItems(items) {
 6  return [...items].sort((a, b) => {
 7    const aVetoed = Object.values(a.votes).includes('veto');
 8    const bVetoed = Object.values(b.votes).includes('veto');
 9    if (aVetoed && !bVetoed) return 1;
10    if (!aVetoed && bVetoed) return -1;
11    const aScore = Object.values(a.votes).reduce((sum, v) => 
12      sum + (v === 'up' ? 1 : v === 'down' ? -1 : 0), 0);
13    const bScore = Object.values(b.votes).reduce((sum, v) => 
14      sum + (v === 'up' ? 1 : v === 'down' ? -1 : 0), 0);
15    return bScore - aScore;
16  });
17}
18
19export function escapeHtml(text) {
20  const div = document.createElement('div');
21  div.textContent = text;
22  return div.innerHTML;
23}
24
25export function updateConnectionStatus(status) {
26  const statusDot = document.querySelector('.status-dot');
27  const statusText = document.querySelector('.status-text');
28  
29  statusDot.className = 'status-dot ' + status;
30  
31  const statusLabels = {
32    connecting: 'Connecting...',
33    connected: 'Connected',
34    disconnected: 'Disconnected'
35  };
36  
37  statusText.textContent = statusLabels[status] || status;
38}
39
40function formatTimeSince(timestamp) {
41  const seconds = Math.floor((Date.now() - timestamp) / 1000);
42  
43  if (seconds < 5) return 'just now';
44  if (seconds < 60) return `${seconds}s ago`;
45  
46  const minutes = Math.floor(seconds / 60);
47  if (minutes < 60) return `${minutes}m ago`;
48  
49  const hours = Math.floor(minutes / 60);
50  if (hours < 24) return `${hours}h ago`;
51  
52  const days = Math.floor(hours / 24);
53  return `${days}d ago`;
54}
55
56export function updateLastSync(lastSyncTime) {
57  const lastSyncEl = document.querySelector('.last-sync');
58  if (!lastSyncTime) {
59    lastSyncEl.textContent = '';
60    return;
61  }
62  
63  lastSyncEl.textContent = '• ' + formatTimeSince(lastSyncTime);
64}
65
66export function setUIEnabled(enabled, listScreen, listContainer) {
67  const inputs = listScreen.querySelectorAll('input, textarea, button');
68  inputs.forEach(input => {
69    // Don't disable the leave button
70    if (input.id === 'leave-btn') return;
71    input.disabled = !enabled;
72  });
73  
74  if (enabled) {
75    listContainer.classList.remove('disabled');
76  } else {
77    listContainer.classList.add('disabled');
78  }
79}