mirror of
https://github.com/dergigi/boris.git
synced 2026-02-22 15:35:23 +01:00
- Remove settings parameter from useImageCache and useCacheImageOnLoad hooks as it was never used - Update all call sites in CardView, CompactView, LargeView, and ReaderHeader - Remove settings prop from BookmarkItem and its child view components - Remove settings prop from BookmarkList component - Update ThreePaneLayout to not pass settings to BookmarkList This change cascades through the component tree to clean up unused props that were introduced when we refactored the image caching to use Service Worker instead of local storage.
130 lines
4.8 KiB
TypeScript
130 lines
4.8 KiB
TypeScript
import React, { useState } from 'react'
|
|
import { faBookOpen, faPlay, faEye } from '@fortawesome/free-solid-svg-icons'
|
|
import { useEventModel } from 'applesauce-react/hooks'
|
|
import { Models } from 'applesauce-core'
|
|
import { npubEncode, neventEncode } from 'nostr-tools/nip19'
|
|
import { IndividualBookmark } from '../types/bookmarks'
|
|
import { extractUrlsFromContent } from '../services/bookmarkHelpers'
|
|
import { classifyUrl } from '../utils/helpers'
|
|
import { ViewMode } from './Bookmarks'
|
|
import { getPreviewImage, fetchOgImage } from '../utils/imagePreview'
|
|
import { CompactView } from './BookmarkViews/CompactView'
|
|
import { LargeView } from './BookmarkViews/LargeView'
|
|
import { CardView } from './BookmarkViews/CardView'
|
|
|
|
interface BookmarkItemProps {
|
|
bookmark: IndividualBookmark
|
|
index: number
|
|
onSelectUrl?: (url: string, bookmark?: { id: string; kind: number; tags: string[][]; pubkey: string }) => void
|
|
viewMode?: ViewMode
|
|
}
|
|
|
|
export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onSelectUrl, viewMode = 'cards' }) => {
|
|
const [ogImage, setOgImage] = useState<string | null>(null)
|
|
|
|
const short = (v: string) => `${v.slice(0, 8)}...${v.slice(-8)}`
|
|
|
|
// For web bookmarks (kind:39701), URL is stored in the 'd' tag
|
|
const isWebBookmark = bookmark.kind === 39701
|
|
const webBookmarkUrl = isWebBookmark ? bookmark.tags.find(t => t[0] === 'd')?.[1] : null
|
|
|
|
// Extract URLs from bookmark content (for regular bookmarks)
|
|
// For web bookmarks, ensure URL has protocol
|
|
const extractedUrls = webBookmarkUrl
|
|
? [webBookmarkUrl.startsWith('http') ? webBookmarkUrl : `https://${webBookmarkUrl}`]
|
|
: extractUrlsFromContent(bookmark.content)
|
|
const hasUrls = extractedUrls.length > 0
|
|
const firstUrl = hasUrls ? extractedUrls[0] : null
|
|
const firstUrlClassification = firstUrl ? classifyUrl(firstUrl) : null
|
|
|
|
// For kind:30023 articles, extract image and summary tags (per NIP-23)
|
|
// Note: We extract directly from tags here since we don't have the full event.
|
|
// When we have full events, we use getArticleImage() helper (see articleService.ts)
|
|
const isArticle = bookmark.kind === 30023
|
|
const articleImage = isArticle ? bookmark.tags.find(t => t[0] === 'image')?.[1] : undefined
|
|
const articleSummary = isArticle ? bookmark.tags.find(t => t[0] === 'summary')?.[1] : undefined
|
|
|
|
// Fetch OG image for large view (hook must be at top level)
|
|
const instantPreview = firstUrl ? getPreviewImage(firstUrl, firstUrlClassification?.type || '') : null
|
|
React.useEffect(() => {
|
|
if (viewMode === 'large' && firstUrl && !instantPreview && !ogImage && !articleImage) {
|
|
fetchOgImage(firstUrl).then(setOgImage)
|
|
}
|
|
}, [viewMode, firstUrl, instantPreview, ogImage, articleImage])
|
|
|
|
// Resolve author profile using applesauce
|
|
const authorProfile = useEventModel(Models.ProfileModel, [bookmark.pubkey])
|
|
const authorNpub = npubEncode(bookmark.pubkey)
|
|
const isHexId = /^[0-9a-f]{64}$/i.test(bookmark.id)
|
|
const eventNevent = isHexId ? neventEncode({ id: bookmark.id }) : undefined
|
|
|
|
// Get display name for author
|
|
const getAuthorDisplayName = () => {
|
|
if (authorProfile?.name) return authorProfile.name
|
|
if (authorProfile?.display_name) return authorProfile.display_name
|
|
if (authorProfile?.nip05) return authorProfile.nip05
|
|
return short(bookmark.pubkey) // fallback to short pubkey
|
|
}
|
|
|
|
// use helper from kindIcon.ts
|
|
|
|
const getIconForUrlType = (url: string) => {
|
|
const classification = classifyUrl(url)
|
|
switch (classification.type) {
|
|
case 'youtube':
|
|
case 'video':
|
|
return faPlay
|
|
case 'image':
|
|
return faEye
|
|
default:
|
|
return faBookOpen
|
|
}
|
|
}
|
|
|
|
const handleReadNow = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
event.preventDefault()
|
|
|
|
// For kind:30023 articles, pass the bookmark data instead of URL
|
|
if (bookmark.kind === 30023) {
|
|
if (onSelectUrl) {
|
|
onSelectUrl('', { id: bookmark.id, kind: bookmark.kind, tags: bookmark.tags, pubkey: bookmark.pubkey })
|
|
}
|
|
return
|
|
}
|
|
|
|
// For regular bookmarks with URLs
|
|
if (!hasUrls) return
|
|
const firstUrl = extractedUrls[0]
|
|
if (onSelectUrl) {
|
|
onSelectUrl(firstUrl)
|
|
} else {
|
|
window.open(firstUrl, '_blank')
|
|
}
|
|
}
|
|
|
|
const sharedProps = {
|
|
bookmark,
|
|
index,
|
|
hasUrls,
|
|
extractedUrls,
|
|
onSelectUrl,
|
|
authorNpub,
|
|
eventNevent,
|
|
getAuthorDisplayName,
|
|
handleReadNow,
|
|
articleImage,
|
|
articleSummary
|
|
}
|
|
|
|
if (viewMode === 'compact') {
|
|
return <CompactView {...sharedProps} />
|
|
}
|
|
|
|
if (viewMode === 'large') {
|
|
const previewImage = articleImage || instantPreview || ogImage
|
|
return <LargeView {...sharedProps} getIconForUrlType={getIconForUrlType} previewImage={previewImage} />
|
|
}
|
|
|
|
return <CardView {...sharedProps} getIconForUrlType={getIconForUrlType} articleImage={articleImage} />
|
|
}
|