mirror of
https://github.com/lumina-rocks/lumina.git
synced 2026-06-04 01:31:13 +02:00
fix: improve image handling and loading states across components
* Refactored image rendering in CommentCard, NoteCard, GalleryCard, QuickViewNoteCard, and TrendingImage components to use Next.js Image component for better performance and responsiveness. * Updated loading state management in FollowButton and LoginForm components to ensure proper dependency tracking. * Cleaned up unused Head imports in Tag and Upload pages for better code clarity. * Enhanced dependency arrays in useEffect hooks for improved reliability in various components.
This commit is contained in:
@@ -55,7 +55,7 @@ export default function RelaysPage() {
|
||||
}
|
||||
|
||||
return () => clearTimeout(loadingTimeout);
|
||||
}, [connectedRelays, refreshKey]);
|
||||
}, [connectedRelays, refreshKey, loading]);
|
||||
|
||||
// Function to refresh NIP-65 relays for the current user
|
||||
const refreshNip65Relays = async () => {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use client';
|
||||
|
||||
import Head from "next/head";
|
||||
import { useNostrEvents } from "nostr-react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
@@ -68,12 +66,6 @@ export default function TagPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>LUMINA.rocks - Tags</title>
|
||||
<meta name="description" content="Explore tags on LUMINA" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<div className="px-2 md:px-6">
|
||||
<Tabs defaultValue="trending" className="mt-4">
|
||||
<TabsList className="mb-4 w-full grid grid-cols-1">
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use client';
|
||||
|
||||
import Head from "next/head";
|
||||
import { useEffect } from "react";
|
||||
import UploadComponent from "@/components/UploadComponent";
|
||||
|
||||
@@ -19,12 +17,6 @@ export default function UploadPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>LUMINA.rocks</title>
|
||||
<meta name="description" content="Yet another nostr web ui" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<div className="py-2 px-2">
|
||||
<UploadComponent />
|
||||
</div>
|
||||
|
||||
@@ -28,6 +28,7 @@ import { Avatar, AvatarImage } from '@/components/ui/avatar';
|
||||
import ViewRawButton from '@/components/ViewRawButton';
|
||||
import ViewNoteButton from './ViewNoteButton';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
|
||||
interface CommentCardProps {
|
||||
pubkey: string;
|
||||
@@ -77,17 +78,21 @@ const NoteCard: React.FC<CommentCardProps> = ({ pubkey, text, eventId, tags, eve
|
||||
<div className='py-4'>
|
||||
{
|
||||
<div className='w-full h-full px-10'>
|
||||
{imageSrc && imageSrc.length > 1 ? (
|
||||
{imageSrc && imageSrc.length > 1 ? (
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
{imageSrc.map((src, index) => (
|
||||
<CarouselItem key={index}>
|
||||
<img
|
||||
key={index}
|
||||
src={src}
|
||||
className='rounded lg:rounded-lg'
|
||||
style={{ maxWidth: '100%', maxHeight: '100vh', objectFit: 'contain', margin: 'auto' }}
|
||||
/>
|
||||
<div className="relative w-full" style={{ minHeight: '300px', maxHeight: '100vh' }}>
|
||||
<Image
|
||||
src={src}
|
||||
alt={textWithoutImage || 'Image'}
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="rounded lg:rounded-lg object-contain"
|
||||
priority={index === 0}
|
||||
/>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
))}
|
||||
</CarouselContent>
|
||||
@@ -95,7 +100,17 @@ const NoteCard: React.FC<CommentCardProps> = ({ pubkey, text, eventId, tags, eve
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
) : (
|
||||
imageSrc ? <img src={imageSrc[0]} className='rounded lg:rounded-lg' style={{ maxWidth: '100%', maxHeight: '100vh', objectFit: 'contain', margin: 'auto' }} /> : ""
|
||||
imageSrc ? (
|
||||
<div className="relative w-full" style={{ minHeight: '300px', maxHeight: '100vh' }}>
|
||||
<Image
|
||||
src={imageSrc[0]}
|
||||
alt={textWithoutImage || 'Image'}
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="rounded lg:rounded-lg object-contain"
|
||||
/>
|
||||
</div>
|
||||
) : ""
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -37,11 +37,11 @@ const FollowButton: React.FC<FollowButtonProps> = ({ pubkey, userPubkey }) => {
|
||||
followingPubkeys = followingPubkeys.filter((tag) => tag);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (followingPubkeys.includes(pubkey)) {
|
||||
setIsFollowing(true);
|
||||
}
|
||||
}, [followingPubkeys, isFollowing, setIsFollowing]);
|
||||
useEffect(() => {
|
||||
if (followingPubkeys.includes(pubkey)) {
|
||||
setIsFollowing(true);
|
||||
}
|
||||
}, [followingPubkeys, pubkey]);
|
||||
|
||||
const handleFollow = async () => {
|
||||
// if (isLoggedIn) {
|
||||
|
||||
@@ -31,13 +31,16 @@ const GalleryCard: React.FC<GalleryCardProps> = ({ pubkey, eventId, imageUrl, li
|
||||
<div>
|
||||
<div className='d-flex justify-content-center align-items-center'>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<img
|
||||
src={imageUrl}
|
||||
className='rounded lg:rounded-lg w-full h-full object-cover'
|
||||
style={{ maxHeight: '75vh', margin: 'auto' }}
|
||||
alt={eventId}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="relative w-full" style={{ minHeight: '200px', maxHeight: '75vh' }}>
|
||||
<Image
|
||||
src={imageUrl}
|
||||
alt={eventId}
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="rounded lg:rounded-lg object-cover"
|
||||
priority={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -118,7 +118,7 @@ export function LoginForm() {
|
||||
qrScannerRef.current.stop().catch(console.error);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
}, [completeLogin]);
|
||||
|
||||
// Handle QR Scanner dialog
|
||||
const startQRScanner = () => {
|
||||
|
||||
@@ -27,6 +27,7 @@ import ReactionButton from '@/components/ReactionButton';
|
||||
import { Avatar, AvatarImage } from '@/components/ui/avatar';
|
||||
import ViewNoteButton from './ViewNoteButton';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { Event as NostrEvent } from "nostr-tools";
|
||||
import ZapButton from './ZapButton';
|
||||
import CardOptionsDropdown from './CardOptionsDropdown';
|
||||
@@ -91,14 +92,16 @@ const NoteCard: React.FC<NoteCardProps> = ({ pubkey, text, eventId, tags, event,
|
||||
<CarouselContent>
|
||||
{imageSrc.map((src, index) => (
|
||||
<CarouselItem key={index}>
|
||||
<img
|
||||
key={index}
|
||||
src={src}
|
||||
className='rounded lg:rounded-lg w-full h-auto object-contain'
|
||||
style={{ maxHeight: '66vh', margin: 'auto' }}
|
||||
alt={textWithoutImage || "Post image"}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="relative w-full" style={{ minHeight: '300px', maxHeight: '66vh' }}>
|
||||
<Image
|
||||
src={src}
|
||||
alt={textWithoutImage || "Post image"}
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="rounded lg:rounded-lg object-contain"
|
||||
priority={index === 0}
|
||||
/>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
))}
|
||||
</CarouselContent>
|
||||
@@ -106,14 +109,17 @@ const NoteCard: React.FC<NoteCardProps> = ({ pubkey, text, eventId, tags, event,
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
) : (
|
||||
imageSrc ?
|
||||
<img
|
||||
src={imageSrc[0]}
|
||||
className='rounded lg:rounded-lg w-full h-auto object-contain'
|
||||
style={{ maxHeight: '66vh', margin: 'auto' }}
|
||||
alt={textWithoutImage || "Post image"}
|
||||
loading="lazy"
|
||||
/> : ""
|
||||
imageSrc ? (
|
||||
<div className="relative w-full" style={{ minHeight: '300px', maxHeight: '66vh' }}>
|
||||
<Image
|
||||
src={imageSrc[0]}
|
||||
alt={textWithoutImage || "Post image"}
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="rounded lg:rounded-lg object-contain"
|
||||
/>
|
||||
</div>
|
||||
) : ""
|
||||
)}
|
||||
</div>
|
||||
<div className='w-full h-full px-10'>
|
||||
|
||||
@@ -45,12 +45,15 @@ const QuickViewNoteCard: React.FC<NoteCardProps> = ({ pubkey, text, eventId, tag
|
||||
<div className="absolute top-2 right-2 w-7 h-7 lg:w-12 lg:h-12 bg-black bg-opacity-40 rounded-lg flex items-center justify-center">
|
||||
<StackIcon className='absolute w-7 h-7 lg:w-12 lg:h-12'/>
|
||||
</div>
|
||||
<img src={imageSrc[0]}
|
||||
className='rounded lg:rounded-lg w-full h-auto object-cover'
|
||||
style={{ maxHeight: '75vh', margin: 'auto' }}
|
||||
alt={text}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="relative w-full" style={{ minHeight: '200px', maxHeight: '75vh' }}>
|
||||
<Image
|
||||
src={imageSrc[0]}
|
||||
alt={text}
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="rounded lg:rounded-lg object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : imageSrc && imageSrc.length > 0 ? (
|
||||
<div style={{ position: 'relative' }}>
|
||||
@@ -59,12 +62,15 @@ const QuickViewNoteCard: React.FC<NoteCardProps> = ({ pubkey, text, eventId, tag
|
||||
<PlayIcon className='absolute w-7 h-7 lg:w-12 lg:h-12' />
|
||||
</div>
|
||||
}
|
||||
<img src={imageSrc[0]}
|
||||
className='rounded lg:rounded-lg w-full h-auto object-cover'
|
||||
style={{ maxHeight: '75vh', margin: 'auto' }}
|
||||
alt={text}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="relative w-full" style={{ minHeight: '200px', maxHeight: '75vh' }}>
|
||||
<Image
|
||||
src={imageSrc[0]}
|
||||
alt={text}
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="rounded lg:rounded-lg object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : videoSrc && videoSrc.length > 0 ? (
|
||||
<div style={{ position: 'relative' }}>
|
||||
|
||||
@@ -66,7 +66,15 @@ const TrendingImage: React.FC<TrendingImageProps> = ({ eventId, pubkey }) => {
|
||||
{imageSrc && imageSrc.length > 0 && (
|
||||
<div style={{ position: 'relative', width: '100%', height: '100%', overflow: 'hidden' }}>
|
||||
<Link href={hrefNote}>
|
||||
<img src={imageSrc[0]} className='rounded lg:rounded-lg' style={{ width: '100%', height: '100%', objectFit: 'cover' }} alt={text} />
|
||||
<div className="relative w-full" style={{ minHeight: '200px' }}>
|
||||
<Image
|
||||
src={imageSrc[0]}
|
||||
alt={text}
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="rounded lg:rounded-lg object-cover"
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
// <img src={imageSrc[0]} style={{ maxWidth: '100%', maxHeight: '100vh', objectFit: 'cover', margin: 'auto' }} alt={text} />
|
||||
|
||||
@@ -76,13 +76,15 @@ const TrendingImageNew: React.FC<TrendingImageNewProps> = ({ event }) => {
|
||||
{imageUrl && (
|
||||
<div style={{ position: 'relative', width: '100%', height: '100%', overflow: 'hidden' }}>
|
||||
<Link href={hrefNote} onClick={hasNsfwTag && !showSensitiveContent ? (e) => e.preventDefault() : undefined}>
|
||||
<img
|
||||
src={imageUrl}
|
||||
className={`rounded lg:rounded-lg w-full h-full object-cover ${hasNsfwTag && !showSensitiveContent ? 'blur-xl' : ''}`}
|
||||
style={{ margin: 'auto' }}
|
||||
alt={text}
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="relative w-full h-full">
|
||||
<img
|
||||
src={imageUrl}
|
||||
className={`rounded lg:rounded-lg w-full h-full object-cover ${hasNsfwTag && !showSensitiveContent ? 'blur-xl' : ''}`}
|
||||
style={{ margin: 'auto' }}
|
||||
alt={text}
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
{hasNsfwTag && !showSensitiveContent && (
|
||||
<div
|
||||
className="absolute inset-0 flex flex-col items-center justify-center"
|
||||
|
||||
@@ -63,11 +63,10 @@ export default function ZapButton({ event }: { event: any }) {
|
||||
// Effect to check for new zap receipts when showing an invoice
|
||||
useEffect(() => {
|
||||
if (invoice) {
|
||||
// Store the current count of zap receipts when invoice is generated
|
||||
invoiceEventsCountRef.current = events.length;
|
||||
setPaymentComplete(false);
|
||||
}
|
||||
}, [invoice]);
|
||||
}, [invoice, events.length]);
|
||||
|
||||
// Effect to detect new zap receipts after invoice is shown
|
||||
useEffect(() => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import ConnectedRelaysButton from "@/components/headerComponents/ConnectedRelays
|
||||
import AuthButton from "./AuthButton"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { UserIcon } from "lucide-react"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
|
||||
export function TopNavigation() {
|
||||
const [pubkey, setPubkey] = useState<string | null>(null)
|
||||
@@ -47,6 +48,7 @@ export function TopNavigation() {
|
||||
<TopNavigationItems items={siteConfig.mainNav} />
|
||||
<div className="flex flex-1 items-center justify-end space-x-4">
|
||||
<nav className="flex items-center space-x-2">
|
||||
<Badge variant="secondary" className="hidden sm:inline">v{siteConfig.version}</Badge>
|
||||
<ConnectedRelaysButton />
|
||||
<DropdownThemeMode />
|
||||
{pubkey !== null ? <AvatarDropdown /> : <AuthButton />}
|
||||
|
||||
@@ -15,19 +15,15 @@ const NIP05: React.FC<NIP05Props> = ({ nip05, pubkey }) => {
|
||||
let domain = nip05.split('@')[1]
|
||||
|
||||
useEffect(() => {
|
||||
if(nip05.length > 0) {
|
||||
if (nip05.length > 0) {
|
||||
fetch(`https://${domain}/.well-known/nostr.json?name=${name}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.names[name] === pubkey) {
|
||||
setIsValid(true);
|
||||
} else {
|
||||
setIsValid(false);
|
||||
}
|
||||
setIsLoading(false);
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
setIsValid(data.names[name] === pubkey);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}
|
||||
}, [nip05, pubkey]);
|
||||
}, [nip05, pubkey, domain, name]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -54,7 +54,7 @@ function TabsContentWrapper(props: {
|
||||
|
||||
return pathname + (asString ? "?" + asString : "");
|
||||
},
|
||||
[searchParams, props.searchParam],
|
||||
[searchParams, searchParam, pathname, props.defaultValue],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user