feat: enhance layout and configuration

* Added reactStrictMode to next.config.mjs for improved performance.
* Refactored RootLayout to use ClientProviders for better structure and removed unused state management for relay URLs.
* Introduced metadata for the application in RootLayout.
* Cleaned up Home component by removing unnecessary useEffect for document title.
* Added new ProfilePageComponent and providers.tsx for better component organization.
* Updated TrendingImagesNew and Search components for improved functionality and code clarity.
This commit is contained in:
2025-08-07 23:34:40 +02:00
parent 7092821880
commit fdc1d3bc02
9 changed files with 93 additions and 87 deletions

View File

View File

@@ -1,3 +1,5 @@
"use client";
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { queryProfile } from "nostr-tools/nip05"
@@ -18,20 +20,17 @@ export function Search() {
let value = inputValue.trim();
value = value.replaceAll('nostr:', '');
if (value.startsWith('npub')) { // npub Search
// window.location.href = `/profile/${inputValue}`;
if (value.startsWith('npub')) {
router.push(`/profile/${value}`);
} else if (value.startsWith('#')) { // Hashtag Search
// window.location.href = `/tag/${inputValue.replaceAll('#', '')}`;
} else if (value.startsWith('#')) {
router.push(`/tag/${value.replaceAll('#', '')}`);
} else if(value.includes('@')) { // NIP-05 Search
// if inputValue starts with @, then add a "_" at the beginning
} else if(value.includes('@')) {
if(value.startsWith('@')) {
setInputValue('_' + value);
}
let profile = await queryProfile(value);
if(profile?.pubkey !== undefined) { // Only redirect if profile is found
if(profile?.pubkey !== undefined) {
router.push(`/profile/${nip19.npubEncode(profile?.pubkey)}`);
}
} else {
@@ -55,9 +54,8 @@ export function Search() {
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={handleKeyDown}
/>
{/* <Button type="submit" onClick={calculateAndRedirect}>Search</Button> */}
<Button type="submit" onClick={calculateAndRedirect}>
{isLoading ? <ReloadIcon className="mr-2 h-4 w-4 animate-spin" /> : 'Search'} {/* Spinner-Komponente anzeigen, wenn geladen wird */}
{isLoading ? <ReloadIcon className="mr-2 h-4 w-4 animate-spin" /> : 'Search'}
</Button>
</div>
)

View File

@@ -1,3 +1,5 @@
"use client";
import React, { useState } from 'react';
import { useProfile } from "nostr-react";
import { nip19 } from "nostr-tools";
@@ -29,10 +31,7 @@ const TrendingImageNew: React.FC<TrendingImageNewProps> = ({ event }) => {
pubkey: event.pubkey,
});
// Check if the event has nsfw or sexy tags
const hasNsfwTag = hasNsfwContent(event.tags);
// State to control image blur
const [showSensitiveContent, setShowSensitiveContent] = useState(false);
const npubShortened = (() => {
@@ -44,7 +43,6 @@ const TrendingImageNew: React.FC<TrendingImageNewProps> = ({ event }) => {
const title = userData?.username || userData?.display_name || userData?.name || userData?.npub || npubShortened;
const text = event.content.replaceAll('\n', ' ');
// Get image URL from imeta tags
const imageUrl = event.tags.find(tag => tag[0] === 'imeta' && tag[1]?.startsWith('url '))
?.slice(1)[0]?.replace('url ', '');
@@ -52,7 +50,6 @@ const TrendingImageNew: React.FC<TrendingImageNewProps> = ({ event }) => {
const hrefNote = `/note/${nip19.noteEncode(event.id)}`;
const profileImageSrc = userData?.picture || "https://robohash.org/" + event.pubkey;
// Toggle sensitive content visibility
const toggleSensitiveContent = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();

View File

@@ -1,3 +1,5 @@
"use client";
import React, { useState, useEffect } from 'react';
import TrendingImage from '@/components/TrendingImageNew';
import { Spinner } from '@/components/spinner';
@@ -6,7 +8,6 @@ export function TrendingImagesNew() {
const [events, setEvents] = useState<any[]>([]);
useEffect(() => {
// TODO: Fetch trending images from luminas own relay via http call
fetch('https://relay.lumina.rocks/api/trending/kind20')
.then(res => res.json())
.then(data => setEvents(data.trending))
@@ -20,7 +21,7 @@ export function TrendingImagesNew() {
<h1 className="text-3xl font-bold">Currently Trending</h1>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mt-6">
{events && events.length > 0 ? (
events.map((event, index) => (
events.map((event) => (
<TrendingImage key={event.id} event={event} />
))
) : (

67
components/providers.tsx Normal file
View File

@@ -0,0 +1,67 @@
"use client";
import { ReactNode, useEffect, useState } from "react";
import { NostrProvider } from "nostr-react";
import { ThemeProvider } from "@/components/theme-provider";
import { TopNavigation } from "@/components/headerComponents/TopNavigation";
import BottomBar from "@/components/BottomBar";
import { Toaster } from "@/components/ui/toaster";
import Umami from "@/components/Umami";
type ClientProvidersProps = {
children: ReactNode;
};
export default function ClientProviders({ children }: ClientProvidersProps) {
const [relayUrls, setRelayUrls] = useState<string[]>([
"wss://relay.nostr.band",
"wss://relay.damus.io",
]);
useEffect(() => {
try {
const stored = localStorage.getItem("customRelays");
const customRelays: unknown = stored ? JSON.parse(stored) : [];
if (Array.isArray(customRelays) && customRelays.length > 0) {
const sanitizedRelays = customRelays
.filter((r): r is string => typeof r === "string")
.map((relay) => (relay.endsWith("/") ? relay.slice(0, -1) : relay));
setRelayUrls((prev) => Array.from(new Set([...prev, ...sanitizedRelays])));
}
} catch (error) {
console.error("Error loading custom relays:", error);
}
}, []);
return (
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
themes={[
"light",
"dark",
"purple-light",
"purple-dark",
"vintage-light",
"vintage-dark",
"neo-brutalism-light",
"neo-brutalism-dark",
"nature-light",
"nature-dark",
"system",
]}
>
<Umami />
<div className="main-content pb-14">
<NostrProvider relayUrls={relayUrls} debug={false}>
<TopNavigation />
<Toaster />
{children}
</NostrProvider>
</div>
<BottomBar />
</ThemeProvider>
);
}