chore: refactored to use a tags
18b7b581
2 file(s) · +69 −73
| 57 | 57 | const post = record.value; |
|
| 58 | 58 | const createdAt = new Date(post.createdAt).toLocaleDateString(); |
|
| 59 | 59 | ||
| 60 | + | // Extract rkey from the record URI (format: at://did/collection/rkey) |
|
| 61 | + | const rkey = record.uri.split('/').pop(); |
|
| 62 | + | ||
| 60 | 63 | // Handle images |
|
| 61 | 64 | let imagesHTML = ''; |
|
| 62 | 65 | if (post.embed && post.embed.$type === 'app.bsky.embed.images' && post.embed.images) { |
|
| 86 | 89 | } |
|
| 87 | 90 | ||
| 88 | 91 | return ` |
|
| 89 | - | <article class="border-b pb-6 mb-6 last:border-b-0"> |
|
| 90 | - | <p class="mb-2">${post.text}</p> |
|
| 91 | - | ${imagesHTML} |
|
| 92 | - | <time class="text-sm text-gray-500 mt-2 block">${createdAt}</time> |
|
| 93 | - | </article> |
|
| 92 | + | <a href="/pds?rkey=${rkey}" class="block border-b pb-6 mb-6 last:border-b-0"> |
|
| 93 | + | <article> |
|
| 94 | + | <p class="mb-2">${post.text}</p> |
|
| 95 | + | ${imagesHTML} |
|
| 96 | + | <time class="text-sm text-gray-500 mt-2 block">${createdAt}</time> |
|
| 97 | + | </article> |
|
| 98 | + | </a> |
|
| 94 | 99 | `; |
|
| 95 | 100 | }).join(''); |
|
| 96 | 101 | ||
| 12 | 12 | </div> |
|
| 13 | 13 | ||
| 14 | 14 | <script> |
|
| 15 | - | import { AtpAgent } from '@atproto/api'; |
|
| 15 | + | const DID = 'did:plc:ia2zdnhjaokf5lazhxrmj6eu'; |
|
| 16 | + | const PDS_URL = 'https://polybius.social'; |
|
| 16 | 17 | ||
| 17 | 18 | const urlParams = new URLSearchParams(window.location.search); |
|
| 18 | 19 | const rkey = urlParams.get('rkey'); |
|
| 19 | - | const YOUR_DID = 'did:plc:ia2zdnhjaokf5lazhxrmj6eu'; |
|
| 20 | - | const atUri = `at://${YOUR_DID}/app.bsky.feed.post/${rkey}`; |
|
| 21 | 20 | ||
| 22 | - | const agent = new AtpAgent({ service: 'https://public.api.bsky.app' }); |
|
| 21 | + | async function fetchPost() { |
|
| 22 | + | if (!rkey) { |
|
| 23 | + | document.getElementById('post-container').innerHTML = '<p>No post specified.</p>'; |
|
| 24 | + | return; |
|
| 25 | + | } |
|
| 23 | 26 | ||
| 24 | - | function renderPost(post) { |
|
| 25 | - | const createdAt = new Date(post.record.createdAt).toLocaleDateString(); |
|
| 27 | + | try { |
|
| 28 | + | const response = await fetch( |
|
| 29 | + | `${PDS_URL}/xrpc/com.atproto.repo.getRecord?` + |
|
| 30 | + | new URLSearchParams({ |
|
| 31 | + | repo: DID, |
|
| 32 | + | collection: 'app.bsky.feed.post', |
|
| 33 | + | rkey: rkey |
|
| 34 | + | }) |
|
| 35 | + | ); |
|
| 26 | 36 | ||
| 27 | - | // Handle images and other embeds |
|
| 28 | - | let embedHTML = ''; |
|
| 29 | - | if (post.embed) { |
|
| 37 | + | if (!response.ok) { |
|
| 38 | + | throw new Error(`HTTP error! status: ${response.status}`); |
|
| 39 | + | } |
|
| 40 | + | ||
| 41 | + | const data = await response.json(); |
|
| 42 | + | const post = data.value; |
|
| 43 | + | const createdAt = new Date(post.createdAt).toLocaleDateString(); |
|
| 44 | + | ||
| 30 | 45 | // Handle images |
|
| 31 | - | if (post.embed.images) { |
|
| 32 | - | const imageElements = post.embed.images.map(image => ` |
|
| 33 | - | <img |
|
| 34 | - | src="${image.fullsize}" |
|
| 35 | - | alt="${image.alt || 'Image from post'}" |
|
| 36 | - | class="rounded-lg max-w-full h-auto cursor-pointer" |
|
| 37 | - | loading="lazy" |
|
| 38 | - | onclick="window.open('${image.fullsize}', '_blank')" |
|
| 39 | - | /> |
|
| 40 | - | `).join(''); |
|
| 46 | + | let imagesHTML = ''; |
|
| 47 | + | if (post.embed && post.embed.$type === 'app.bsky.embed.images' && post.embed.images) { |
|
| 48 | + | const imageElements = post.embed.images.map(image => { |
|
| 49 | + | // Construct blob URL - images are stored as blobs on the PDS |
|
| 50 | + | const blobUrl = `${PDS_URL}/xrpc/com.atproto.sync.getBlob?` + |
|
| 51 | + | new URLSearchParams({ |
|
| 52 | + | did: DID, |
|
| 53 | + | cid: image.image.ref.$link |
|
| 54 | + | }); |
|
| 41 | 55 | ||
| 42 | - | embedHTML = ` |
|
| 43 | - | <div class="mt-4 grid gap-2 ${post.embed.images.length === 1 ? 'grid-cols-1' : 'grid-cols-2'}"> |
|
| 56 | + | return ` |
|
| 57 | + | <img |
|
| 58 | + | src="${blobUrl}" |
|
| 59 | + | alt="${image.alt || 'Image from post'}" |
|
| 60 | + | class="max-w-full h-auto" |
|
| 61 | + | loading="lazy" |
|
| 62 | + | /> |
|
| 63 | + | `; |
|
| 64 | + | }).join(''); |
|
| 65 | + | ||
| 66 | + | imagesHTML = ` |
|
| 67 | + | <div class="mt-3 grid gap-2 ${post.embed.images.length === 1 ? 'grid-cols-1' : 'grid-cols-2'}"> |
|
| 44 | 68 | ${imageElements} |
|
| 45 | 69 | </div> |
|
| 46 | 70 | `; |
|
| 47 | 71 | } |
|
| 48 | - | // Handle external links |
|
| 49 | - | else if (post.embed.external) { |
|
| 50 | - | const external = post.embed.external; |
|
| 51 | - | embedHTML = ` |
|
| 52 | - | <a href="${external.uri}" target="_blank" rel="noopener noreferrer" |
|
| 53 | - | class="block mt-4 p-4 border rounded-lg hover:bg-gray-50 transition-colors"> |
|
| 54 | - | ${external.thumb ? `<img src="${external.thumb}" alt="Link preview" class="w-full h-32 object-cover rounded mb-3" />` : ''} |
|
| 55 | - | <div class="font-medium text-lg">${external.title || 'External Link'}</div> |
|
| 56 | - | ${external.description ? `<div class="text-sm text-gray-600 mt-1">${external.description}</div>` : ''} |
|
| 57 | - | <div class="text-xs text-gray-400 mt-2">${external.uri}</div> |
|
| 58 | - | </a> |
|
| 59 | - | `; |
|
| 60 | - | } |
|
| 61 | - | // Handle quoted posts |
|
| 62 | - | else if (post.embed.record) { |
|
| 63 | - | const quotedPost = post.embed.record; |
|
| 64 | - | embedHTML = ` |
|
| 65 | - | <div class="mt-4 p-4 border-l-4 border-blue-400 bg-gray-50 rounded"> |
|
| 66 | - | <div class="text-sm text-gray-600 mb-2">Quoting:</div> |
|
| 67 | - | <div>${quotedPost.value?.text || 'Quoted post'}</div> |
|
| 68 | - | <div class="text-xs text-gray-400 mt-2"> |
|
| 69 | - | ${quotedPost.value?.createdAt ? new Date(quotedPost.value.createdAt).toLocaleDateString() : ''} |
|
| 70 | - | </div> |
|
| 71 | - | </div> |
|
| 72 | - | `; |
|
| 73 | - | } |
|
| 74 | - | } |
|
| 75 | 72 | ||
| 76 | - | return ` |
|
| 77 | - | <article class="max-w-2xl mx-auto"> |
|
| 78 | - | <h1 class="mb-4">${post.record.text}</h1> |
|
| 79 | - | ${embedHTML} |
|
| 80 | - | <div class="mt-6 pt-4 border-t"> |
|
| 81 | - | <time class="text-sm text-gray-500">${createdAt}</time> |
|
| 82 | - | </div> |
|
| 83 | - | </article> |
|
| 84 | - | `; |
|
| 85 | - | } |
|
| 73 | + | document.getElementById('post-container').innerHTML = ` |
|
| 74 | + | <article class="max-w-2xl mx-auto"> |
|
| 75 | + | <p class="mb-2">${post.text}</p> |
|
| 76 | + | ${imagesHTML} |
|
| 77 | + | <time class="text-sm text-gray-500 mt-2 block">${createdAt}</time> |
|
| 78 | + | </article> |
|
| 79 | + | `; |
|
| 86 | 80 | ||
| 87 | - | agent.app.bsky.feed.getPostThread({ uri: atUri }) |
|
| 88 | - | .then(({ data }) => { |
|
| 89 | - | const post = data.thread.post; |
|
| 90 | - | ||
| 91 | - | document.getElementById('post-container').innerHTML = renderPost(post); |
|
| 92 | - | document.title = post.record.text; |
|
| 93 | - | }) |
|
| 94 | - | .catch(err => { |
|
| 81 | + | document.title = post.text; |
|
| 82 | + | } catch (err) { |
|
| 95 | 83 | console.error('Error fetching post:', err); |
|
| 96 | 84 | document.getElementById('post-container').innerHTML = |
|
| 97 | - | `<p class="text-red-600">Post not found: ${err.message}</p>`; |
|
| 98 | - | }); |
|
| 85 | + | '<p>Error loading post. Make sure your PDS is accessible.</p>'; |
|
| 86 | + | } |
|
| 87 | + | } |
|
| 88 | + | ||
| 89 | + | fetchPost(); |
|
| 99 | 90 | </script> |
|
| 100 | 91 | ||
| 101 | 92 | </PageLayout> |