A personal website powered by Astro and ATProto
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>