feat: show reading progress in compact and card view bookmarks

- Add readingProgress prop to BookmarkItem component
- Display reading progress in CompactView with 2px indicator
- Display reading progress in CardView with 3px indicator
- Progress color matches main app: blue (reading), green (completed), neutral (started)
- Add getBookmarkReadingProgress helper in Me.tsx
- Show progress only for kind:30023 articles with progress > 0
- Reading progress now visible across all bookmark view modes
This commit is contained in:
Gigi
2025-10-19 22:17:35 +02:00
parent de32310801
commit c1d23fac7b
4 changed files with 90 additions and 4 deletions

View File

@@ -19,9 +19,10 @@ interface BookmarkItemProps {
index: number
onSelectUrl?: (url: string, bookmark?: { id: string; kind: number; tags: string[][]; pubkey: string }) => void
viewMode?: ViewMode
readingProgress?: number
}
export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onSelectUrl, viewMode = 'cards' }) => {
export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onSelectUrl, viewMode = 'cards', readingProgress }) => {
const [ogImage, setOgImage] = useState<string | null>(null)
const short = (v: string) => `${v.slice(0, 8)}...${v.slice(-8)}`
@@ -139,7 +140,8 @@ export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onS
handleReadNow,
articleImage,
articleSummary,
contentTypeIcon: getContentTypeIcon()
contentTypeIcon: getContentTypeIcon(),
readingProgress
}
if (viewMode === 'compact') {

View File

@@ -24,6 +24,7 @@ interface CardViewProps {
articleImage?: string
articleSummary?: string
contentTypeIcon: IconDefinition
readingProgress?: number
}
export const CardView: React.FC<CardViewProps> = ({
@@ -38,7 +39,8 @@ export const CardView: React.FC<CardViewProps> = ({
handleReadNow,
articleImage,
articleSummary,
contentTypeIcon
contentTypeIcon,
readingProgress
}) => {
const firstUrl = hasUrls ? extractedUrls[0] : null
const firstUrlClassificationType = firstUrl ? classifyUrl(firstUrl)?.type : null
@@ -52,6 +54,14 @@ export const CardView: React.FC<CardViewProps> = ({
const shouldTruncate = !expanded && contentLength > 210
const isArticle = bookmark.kind === 30023
// Calculate progress color (matching BlogPostCard logic)
let progressColor = '#6366f1' // Default blue (reading)
if (readingProgress && readingProgress >= 0.95) {
progressColor = '#10b981' // Green (completed)
} else if (readingProgress && readingProgress > 0 && readingProgress <= 0.10) {
progressColor = 'var(--color-text)' // Neutral text color (started)
}
// Determine which image to use (article image, instant preview, or OG image)
const previewImage = articleImage || instantPreview || ogImage
const cachedImage = useImageCache(previewImage || undefined)
@@ -163,6 +173,28 @@ export const CardView: React.FC<CardViewProps> = ({
</button>
)}
{/* Reading progress indicator for articles */}
{isArticle && readingProgress !== undefined && readingProgress > 0 && (
<div
style={{
height: '3px',
width: '100%',
background: 'var(--color-border)',
overflow: 'hidden',
marginTop: '0.75rem'
}}
>
<div
style={{
height: '100%',
width: `${Math.round(readingProgress * 100)}%`,
background: progressColor,
transition: 'width 0.3s ease, background 0.3s ease'
}}
/>
</div>
)}
<div className="bookmark-footer">
<div className="bookmark-meta-minimal">
<Link

View File

@@ -13,6 +13,7 @@ interface CompactViewProps {
onSelectUrl?: (url: string, bookmark?: { id: string; kind: number; tags: string[][]; pubkey: string }) => void
articleSummary?: string
contentTypeIcon: IconDefinition
readingProgress?: number
}
export const CompactView: React.FC<CompactViewProps> = ({
@@ -22,12 +23,21 @@ export const CompactView: React.FC<CompactViewProps> = ({
extractedUrls,
onSelectUrl,
articleSummary,
contentTypeIcon
contentTypeIcon,
readingProgress
}) => {
const isArticle = bookmark.kind === 30023
const isWebBookmark = bookmark.kind === 39701
const isClickable = hasUrls || isArticle || isWebBookmark
// Calculate progress color (matching BlogPostCard logic)
let progressColor = '#6366f1' // Default blue (reading)
if (readingProgress && readingProgress >= 0.95) {
progressColor = '#10b981' // Green (completed)
} else if (readingProgress && readingProgress > 0 && readingProgress <= 0.10) {
progressColor = 'var(--color-text)' // Neutral text color (started)
}
const handleCompactClick = () => {
if (!onSelectUrl) return
@@ -62,6 +72,28 @@ export const CompactView: React.FC<CompactViewProps> = ({
<span className="bookmark-date-compact">{formatDateCompact(bookmark.created_at)}</span>
{/* CTA removed */}
</div>
{/* Reading progress indicator for articles */}
{isArticle && readingProgress !== undefined && readingProgress > 0 && (
<div
style={{
height: '2px',
width: '100%',
background: 'var(--color-border)',
overflow: 'hidden',
margin: '0'
}}
>
<div
style={{
height: '100%',
width: `${Math.round(readingProgress * 100)}%`,
background: progressColor,
transition: 'width 0.3s ease, background 0.3s ease'
}}
/>
</div>
)}
</div>
)
}

View File

@@ -471,6 +471,25 @@ const Me: React.FC<MeProps> = ({
}
}
// Helper to get reading progress for a bookmark
const getBookmarkReadingProgress = (bookmark: IndividualBookmark): number | undefined => {
if (bookmark.kind === 30023) {
const dTag = bookmark.tags.find(t => t[0] === 'd')?.[1]
if (!dTag) return undefined
try {
const naddr = nip19.naddrEncode({
kind: 30023,
pubkey: bookmark.pubkey,
identifier: dTag
})
return readingProgressMap.get(naddr)
} catch (err) {
return undefined
}
}
return undefined
}
// Merge and flatten all individual bookmarks
const allIndividualBookmarks = bookmarks.flatMap(b => b.individualBookmarks || [])
.filter(hasContent)
@@ -567,6 +586,7 @@ const Me: React.FC<MeProps> = ({
index={index}
viewMode="cards"
onSelectUrl={handleSelectUrl}
readingProgress={getBookmarkReadingProgress(individualBookmark)}
/>
))}
</div>