Files
boris/src/components/BookmarkList.tsx
Gigi d452f96f79 fix: pass relayPool as prop instead of using non-existent hook
- Fixed crash when opening bookmark bar
- Removed non-existent Hooks.useRelayPool() call
- Added relayPool prop to SidebarHeader, BookmarkList, and ThreePaneLayout
- Threaded relayPool through component hierarchy from Bookmarks down
- Web bookmark creation now works correctly
2025-10-08 09:49:02 +01:00

157 lines
5.1 KiB
TypeScript

import React from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faChevronLeft, faBookmark, faSpinner, faList, faThLarge, faImage } from '@fortawesome/free-solid-svg-icons'
import { RelayPool } from 'applesauce-relay'
import { Bookmark, IndividualBookmark } from '../types/bookmarks'
import { BookmarkItem } from './BookmarkItem'
import SidebarHeader from './SidebarHeader'
import IconButton from './IconButton'
import { ViewMode } from './Bookmarks'
import { extractUrlsFromContent } from '../services/bookmarkHelpers'
interface BookmarkListProps {
bookmarks: Bookmark[]
onSelectUrl?: (url: string, bookmark?: { id: string; kind: number; tags: string[][]; pubkey: string }) => void
isCollapsed: boolean
onToggleCollapse: () => void
onLogout: () => void
viewMode: ViewMode
onViewModeChange: (mode: ViewMode) => void
selectedUrl?: string
onOpenSettings: () => void
onRefresh?: () => void
isRefreshing?: boolean
loading?: boolean
relayPool: RelayPool | null
}
export const BookmarkList: React.FC<BookmarkListProps> = ({
bookmarks,
onSelectUrl,
isCollapsed,
onToggleCollapse,
onLogout,
viewMode,
onViewModeChange,
selectedUrl,
onOpenSettings,
onRefresh,
isRefreshing,
loading = false,
relayPool
}) => {
// Helper to check if a bookmark has either content or a URL
const hasContentOrUrl = (ib: IndividualBookmark) => {
// Check if has content (text)
const hasContent = ib.content && ib.content.trim().length > 0
// Check if has URL
let hasUrl = false
// For web bookmarks (kind:39701), URL is in the 'd' tag
if (ib.kind === 39701) {
const dTag = ib.tags?.find((t: string[]) => t[0] === 'd')?.[1]
hasUrl = !!dTag && dTag.trim().length > 0
} else {
// For other bookmarks, extract URLs from content
const urls = extractUrlsFromContent(ib.content || '')
hasUrl = urls.length > 0
}
// Always show articles (kind:30023) as they have special handling
if (ib.kind === 30023) return true
// Otherwise, must have either content or URL
return hasContent || hasUrl
}
// Merge and flatten all individual bookmarks from all lists
// Re-sort after flattening to ensure newest first across all lists
const allIndividualBookmarks = bookmarks.flatMap(b => b.individualBookmarks || [])
.filter(hasContentOrUrl)
.sort((a, b) => ((b.added_at || 0) - (a.added_at || 0)) || ((b.created_at || 0) - (a.created_at || 0)))
if (isCollapsed) {
// Check if the selected URL is in bookmarks
const isBookmarked = selectedUrl && bookmarks.some(bookmark => {
const bookmarkUrl = bookmark.url
return bookmarkUrl === selectedUrl || selectedUrl.includes(bookmarkUrl) || bookmarkUrl.includes(selectedUrl)
})
return (
<div className="bookmarks-container collapsed">
<button
onClick={onToggleCollapse}
className={`toggle-sidebar-btn with-icon ${isBookmarked ? 'is-bookmarked' : ''}`}
title="Expand bookmarks sidebar"
aria-label="Expand bookmarks sidebar"
>
<FontAwesomeIcon icon={faChevronLeft} />
<FontAwesomeIcon icon={faBookmark} className={isBookmarked ? 'glow-blue' : ''} />
</button>
</div>
)
}
return (
<div className="bookmarks-container">
<SidebarHeader
onToggleCollapse={onToggleCollapse}
onLogout={onLogout}
onOpenSettings={onOpenSettings}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
relayPool={relayPool}
/>
{loading ? (
<div className="loading">
<FontAwesomeIcon icon={faSpinner} spin />
</div>
) : allIndividualBookmarks.length === 0 ? (
<div className="empty-state">
<p>No bookmarks found.</p>
<p>Add bookmarks using your nostr client to see them here.</p>
</div>
) : (
<div className="bookmarks-list">
<div className={`bookmarks-grid bookmarks-${viewMode}`}>
{allIndividualBookmarks.map((individualBookmark, index) =>
<BookmarkItem
key={`${individualBookmark.id}-${index}`}
bookmark={individualBookmark}
index={index}
onSelectUrl={onSelectUrl}
viewMode={viewMode}
/>
)}
</div>
</div>
)}
<div className="view-mode-controls">
<IconButton
icon={faList}
onClick={() => onViewModeChange('compact')}
title="Compact list view"
ariaLabel="Compact list view"
variant={viewMode === 'compact' ? 'primary' : 'ghost'}
/>
<IconButton
icon={faThLarge}
onClick={() => onViewModeChange('cards')}
title="Cards view"
ariaLabel="Cards view"
variant={viewMode === 'cards' ? 'primary' : 'ghost'}
/>
<IconButton
icon={faImage}
onClick={() => onViewModeChange('large')}
title="Large preview view"
ariaLabel="Large preview view"
variant={viewMode === 'large' ? 'primary' : 'ghost'}
/>
</div>
</div>
)
}