Add a bsky post cid to have display comments directly in the blog post
.zed/settings.json
.zed/settings.json
This file has not been changed.
+24
-68
app/components/BskyComments.vue
+24
-68
app/components/BskyComments.vue
···
29
30
<template>
31
<div class="md:w-[80%] mx-auto mt-16">
32
-
<div class="flex items-baseline gap-4">
33
<h3 class="font-bold text-xl">Join the conversation!</h3>
34
-
<p class="text-gray-500 text-sm" title="Replies">
35
-
<Icon name="ri:reply-line" class="-mb-[2px] mr-1" />
36
-
{{post.post.replyCount}}
37
-
</p>
38
-
<p class="text-gray-500 text-sm" title="Likes">
39
-
<Icon name="ri:heart-3-line" class="-mb-[2px] mr-1" />
40
-
<span>
41
-
{{post.post.likeCount}}
42
-
</span>
43
-
</p>
44
-
<p class="text-gray-500 text-sm" title="Bookmarks">
45
-
<Icon name="ri:bookmark-line" class="-mb-[2px] mr-1" />
46
-
{{post.post.bookmarkCount}}
47
-
</p>
48
</div>
49
50
<p class="text-gray-600 text-md mb-6">
···
60
<div>No replies yet!</div>
61
</div>
62
63
-
<div v-else v-for="reply in post.replies" class="mt-6">
64
-
<BskyPost :post="reply" :depth="0" />
65
-
66
-
<!-- <a :href="`https://bsky.app/profile/${reply.post.author.handle}`" class="flex items-center gap-2 text-blue-500 hover:underline w-fit">
67
-
<img :src="reply.post.author.avatar" :alt="reply.post.author.displayName" class="size-8 rounded-full" />
68
-
<span>
69
-
{{ reply.post.author.displayName }}
70
-
</span>
71
-
</a>
72
-
<div class="ml-10">{{ reply.post.record.text }}</div>
73
-
<div class="flex items-baseline gap-4 ml-10 mt-2">
74
-
<p class="text-gray-500 text-sm" title="Replies">
75
-
<Icon name="ri:reply-line" class="-mb-[2px] mr-1" />
76
-
{{reply.post.replyCount}}
77
-
</p>
78
-
<p class="text-gray-500 text-sm" title="Likes">
79
-
<Icon name="ri:heart-3-line" class="-mb-[2px] mr-1" />
80
-
<span>
81
-
{{reply.post.likeCount}}
82
-
</span>
83
-
</p>
84
-
<p class="text-gray-500 text-sm" title="Bookmarks">
85
-
<Icon name="ri:bookmark-line" class="-mb-[2px] mr-1" />
86
-
{{reply.post.bookmarkCount}}
87
-
</p>
88
-
</div>
89
-
90
-
<div v-for="rep in reply.replies" class="mt-6 ml-10">
91
-
<a :href="`https://bsky.app/profile/${rep.post.author.handle}`" class="flex items-center gap-2 text-blue-500 hover:underline w-fit">
92
-
<img :src="rep.post.author.avatar" :alt="rep.post.author.displayName" class="size-8 rounded-full" />
93
-
<span>
94
-
{{ rep.post.author.displayName }}
95
-
</span>
96
-
</a>
97
-
<div class="ml-10">{{ rep.post.record.text }}</div>
98
-
<div class="flex items-baseline gap-4 ml-10 mt-2">
99
-
<p class="text-gray-500 text-sm" title="Replies">
100
-
<Icon name="ri:reply-line" class="-mb-[2px] mr-1" />
101
-
{{rep.post.replyCount}}
102
-
</p>
103
-
<p class="text-gray-500 text-sm" title="Likes">
104
-
<Icon name="ri:heart-3-line" class="-mb-[2px] mr-1" />
105
-
<span>
106
-
{{rep.post.likeCount}}
107
-
</span>
108
-
</p>
109
-
<p class="text-gray-500 text-sm" title="Bookmarks">
110
-
<Icon name="ri:bookmark-line" class="-mb-[2px] mr-1" />
111
-
{{rep.post.bookmarkCount}}
112
-
</p>
113
-
</div>
114
-
</div> -->
115
-
</div>
116
</div>
117
</div>
118
</template>
···
29
30
<template>
31
<div class="md:w-[80%] mx-auto mt-16">
32
+
<div class="flex items-baseline flex-col md:flex-row md:gap-4 mb-2 md:mb-0">
33
<h3 class="font-bold text-xl">Join the conversation!</h3>
34
+
<div class="flex items-center gap-2">
35
+
<p class="text-gray-500 text-sm" title="Replies">
36
+
<Icon name="ri:reply-line" class="-mb-[2px] mr-1" />
37
+
{{post.post.replyCount}}
38
+
</p>
39
+
<p class="text-gray-500 text-sm" title="Likes">
40
+
<Icon name="ri:heart-3-line" class="-mb-[2px] mr-1" />
41
+
<span>
42
+
{{post.post.likeCount}}
43
+
</span>
44
+
</p>
45
+
<p class="text-gray-500 text-sm" title="Bookmarks">
46
+
<Icon name="ri:bookmark-line" class="-mb-[2px] mr-1" />
47
+
{{post.post.bookmarkCount}}
48
+
</p>
49
+
</div>
50
</div>
51
52
<p class="text-gray-600 text-md mb-6">
···
62
<div>No replies yet!</div>
63
</div>
64
65
+
<BskyPost
66
+
v-else
67
+
v-for="reply in post.replies"
68
+
:key="reply.post.cid"
69
+
:post="reply"
70
+
:depth="0"
71
+
/>
72
</div>
73
</div>
74
</template>
+59
-32
app/components/BskyPost.vue
+59
-32
app/components/BskyPost.vue
···
8
}>();
9
const { post, depth } = toRefs(props);
10
11
-
const MAX_DEPTH = 2; // Max number of replies to a reply
12
</script>
13
14
<template>
15
-
<div v-if="post && depth <= MAX_DEPTH" :class="['mt-6', depth > 0 ? 'ml-10' : '']">
16
-
<a :href="`https://bsky.app/profile/${post.post.author.handle}`" class="flex items-center gap-2 text-blue-500 hover:underline w-fit">
17
<img :src="post.post.author.avatar" :alt="post.post.author.displayName" class="size-8 rounded-full" />
18
-
<span>
19
-
{{ post.post.author.displayName }}
20
-
</span>
21
</a>
22
-
<div class="ml-10">{{ post.post.record.text }}</div>
23
-
<div class="flex items-baseline gap-4 ml-10 mt-2">
24
-
<p class="text-gray-500 text-sm" title="Replies">
25
-
<Icon name="ri:reply-line" class="-mb-[2px] mr-1" />
26
-
{{post.post.replyCount}}
27
-
</p>
28
-
<p class="text-gray-500 text-sm" title="Likes">
29
-
<Icon name="ri:heart-3-line" class="-mb-[2px] mr-1" />
30
-
<span>
31
-
{{post.post.likeCount}}
32
-
</span>
33
-
</p>
34
-
<p class="text-gray-500 text-sm" title="Bookmarks">
35
-
<Icon name="ri:bookmark-line" class="-mb-[2px] mr-1" />
36
-
{{post.post.bookmarkCount}}
37
-
</p>
38
-
</div>
39
-
40
-
<div v-if="post.replies">
41
-
<div v-if="depth === MAX_DEPTH">
42
-
<a :href="`https://bsky.app/profile/${post.post.author.handle}/post/${extractPostId(post.post.uri)}`" class="text-gray-500 text-sm flex items-center gap-2 mt-4 ml-10">
43
-
View more replies on Bluesky
44
-
<Icon name='ri:arrow-drop-right-line' />
45
-
</a>
46
</div>
47
-
<div v-for="reply in post.replies">
48
-
<BskyPost v-if="reply.$type === 'app.bsky.feed.defs#threadViewPost'" :post="reply" :depth="depth + 1" />
49
</div>
50
</div>
51
</div>
52
</template>
···
8
}>();
9
const { post, depth } = toRefs(props);
10
11
+
const MAX_DEPTH = 3; // Max number of replies to a reply
12
+
13
+
console.log(post);
14
</script>
15
16
<template>
17
+
<div v-if="post && depth < MAX_DEPTH" class="mt-6 bsky-post">
18
+
<a :href="`https://bsky.app/profile/${post.post.author.handle}`">
19
<img :src="post.post.author.avatar" :alt="post.post.author.displayName" class="size-8 rounded-full" />
20
</a>
21
+
<div class="flex flex-col gap-1">
22
+
<a :href="`https://bsky.app/profile/${post.post.author.handle}`" class="text-md font-bold w-fit">
23
+
{{ post.post.author.displayName }}
24
+
</a>
25
+
<div>
26
+
{{ post.post.record.text }}
27
</div>
28
+
<div class="flex items-baseline gap-4 mt-2">
29
+
<p class="text-gray-500 text-sm" title="Replies">
30
+
<Icon name="ri:reply-line" class="-mb-[2px] mr-1" />
31
+
{{post.post.replyCount}}
32
+
</p>
33
+
<p class="text-gray-500 text-sm" title="Likes">
34
+
<Icon name="ri:heart-3-line" class="-mb-[2px] mr-1" />
35
+
<span>
36
+
{{post.post.likeCount}}
37
+
</span>
38
+
</p>
39
+
<p class="text-gray-500 text-sm" title="Bookmarks">
40
+
<Icon name="ri:bookmark-line" class="-mb-[2px] mr-1" />
41
+
{{post.post.bookmarkCount}}
42
+
</p>
43
</div>
44
+
45
+
<div v-if="post.replies && post.replies.length > 0">
46
+
<div v-if="depth+1 === MAX_DEPTH">
47
+
<a :href="`https://bsky.app/profile/${post.post.author.handle}/post/${extractPostId(post.post.uri)}`" class="text-gray-500 text-sm flex items-center gap-2 mt-4">
48
+
View more replies on Bluesky
49
+
<Icon name='ri:arrow-drop-right-line' />
50
+
</a>
51
+
</div>
52
+
<div v-else v-for="reply in post.replies">
53
+
<BskyPost v-if="reply.$type === 'app.bsky.feed.defs#threadViewPost'" :post="reply" :depth="depth + 1" />
54
+
</div>
55
+
</div>
56
</div>
57
</div>
58
</template>
59
+
60
+
<style scoped>
61
+
.bsky-post {
62
+
@apply gap-2 md:gap-4;
63
+
64
+
display: grid;
65
+
grid-template-columns: auto 1fr;
66
+
grid-template-areas:
67
+
"avatar content";
68
+
align-items: start;
69
+
70
+
& > a:has(> img) {
71
+
grid-area: avatar;
72
+
}
73
+
74
+
& > div {
75
+
grid-area: content;
76
+
}
77
+
78
+
}
79
+
</style>
+1
-1
app/pages/posts/[...slug].vue
+1
-1
app/pages/posts/[...slug].vue
app/util/atproto.ts
app/util/atproto.ts
This file has not been changed.
blog.config.ts
blog.config.ts
This file has not been changed.
content/posts/blog-template.md
content/posts/blog-template.md
This file has not been changed.
content/posts/embracing-atproto-pt-1-hosting-pds.md
content/posts/embracing-atproto-pt-1-hosting-pds.md
This file has not been changed.
content/posts/embracing-atproto-pt-2-tangled-knot.md
content/posts/embracing-atproto-pt-2-tangled-knot.md
This file has not been changed.
content/posts/extending-openauth.md
content/posts/extending-openauth.md
This file has not been changed.
content.config.ts
content.config.ts
This file has not been changed.
deno.jsonc
deno.jsonc
This file has not been changed.
globals.ts
globals.ts
This file has not been changed.
package.json
package.json
This file has not been changed.
pnpm-lock.yaml
pnpm-lock.yaml
This file has not been changed.
tsconfig.json
tsconfig.json
This file has not been changed.
History
5 rounds
1 comment
10 commits
expand
collapse
chore: add bsky post cid to content schema
chore: add deps
feat: first draft at bsky replies
fix: add loading state
feat: improve appearance
feat: add icons and bsky stats
feat: add bsky post CIDs to more blog posts
feat: show reply replies, max depth 2
fix: properly align elements
fix: max depth truncate + post elements alignment
expand 0 comments
pull request successfully merged
8 commits
expand
collapse
chore: add bsky post cid to content schema
chore: add deps
feat: first draft at bsky replies
fix: add loading state
feat: improve appearance
feat: add icons and bsky stats
feat: add bsky post CIDs to more blog posts
feat: show reply replies, max depth 2
Still needs some minor adjustments