Personal Site
0
fork

Configure Feed

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

Add helper functions to transform data into the relevant embed structure for rendering

+106 -1
+106 -1
src/components/home/feeds/BskyPost.astro
··· 1 1 --- 2 - import { docResolver } from "./atproto"; 2 + import { client, docResolver } from "./atproto"; 3 3 import { 4 + is, 4 5 type Blob, 5 6 type LegacyBlob, 7 + type ResourceUri, 6 8 } from "@atcute/lexicons"; 7 9 import { AppBskyFeedPost } from "@atcute/bluesky"; 10 + import { throws } from "/utils"; 8 11 9 12 export interface Author { 10 13 did: `did:${string}:${string}`; ··· 87 90 | embedExternal 88 91 | embedRecord 89 92 | embedRecordWithMedia; 93 + 94 + // generate embed types 95 + const images = ( 96 + images: { alt: string; image: Blob | LegacyBlob }[], 97 + ): embedImages => ({ 98 + $type: "app.bsky.embed.images", 99 + images: images.map((image) => ({ 100 + alt: image.alt, 101 + cid: image.image, 102 + })), 103 + }); 104 + 105 + const video = ( 106 + video: Blob | LegacyBlob, 107 + alt: string | undefined, 108 + captions: 109 + | { 110 + file: Blob | LegacyBlob; 111 + lang: string; 112 + }[] 113 + | undefined, 114 + ): embedVideo => ({ 115 + $type: "app.bsky.embed.video", 116 + cid: video, 117 + alt: alt, 118 + subtitles: 119 + captions && 120 + captions.map((captions) => ({ 121 + cid: captions.file, 122 + lang: captions.lang, 123 + })), 124 + }); 125 + 126 + const external = (external: { 127 + title: string; 128 + uri: `${string}:${string}`; 129 + thumb?: Blob | LegacyBlob | undefined; 130 + }): embedExternal => ({ 131 + $type: "app.bsky.embed.external", 132 + title: external.title, 133 + url: external.uri, 134 + thumb: external.thumb, 135 + }); 136 + 137 + const record = async (uri: ResourceUri): Promise<embedRecord> => { 138 + const data = await client 139 + .get("app.bsky.feed.getPosts", { 140 + params: { 141 + uris: [uri], 142 + }, 143 + }) 144 + .then(({ ok, data }) => 145 + ok ? data : throws(data.error + "\n" + data.message), 146 + ); 147 + 148 + return { 149 + $type: "app.bsky.embed.record", 150 + record: is(AppBskyFeedPost.mainSchema, data.posts[0].record) 151 + ? data.posts[0].record 152 + : throws("Malformed embeded post"), 153 + author: { 154 + did: data.posts[0].author.did, 155 + displayName: data.posts[0].author.displayName, 156 + handle: data.posts[0].author.handle, 157 + avatar: data.posts[0].author.avatar, 158 + }, 159 + }; 160 + }; 161 + 162 + const recordWithMedia = async ( 163 + uri: ResourceUri, 164 + media: 165 + | { 166 + $type: "app.bsky.embed.images"; 167 + images: { alt: string; image: Blob | LegacyBlob }[]; 168 + } 169 + | { 170 + $type: "app.bsky.embed.video"; 171 + video: Blob | LegacyBlob; 172 + alt?: string | undefined; 173 + captions?: { lang: string; file: Blob | LegacyBlob }[] | undefined; 174 + } 175 + | { 176 + $type: "app.bsky.embed.external"; 177 + external: { 178 + title: string; 179 + uri: `${string}:${string}`; 180 + thumb?: Blob | LegacyBlob | undefined; 181 + }; 182 + }, 183 + ): Promise<embedRecordWithMedia> => { 184 + return { 185 + $type: "app.bsky.embed.recordWithMedia", 186 + record: await record(uri), 187 + media: 188 + media.$type === "app.bsky.embed.images" 189 + ? images(media.images) 190 + : media.$type === "app.bsky.embed.video" 191 + ? video(media.video, media.alt, media.captions) 192 + : external(media.external), 193 + }; 194 + }; 90 195 ---