Something went wrong. Try again.
Central platform for European atproto.<cc> country community websites atproto.eu
community atproto
Something went wrong. Try again.
5.7 kB · 149 lines
Astro
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150---import type { FeedPost } from '../lib/atproto/types';
interface Props { post: FeedPost;}
const { post } = Astro.props;// Community post permalinks open in mu.social (same path shape as bsky.app).const muPost = (handle: string, uri: string) => `https://mu.social/profile/${handle}/post/${uri.split('/').pop()}`;const postUrl = muPost(post.author.handle, post.uri);// Dutch date format (DD-MM-YYYY) on both language sides — Netherlands community site.const date = new Date(post.createdAt).toLocaleDateString('nl-NL', { day: '2-digit', month: '2-digit', year: 'numeric',});
const ext = post.embed?.external;let extHost = '';try { if (ext) extHost = new URL(ext.uri).hostname.replace(/^www\./, '');} catch { extHost = '';}const quote = post.embed?.quote;const quoteUrl = quote ? muPost(quote.handle, quote.uri) : undefined;---
<article class="post-card"> <div class="post-head"> { post.author.avatar && ( <img class="post-avatar" src={post.author.avatar} alt="" width="32" height="32" loading="lazy" /> ) } <span class="post-name">{post.author.displayName ?? post.author.handle}</span> <span class="post-handle">@{post.author.handle}</span> </div>
{post.text && <p class="post-text">{post.text}</p>}
{ post.embed?.images?.length && ( <div class="post-images" data-count={post.embed.images.length}> {post.embed.images.map((im) => ( <img src={im.thumb} alt={im.alt} loading="lazy" /> ))} </div> ) }
{ ext && (ext.gif ? ( <img class="post-gif" src={ext.uri} alt={ext.title} loading="lazy" /> ) : ( <a class="post-external" href={ext.uri} rel="noopener"> {ext.thumb && <img src={ext.thumb} alt="" loading="lazy" />} <span class="ext-body"> <span class="ext-title">{ext.title || ext.uri}</span> {ext.description && <span class="ext-desc">{ext.description}</span>} {extHost && <span class="ext-host">{extHost}</span>} </span> </a> )) }
{ quote && ( <a class="post-quote" href={quoteUrl} rel="noopener"> <span class="quote-head"> {quote.avatar && <img src={quote.avatar} alt="" width="20" height="20" loading="lazy" />} <span class="quote-name">{quote.displayName ?? quote.handle}</span> <span class="quote-handle">@{quote.handle}</span> </span> {quote.text && <span class="quote-text">{quote.text}</span>} {quote.images?.length && ( <span class="quote-images" data-count={quote.images.length}> {quote.images.map((im) => ( <img src={im.thumb} alt={im.alt} loading="lazy" /> ))} </span> )} {quote.external?.gif ? ( <img class="quote-gif" src={quote.external.uri} alt={quote.external.title} loading="lazy" /> ) : quote.external ? ( <span class="quote-external"> {quote.external.thumb && <img src={quote.external.thumb} alt="" loading="lazy" />} <span class="quote-ext-title">{quote.external.title || quote.external.uri}</span> </span> ) : null} </a> ) }
<div class="post-foot"> <a class="post-date" href={postUrl} rel="noopener" data-ts={post.createdAt} title={date}>{date}</a>
<a class="post-act" href={postUrl} rel="noopener" data-count={post.replyCount} aria-label="Reply"> <svg class="act-ico" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M21 11.5a8.4 8.4 0 0 1-8.5 8.5 8.5 8.5 0 0 1-3.8-.9L3 21l1.9-5.7A8.4 8.4 0 0 1 4 11.5 8.5 8.5 0 0 1 12.5 3 8.4 8.4 0 0 1 21 11.5z" /> </svg> <span class="act-count">{post.replyCount}</span> </a>
<button class="post-act post-repost" type="button" data-uri={post.uri} data-cid={post.cid} data-url={postUrl} data-count={post.repostCount} aria-label="Repost" > <svg class="act-ico" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M17 1l4 4-4 4" /> <path d="M3 11V9a4 4 0 0 1 4-4h14" /> <path d="M7 23l-4-4 4-4" /> <path d="M21 13v2a4 4 0 0 1-4 4H3" /> </svg> <span class="act-count">{post.repostCount}</span> </button>
<a class="post-act" href={postUrl} rel="noopener" data-count={post.quoteCount} aria-label="Quote"> <svg class="act-ico" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 4h16v11H8l-4 4z" /> <path d="M9.2 8.4c-.9 0-1.6.7-1.6 1.6s.7 1.6 1.6 1.6c0 1-.5 1.6-1.1 1.9M14.6 8.4c-.9 0-1.6.7-1.6 1.6s.7 1.6 1.6 1.6c0 1-.5 1.6-1.1 1.9" /> </svg> <span class="act-count">{post.quoteCount}</span> </a>
<button class="post-act post-like" type="button" data-uri={post.uri} data-cid={post.cid} data-url={postUrl} data-count={post.likeCount} aria-label="Like" > <svg class="act-ico" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M20.8 5.6a5 5 0 0 0-7.1 0L12 7.3l-1.7-1.7a5 5 0 1 0-7.1 7.1L12 21l8.8-8.3a5 5 0 0 0 0-7.1z" /> </svg> <span class="act-count">{post.likeCount}</span> </button> </div></article>