From 096478bcecfc6b3f6acceb3064ef6d9d28856719 Mon Sep 17 00:00:00 2001 From: Gigi Date: Sat, 11 Oct 2025 08:55:37 +0100 Subject: [PATCH] feat: add author info card for nostr-native articles - Create AuthorCard component showing profile picture and bio - Display author card after mark as read button - Only shown for nostr-native articles (not external URLs) - Fetch author profile data using applesauce ProfileModel - Card displays author name, avatar, and bio (truncated to 3 lines) - Responsive design with smaller avatar on mobile - Elegant card styling matching app design system Author information helps readers learn more about article authors directly within the reading experience. --- src/components/AuthorCard.tsx | 43 +++++++++++++++ src/components/ContentPanel.tsx | 8 +++ src/index.css | 93 +++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 src/components/AuthorCard.tsx diff --git a/src/components/AuthorCard.tsx b/src/components/AuthorCard.tsx new file mode 100644 index 00000000..a706b851 --- /dev/null +++ b/src/components/AuthorCard.tsx @@ -0,0 +1,43 @@ +import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faUserCircle } from '@fortawesome/free-solid-svg-icons' +import { useEventModel } from 'applesauce-react/hooks' +import { Models } from 'applesauce-core' + +interface AuthorCardProps { + authorPubkey: string +} + +const AuthorCard: React.FC = ({ authorPubkey }) => { + const profile = useEventModel(Models.ProfileModel, [authorPubkey]) + + const getAuthorName = () => { + if (profile?.name) return profile.name + if (profile?.display_name) return profile.display_name + return `${authorPubkey.slice(0, 8)}...${authorPubkey.slice(-8)}` + } + + const authorImage = profile?.picture || profile?.image + const authorBio = profile?.about + + return ( +
+
+ {authorImage ? ( + {getAuthorName()} + ) : ( + + )} +
+
+
{getAuthorName()}
+ {authorBio && ( +

{authorBio}

+ )} +
+
+ ) +} + +export default AuthorCard + diff --git a/src/components/ContentPanel.tsx b/src/components/ContentPanel.tsx index 39375c67..4ce70871 100644 --- a/src/components/ContentPanel.tsx +++ b/src/components/ContentPanel.tsx @@ -16,6 +16,7 @@ import { useHighlightedContent } from '../hooks/useHighlightedContent' import { useHighlightInteractions } from '../hooks/useHighlightInteractions' import { UserSettings } from '../services/settingsService' import { createEventReaction, createWebsiteReaction } from '../services/reactionService' +import AuthorCard from './AuthorCard' interface ContentPanelProps { loading: boolean @@ -222,6 +223,13 @@ const ContentPanel: React.FC = ({ )} + + {/* Author info card for nostr-native articles */} + {isNostrArticle && currentArticle && ( +
+ +
+ )} ) : (
diff --git a/src/index.css b/src/index.css index c3c9a534..0c8bcfef 100644 --- a/src/index.css +++ b/src/index.css @@ -1018,6 +1018,99 @@ body.mobile-sidebar-open { } } +/* Author Card */ +.author-card-container { + display: flex; + justify-content: center; + padding: 2rem 1rem; +} + +.author-card { + display: flex; + gap: 1rem; + padding: 1.5rem; + background: #1a1a1a; + border: 1px solid #333; + border-radius: 12px; + max-width: 600px; + width: 100%; +} + +.author-card-avatar { + flex-shrink: 0; + width: 60px; + height: 60px; + border-radius: 50%; + overflow: hidden; + background: #2a2a2a; + display: flex; + align-items: center; + justify-content: center; + color: #666; +} + +.author-card-avatar img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.author-card-avatar svg { + font-size: 2.5rem; +} + +.author-card-content { + flex: 1; + min-width: 0; +} + +.author-card-name { + font-size: 1rem; + font-weight: 600; + color: #ddd; + margin-bottom: 0.5rem; +} + +.author-card-bio { + font-size: 0.9rem; + color: #999; + line-height: 1.5; + margin: 0; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} + +@media (max-width: 768px) { + .author-card-container { + padding: 1.5rem 1rem; + } + + .author-card { + padding: 1rem; + } + + .author-card-avatar { + width: 48px; + height: 48px; + } + + .author-card-avatar svg { + font-size: 2rem; + } + + .author-card-name { + font-size: 0.95rem; + } + + .author-card-bio { + font-size: 0.85rem; + -webkit-line-clamp: 2; + } +} + .bookmark-item { background: #1a1a1a; padding: 1.5rem;