Add new pages and layout components for Upload, Hashtags, Search, Notifications, and Messages; update existing pages to use Layout component

This commit is contained in:
2025-11-21 23:52:02 +01:00
parent 2dda3075c7
commit dee4a35100
10 changed files with 226 additions and 30 deletions

View File

@@ -3,6 +3,11 @@ import { ScrollToTop } from "./components/ScrollToTop";
import Index from "./pages/Index";
import { NIP19Page } from "./pages/NIP19Page";
import { Upload } from "./pages/Upload";
import { Hashtags } from "./pages/Hashtags";
import { SearchPage } from "./pages/Search";
import { Notifications } from "./pages/Notifications";
import Messages from "./pages/Messages";
import NotFound from "./pages/NotFound";
export function AppRouter() {
@@ -11,6 +16,11 @@ export function AppRouter() {
<ScrollToTop />
<Routes>
<Route path="/" element={<Index />} />
<Route path="/upload" element={<Upload />} />
<Route path="/hashtags" element={<Hashtags />} />
<Route path="/search" element={<SearchPage />} />
<Route path="/notifications" element={<Notifications />} />
<Route path="/messages" element={<Messages />} />
{/* NIP-19 route for npub1, note1, naddr1, nevent1, nprofile1 */}
<Route path="/:nip19" element={<NIP19Page />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}

94
src/components/Layout.tsx Normal file
View File

@@ -0,0 +1,94 @@
import { Link, useLocation } from 'react-router-dom';
import { Home, Upload, Hash, Search, Bell } from 'lucide-react';
import { LoginArea } from './auth/LoginArea';
import { Button } from './ui/button';
import { useTheme } from '@/hooks/useTheme';
import { Moon, Sun } from 'lucide-react';
interface LayoutProps {
children: React.ReactNode;
}
export function Layout({ children }: LayoutProps) {
const location = useLocation();
const { theme, setTheme } = useTheme();
const navItems = [
{ path: '/', icon: Home, label: 'Home' },
{ path: '/upload', icon: Upload, label: 'Upload' },
{ path: '/hashtags', icon: Hash, label: 'Hashtags' },
{ path: '/search', icon: Search, label: 'Search' },
{ path: '/notifications', icon: Bell, label: 'Notifications' },
];
const isActive = (path: string) => {
return location.pathname === path;
};
return (
<div className="min-h-screen flex flex-col">
{/* Header */}
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container flex h-16 items-center justify-between px-4">
{/* Logo */}
<Link to="/" className="flex items-center space-x-2">
<span className="text-2xl font-bold bg-gradient-to-r from-primary to-primary/60 bg-clip-text text-transparent">
LUMINA
</span>
</Link>
{/* Right side actions */}
<div className="flex items-center gap-3">
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
aria-label="Toggle theme"
>
{theme === 'light' ? (
<Moon className="h-5 w-5" />
) : (
<Sun className="h-5 w-5" />
)}
</Button>
<LoginArea className="max-w-60" />
</div>
</div>
</header>
{/* Main content */}
<main className="flex-1 pb-20">
{children}
</main>
{/* Footer Navigation - Instagram style */}
<nav className="fixed bottom-0 left-0 right-0 z-50 border-t bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container flex h-16 items-center justify-around px-4 max-w-2xl mx-auto">
{navItems.map((item) => {
const Icon = item.icon;
const active = isActive(item.path);
return (
<Link
key={item.path}
to={item.path}
className={`flex flex-col items-center justify-center gap-1 transition-colors ${
active
? 'text-primary'
: 'text-muted-foreground hover:text-foreground'
}`}
aria-label={item.label}
>
<Icon
className={`h-6 w-6 ${active ? 'fill-current' : ''}`}
strokeWidth={active ? 2.5 : 2}
/>
<span className="text-xs font-medium">{item.label}</span>
</Link>
);
})}
</div>
</nav>
</div>
);
}

14
src/pages/Hashtags.tsx Normal file
View File

@@ -0,0 +1,14 @@
import { Layout } from '@/components/Layout';
export function Hashtags() {
return (
<Layout>
<div className="container py-8">
<div className="max-w-2xl mx-auto">
<h1 className="text-3xl font-bold mb-4">Hashtags</h1>
<p className="text-muted-foreground">Hashtags page - coming soon</p>
</div>
</div>
</Layout>
);
}

View File

@@ -1,4 +1,5 @@
import { useSeoMeta } from '@unhead/react';
import { Layout } from '@/components/Layout';
const Index = () => {
useSeoMeta({
@@ -7,16 +8,18 @@ const Index = () => {
});
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100 dark:bg-gray-900">
<div className="text-center">
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-gray-100">
Welcome to Your Blank App
</h1>
<p className="text-xl text-gray-600 dark:text-gray-400">
Start building your amazing project here!
</p>
<Layout>
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold mb-4">
Welcome to Your Blank App
</h1>
<p className="text-xl text-muted-foreground">
Start building your amazing project here!
</p>
</div>
</div>
</div>
</Layout>
);
};

View File

@@ -1,4 +1,5 @@
import { useSeoMeta } from '@unhead/react';
import { Layout } from '@/components/Layout';
import { DMMessagingInterface } from '@/components/dm/DMMessagingInterface';
const Messages = () => {
@@ -8,16 +9,11 @@ const Messages = () => {
});
return (
<div className="min-h-screen bg-background">
<div className="container mx-auto p-4 h-screen flex flex-col">
{/* Header */}
<div className="flex items-center justify-between mb-4">
<h1 className="text-2xl font-semibold">Messages</h1>
</div>
<Layout>
<div className="container mx-auto p-4 h-full flex flex-col">
<DMMessagingInterface className="flex-1" />
</div>
</div>
</Layout>
);
};

View File

@@ -1,5 +1,6 @@
import { nip19 } from 'nostr-tools';
import { useParams } from 'react-router-dom';
import { Layout } from '@/components/Layout';
import NotFound from './NotFound';
export function NIP19Page() {
@@ -22,19 +23,51 @@ export function NIP19Page() {
case 'npub':
case 'nprofile':
// AI agent should implement profile view here
return <div>Profile placeholder</div>;
return (
<Layout>
<div className="container py-8">
<div className="max-w-2xl mx-auto">
Profile placeholder
</div>
</div>
</Layout>
);
case 'note':
// AI agent should implement note view here
return <div>Note placeholder</div>;
return (
<Layout>
<div className="container py-8">
<div className="max-w-2xl mx-auto">
Note placeholder
</div>
</div>
</Layout>
);
case 'nevent':
// AI agent should implement event view here
return <div>Event placeholder</div>;
return (
<Layout>
<div className="container py-8">
<div className="max-w-2xl mx-auto">
Event placeholder
</div>
</div>
</Layout>
);
case 'naddr':
// AI agent should implement addressable event view here
return <div>Addressable event placeholder</div>;
return (
<Layout>
<div className="container py-8">
<div className="max-w-2xl mx-auto">
Addressable event placeholder
</div>
</div>
</Layout>
);
default:
return <NotFound />;

View File

@@ -1,6 +1,8 @@
import { useSeoMeta } from "@unhead/react";
import { useLocation } from "react-router-dom";
import { useLocation, Link } from "react-router-dom";
import { useEffect } from "react";
import { Layout } from "@/components/Layout";
import { Button } from "@/components/ui/button";
const NotFound = () => {
const location = useLocation();
@@ -18,15 +20,17 @@ const NotFound = () => {
}, [location.pathname]);
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100 dark:bg-gray-900">
<div className="text-center">
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-gray-100">404</h1>
<p className="text-xl text-gray-600 dark:text-gray-400 mb-4">Oops! Page not found</p>
<a href="/" className="text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 underline">
Return to Home
</a>
<Layout>
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold mb-4">404</h1>
<p className="text-xl text-muted-foreground mb-4">Oops! Page not found</p>
<Button asChild>
<Link to="/">Return to Home</Link>
</Button>
</div>
</div>
</div>
</Layout>
);
};

View File

@@ -0,0 +1,14 @@
import { Layout } from '@/components/Layout';
export function Notifications() {
return (
<Layout>
<div className="container py-8">
<div className="max-w-2xl mx-auto">
<h1 className="text-3xl font-bold mb-4">Notifications</h1>
<p className="text-muted-foreground">Notifications page - coming soon</p>
</div>
</div>
</Layout>
);
}

14
src/pages/Search.tsx Normal file
View File

@@ -0,0 +1,14 @@
import { Layout } from '@/components/Layout';
export function SearchPage() {
return (
<Layout>
<div className="container py-8">
<div className="max-w-2xl mx-auto">
<h1 className="text-3xl font-bold mb-4">Search</h1>
<p className="text-muted-foreground">Search page - coming soon</p>
</div>
</div>
</Layout>
);
}

14
src/pages/Upload.tsx Normal file
View File

@@ -0,0 +1,14 @@
import { Layout } from '@/components/Layout';
export function Upload() {
return (
<Layout>
<div className="container py-8">
<div className="max-w-2xl mx-auto">
<h1 className="text-3xl font-bold mb-4">Upload</h1>
<p className="text-muted-foreground">Upload page - coming soon</p>
</div>
</div>
</Layout>
);
}