This commit is contained in:
Sewmina 2025-05-31 19:48:39 +05:30
parent ac6aae672d
commit 5d3a7505a5
4 changed files with 255 additions and 1 deletions

137
package-lock.json generated
View File

@ -21,6 +21,7 @@
"react-dom": "^19.1.0",
"react-icons": "^5.5.0",
"react-toastify": "^11.0.5",
"socket.io-client": "^4.8.1",
"sonner": "^2.0.3",
"viem": "^2.24.2"
},
@ -2276,6 +2277,12 @@
"integrity": "sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==",
"license": "MIT"
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
"license": "MIT"
},
"node_modules/@solana/buffer-layout": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz",
@ -5075,6 +5082,66 @@
"once": "^1.4.0"
}
},
"node_modules/engine.io-client": {
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
"integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1",
"xmlhttprequest-ssl": "~2.1.1"
}
},
"node_modules/engine.io-client/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/enhanced-resolve": {
"version": "5.18.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
@ -9243,6 +9310,68 @@
"is-arrayish": "^0.3.1"
}
},
"node_modules/socket.io-client": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.6.1",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-client/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socket.io-parser": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/sonic-boom": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz",
@ -10438,6 +10567,14 @@
}
}
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",

View File

@ -22,6 +22,7 @@
"react-dom": "^19.1.0",
"react-icons": "^5.5.0",
"react-toastify": "^11.0.5",
"socket.io-client": "^4.8.1",
"sonner": "^2.0.3",
"viem": "^2.24.2"
},

View File

@ -5,6 +5,7 @@ import Header from "@/components/Header";
import HeroSection from "@/components/HeroSection";
import Leaderboard from "@/components/Leaderboard";
import Activities from "@/components/Activities";
import GlobalChat from "@/components/GlobalChat";
import { PrivyProvider } from "@privy-io/react-auth";
import { toSolanaWalletConnectors } from "@privy-io/react-auth/solana";
import { Toaster } from "sonner";
@ -43,7 +44,7 @@ export default function Home() {
<Activities />
<div className="container mt-10"></div>
<GlobalChat />
<Footer />
</>
</PrivyProvider>

View File

@ -0,0 +1,115 @@
"use client";
import { useEffect, useState } from 'react';
import { usePrivy } from '@privy-io/react-auth';
import { io, Socket } from 'socket.io-client';
import { toast } from 'sonner';
interface ChatMessage {
id: string;
user: string;
message: string;
timestamp: number;
}
export default function GlobalChat() {
const { user, authenticated } = usePrivy();
const [socket, setSocket] = useState<Socket | null>(null);
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [newMessage, setNewMessage] = useState('');
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
if (!authenticated) return;
// Initialize socket connection
const socketInstance = io(process.env.NEXT_PUBLIC_SOCKET_URL || 'http://localhost:3001', {
auth: {
token: user?.id // Using Privy user ID as authentication
}
});
socketInstance.on('connect', () => {
setIsConnected(true);
toast.success('Connected to chat');
});
socketInstance.on('disconnect', () => {
setIsConnected(false);
toast.error('Disconnected from chat');
});
socketInstance.on('chat message', (message: ChatMessage) => {
setMessages(prev => [...prev, message]);
});
setSocket(socketInstance);
return () => {
socketInstance.disconnect();
};
}, [authenticated, user]);
const sendMessage = (e: React.FormEvent) => {
e.preventDefault();
if (!socket || !newMessage.trim() || !user) return;
const message: ChatMessage = {
id: Date.now().toString(),
user: user.id,
message: newMessage.trim(),
timestamp: Date.now()
};
socket.emit('chat message', message);
setNewMessage('');
};
if (!authenticated) {
return (
<div className="p-4 text-center text-gray-400">
Please log in to join the chat
</div>
);
}
return (
<div className="fixed bottom-4 right-4 w-80 bg-[rgb(30,30,30)] rounded-lg shadow-lg">
<div className="p-4 border-b border-gray-700">
<h3 className="text-lg font-semibold text-white">Global Chat</h3>
<div className="text-sm text-gray-400">
{isConnected ? 'Connected' : 'Disconnected'}
</div>
</div>
<div className="h-96 overflow-y-auto p-4 space-y-2">
{messages.map((msg) => (
<div key={msg.id} className="bg-[rgb(40,40,40)] p-2 rounded">
<div className="text-sm text-gray-400">
{msg.user === user?.id ? 'You' : `User ${msg.user.slice(0, 6)}`}
</div>
<div className="text-white">{msg.message}</div>
</div>
))}
</div>
<form onSubmit={sendMessage} className="p-4 border-t border-gray-700">
<div className="flex gap-2">
<input
type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
placeholder="Type a message..."
className="flex-1 bg-[rgb(40,40,40)] text-white px-3 py-2 rounded focus:outline-none focus:ring-2 focus:ring-orange-500"
/>
<button
type="submit"
className="bg-orange-500 text-white px-4 py-2 rounded hover:bg-orange-600 transition-colors"
>
Send
</button>
</div>
</form>
</div>
);
}