mirror of
https://github.com/dergigi/boris.git
synced 2026-02-16 12:34:41 +01:00
- Add imageCacheService with localStorage-based image caching and LRU eviction - Create useImageCache hook for React components to fetch and cache images - Integrate image caching with article service to cache cover images on load - Add image cache settings (enable/disable, size limit) to user settings - Update ReaderHeader to use cached images for article covers - Update BookmarkViews (CardView, LargeView) to use cached images - Add image cache configuration UI in OfflineModeSettings with: - Toggle to enable/disable image caching - Slider to set cache size limit (10-200 MB) - Display current cache stats (size and image count) - Clear cache button Images are cached in localStorage for offline viewing, with a configurable size limit (default 50MB). LRU eviction ensures cache stays within limits.
114 lines
3.7 KiB
TypeScript
114 lines
3.7 KiB
TypeScript
import React from 'react'
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
import { IndividualBookmark } from '../../types/bookmarks'
|
|
import { formatDate } from '../../utils/bookmarkUtils'
|
|
import ContentWithResolvedProfiles from '../ContentWithResolvedProfiles'
|
|
import { IconGetter } from './shared'
|
|
import { useImageCache } from '../../hooks/useImageCache'
|
|
import { UserSettings } from '../../services/settingsService'
|
|
|
|
interface LargeViewProps {
|
|
bookmark: IndividualBookmark
|
|
index: number
|
|
hasUrls: boolean
|
|
extractedUrls: string[]
|
|
onSelectUrl?: (url: string, bookmark?: { id: string; kind: number; tags: string[][]; pubkey: string }) => void
|
|
getIconForUrlType: IconGetter
|
|
firstUrlClassification: { buttonText: string } | null
|
|
previewImage: string | null
|
|
authorNpub: string
|
|
eventNevent?: string
|
|
getAuthorDisplayName: () => string
|
|
handleReadNow: (e: React.MouseEvent<HTMLButtonElement>) => void
|
|
articleSummary?: string
|
|
settings?: UserSettings
|
|
}
|
|
|
|
export const LargeView: React.FC<LargeViewProps> = ({
|
|
bookmark,
|
|
index,
|
|
hasUrls,
|
|
extractedUrls,
|
|
onSelectUrl,
|
|
getIconForUrlType,
|
|
firstUrlClassification,
|
|
previewImage,
|
|
authorNpub,
|
|
eventNevent,
|
|
getAuthorDisplayName,
|
|
handleReadNow,
|
|
articleSummary,
|
|
settings
|
|
}) => {
|
|
const cachedImage = useImageCache(previewImage || undefined, settings)
|
|
const isArticle = bookmark.kind === 30023
|
|
|
|
return (
|
|
<div key={`${bookmark.id}-${index}`} className={`individual-bookmark large ${bookmark.isPrivate ? 'private-bookmark' : ''}`}>
|
|
{(hasUrls || (isArticle && cachedImage)) && (
|
|
<div
|
|
className="large-preview-image"
|
|
onClick={() => {
|
|
if (isArticle) {
|
|
handleReadNow({ preventDefault: () => {} } as React.MouseEvent<HTMLButtonElement>)
|
|
} else {
|
|
onSelectUrl?.(extractedUrls[0])
|
|
}
|
|
}}
|
|
style={cachedImage ? { backgroundImage: `url(${cachedImage})` } : undefined}
|
|
>
|
|
{!previewImage && hasUrls && (
|
|
<div className="preview-placeholder">
|
|
<FontAwesomeIcon icon={getIconForUrlType(extractedUrls[0])} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
<div className="large-content">
|
|
{isArticle && articleSummary ? (
|
|
<div className="large-text article-summary">
|
|
<ContentWithResolvedProfiles content={articleSummary} />
|
|
</div>
|
|
) : bookmark.content && (
|
|
<div className="large-text">
|
|
<ContentWithResolvedProfiles content={bookmark.content} />
|
|
</div>
|
|
)}
|
|
|
|
<div className="large-footer">
|
|
<span className="large-author">
|
|
<a
|
|
href={`https://search.dergigi.com/p/${authorNpub}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="author-link-minimal"
|
|
>
|
|
{getAuthorDisplayName()}
|
|
</a>
|
|
</span>
|
|
|
|
{eventNevent && (
|
|
<a
|
|
href={`https://search.dergigi.com/e/${eventNevent}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="bookmark-date-link"
|
|
>
|
|
{formatDate(bookmark.created_at)}
|
|
</a>
|
|
)}
|
|
|
|
{(hasUrls && firstUrlClassification) || isArticle ? (
|
|
<button className="large-read-button" onClick={handleReadNow}>
|
|
<FontAwesomeIcon icon={isArticle ? getIconForUrlType('') : getIconForUrlType(extractedUrls[0])} />
|
|
{isArticle ? 'Read Article' : firstUrlClassification?.buttonText}
|
|
</button>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|