[READ-ONLY] Mirror of https://github.com/flo-bit/room. tiny 3d rooms saved locally or in your bluesky account, svelte/threlte flo-bit.dev/room/
0

Configure Feed

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

room / src / lib / components / extra / post / index.ts
3.6 kB 158 lines
1import { RichText } from '@atproto/api'; 2 3export type PostEmbedImage = { 4 type: 'images'; 5 6 images: { 7 alt: string; 8 thumb: string; 9 fullsize: string; 10 aspectRatio?: { 11 width: number; 12 height: number; 13 }; 14 }[]; 15}; 16 17export type PostEmbedExternal = { 18 type: 'external'; 19 20 external: { 21 href: string; 22 thumb: string; 23 title: string; 24 description: string; 25 }; 26}; 27 28export type PostEmbedVideo = { 29 type: 'video'; 30 31 video: { 32 playlist: string; 33 34 thumb: string; 35 alt: string; 36 37 aspectRatio?: { 38 width: number; 39 height: number; 40 }; 41 }; 42}; 43 44export type UnknownEmbed = { 45 type: 'unknown'; 46} & Record<string, unknown>; 47 48function blueskyEmbedTypeToEmbedType(type: string) { 49 switch (type) { 50 case 'app.bsky.embed.external#view': 51 case 'app.bsky.embed.external': 52 return 'external'; 53 case 'app.bsky.embed.images#view': 54 case 'app.bsky.embed.images': 55 return 'images'; 56 case 'app.bsky.embed.video#view': 57 case 'app.bsky.embed.video': 58 return 'video'; 59 default: 60 return 'unknown'; 61 } 62} 63 64export type PostData = { 65 reposted?: { handle: string; href: string }; 66 67 author: { 68 displayName: string; 69 handle: string; 70 avatar: string; 71 href: string; 72 }; 73 74 replyCount: number; 75 repostCount: number; 76 likeCount: number; 77 78 createdAt: string; 79 80 embed?: PostEmbedImage | PostEmbedExternal | PostEmbedVideo | UnknownEmbed; 81 82 htmlContent?: string; 83}; 84 85// eslint-disable-next-line @typescript-eslint/no-explicit-any 86export function blueskyPostToPostData(post: any): PostData { 87 return { 88 author: { 89 displayName: post.author.displayName, 90 handle: post.author.handle, 91 avatar: post.author.avatar, 92 href: '#' 93 }, 94 replyCount: post.replyCount, 95 repostCount: post.repostCount, 96 likeCount: post.likeCount, 97 createdAt: post.createdAt, 98 99 embed: { 100 type: blueskyEmbedTypeToEmbedType(post.embed.$type), 101 // eslint-disable-next-line @typescript-eslint/no-explicit-any 102 images: post.embed.images?.map((image: any) => ({ 103 alt: image.alt, 104 thumb: image.thumb, 105 aspectRatio: image.aspectRatio, 106 fullsize: image.fullsize 107 })), 108 external: post.embed.external 109 ? { 110 href: post.embed.external.uri, 111 title: post.embed.external.title, 112 description: post.embed.external.description, 113 thumb: post.embed.external.thumb 114 } 115 : undefined, 116 video: post.embed 117 ? { 118 playlist: post.embed.playlist, 119 thumb: post.embed.thumbnail, 120 alt: post.embed.alt, 121 aspectRatio: post.embed.aspectRatio 122 } 123 : undefined 124 } as PostEmbedExternal | PostEmbedVideo | PostEmbedImage | UnknownEmbed, 125 126 htmlContent: blueskyPostToHTML(post) 127 }; 128} 129 130// eslint-disable-next-line @typescript-eslint/no-explicit-any 131export function blueskyPostToHTML(post: any, baseBskyUrl: string = 'https://bsky.app') { 132 if (!post?.record) { 133 return ''; 134 } 135 const rt = new RichText(post.record); 136 let html = ''; 137 138 const createLink = (href: string, text: string) => { 139 return `<a target="_blank" rel="noopener noreferrer nofollow" href="${encodeURI(href)}">${encodeURI(text)}</a>`; 140 }; 141 142 for (const segment of rt.segments()) { 143 if (!segment) continue; 144 if (segment.isLink() && segment.link?.uri) { 145 html += createLink(segment.link?.uri, segment.text); 146 } else if (segment.isMention() && segment.mention?.did) { 147 html += createLink(`${baseBskyUrl}/profile/${segment.mention?.did}`, segment.text); 148 } else if (segment.isTag() && segment.tag?.tag) { 149 html += createLink(`${baseBskyUrl}/hashtag/${segment.tag?.tag}`, segment.text); 150 } else { 151 html += segment.text; 152 } 153 } 154 155 return html.replace(/\n/g, '<br>'); 156} 157 158export { default as Post } from './Post.svelte';