Personal blog finxol.io
blog

chore: improve typing and documentation

authored by finxol.io and committed by

Tangled 432c44ee 299eedbb

+26 -10
+13 -9
app/components/BskyComments.vue
··· 1 1 <script setup lang="ts"> 2 - import { getBskyReplies, type ReplyThread } from "~/util/atproto"; 2 + import type { AppBskyFeedDefs } from "@atcute/bluesky"; 3 + import { getBskyReplies } from "~/util/atproto"; 3 4 4 5 const props = defineProps({ 5 6 cid: { ··· 11 12 12 13 const data = ref(await getBskyReplies(cid.value)); 13 14 const err = ref(""); 14 - const post = ref(); 15 + const post = ref<AppBskyFeedDefs.ThreadViewPost>(); 15 16 16 17 if (data.value.$type === "app.bsky.feed.defs#blockedPost") { 17 18 err.value = "Post is blocked"; ··· 23 24 24 25 if (data.value.$type === "app.bsky.feed.defs#threadViewPost") { 25 26 console.log(data.value); 26 - post.value = data.value; 27 + post.value = data.value as AppBskyFeedDefs.ThreadViewPost; 27 28 } 28 29 </script> 29 30 ··· 31 32 <div class="md:w-[80%] mx-auto mt-16"> 32 33 <div class="flex items-baseline flex-col md:flex-row md:gap-4 mb-2 md:mb-0"> 33 34 <h3 class="font-bold text-xl">Join the conversation!</h3> 34 - <div class="flex items-center gap-2"> 35 + <div v-if="post" class="flex items-center gap-6"> 35 36 <p class="text-gray-500 text-sm" title="Replies"> 36 37 <Icon name="ri:reply-line" class="-mb-[2px] mr-1" /> 37 38 {{post.post.replyCount}} ··· 51 52 </div> 52 53 </div> 53 54 54 - <p class="text-gray-600 text-md mb-6"> 55 + <div v-if="err"> 56 + <p class="mt-2 text-gray-700 dark:text-gray-500"> 57 + {{ err }} 58 + </p> 59 + </div> 60 + 61 + <p v-if="post" class="text-gray-600 dark:text-gray-500 text-md mb-6"> 55 62 <a class="underline" :href="`https://bsky.app/profile/${post.post.author.handle}/post/${cid}`">Reply on Bluesky</a> to take part in the discussion. 56 63 </p> 57 64 58 - <div v-if="err"> 59 - <div>{{ err }}</div> 60 - </div> 61 65 62 66 <div v-if="post"> 63 67 <div v-if="post.post.replyCount === 0"> ··· 66 70 67 71 <BskyPost 68 72 v-else 69 - v-for="reply in post.replies" 73 + v-for="reply in post.replies?.filter(reply => reply.$type === 'app.bsky.feed.defs#threadViewPost')" 70 74 :key="reply.post.cid" 71 75 :post="reply" 72 76 :depth="0"
+13 -1
app/util/atproto.ts
··· 5 5 import config from "@/../blog.config"; 6 6 7 7 const handler = simpleFetchHandler({ 8 + // Simply hit up the Bluesky API 8 9 service: "https://public.api.bsky.app" 9 10 }); 10 11 const rpc = new Client({ handler }); ··· 14 15 | AppBskyFeedDefs.BlockedPost 15 16 | AppBskyFeedDefs.NotFoundPost; 16 17 18 + /** 19 + * Fetch the first 10 replies to a post 20 + * @param cid 21 + * @returns 22 + */ 17 23 export async function getBskyReplies(cid: string) { 18 24 // uri should be in format: at://did:plc:xxx/app.bsky.feed.post/xxxxx 19 25 const uri: ResourceUri = `at://${config.authorDid}/app.bsky.feed.post/${cid}`; ··· 21 27 const { ok, data } = await rpc.get("app.bsky.feed.getPostThread", { 22 28 params: { 23 29 uri, 24 - depth: 10 30 + depth: 6 // default 25 31 } 26 32 }); 27 33 28 34 if (!ok) { 35 + // Handle fetch errors as 'not found'. Could be cleaner, but works just fine. 29 36 console.error("Error fetching thread:", data.error); 30 37 return { $type: "app.bsky.feed.defs#notFoundPost" }; 31 38 } ··· 37 44 return { $type: "app.bsky.feed.defs#notFoundPost" }; 38 45 } 39 46 47 + /** 48 + * Extract post id from an atproto uri 49 + * @param uri The atproto uri, such as at://did:plc:user/app.bsky.feed.post/xxxxx` 50 + * @returns The post id 51 + */ 40 52 export function extractPostId(uri: ResourceUri) { 41 53 if (uri.includes("app.bsky.feed.post")) { 42 54 const parts = uri.split("/");