Skip to content

Commit 38742a3

Browse files
committed
fix: implemented Salma's feedback
1 parent 37b7af4 commit 38742a3

12 files changed

Lines changed: 114 additions & 160 deletions

File tree

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
<script setup lang="ts">
2+
import type { FederatedArticleInput } from '#shared/types/blog-post'
3+
24
const props = defineProps<{
35
headline: string
4-
articles: {
5-
url: string
6-
// WARN: Must not contain @ symbol prefix
7-
authorHandle: string
8-
}[]
6+
articles: FederatedArticleInput[]
97
}>()
108
119
const contentKey = computed(() => props.articles.map(a => a.url).join('-'))
@@ -25,34 +23,36 @@ const { data: federatedArticles, status } = await useAsyncData(
2523
<h2 class="font-mono text-xl font-medium text-fg">
2624
{{ headline }}
2725
</h2>
28-
<div
26+
<section
2927
v-if="federatedArticles?.length"
3028
class="grid gap-4 grid-cols-[repeat(auto-fit,minmax(14rem,1fr))] transition-[grid-template-cols]"
3129
>
32-
<section
30+
<a
31+
:href="article.url"
32+
target="_blank"
33+
rel="noopener noreferrer"
3334
v-for="article in federatedArticles"
3435
:key="article.url"
35-
class="rounded-lg border border-border p-2 transition-shadow hover:shadow-lg hover:shadow-gray-500/50"
36+
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"
3637
>
37-
<a
38-
:href="article.url"
39-
target="_blank"
40-
rel="noopener noreferrer"
41-
class="grid grid-cols-[auto_1fr] items-center gap-x-5 no-underline hover:no-underline"
42-
>
43-
<AuthorAvatar
44-
v-if="article?.author"
45-
:author="article.author"
46-
size="md"
47-
class="row-span-2"
48-
/>
49-
<div class="flex flex-col gap-y-4">
50-
<p class="text-lg font-bold text-fg uppercase line-clamp-2 m-0">{{ article.title }}</p>
51-
<p class="text-md font-semibold text-fg-muted m-0">{{ article.author.name }}</p>
52-
<p class="text-xs line-clamp-1 text-fg-subtle m-0">{{ article.description }}</p>
53-
</div>
54-
</a>
55-
</section>
56-
</div>
38+
<AuthorAvatar
39+
v-if="article?.author"
40+
:author="article.author"
41+
size="md"
42+
class="row-span-2"
43+
/>
44+
<div class="flex flex-col">
45+
<p class="text-lg text-fg uppercase leading-tight m-0">
46+
{{ article.title }}
47+
</p>
48+
<p class="text-md font-semibold text-fg-muted leading-none mt-2">
49+
{{ article.author.name }}
50+
</p>
51+
<p class="text-xs text-fg-subtle leading-snug m-0">
52+
{{ article.description }}
53+
</p>
54+
</div>
55+
</a>
56+
</section>
5757
</article>
5858
</template>

app/composables/useFederatedArticles.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,8 @@
11
import type { BlogMetaResponse } from '#shared/schemas/atproto'
22
import type { ResolvedAuthor } from '#shared/schemas/blog'
3+
import type { FederatedArticleInput, ResolvedFederatedArticle } from '#shared/types/blog-post'
34
import { type AtIdentifierString } from '@atproto/lex'
45

5-
// Input Interface
6-
export interface FederatedArticleInput {
7-
url: string
8-
authorHandle: string
9-
}
10-
11-
// Output Interface
12-
export type ResolvedFederatedArticle = Omit<BlogMetaResponse, 'author' | '_meta'> & {
13-
url: string
14-
author: ResolvedAuthor
15-
}
16-
176
export async function useFederatedArticles(
187
federatedArticles: FederatedArticleInput[],
198
): Promise<ResolvedFederatedArticle[]> {
@@ -69,7 +58,8 @@ export async function useFederatedArticles(
6958
return {
7059
url: article.url,
7160
title: meta?.title || 'Untitled',
72-
description: meta?.description,
61+
// INFO: Prefer input description, otherwise fallback to fetched meta
62+
description: article.description || meta?.description,
7363
image: meta?.image,
7464
author: resolvedAuthor,
7565
}

app/pages/blog/alpha-release.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,39 +116,47 @@ And thank you: thank you to every single human who has contributed to npmx so fa
116116
---
117117

118118
<BlogPostFederatedArticles
119-
headline="Elsewhere..."
119+
headline="Read more from the community"
120120
:articles="[
121121
{
122122
url: 'https://dholms.leaflet.pub/3meluqcwky22a',
123-
authorHandle: 'dholms.dev'
123+
authorHandle: 'dholms.dev',
124+
description: ''
124125
},
125126
{
126127
url: 'https://whitep4nth3r.com/blog/website-redesign-2026/',
127-
authorHandle: 'whitep4nth3r.com'
128+
authorHandle: 'whitep4nth3r.com',
129+
description: 'I am a hardcoded description of Salma\'s lovely article!!'
128130
},
129131
{
130132
url: 'https://roe.dev/blog/the-golden-thread',
131-
authorHandle: 'danielroe.dev'
133+
authorHandle: 'danielroe.dev',
134+
description: ''
132135
},
133136
{
134137
url: 'https://blog.muni.town/village-scale-resilience/',
135-
authorHandle: 'erlend.sh'
138+
authorHandle: 'erlend.sh',
139+
description: ''
136140
},
137141
{
138142
url: 'https://www.pfrazee.com/blog/atmospheric-computing',
139-
authorHandle: 'pfrazee.com'
143+
authorHandle: 'pfrazee.com',
144+
description: ''
140145
},
141146
{
142147
url: 'https://marvins-guide.leaflet.pub/3mckm76mfws2h',
143-
authorHandle: 'baileytownsend.dev'
148+
authorHandle: 'baileytownsend.dev',
149+
description: ''
144150
},
145151
{
146152
url: 'https://patak.cat/blog/update.html',
147-
authorHandle: 'patak.cat'
153+
authorHandle: 'patak.cat',
154+
description: ''
148155
},
149156
{
150157
url: 'https://zeu.dev/blog/rnlive-partykit/',
151-
authorHandle: 'zeu.dev'
158+
authorHandle: 'zeu.dev',
159+
description: ''
152160
}
153161
]"
154162
/>

app/pages/blog/atproto.md

Lines changed: 0 additions & 28 deletions
This file was deleted.

app/pages/blog/nuxt.md

Lines changed: 0 additions & 18 deletions
This file was deleted.

app/pages/blog/open-source.md

Lines changed: 0 additions & 16 deletions
This file was deleted.

app/pages/blog/package-registries.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,47 @@ Shortest explanation: Production grade JavaScript is weird.
1818
---
1919

2020
<BlogPostFederatedArticles
21-
headline="Elsewhere..."
21+
headline="Read more from the community"
2222
:articles="[
2323
{
2424
url: 'https://dholms.leaflet.pub/3meluqcwky22a',
25-
authorHandle: 'dholms.dev'
25+
authorHandle: 'dholms.dev',
26+
description: ''
2627
},
2728
{
2829
url: 'https://whitep4nth3r.com/blog/website-redesign-2026/',
29-
authorHandle: 'whitep4nth3r.com'
30+
authorHandle: 'whitep4nth3r.com',
31+
description: 'I am a hardcoded description of Salma\'s lovely article!!'
3032
},
3133
{
3234
url: 'https://roe.dev/blog/the-golden-thread',
33-
authorHandle: 'danielroe.dev'
35+
authorHandle: 'danielroe.dev',
36+
description: ''
3437
},
3538
{
3639
url: 'https://blog.muni.town/village-scale-resilience/',
37-
authorHandle: 'erlend.sh'
40+
authorHandle: 'erlend.sh',
41+
description: ''
3842
},
3943
{
4044
url: 'https://www.pfrazee.com/blog/atmospheric-computing',
41-
authorHandle: 'pfrazee.com'
45+
authorHandle: 'pfrazee.com',
46+
description: ''
4247
},
4348
{
4449
url: 'https://marvins-guide.leaflet.pub/3mckm76mfws2h',
45-
authorHandle: 'baileytownsend.dev'
50+
authorHandle: 'baileytownsend.dev',
51+
description: ''
4652
},
4753
{
4854
url: 'https://patak.cat/blog/update.html',
49-
authorHandle: 'patak.cat'
55+
authorHandle: 'patak.cat',
56+
description: ''
5057
},
5158
{
5259
url: 'https://zeu.dev/blog/rnlive-partykit/',
53-
authorHandle: 'zeu.dev'
60+
authorHandle: 'zeu.dev',
61+
description: ''
5462
}
5563
]"
5664
/>

app/pages/blog/server-components.md

Lines changed: 0 additions & 15 deletions
This file was deleted.

app/pages/blog/test-fail.md

Lines changed: 0 additions & 14 deletions
This file was deleted.

server/api/atproto/blog-meta.get.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { flatten, safeParse } from 'valibot'
22
import { BlogMetaRequestSchema } from '#shared/schemas/atproto'
33
import { handleApiError } from '#server/utils/error-handler'
4-
import { BLOG_META_TAG_REGEX } from '#shared/utils/constants'
4+
import { BLOG_META_TAG_REGEX, META_TAG_TITLE_REGEX } from '#shared/utils/constants'
55

66
export default defineEventHandler(async event => {
77
const query = getQuery(event)
@@ -32,14 +32,19 @@ export default defineEventHandler(async event => {
3232
}
3333
}
3434

35+
// Capture the raw meta values
36+
const rawTitle = getMeta(html, metaTags, ['og:title', 'twitter:title', 'title'])
37+
const rawAuthor = getMeta(html, metaTags, ['author', 'og:site_name', 'twitter:site'])
38+
const rawDesc = getMeta(html, metaTags, [
39+
'og:description',
40+
'twitter:description',
41+
'description',
42+
])
43+
3544
return {
36-
title: getMeta(html, metaTags, ['og:title', 'twitter:title', 'title']),
37-
author: getMeta(html, metaTags, ['author', 'og:site_name', 'twitter:site']),
38-
description: getMeta(html, metaTags, [
39-
'og:description',
40-
'twitter:description',
41-
'description',
42-
]),
45+
title: decodeHtmlEntities(rawTitle),
46+
author: decodeHtmlEntities(rawAuthor),
47+
description: decodeHtmlEntities(rawDesc),
4348
image: getMeta(html, metaTags, ['og:image', 'twitter:image', 'og:image:secure_url']),
4449
// INFO: Extract all meta tags for debugging
4550
_meta: metaTags,
@@ -53,7 +58,7 @@ export default defineEventHandler(async event => {
5358
}
5459
})
5560

56-
// INFO: Helper to get specific meta Simple regex extraction
61+
// INFO: Extracts the specific meta tags via a simple regex
5762
const getMeta = (html: string, metaTags: Record<string, string>, keys: string[]) => {
5863
for (const key of keys) {
5964
if (metaTags[key]) return metaTags[key]
@@ -63,7 +68,20 @@ const getMeta = (html: string, metaTags: Record<string, string>, keys: string[])
6368
}
6469
// Fallback to title tag
6570
if (keys.includes('title')) {
66-
return html.match(/<title>([^<]*)<\/title>/i)?.[1] || null
71+
return html.match(META_TAG_TITLE_REGEX)?.[1] || null
6772
}
6873
return null
6974
}
75+
76+
// INFO: Prettifies the HTML entities via a simple decoder
77+
function decodeHtmlEntities(str: string | null | undefined): string | null {
78+
if (!str) return null
79+
return str
80+
.replace(/&#x27;/g, "'")
81+
.replace(/&#39;/g, "'")
82+
.replace(/&quot;/g, '"')
83+
.replace(/&amp;/g, '&')
84+
.replace(/&lt;/g, '<')
85+
.replace(/&gt;/g, '>')
86+
// Add more if needed, but these cover 99% of OG tag issues
87+
}

0 commit comments

Comments
 (0)