diff --git a/app/chat/[pubkey]/page.tsx b/app/chat/[pubkey]/page.tsx
new file mode 100644
index 0000000..9a88fe1
--- /dev/null
+++ b/app/chat/[pubkey]/page.tsx
@@ -0,0 +1,23 @@
+import { Metadata } from 'next';
+import ChatInterface from '@/components/chat/ChatInterface';
+
+export const metadata: Metadata = {
+ title: 'Chat | Lumina',
+ description: 'Direct message chat with a Nostr user',
+};
+
+interface ChatPageProps {
+ params: {
+ pubkey: string;
+ }
+}
+
+export default function ChatPage({ params }: ChatPageProps) {
+ const { pubkey } = params;
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/components/chat/ChatHeader.tsx b/components/chat/ChatHeader.tsx
new file mode 100644
index 0000000..acb4d94
--- /dev/null
+++ b/components/chat/ChatHeader.tsx
@@ -0,0 +1,50 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import { useProfile } from 'nostr-react';
+import { ChevronLeft } from 'lucide-react';
+import { useRouter } from 'next/navigation';
+import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
+
+interface ChatHeaderProps {
+ pubkey: string;
+}
+
+export default function ChatHeader({ pubkey }: ChatHeaderProps) {
+ const router = useRouter();
+ const { data: userData } = useProfile({
+ pubkey,
+ });
+
+ const [displayName, setDisplayName] = useState(pubkey.substring(0, 8) + '...');
+
+ useEffect(() => {
+ if (userData?.name) {
+ setDisplayName(userData.name);
+ }
+ }, [userData]);
+
+ return (
+
+
+
+
+
+ {displayName.substring(0, 2).toUpperCase()}
+
+
+
+
{displayName}
+ {userData?.nip05 && (
+
{userData.nip05}
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/components/chat/ChatInterface.tsx b/components/chat/ChatInterface.tsx
new file mode 100644
index 0000000..d17eb1d
--- /dev/null
+++ b/components/chat/ChatInterface.tsx
@@ -0,0 +1,65 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { useNostrEvents, dateToUnix } from 'nostr-react';
+import ChatHeader from './ChatHeader';
+import MessageList from './MessageList';
+import MessageInput from './MessageInput';
+
+interface ChatInterfaceProps {
+ pubkey: string;
+}
+
+export default function ChatInterface({ pubkey }: ChatInterfaceProps) {
+ const [messages, setMessages] = useState([]);
+ const now = new Date();
+
+ // Fetch direct messages (kind 4) between the current user and the specified pubkey
+ const { events } = useNostrEvents({
+ filter: {
+ kinds: [4], // Kind 4 is direct messages in Nostr
+ authors: [/* would be populated with current user pubkey */],
+ since: dateToUnix(new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)), // Last 7 days
+ },
+ });
+
+ // For demo purposes, let's add some dummy messages
+ useEffect(() => {
+ const dummyMessages = [
+ { id: '1', sender: 'you', content: 'Hello there!', timestamp: new Date(now.getTime() - 3600000) },
+ { id: '2', sender: pubkey, content: 'Hi! How are you?', timestamp: new Date(now.getTime() - 3500000) },
+ { id: '3', sender: 'you', content: 'I\'m doing well, thanks for asking!', timestamp: new Date(now.getTime() - 3400000) },
+ { id: '4', sender: pubkey, content: 'That\'s great to hear! What have you been up to?', timestamp: new Date(now.getTime() - 3300000) },
+ { id: '5', sender: 'you', content: 'Just exploring the Nostr protocol and building some cool stuff with it.', timestamp: new Date(now.getTime() - 3200000) },
+ { id: '6', sender: pubkey, content: 'That sounds interesting! I\'ve been hearing a lot about Nostr lately.', timestamp: new Date(now.getTime() - 3100000) },
+ { id: '7', sender: 'you', content: 'Yeah, it\'s a fascinating protocol for decentralized social networking. Very simple yet powerful.', timestamp: new Date(now.getTime() - 3000000) },
+ { id: '8', sender: pubkey, content: 'I should look into it more. Any resources you recommend?', timestamp: new Date(now.getTime() - 2900000) },
+ { id: '9', sender: 'you', content: 'Check out the NIPs (Nostr Implementation Possibilities) on GitHub, and there are several good clients to try out.', timestamp: new Date(now.getTime() - 2800000) },
+ { id: '10', sender: pubkey, content: 'Thanks for the tip! I\'ll definitely check those out.', timestamp: new Date(now.getTime() - 2700000) },
+ ];
+
+ setMessages(dummyMessages);
+ }, [pubkey]);
+
+ const handleSendMessage = (message: string) => {
+ if (!message.trim()) return;
+
+ // In a real implementation, this would publish to Nostr
+ const newMessage = {
+ id: Date.now().toString(),
+ sender: 'you',
+ content: message,
+ timestamp: new Date(),
+ };
+
+ setMessages(prev => [...prev, newMessage]);
+ };
+
+ return (
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/components/chat/MessageInput.tsx b/components/chat/MessageInput.tsx
new file mode 100644
index 0000000..78339d1
--- /dev/null
+++ b/components/chat/MessageInput.tsx
@@ -0,0 +1,78 @@
+'use client';
+
+import { useState, useRef } from 'react';
+import { SendHorizonal, PaperclipIcon } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+
+interface MessageInputProps {
+ onSendMessage: (message: string) => void;
+}
+
+export default function MessageInput({ onSendMessage }: MessageInputProps) {
+ const [message, setMessage] = useState('');
+ const textareaRef = useRef(null);
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (message.trim()) {
+ onSendMessage(message);
+ setMessage('');
+ }
+ };
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ // Send on Enter (without shift for a new line)
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ handleSubmit(e);
+ }
+ };
+
+ // Auto-resize textarea based on content
+ const handleInput = () => {
+ const textarea = textareaRef.current;
+ if (textarea) {
+ textarea.style.height = 'auto';
+ textarea.style.height = `${Math.min(textarea.scrollHeight, 150)}px`;
+ }
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/components/chat/MessageList.tsx b/components/chat/MessageList.tsx
new file mode 100644
index 0000000..f586994
--- /dev/null
+++ b/components/chat/MessageList.tsx
@@ -0,0 +1,56 @@
+'use client';
+
+import { useRef, useEffect } from 'react';
+import { ScrollArea } from '@/components/ui/scroll-area';
+
+interface Message {
+ id: string;
+ sender: string;
+ content: string;
+ timestamp: Date;
+}
+
+interface MessageListProps {
+ messages: Message[];
+ currentUserPubkey: string;
+}
+
+export default function MessageList({ messages, currentUserPubkey }: MessageListProps) {
+ const messagesEndRef = useRef(null);
+
+ // Auto-scroll to bottom when new messages arrive
+ useEffect(() => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, [messages]);
+
+ return (
+
+
+ {messages.map((message) => {
+ const isCurrentUser = message.sender === currentUserPubkey;
+
+ return (
+
+
+
{message.content}
+
+ {new Date(message.timestamp).toLocaleString()}
+
+
+
+ );
+ })}
+
+
+
+ );
+}
\ No newline at end of file