|
| 1 | +import type { BlogMetaResponse } from '#shared/schemas/atproto' |
| 2 | +import type { ResolvedAuthor } from '#shared/schemas/blog' |
| 3 | +import type { FederatedArticleInput, ResolvedFederatedArticle } from '#shared/types/blog-post' |
| 4 | +import { type AtIdentifierString } from '@atproto/lex' |
| 5 | + |
| 6 | +export async function useFederatedArticles( |
| 7 | + federatedArticles: FederatedArticleInput[], |
| 8 | +): Promise<ResolvedFederatedArticle[]> { |
| 9 | + if (!federatedArticles || federatedArticles.length === 0) return [] |
| 10 | + |
| 11 | + // 1. Prepare batch author request |
| 12 | + const authorQueryItems = federatedArticles.map(article => ({ |
| 13 | + name: article.authorHandle, |
| 14 | + blueskyHandle: article.authorHandle, |
| 15 | + })) |
| 16 | + |
| 17 | + // 2. Execute Fetches |
| 18 | + const [authorResponse, ...blogMetaResponses] = await Promise.all([ |
| 19 | + // Batch Author Fetch |
| 20 | + $fetch<{ authors: any[] }>('/api/atproto/bluesky-author-profiles', { |
| 21 | + query: { authors: JSON.stringify(authorQueryItems) }, |
| 22 | + }).catch(error => { |
| 23 | + console.error('Failed to fetch bluesky authors:', error) |
| 24 | + return { authors: [] } |
| 25 | + }), |
| 26 | + |
| 27 | + // Parallel Blog Meta Fetches |
| 28 | + ...federatedArticles.map(article => |
| 29 | + $fetch<BlogMetaResponse>('/api/atproto/blog-meta', { |
| 30 | + query: { url: article.url }, |
| 31 | + }).catch( |
| 32 | + () => |
| 33 | + ({ |
| 34 | + // Fallback if scraping fails |
| 35 | + title: 'Untitled Article', |
| 36 | + author: undefined, |
| 37 | + description: undefined, |
| 38 | + image: undefined, |
| 39 | + _meta: {}, |
| 40 | + _fetchedAt: '', |
| 41 | + }) as BlogMetaResponse, |
| 42 | + ), |
| 43 | + ), |
| 44 | + ]) |
| 45 | + |
| 46 | + // 3. Merge Data |
| 47 | + return federatedArticles.map((article, index) => { |
| 48 | + const meta = blogMetaResponses[index] |
| 49 | + const authorProfile = authorResponse?.authors?.[index] |
| 50 | + |
| 51 | + const resolvedAuthor: ResolvedAuthor = { |
| 52 | + name: meta?.author || authorProfile?.displayName || article.authorHandle, |
| 53 | + blueskyHandle: article.authorHandle as AtIdentifierString, |
| 54 | + avatar: authorProfile?.avatar || null, |
| 55 | + profileUrl: authorProfile?.profileUrl || null, |
| 56 | + } |
| 57 | + |
| 58 | + return { |
| 59 | + url: article.url, |
| 60 | + title: meta?.title || 'Untitled', |
| 61 | + // INFO: Prefer input description, otherwise fallback to fetched meta |
| 62 | + description: article.description || meta?.description, |
| 63 | + image: meta?.image, |
| 64 | + author: resolvedAuthor, |
| 65 | + } |
| 66 | + }) |
| 67 | +} |
0 commit comments