A community-maintained directory of Bluesky Personal Data Servers (PDS). pdslist.wisp.place
at main 6.6 kB view raw
1// Load PDS data and populate table 2document.addEventListener('DOMContentLoaded', function() { 3 // Check if mobile and show popup 4 if (window.innerWidth < 768) { 5 document.getElementById('mobile-popup').style.display = 'flex'; 6 } 7 8 // Close mobile popup button 9 document.getElementById('close-mobile-popup').addEventListener('click', function() { 10 document.getElementById('mobile-popup').style.display = 'none'; 11 }); 12 13 // Search input 14 document.getElementById('search-input').addEventListener('input', filterData); 15 16 // Invite filter radios 17 document.querySelectorAll('input[name="invite-filter"]').forEach(radio => { 18 radio.addEventListener('change', filterData); 19 }); 20 21 loadPDSData(); 22}); 23 24// Store all PDS data globally for filtering 25let allPDSData = []; 26 27async function loadPDSData() { 28 const loading = document.getElementById('loading'); 29 const content = document.getElementById('content'); 30 const tableBody = document.getElementById('table-body'); 31 const noData = document.getElementById('no-data'); 32 const serverCount = document.getElementById('server-count'); 33 34 try { 35 const response = await fetch('./pdslist.json'); 36 const pdsData = await response.json(); 37 allPDSData = pdsData; // Store for filtering 38 39 // Hide loading 40 loading.style.display = 'none'; 41 content.style.display = 'block'; 42 43 if (pdsData.length === 0) { 44 noData.style.display = 'block'; 45 return; 46 } 47 48 // Update server count 49 serverCount.textContent = pdsData.length; 50 51 // Populate table 52 pdsData.forEach(pds => { 53 const row = createTableRow(pds); 54 tableBody.appendChild(row); 55 }); 56 57 // Set initial filtered count to total 58 document.getElementById('filtered-count').textContent = pdsData.length; 59 60 } catch (error) { 61 console.error('Error loading PDS data:', error); 62 loading.innerHTML = '<p style="color: red;">Error loading database. Please check console.</p>'; 63 } 64} 65 66function filterData() { 67 const searchTerm = document.getElementById('search-input').value.toLowerCase(); 68 const inviteFilter = document.querySelector('input[name="invite-filter"]:checked').value; 69 70 const tableBody = document.getElementById('table-body'); 71 const rows = tableBody.querySelectorAll('tr'); 72 let visibleCount = 0; 73 74 rows.forEach((row, index) => { 75 const pds = allPDSData[index]; 76 if (shouldShowRow(pds, searchTerm, inviteFilter)) { 77 row.style.display = ''; 78 visibleCount++; 79 } else { 80 row.style.display = 'none'; 81 } 82 }); 83 84 // Update the filtered count 85 document.getElementById('filtered-count').textContent = visibleCount; 86} 87 88function shouldShowRow(pds, searchTerm, inviteFilter) { 89 // Apply search filter 90 const searchMatch = !searchTerm || 91 pds.url.toLowerCase().includes(searchTerm) || 92 (pds.supportedHandles && pds.supportedHandles.some(h => h.toLowerCase().includes(searchTerm))) || 93 (pds.maintainer && pds.maintainer.toLowerCase().includes(searchTerm)) || 94 (pds.contactEmail && pds.contactEmail.toLowerCase().includes(searchTerm)); 95 96 // Apply invite filter 97 const inviteMatch = inviteFilter === 'all' || 98 (inviteFilter === 'yes' && pds.inviteCodeRequired) || 99 (inviteFilter === 'no' && !pds.inviteCodeRequired); 100 101 return searchMatch && inviteMatch; 102} 103 104function createTableRow(pds) { 105 const row = document.createElement('tr'); 106 107 // URL 108 const urlCell = document.createElement('td'); 109 const urlLink = document.createElement('p'); 110 // sanitize URL by removing any '@' characters 111 const safeUrl = pds.url ? pds.url.replace(/@/g, '') : ''; 112 urlLink.href = safeUrl; 113 urlLink.target = '_blank'; 114 urlLink.textContent = safeUrl; 115 urlCell.appendChild(urlLink); 116 row.appendChild(urlCell); 117 118 // Handles 119 const handlesCell = document.createElement('td'); 120 if (pds.supportedHandles && pds.supportedHandles.length > 0) { 121 handlesCell.textContent = pds.supportedHandles.join(', '); 122 } else { 123 handlesCell.textContent = 'N/A'; 124 } 125 row.appendChild(handlesCell); 126 127 // Maintainer 128 const maintainerCell = document.createElement('td'); 129 if (pds.maintainer) { 130 const link = document.createElement('a'); 131 // sanitize maintainer by removing any '@' characters before using in URL and display 132 const safeMaintainer = pds.maintainer.replace(/@/g, ''); 133 link.href = `https://madebydanny.uk/followonbsky.html?did=${encodeURIComponent(safeMaintainer)}`; 134 link.target = '_blank'; 135 link.textContent = safeMaintainer; 136 maintainerCell.appendChild(link); 137 } else { 138 maintainerCell.textContent = '—'; 139 } 140 row.appendChild(maintainerCell); 141 142 // Server Location 143 const locationCell = document.createElement('td'); 144 if (pds.serverLocation) { 145 locationCell.textContent = pds.serverLocation; 146 } else { 147 locationCell.textContent = '—'; 148 } 149 row.appendChild(locationCell); 150 151 // Contact Email 152 const emailCell = document.createElement('td'); 153 if (pds.contactEmail) { 154 const link = document.createElement('a'); 155 link.href = `mailto:${pds.contactEmail}`; 156 link.textContent = pds.contactEmail; 157 emailCell.appendChild(link); 158 } else { 159 emailCell.textContent = '—'; 160 } 161 row.appendChild(emailCell); 162 163 // Invite Code Required 164 const inviteCell = document.createElement('td'); 165 inviteCell.textContent = pds.inviteCodeRequired ? 'Yes' : 'No'; 166 row.appendChild(inviteCell); 167 168 // Terms of Service 169 const tosCell = document.createElement('td'); 170 if (pds.tosUrl) { 171 const link = document.createElement('a'); 172 link.href = pds.tosUrl; 173 link.target = '_blank'; 174 link.rel = 'noopener noreferrer'; 175 link.textContent = 'Link'; 176 tosCell.appendChild(link); 177 } else { 178 tosCell.textContent = '—'; 179 } 180 row.appendChild(tosCell); 181 182 // Privacy Policy 183 const privacyCell = document.createElement('td'); 184 if (pds.privacyUrl) { 185 const link = document.createElement('a'); 186 link.href = pds.privacyUrl; 187 link.target = '_blank'; 188 link.rel = 'noopener noreferrer'; 189 link.textContent = 'Link'; 190 privacyCell.appendChild(link); 191 } else { 192 privacyCell.textContent = '—'; 193 } 194 row.appendChild(privacyCell); 195 196 return row; 197}