mirror of
https://github.com/lumina-rocks/lumina.git
synced 2026-04-09 23:16:47 +02:00
120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { useProfile } from "nostr-react";
|
|
import {
|
|
nip19,
|
|
} from "nostr-tools";
|
|
import {
|
|
Card,
|
|
SmallCardContent,
|
|
} from "@/components/ui/card"
|
|
import Link from 'next/link';
|
|
import Image from 'next/image';
|
|
import { extractDimensions, getChecksumSha256 } from '@/utils/utils';
|
|
import { CheckCircle, XCircle } from 'lucide-react'; // Import icons for verification status
|
|
|
|
interface QuickViewKind20NoteCardProps {
|
|
pubkey: string;
|
|
text: string;
|
|
image: string;
|
|
eventId: string;
|
|
tags: string[][];
|
|
event: any;
|
|
linkToNote: boolean;
|
|
}
|
|
|
|
const readFileAsArrayBuffer = (file: File): Promise<ArrayBuffer> => {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader()
|
|
reader.onload = () => resolve(reader.result as ArrayBuffer)
|
|
reader.onerror = () => reject(reader.error)
|
|
reader.readAsArrayBuffer(file)
|
|
})
|
|
}
|
|
|
|
const QuickViewKind20NoteCard: React.FC<QuickViewKind20NoteCardProps> = ({ pubkey, text, image, eventId, tags, event, linkToNote }) => {
|
|
const [imageError, setImageError] = useState(false);
|
|
const [verificationStatus, setVerificationStatus] = useState<boolean | null>(null);
|
|
|
|
useEffect(() => {
|
|
// Skip verification if there's no valid image
|
|
if (!image || !image.startsWith("http") || imageError) return;
|
|
|
|
const verifyImage = async () => {
|
|
try {
|
|
// get hash of the image from event tags
|
|
let eventImageHash = tags.find((tag) => tag[0] === "x")?.[1];
|
|
|
|
if(!eventImageHash) {
|
|
eventImageHash = tags.find((tag) => tag[0] === "imeta")?.find(tag => tag.startsWith("x"))?.split(" ")[1];
|
|
}
|
|
|
|
if (eventImageHash) {
|
|
// get blob from the image url
|
|
const response = await fetch(image);
|
|
const blob = await response.blob();
|
|
const sha256 = await getChecksumSha256(blob);
|
|
|
|
// Determine verification status
|
|
setVerificationStatus(eventImageHash === sha256);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error verifying image:", error);
|
|
setVerificationStatus(null);
|
|
}
|
|
};
|
|
|
|
verifyImage();
|
|
}, [image, tags, imageError]);
|
|
|
|
if (!image || !image.startsWith("http") || imageError) return null;
|
|
|
|
const processedText = text.replaceAll('\n', ' ');
|
|
const encodedNoteId = nip19.noteEncode(event.id);
|
|
|
|
const { width, height } = extractDimensions(event);
|
|
|
|
const card = (
|
|
<Card className="aspect-square">
|
|
<SmallCardContent className="h-full p-0">
|
|
<div className="h-full w-full">
|
|
<div className='relative w-full h-full'>
|
|
<Image
|
|
src={image || "/placeholder.svg"}
|
|
alt={processedText}
|
|
fill
|
|
sizes="(max-width: 768px) 100vw, 300px"
|
|
className='rounded lg:rounded-lg object-cover'
|
|
priority
|
|
onError={() => setImageError(true)}
|
|
/>
|
|
|
|
{/* Verification status indicator */}
|
|
{verificationStatus !== null && (
|
|
<div className="absolute top-2 right-2 z-10 bg-black/50 rounded-full p-1">
|
|
{verificationStatus ? (
|
|
<CheckCircle className="h-5 w-5 text-green-500" />
|
|
) : (
|
|
<XCircle className="h-5 w-5 text-red-500" />
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</SmallCardContent>
|
|
</Card>
|
|
);
|
|
|
|
return (
|
|
<>
|
|
{linkToNote ? (
|
|
<Link href={`/note/${encodedNoteId}`} className="block w-full aspect-square">
|
|
{card}
|
|
</Link>
|
|
) : (
|
|
card
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default QuickViewKind20NoteCard; |