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}
+
+ {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}
-
- {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}
+
+ {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}
-
- {summary && (
-
- {summary}
-
- )}
-
-
-
-
-
-
- {hashtags.length > 0 && (
-
- {hashtags.map((tag) => (
-
- #{tag}
-
- ))}
-
- )}
-
-
-
- );
- })}
+ {visiblePosts.map((post) => (
+
+ ))}