Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 10 additions & 33 deletions app/components/BlueskyComment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ function getCommentUrl(comment: Comment): string {
}
const props = defineProps<{
comment: Comment
depth: number
isReply?: boolean
replyingTo?: string
}>()

const MaxDepth = 4

function getFeatureUrl(feature: RichtextFeature): string | undefined {
if (feature.$type === 'app.bsky.richtext.facet#link') return feature.uri
if (feature.$type === 'app.bsky.richtext.facet#mention')
Expand All @@ -41,7 +40,7 @@ function getHostname(uri: string): string {
</script>

<template>
<div :class="depth === 0 ? 'flex gap-3' : 'flex gap-3 mt-3'">
<div class="flex gap-3">
<!-- Avatar -->
<a
:href="`https://bsky.app/profile/${comment.author.handle}`"
Expand All @@ -53,7 +52,7 @@ function getHostname(uri: string): string {
v-if="comment.author.avatar"
:src="comment.author.avatar"
:alt="comment.author.displayName || comment.author.handle"
:class="['rounded-full', depth === 0 ? 'w-10 h-10' : 'w-8 h-8']"
:class="['rounded-full', isReply ? 'w-8 h-8' : 'w-10 h-10']"
width="40"
height="40"
loading="lazy"
Expand All @@ -62,14 +61,19 @@ function getHostname(uri: string): string {
v-else
:class="[
'rounded-full bg-border flex items-center justify-center text-fg-muted',
depth === 0 ? 'w-10 h-10' : 'w-8 h-8 text-sm',
isReply ? 'w-8 h-8 text-sm' : 'w-10 h-10',
]"
>
{{ (comment.author.displayName || comment.author.handle).charAt(0).toUpperCase() }}
</div>
</a>

<div class="flex-1 min-w-0">
<!-- Replying to label -->
<div v-if="replyingTo" class="text-xs text-fg-subtle mb-0.5">
{{ $t('blog.atproto.replying_to', { name: replyingTo }) }}
</div>

<!-- Author info + timestamp -->
<div class="flex flex-wrap items-baseline gap-x-2 gap-y-0">
<a
Expand Down Expand Up @@ -171,33 +175,6 @@ function getHostname(uri: string): string {
{{ $t('blog.atproto.repost_count', { count: comment.repostCount }, comment.repostCount) }}
</span>
</div>

<!-- Nested replies -->
<template v-if="comment.replies.length > 0">
<div v-if="depth < MaxDepth" class="mt-2 ps-2 border-is-2 border-border flex flex-col">
<BlueskyComment
v-for="reply in comment.replies"
:key="reply.uri"
:comment="reply"
:depth="depth + 1"
/>
</div>
<a
v-else
:href="getCommentUrl(comment.replies[0]!)"
target="_blank"
rel="noopener noreferrer"
class="mt-2 block text-sm link"
>
{{
$t(
'blog.atproto.more_replies',
{ count: comment.replies.length },
comment.replies.length,
)
}}
</a>
</template>
</div>
</div>
</template>
72 changes: 72 additions & 0 deletions app/components/BlueskyCommentThread.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<script setup lang="ts">
import type { Comment } from '#shared/types/blog-post'

const props = defineProps<{
comment: Comment
}>()

interface FlatReply {
comment: Comment
replyingTo?: string
}

function flattenReplies(comment: Comment): FlatReply[] {
const result: FlatReply[] = []
function walk(replies: Comment[], parentName: string, isDirectReply: boolean) {
for (const reply of replies) {
result.push({
comment: reply,
replyingTo: isDirectReply ? undefined : parentName,
})
if (reply.replies.length > 0) {
walk(reply.replies, reply.author.displayName || reply.author.handle, false)
}
}
}
walk(comment.replies, comment.author.displayName || comment.author.handle, true)
return result
}

const flatReplies = computed(() => flattenReplies(props.comment))
const totalReplyCount = computed(() => flatReplies.value.length)
const showReplies = ref(false)
</script>

<template>
<div>
<!-- Top-level comment -->
<BlueskyComment :comment="comment" />

<!-- Replies section -->
<div v-if="totalReplyCount > 0" class="ms-13 mt-2">
<!-- Toggle button -->
<button
v-if="!showReplies"
class="text-sm text-accent font-medium hover:underline cursor-pointer"
@click="showReplies = true"
>
{{ $t('blog.atproto.view_replies', { count: totalReplyCount }, totalReplyCount) }}
</button>

<!-- Expanded replies -->
<template v-else>
<button
class="text-sm text-accent font-medium hover:underline mb-3 cursor-pointer"
@click="showReplies = false"
>
{{ $t('blog.atproto.hide_replies') }}
</button>

<div class="flex flex-col gap-4">
<BlueskyComment
v-for="reply in flatReplies"
:key="reply.comment.uri"
:comment="reply.comment"
:replying-to="reply.replyingTo"
is-reply
/>
</div>
</template>
</div>
</div>
</template>
7 changes: 1 addition & 6 deletions app/components/BlueskyComments.vue
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,7 @@ const postUrl = computed(() => data.value?.postUrl)
</LinkBase>
</div>

<BlueskyComment
v-for="reply in thread.replies"
:key="reply.uri"
:comment="reply"
:depth="0"
/>
<BlueskyCommentThread v-for="reply in thread.replies" :key="reply.uri" :comment="reply" />

<LinkBase v-if="postUrl" variant="button-primary" :to="postUrl">
{{ $t('blog.atproto.like_or_reply_on_bluesky') }}
Expand Down
2 changes: 1 addition & 1 deletion app/components/Link/Base.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const keyboardShortcutsEnabled = useKeyboardShortcuts()
'text-xs px-2 py-0.5': isButtonSmall,
'bg-transparent text-fg hover:(bg-fg/10 text-accent) focus-visible:(bg-fg/10 text-accent) aria-[current=true]:(bg-fg/10 text-accent border-fg/20 hover:enabled:(bg-fg/20 text-fg/50))':
variant === 'button-secondary',
'text-bg bg-fg hover:(bg-fg/50 text-accent) focus-visible:(bg-fg/50) aria-current:(bg-fg text-bg border-fg hover:enabled:(text-bg/50))':
'text-bg bg-fg hover:(bg-fg/80 text-bg) focus-visible:(bg-fg/80 text-bg) aria-current:(bg-fg text-bg border-fg hover:enabled:(text-bg/50))':
variant === 'button-primary',
Comment thread
Adebesin-Cell marked this conversation as resolved.
Outdated
}"
:to="to"
Expand Down
4 changes: 2 additions & 2 deletions app/components/global/BlogPostFederatedArticles.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const federatedArticles = computed(() => {
</script>

<template>
<aside class="px-4 sm:-mx-6 sm:px-6 sm:-my-3 sm:py-3 sm:rounded-md">
<aside class="sm:-mx-6 sm:px-6 sm:-my-3 sm:py-3 sm:rounded-md">
<h2 class="font-mono text-xl font-medium text-fg mt-0">
{{ headline }}
</h2>
Expand All @@ -55,7 +55,7 @@ const federatedArticles = computed(() => {
rel="noopener noreferrer"
v-for="article in federatedArticles"
:key="article.url"
class="grid grid-cols-[auto_1fr] gap-x-5 no-underline hover:no-underline rounded-lg border border-border p-4 transition-shadow hover:shadow-lg hover:shadow-gray-500/50"
class="grid grid-cols-[auto_1fr] gap-x-5 no-underline hover:no-underline rounded-lg border border-border p-4 transition-all hover:shadow-md hover:shadow-black/5 dark:hover:shadow-white/5 hover:border-border-hover"
Comment thread
Adebesin-Cell marked this conversation as resolved.
Outdated
>
<AuthorAvatar v-if="article.author" :author="article.author" size="md" class="row-span-2" />
<div class="flex flex-col">
Expand Down
17 changes: 16 additions & 1 deletion app/components/global/BlogPostWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const blueskyPostUri = computed(() => blueskyLink.value?.postUri ?? null)
<AuthorList :authors="post.authors" variant="expanded" />
</div>
</div>
<article class="max-w-prose mx-auto p-2 prose dark:prose-invert">
<article class="max-w-prose mx-auto prose dark:prose-invert">
<div class="text-sm text-fg-muted font-mono mb-4">
<DateTime :datetime="frontmatter.date" year="numeric" month="short" day="numeric" />
</div>
Expand All @@ -77,4 +77,19 @@ const blueskyPostUri = computed(() => blueskyLink.value?.postUri ?? null)
:deep(.markdown-body) {
@apply prose dark:prose-invert;
}

:deep(.prose a:not(.not-prose a)) {
text-decoration: underline;
text-underline-offset: 0.2rem;
text-decoration-thickness: 1px;
text-decoration-color: var(--fg-subtle);
transition:
text-decoration-color 0.2s,
color 0.2s;
}
Comment thread
Adebesin-Cell marked this conversation as resolved.
Outdated

:deep(.prose a:not(.not-prose a):hover) {
text-decoration-color: var(--fg);
color: var(--fg);
}
</style>
14 changes: 8 additions & 6 deletions app/components/global/BlueskyPostEmbed.client.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,16 @@ const postUrl = computed(() => {
:href="postUrl ?? '#'"
target="_blank"
rel="noopener noreferrer"
class="not-prose block rounded-lg border border-border bg-bg-subtle p-4 sm:p-5 no-underline hover:border-border-hover transition-colors duration-200"
class="not-prose block rounded-lg border border-border bg-bg-subtle p-4 sm:p-5 no-underline hover:border-border-hover transition-colors duration-200 relative"
>
<!-- Bluesky icon -->
<span
class="i-simple-icons:bluesky w-5 h-5 text-fg-subtle absolute top-4 end-4 sm:top-5 sm:end-5"
aria-hidden="true"
/>
Comment thread
Adebesin-Cell marked this conversation as resolved.
Outdated

<!-- Author row -->
<div class="flex items-center gap-3 mb-3">
<div class="flex items-center gap-3 mb-3 pe-7">
<img
v-if="post.author.avatar"
:src="`${post.author.avatar}?size=48`"
Expand All @@ -126,10 +132,6 @@ const postUrl = computed(() => {
</div>
<div class="text-sm text-fg-subtle truncate">@{{ post.author.handle }}</div>
</div>
<span
class="i-simple-icons:bluesky w-5 h-5 text-fg-subtle ms-auto shrink-0"
aria-hidden="true"
/>
</div>

<!-- Post text -->
Expand Down
3 changes: 1 addition & 2 deletions i18n/locales/az-AZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@
"updating": "Yenilənir...",
"reply_count": "{count} cavab",
"like_count": "{count} bəyənmə",
"repost_count": "{count} yenidən paylaşım",
"more_replies": "{count} cavab daha..."
"repost_count": "{count} yenidən paylaşım"
}
},
"settings": {
Expand Down
3 changes: 1 addition & 2 deletions i18n/locales/cs-CZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@
"updating": "Aktualizace ...",
"reply_count": "{count} odpověď | {count} odpovědi | {count} odpovědí",
"like_count": "{count} lajk | {count} lajky | {count} lajků",
"repost_count": "{count} repost | {count} reposty | {count} repostů",
"more_replies": "{count} další odpověď ... | {count} další odpovědi ... | {count} dalších odpovědí ..."
"repost_count": "{count} repost | {count} reposty | {count} repostů"
}
},
"settings": {
Expand Down
3 changes: 1 addition & 2 deletions i18n/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@
"updating": "(wird aktualisiert...)",
"reply_count": "{count} Antwort | {count} Antworten",
"like_count": "{count} Like | {count} Likes",
"repost_count": "{count} Repost | {count} Reposts",
"more_replies": "{count} weitere Antwort anzeigen | {count} weitere Antworten anzeigen"
"repost_count": "{count} Repost | {count} Reposts"
}
},
"settings": {
Expand Down
4 changes: 3 additions & 1 deletion i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@
"reply_count": "{count} reply | {count} replies",
"like_count": "{count} like | {count} likes",
"repost_count": "{count} repost | {count} reposts",
"more_replies": "{count} more reply... | {count} more replies..."
"replying_to": "Replying to {name}",
"view_replies": "View {count} reply | View {count} replies",
"hide_replies": "Hide replies"
}
},
"settings": {
Expand Down
3 changes: 1 addition & 2 deletions i18n/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@
"updating": "Actualizando...",
"reply_count": "{count} respuesta | {count} respuestas",
"like_count": "{count} me gusta | {count} me gusta",
"repost_count": "{count} republicación | {count} republicaciones",
"more_replies": "{count} respuesta más... | {count} respuestas más..."
"repost_count": "{count} republicación | {count} republicaciones"
}
},
"settings": {
Expand Down
3 changes: 1 addition & 2 deletions i18n/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@
"updating": "Mise à jour...",
"reply_count": "{count} réponse | {count} réponses",
"like_count": "{count} j'aime | {count} j'aime",
"repost_count": "{count} repartage | {count} repartages",
"more_replies": "{count} réponse de plus... | {count} réponses de plus..."
"repost_count": "{count} repartage | {count} repartages"
}
},
"settings": {
Expand Down
3 changes: 1 addition & 2 deletions i18n/locales/id-ID.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@
"updating": "Memperbarui...",
"reply_count": "{count} balasan | {count} balasan",
"like_count": "{count} suka | {count} suka",
"repost_count": "{count} repost | {count} repost",
"more_replies": "{count} balasan lainnya... | {count} balasan lainnya..."
"repost_count": "{count} repost | {count} repost"
}
},
"settings": {
Expand Down
3 changes: 1 addition & 2 deletions i18n/locales/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@
"updating": "更新中...",
"reply_count": "{count} 件の返信",
"like_count": "{count} 件のいいね",
"repost_count": "{count} 件のリポスト",
"more_replies": "さらに {count} 件のリプライを表示..."
"repost_count": "{count} 件のリポスト"
}
},
"settings": {
Expand Down
3 changes: 1 addition & 2 deletions i18n/locales/pl-PL.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@
"updating": "Aktualizowanie...",
"reply_count": "{count} odpowiedzi | {count} odpowiedź | {count} odpowiedzi | {count} odpowiedzi | {count} odpowiedzi",
"like_count": "{count} polubień | {count} polubienie | {count} polubienia | {count} polubień | {count} polubień",
"repost_count": "{count} udostępnień | {count} udostępnienie | {count} udostępnienia | {count} udostępnień | {count} udostępnień",
"more_replies": "{count} odpowiedzi więcej... | {count} odpowiedź więcej... | {count} odpowiedzi więcej... | {count} odpowiedzi więcej... | {count} odpowiedzi więcej..."
"repost_count": "{count} udostępnień | {count} udostępnienie | {count} udostępnienia | {count} udostępnień | {count} udostępnień"
}
},
"settings": {
Expand Down
Loading
Loading