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
29
30
30
<template>
31
31
<div class="md:w-[80%] mx-auto mt-16">
32
-
<div class="flex items-baseline gap-4">
32
+
<div class="flex items-baseline flex-col md:flex-row md:gap-4 mb-2 md:mb-0">
33
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>
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>
48
50
</div>
49
51
50
52
<p class="text-gray-600 text-md mb-6">
···
60
62
<div>No replies yet!</div>
61
63
</div>
62
64
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>
65
+
<BskyPost
66
+
v-else
67
+
v-for="reply in post.replies"
68
+
:key="reply.post.cid"
69
+
:post="reply"
70
+
:depth="0"
71
+
/>
116
72
</div>
117
73
</div>
118
74
</template>
+59
-32
app/components/BskyPost.vue
+59
-32
app/components/BskyPost.vue
···
8
8
}>();
9
9
const { post, depth } = toRefs(props);
10
10
11
-
const MAX_DEPTH = 2; // Max number of replies to a reply
11
+
const MAX_DEPTH = 3; // Max number of replies to a reply
12
+
13
+
console.log(post);
12
14
</script>
13
15
14
16
<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
+
<div v-if="post && depth < MAX_DEPTH" class="mt-6 bsky-post">
18
+
<a :href="`https://bsky.app/profile/${post.post.author.handle}`">
17
19
<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
20
</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>
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 }}
46
27
</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" />
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>
49
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>
50
56
</div>
51
57
</div>
52
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
···
95
95
<BskyComments v-if="post.bskyCid" :cid="post.bskyCid" />
96
96
97
97
<template #fallback>
98
-
<h1 class="text-xl font-bold text-stone-600">Loading comments...</h1>
98
+
<h1 class="md:w-[80%] mx-auto mt-16 text-xl font-bold text-stone-600">Loading comments...</h1>
99
99
</template>
100
100
</Suspense>
101
101
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