Retro Bulletin Board Systems on atproto. Web app and TUI. lazy mirror of alyraffauf/atbbs atbbs.xyz
forums python tui atproto bbs
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

web: use cdn with fallback

+37 -9
+1
core/shared.py
··· 29 29 LEXICON_COLLECTIONS: dict[str, str] = _DATA["lexicon_collections"] 30 30 OAUTH_BASE_SCOPES: list[str] = _DATA["oauth_base_scopes"] 31 31 SERVICES: dict[str, str] = _DATA["services"] 32 + CDN: dict[str, str] = _DATA["cdn"] 32 33 DEFAULT_BOARD: dict[str, str] = _DATA["default_board"] 33 34 HANDLE_PLACEHOLDERS: list[str] = _DATA["handle_placeholders"]
+4
data/shared.json
··· 24 24 "constellation": "https://constellation.microcosm.blue/xrpc", 25 25 "lightrail": "https://lightrail.microcosm.blue/xrpc" 26 26 }, 27 + "cdn": { 28 + "url": "https://cdn.bsky.app", 29 + "image_format": "webp" 30 + }, 27 31 "default_board": { 28 32 "slug": "general", 29 33 "name": "General",
+20 -6
web/src/components/post/PostBody.tsx
··· 1 1 import Markdown, { defaultUrlTransform } from "react-markdown"; 2 2 import type { Components } from "react-markdown"; 3 3 import AttachmentLink from "./AttachmentLink"; 4 - import { blobUrl } from "../../lib/atproto"; 4 + import { blobUrl, cdnImageUrl } from "../../lib/atproto"; 5 5 import type { PostAttachment } from "../../lib/bbs"; 6 6 7 7 interface PostBodyProps { ··· 28 28 ); 29 29 } 30 30 31 - function ImageEmbed({ url, alt }: { url: string; alt: string }) { 31 + function ImageEmbed({ 32 + imageUrl, 33 + linkUrl, 34 + alt, 35 + }: { 36 + imageUrl: string; 37 + linkUrl: string; 38 + alt: string; 39 + }) { 32 40 return ( 33 - <a href={url} target="_blank" rel="noreferrer"> 41 + <a href={linkUrl} target="_blank" rel="noreferrer"> 34 42 <img 35 - src={url} 43 + src={imageUrl} 36 44 alt={alt} 37 45 loading="lazy" 38 46 className="max-w-full max-h-96 rounded" ··· 63 71 const ref = findAttachment(src, attachments); 64 72 if (!ref) return <img src={src} alt={alt} />; 65 73 if (!ref.attachment) return <MissingAttachment name={ref.name} />; 66 - const url = blobUrl(pds, did, ref.attachment.file.ref.$link); 67 - return <ImageEmbed url={url} alt={alt ?? ref.name} />; 74 + const cid = ref.attachment.file.ref.$link; 75 + return ( 76 + <ImageEmbed 77 + imageUrl={cdnImageUrl(did, cid)} 78 + linkUrl={blobUrl(pds, did, cid)} 79 + alt={alt ?? ref.name} 80 + /> 81 + ); 68 82 }, 69 83 a({ href, children, ...rest }) { 70 84 const ref = findAttachment(href, attachments);
+6 -3
web/src/lib/atproto.ts
··· 1 1 /** Read-side wrappers for Slingshot and Constellation (no auth needed). */ 2 2 3 3 import { queryClient, STALE_SLOW } from "./queryClient"; 4 - import { SERVICES } from "./shared"; 4 + import { CDN, SERVICES } from "./shared"; 5 5 import { parseAtUri } from "./util"; 6 6 7 7 const SLINGSHOT = SERVICES.slingshot; 8 8 const CONSTELLATION = SERVICES.constellation; 9 9 10 - const BSKY_CDN = "https://cdn.bsky.app"; 11 10 const BSKY_PROFILE = "app.bsky.actor.profile"; 12 11 13 12 // --- Types --- ··· 45 44 46 45 export function blobUrl(pds: string, did: string, cid: string): string { 47 46 return `${pds}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid}`; 47 + } 48 + 49 + export function cdnImageUrl(did: string, cid: string): string { 50 + return `${CDN.url}/img/feed_fullsize/plain/${did}/${cid}@${CDN.image_format}`; 48 51 } 49 52 50 53 // --- Low-level JSON fetcher --- ··· 175 178 try { 176 179 const record = await getRecord(did, BSKY_PROFILE, "self"); 177 180 const cid = extractAvatarCid(record.value); 178 - return cid ? `${BSKY_CDN}/img/avatar/plain/${did}/${cid}` : null; 181 + return cid ? `${CDN.url}/img/avatar/plain/${did}/${cid}` : null; 179 182 } catch { 180 183 return null; 181 184 }
+6
web/src/lib/shared.ts
··· 21 21 lightrail: string; 22 22 } 23 23 24 + export interface Cdn { 25 + url: string; 26 + image_format: string; 27 + } 28 + 24 29 export interface DefaultBoard { 25 30 slug: string; 26 31 name: string; ··· 31 36 export const LEXICON_COLLECTIONS = 32 37 shared.lexicon_collections as LexiconCollections; 33 38 export const SERVICES = shared.services as Services; 39 + export const CDN = shared.cdn as Cdn; 34 40 export const DEFAULT_BOARD = shared.default_board as DefaultBoard; 35 41 export const HANDLE_PLACEHOLDERS = shared.handle_placeholders as string[];