chore: fixed now updates 58b705b0
Steve · 2026-01-09 00:25 1 file(s) · +63 −53
packages/client/src/components/NowUpdates.tsx +63 −53
10 10
const DID = "did:plc:ia2zdnhjaokf5lazhxrmj6eu";
11 11
const PDS_URL = "https://polybius.social";
12 12
13 -
interface Record {
13 +
interface Document {
14 14
	uri: string;
15 -
	value: any;
15 +
	value: {
16 +
		title: string;
17 +
		publishedAt: string;
18 +
		path: string;
19 +
		content?: {
20 +
			markdown?: string;
21 +
		};
22 +
		textContent?: string;
23 +
	};
16 24
}
17 25
18 26
export default function NowUpdates() {
19 -
	const [content, setContent] = useState<string>("<p>Loading...</p>");
27 +
	const [documents, setDocuments] = useState<Document[]>([]);
28 +
	const [loading, setLoading] = useState(true);
29 +
	const [error, setError] = useState(false);
20 30
21 31
	useEffect(() => {
22 32
		async function fetchPosts() {
23 33
			try {
24 -
				// Fetch site.standard.document records only
25 34
				const documentsData = await fetch(
26 35
					`${PDS_URL}/xrpc/com.atproto.repo.listRecords?` +
27 36
						new URLSearchParams({
33 42
					.then((res) => (res.ok ? res.json() : { records: [] }))
34 43
					.catch(() => ({ records: [] }));
35 44
36 -
				const documents: Record[] = (documentsData.records || []).sort(
37 -
					(a: Record, b: Record) => {
45 +
				const sortedDocuments: Document[] = (documentsData.records || []).sort(
46 +
					(a: Document, b: Document) => {
38 47
						const dateA = new Date(a.value.publishedAt);
39 48
						const dateB = new Date(b.value.publishedAt);
40 -
						return dateB.getTime() - dateA.getTime(); // Most recent first
49 +
						return dateB.getTime() - dateA.getTime();
41 50
					},
42 51
				);
43 52
44 -
				if (documents.length === 0) {
45 -
					setContent("<p>No recent updates found.</p>");
46 -
					return;
47 -
				}
48 -
49 -
				const postsHTML = documents
50 -
					.map((record) => {
51 -
						const value = record.value;
52 -
						const path = record.value.path.slice(1)
53 -
						const publishedAt = new Date(
54 -
							value.publishedAt,
55 -
						).toLocaleDateString();
56 -
57 -
						// Extract markdown content
58 -
						let contentHTML = "";
59 -
						if (value.content && value.content.markdown) {
60 -
							contentHTML = md.render(value.content.markdown);
61 -
						} else if (value.textContent) {
62 -
							contentHTML = `<p>${value.textContent}</p>`;
63 -
						}
64 -
65 -
						return `
66 -
              <a href="/now/${path}" class="block border-b pb-6 mb-6 last:border-b-0">
67 -
                <article>
68 -
                  <h3 class="text-lg font-semibold mb-3">${value.title}</h3>
69 -
                  <div class="prose prose-invert max-w-none mb-3">
70 -
                    ${contentHTML}
71 -
                  </div>
72 -
                  <div class="flex items-center gap-2 text-sm text-gray-500">
73 -
                    <time>${publishedAt}</time>
74 -
                  </div>
75 -
                </article>
76 -
              </a>
77 -
            `;
78 -
					})
79 -
					.join("");
80 -
81 -
				setContent(`
82 -
          <div class="space-y-4">
83 -
            <div>${postsHTML}</div>
84 -
          </div>
85 -
        `);
53 +
				setDocuments(sortedDocuments);
54 +
				setLoading(false);
86 55
			} catch (err) {
87 56
				console.error("Error fetching updates:", err);
88 -
				setContent(
89 -
					"<p>Error loading recent updates. Make sure your PDS is accessible.</p>",
90 -
				);
57 +
				setError(true);
58 +
				setLoading(false);
91 59
			}
92 60
		}
93 61
94 62
		fetchPosts();
95 63
	}, []);
96 64
97 -
	return <div dangerouslySetInnerHTML={{ __html: content }} />;
65 +
	if (loading) {
66 +
		return <p>Loading...</p>;
67 +
	}
68 +
69 +
	if (error) {
70 +
		return <p>Error loading recent updates. Make sure your PDS is accessible.</p>;
71 +
	}
72 +
73 +
	if (documents.length === 0) {
74 +
		return <p>No recent updates found.</p>;
75 +
	}
76 +
77 +
	return (
78 +
		<div className="space-y-4">
79 +
			{documents.map((record) => {
80 +
				const value = record.value;
81 +
				const path = value.path.slice(1);
82 +
				const publishedAt = new Date(value.publishedAt).toLocaleDateString();
83 +
84 +
				let contentHTML = "";
85 +
				if (value.content && value.content.markdown) {
86 +
					contentHTML = md.render(value.content.markdown).trim();
87 +
				} else if (value.textContent) {
88 +
					contentHTML = `<p>${value.textContent}</p>`;
89 +
				}
90 +
91 +
				return (
92 +
					<article key={record.uri} className="border-b pb-6 mb-6 last:border-b-0">
93 +
						<a href={`/now/${path}`} className="block hover:opacity-80 transition-opacity">
94 +
							<h3 className="text-lg font-semibold mb-3">{value.title}</h3>
95 +
						</a>
96 +
						<div
97 +
							className="prose prose-invert max-w-none mb-3"
98 +
							dangerouslySetInnerHTML={{ __html: contentHTML }}
99 +
						/>
100 +
						<div className="flex items-center gap-2 text-sm text-gray-500">
101 +
							<time>{publishedAt}</time>
102 +
						</div>
103 +
					</article>
104 +
				);
105 +
			})}
106 +
		</div>
107 +
	);
98 108
}