From 6b5d07d72db9ce8fccb02c8fc0923beb3fb0977a Mon Sep 17 00:00:00 2001 From: highperfocused Date: Sun, 5 Oct 2025 17:46:25 +0200 Subject: [PATCH] Refactor article card rendering in LatestArticles and LatestInHashtag components for improved code reuse and readability --- src/components/LatestArticles.tsx | 159 +++++++++++++++++------------ src/components/LatestInHashtag.tsx | 159 +++++++++++++++++------------ 2 files changed, 182 insertions(+), 136 deletions(-) diff --git a/src/components/LatestArticles.tsx b/src/components/LatestArticles.tsx index 1e006f0..82cce6b 100644 --- a/src/components/LatestArticles.tsx +++ b/src/components/LatestArticles.tsx @@ -1,16 +1,104 @@ import { useState } from 'react'; import { Link } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; +import type { NostrEvent } from '@nostrify/nostrify'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Skeleton } from '@/components/ui/skeleton'; +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Calendar, Newspaper, ChevronDown } from 'lucide-react'; import { useBlogPosts } from '@/hooks/useBlogPosts'; +import { useAuthor } from '@/hooks/useAuthor'; +import { genUserName } from '@/lib/genUserName'; const INITIAL_POSTS_COUNT = 3; const LOAD_MORE_COUNT = 6; +function ArticleCard({ post }: { post: NostrEvent }) { + const { data: author } = useAuthor(post.pubkey); + const metadata = author?.metadata; + + const title = post.tags.find(([name]: [string]) => name === 'title')?.[1] || 'Untitled'; + const summary = post.tags.find(([name]: [string]) => name === 'summary')?.[1]; + const image = post.tags.find(([name]: [string]) => name === 'image')?.[1]; + const publishedAt = post.tags.find(([name]: [string]) => name === 'published_at')?.[1]; + const identifier = post.tags.find(([name]: [string]) => name === 'd')?.[1] || ''; + const hashtags = post.tags + .filter(([name]: [string]) => name === 't') + .map(([, value]: [string, string]) => value) + .slice(0, 3); + + const date = publishedAt + ? new Date(parseInt(publishedAt) * 1000) + : new Date(post.created_at * 1000); + + const naddr = nip19.naddrEncode({ + kind: 30023, + pubkey: post.pubkey, + identifier, + }); + + const displayName = metadata?.name || metadata?.display_name || genUserName(post.pubkey); + const avatarUrl = metadata?.picture; + + return ( + + + {image && ( +
+ {title} +
+ )} + +

+ {title} +

+ {summary && ( +

+ {summary} +

+ )} +
+ +
+ + +
+
+ + + + {displayName.slice(0, 2).toUpperCase()} + + + {displayName} +
+ {hashtags.length > 0 && ( +
+ {hashtags.map((tag: string) => ( + + #{tag} + + ))} +
+ )} +
+
+ + ); +} + export function LatestArticles() { const [visibleCount, setVisibleCount] = useState(INITIAL_POSTS_COUNT); const { data: posts, isLoading } = useBlogPosts(); @@ -68,74 +156,9 @@ export function LatestArticles() { {/* Posts Grid */}
- {visiblePosts.map((post) => { - const title = post.tags.find(([name]) => name === 'title')?.[1] || 'Untitled'; - const summary = post.tags.find(([name]) => name === 'summary')?.[1]; - const image = post.tags.find(([name]) => name === 'image')?.[1]; - const publishedAt = post.tags.find(([name]) => name === 'published_at')?.[1]; - const identifier = post.tags.find(([name]) => name === 'd')?.[1] || ''; - const hashtags = post.tags - .filter(([name]) => name === 't') - .map(([, value]) => value) - .slice(0, 3); - - const date = publishedAt - ? new Date(parseInt(publishedAt) * 1000) - : new Date(post.created_at * 1000); - - const naddr = nip19.naddrEncode({ - kind: 30023, - pubkey: post.pubkey, - identifier, - }); - - return ( - - - {image && ( -
- {title} -
- )} - -

- {title} -

- {summary && ( -

- {summary} -

- )} -
- -
- - -
- {hashtags.length > 0 && ( -
- {hashtags.map((tag) => ( - - #{tag} - - ))} -
- )} -
-
- - ); - })} + {visiblePosts.map((post) => ( + + ))}
{/* Load More Button */} diff --git a/src/components/LatestInHashtag.tsx b/src/components/LatestInHashtag.tsx index a908186..223ecff 100644 --- a/src/components/LatestInHashtag.tsx +++ b/src/components/LatestInHashtag.tsx @@ -1,11 +1,15 @@ import { Link, useNavigate } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; +import type { NostrEvent } from '@nostrify/nostrify'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Skeleton } from '@/components/ui/skeleton'; +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Calendar, Hash, ChevronRight } from 'lucide-react'; import { useBlogPostsByHashtag } from '@/hooks/useBlogPostsByHashtag'; +import { useAuthor } from '@/hooks/useAuthor'; +import { genUserName } from '@/lib/genUserName'; interface LatestInHashtagProps { hashtag: string; @@ -14,6 +18,90 @@ interface LatestInHashtagProps { const INITIAL_POSTS_COUNT = 3; +function HashtagArticleCard({ post }: { post: NostrEvent }) { + const { data: author } = useAuthor(post.pubkey); + const metadata = author?.metadata; + + const title = post.tags.find(([name]: [string]) => name === 'title')?.[1] || 'Untitled'; + const summary = post.tags.find(([name]: [string]) => name === 'summary')?.[1]; + const image = post.tags.find(([name]: [string]) => name === 'image')?.[1]; + const publishedAt = post.tags.find(([name]: [string]) => name === 'published_at')?.[1]; + const identifier = post.tags.find(([name]: [string]) => name === 'd')?.[1] || ''; + const hashtags = post.tags + .filter(([name]: [string]) => name === 't') + .map(([, value]: [string, string]) => value) + .slice(0, 3); + + const date = publishedAt + ? new Date(parseInt(publishedAt) * 1000) + : new Date(post.created_at * 1000); + + const naddr = nip19.naddrEncode({ + kind: 30023, + pubkey: post.pubkey, + identifier, + }); + + const displayName = metadata?.name || metadata?.display_name || genUserName(post.pubkey); + const avatarUrl = metadata?.picture; + + return ( + + + {image && ( +
+ {title} +
+ )} + +

+ {title} +

+ {summary && ( +

+ {summary} +

+ )} +
+ +
+ + +
+
+ + + + {displayName.slice(0, 2).toUpperCase()} + + + {displayName} +
+ {hashtags.length > 0 && ( +
+ {hashtags.map((tag: string) => ( + + #{tag} + + ))} +
+ )} +
+
+ + ); +} + export function LatestInHashtag({ hashtag, icon }: LatestInHashtagProps) { const navigate = useNavigate(); const { data: posts, isLoading } = useBlogPostsByHashtag(hashtag); @@ -82,74 +170,9 @@ export function LatestInHashtag({ hashtag, icon }: LatestInHashtagProps) { {/* Posts Grid */}
- {visiblePosts.map((post) => { - const title = post.tags.find(([name]) => name === 'title')?.[1] || 'Untitled'; - const summary = post.tags.find(([name]) => name === 'summary')?.[1]; - const image = post.tags.find(([name]) => name === 'image')?.[1]; - const publishedAt = post.tags.find(([name]) => name === 'published_at')?.[1]; - const identifier = post.tags.find(([name]) => name === 'd')?.[1] || ''; - const hashtags = post.tags - .filter(([name]) => name === 't') - .map(([, value]) => value) - .slice(0, 3); - - const date = publishedAt - ? new Date(parseInt(publishedAt) * 1000) - : new Date(post.created_at * 1000); - - const naddr = nip19.naddrEncode({ - kind: 30023, - pubkey: post.pubkey, - identifier, - }); - - return ( - - - {image && ( -
- {title} -
- )} - -

- {title} -

- {summary && ( -

- {summary} -

- )} -
- -
- - -
- {hashtags.length > 0 && ( -
- {hashtags.map((tag) => ( - - #{tag} - - ))} -
- )} -
-
- - ); - })} + {visiblePosts.map((post) => ( + + ))}