A personal website powered by Astro and ATProto
at dev 115 lines 3.8 kB view raw
1--- 2import type { AStatusUpdateRecord } from '../../lib/generated/a-status-update'; 3import { AtprotoBrowser } from '../../lib/atproto/atproto-browser'; 4import { loadConfig } from '../../lib/config/site'; 5 6const config = loadConfig(); 7const client = new AtprotoBrowser(); 8 9// Fetch the latest status update 10let latestStatus: AStatusUpdateRecord | null = null; 11try { 12 const records = await client.getAllCollectionRecords(config.atproto.handle, 'a.status.update', 1); 13 14 if (records.length > 0) { 15 latestStatus = records[0].value as AStatusUpdateRecord; 16 } 17} catch (error) { 18 console.error('Failed to fetch status update:', error); 19} 20--- 21 22<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4" id="status-update-container"> 23 {latestStatus ? ( 24 <div class="space-y-2"> 25 <p class="text-lg font-medium text-gray-900 dark:text-white leading-relaxed" id="status-text"> 26 {latestStatus.text} 27 </p> 28 <time class="text-sm text-gray-500 dark:text-gray-400 block" id="status-time" datetime={latestStatus.createdAt}> 29 {new Date(latestStatus.createdAt).toLocaleDateString('en-US', { 30 year: 'numeric', 31 month: 'short', 32 day: 'numeric', 33 hour: '2-digit', 34 minute: '2-digit' 35 })} 36 </time> 37 </div> 38 ) : ( 39 <div class="text-center py-4" id="status-placeholder"> 40 <p class="text-gray-500 italic">No status updates available</p> 41 </div> 42 )} 43</div> 44 45<script> 46 import { startSharedStream, subscribeToStatusUpdates } from '../../lib/atproto/jetstream-client'; 47 48 // Start the shared stream 49 startSharedStream(); 50 51 // Subscribe to status updates 52 const unsubscribe = subscribeToStatusUpdates((event) => { 53 if (event.commit.operation === 'create') { 54 updateStatusDisplay(event.commit.record); 55 } 56 }); 57 58 function updateStatusDisplay(statusData: any) { 59 const container = document.getElementById('status-update-container'); 60 const textEl = document.getElementById('status-text'); 61 const timeEl = document.getElementById('status-time'); 62 const placeholderEl = document.getElementById('status-placeholder'); 63 64 if (!container) return; 65 66 // Remove placeholder if it exists 67 if (placeholderEl) { 68 placeholderEl.remove(); 69 } 70 71 // Update or create text element 72 if (textEl) { 73 textEl.textContent = statusData.text; 74 } else { 75 const newTextEl = document.createElement('p'); 76 newTextEl.className = 'text-lg font-medium text-gray-900 dark:text-white leading-relaxed'; 77 newTextEl.id = 'status-text'; 78 newTextEl.textContent = statusData.text; 79 container.appendChild(newTextEl); 80 } 81 82 // Update or create time element 83 const formattedTime = new Date(statusData.createdAt).toLocaleDateString('en-US', { 84 year: 'numeric', 85 month: 'short', 86 day: 'numeric', 87 hour: '2-digit', 88 minute: '2-digit' 89 }); 90 91 if (timeEl) { 92 timeEl.textContent = formattedTime; 93 timeEl.setAttribute('datetime', statusData.createdAt); 94 } else { 95 const newTimeEl = document.createElement('time'); 96 newTimeEl.className = 'text-sm text-gray-500 dark:text-gray-400 block'; 97 newTimeEl.id = 'status-time'; 98 newTimeEl.setAttribute('datetime', statusData.createdAt); 99 newTimeEl.textContent = formattedTime; 100 container.appendChild(newTimeEl); 101 } 102 103 // Add a subtle animation to indicate the update 104 container.style.transition = 'all 0.3s ease'; 105 container.style.transform = 'scale(1.02)'; 106 setTimeout(() => { 107 container.style.transform = 'scale(1)'; 108 }, 300); 109 } 110 111 // Cleanup on page unload 112 window.addEventListener('beforeunload', () => { 113 unsubscribe(); 114 }); 115</script>