mirror of
https://github.com/lumina-rocks/lumina.git
synced 2026-06-06 10:41:20 +02:00
Feature: Image Proxy (#122)
* feat: Implement image proxying functionality in KIND20Card and QuickViewKind20NoteCard components * fix: Enhance image error handling in KIND20Card and QuickViewKind20NoteCard components * feat: Add imgproxy configuration instructions to README for image proxying support --------- Co-authored-by: highperfocused <highperfocused@pm.me>
This commit is contained in:
@@ -14,6 +14,7 @@ import ZapButton from "./ZapButton"
|
||||
import Image from "next/image"
|
||||
import CardOptionsDropdown from "./CardOptionsDropdown"
|
||||
import { renderTextWithLinkedTags } from "@/utils/textUtils"
|
||||
import { getProxiedImageUrl } from "@/utils/utils"
|
||||
|
||||
// Function to extract all images from a kind 20 event's imeta tags
|
||||
const extractImagesFromEvent = (tags: string[][]): string[] => {
|
||||
@@ -26,6 +27,8 @@ const extractImagesFromEvent = (tags: string[][]): string[] => {
|
||||
.filter(Boolean) as string[]
|
||||
}
|
||||
|
||||
const useImgProxy = process.env.NEXT_PUBLIC_ENABLE_IMGPROXY === "true"
|
||||
|
||||
interface KIND20CardProps {
|
||||
pubkey: string
|
||||
text: string
|
||||
@@ -50,6 +53,7 @@ const KIND20Card: React.FC<KIND20CardProps> = ({
|
||||
})
|
||||
const [currentImage, setCurrentImage] = useState(0);
|
||||
const [imageErrors, setImageErrors] = useState<Record<string, boolean>>({});
|
||||
const [imagesWithoutProxy, setImagesWithoutProxy] = useState<Record<string, boolean>>({});
|
||||
const [api, setApi] = useState<any>(null);
|
||||
|
||||
// Extract all images from imeta tags
|
||||
@@ -61,12 +65,21 @@ const KIND20Card: React.FC<KIND20CardProps> = ({
|
||||
// Filter out images with errors
|
||||
const validImages = allImages.filter(img => !imageErrors[img]);
|
||||
|
||||
// Handle image error by marking that specific image as having an error
|
||||
// Handle image error by first trying without proxy, then marking as error if that fails too
|
||||
const handleImageError = (errorImage: string) => {
|
||||
setImageErrors(prev => ({
|
||||
...prev,
|
||||
[errorImage]: true
|
||||
}));
|
||||
if (imagesWithoutProxy[errorImage]) {
|
||||
// Already tried without proxy, mark as error
|
||||
setImageErrors(prev => ({
|
||||
...prev,
|
||||
[errorImage]: true
|
||||
}));
|
||||
} else {
|
||||
// Try without proxy
|
||||
setImagesWithoutProxy(prev => ({
|
||||
...prev,
|
||||
[errorImage]: true
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Update current image index when carousel slides
|
||||
@@ -134,25 +147,29 @@ const KIND20Card: React.FC<KIND20CardProps> = ({
|
||||
setApi={setApi}
|
||||
>
|
||||
<CarouselContent>
|
||||
{validImages.map((imageUrl, index) => (
|
||||
<CarouselItem key={`${imageUrl}-${index}`}>
|
||||
<div className="w-full flex justify-center">
|
||||
<div className="relative w-full h-auto min-h-[300px] max-h-[80vh] flex justify-center">
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={text}
|
||||
className="rounded-lg w-full h-auto object-contain"
|
||||
// onError={() => handleImageError(imageUrl)}
|
||||
loading="lazy"
|
||||
style={{
|
||||
maxHeight: "80vh",
|
||||
margin: "auto"
|
||||
}}
|
||||
/>
|
||||
{validImages.map((imageUrl, index) => {
|
||||
const shouldUseProxy = useImgProxy && !imagesWithoutProxy[imageUrl];
|
||||
const image = shouldUseProxy ? getProxiedImageUrl(imageUrl, 1200, 0) : imageUrl;
|
||||
return (
|
||||
<CarouselItem key={`${imageUrl}-${index}`}>
|
||||
<div className="w-full flex justify-center">
|
||||
<div className="relative w-full h-auto min-h-[300px] max-h-[80vh] flex justify-center">
|
||||
<img
|
||||
src={image}
|
||||
alt={text}
|
||||
className="rounded-lg w-full h-auto object-contain"
|
||||
onError={() => handleImageError(imageUrl)}
|
||||
loading="lazy"
|
||||
style={{
|
||||
maxHeight: "80vh",
|
||||
margin: "auto"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
))}
|
||||
</CarouselItem>
|
||||
);
|
||||
})}
|
||||
</CarouselContent>
|
||||
{validImages.length > 1 && (
|
||||
<>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "@/components/ui/card"
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { extractDimensions } from '@/utils/utils';
|
||||
import { extractDimensions, getProxiedImageUrl } from '@/utils/utils';
|
||||
|
||||
interface QuickViewKind20NoteCardProps {
|
||||
pubkey: string;
|
||||
@@ -26,8 +26,14 @@ const QuickViewKind20NoteCard: React.FC<QuickViewKind20NoteCardProps> = ({ pubke
|
||||
pubkey,
|
||||
});
|
||||
const [imageError, setImageError] = useState(false);
|
||||
const [tryWithoutProxy, setTryWithoutProxy] = useState(false);
|
||||
|
||||
if (!image || !image.startsWith("http") || imageError) return null;
|
||||
if (!image || !image.startsWith("http")) return null;
|
||||
if (imageError && tryWithoutProxy) return null;
|
||||
|
||||
const useImgProxy = process.env.NEXT_PUBLIC_ENABLE_IMGPROXY === "true" && !tryWithoutProxy;
|
||||
|
||||
image = useImgProxy ? getProxiedImageUrl(image, 500, 0) : image;
|
||||
|
||||
text = text.replaceAll('\n', ' ');
|
||||
const encodedNoteId = nip19.noteEncode(event.id)
|
||||
@@ -44,7 +50,13 @@ const QuickViewKind20NoteCard: React.FC<QuickViewKind20NoteCardProps> = ({ pubke
|
||||
alt={text}
|
||||
className='w-full h-full rounded lg:rounded-lg object-cover'
|
||||
loading="lazy"
|
||||
// onError={() => setImageError(true)}
|
||||
onError={() => {
|
||||
if (tryWithoutProxy) {
|
||||
setImageError(true);
|
||||
} else {
|
||||
setTryWithoutProxy(true);
|
||||
}
|
||||
}}
|
||||
style={{ objectPosition: 'center' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user