feat: Add a large collection of emoji and other frontend assets, including a sound file, and a backend package.json.
This commit is contained in:
@@ -1,24 +1,86 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { io } from 'socket.io-client';
|
||||
import Sidebar from '../components/Sidebar';
|
||||
import ChatArea from '../components/ChatArea';
|
||||
import VoiceStage from '../components/VoiceStage';
|
||||
import { useVoice } from '../contexts/VoiceContext';
|
||||
import FriendsView from '../components/FriendsView';
|
||||
|
||||
const Chat = () => {
|
||||
const [view, setView] = useState('server'); // 'server' | 'me'
|
||||
const [activeChannel, setActiveChannel] = useState(null);
|
||||
const [channels, setChannels] = useState([]);
|
||||
const [username, setUsername] = useState('');
|
||||
const [userId, setUserId] = useState(null);
|
||||
const [channelKeys, setChannelKeys] = useState({}); // { channelId: key_hex }
|
||||
|
||||
const refreshData = () => {
|
||||
const storedUsername = localStorage.getItem('username');
|
||||
const userId = localStorage.getItem('userId');
|
||||
const privateKey = sessionStorage.getItem('privateKey');
|
||||
|
||||
if (storedUsername) setUsername(storedUsername);
|
||||
if (userId) setUserId(userId);
|
||||
|
||||
if (userId && privateKey) {
|
||||
// Fetch Encrypted Channel Keys
|
||||
fetch(`http://localhost:3000/api/channels/keys/${userId}`)
|
||||
.then(res => res.json())
|
||||
.then(async (data) => {
|
||||
const keys = {};
|
||||
for (const item of data) {
|
||||
try {
|
||||
const bundleJson = await window.cryptoAPI.privateDecrypt(privateKey, item.encrypted_key_bundle);
|
||||
const bundle = JSON.parse(bundleJson);
|
||||
Object.assign(keys, bundle);
|
||||
} catch (e) {
|
||||
console.error(`Failed to decrypt keys for channel ${item.channel_id}`, e);
|
||||
}
|
||||
}
|
||||
setChannelKeys(keys);
|
||||
})
|
||||
.catch(err => console.error('Error fetching channel keys:', err));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetch('http://localhost:3000/api/channels')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
setChannels(data);
|
||||
if (data.length > 0 && !activeChannel) {
|
||||
setActiveChannel(data[0].id);
|
||||
if (!activeChannel && data.length > 0) {
|
||||
const firstTextChannel = data.find(c => c.type === 'text');
|
||||
if (firstTextChannel) {
|
||||
setActiveChannel(firstTextChannel.id);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => console.error('Error fetching channels:', err));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
refreshData();
|
||||
|
||||
// Listen for updates (requires socket connection)
|
||||
const socket = io('http://localhost:3000');
|
||||
|
||||
socket.on('new_channel', (channel) => {
|
||||
console.log("New Channel Detected:", channel);
|
||||
refreshData(); // Re-fetch keys/channels
|
||||
});
|
||||
|
||||
socket.on('channel_renamed', () => refreshData());
|
||||
socket.on('channel_deleted', (id) => {
|
||||
refreshData();
|
||||
if (activeChannel === id) setActiveChannel(null);
|
||||
});
|
||||
|
||||
return () => socket.disconnect();
|
||||
}, []);
|
||||
|
||||
if (!activeChannel) return <div style={{ color: 'white', padding: 20 }}>Loading...</div>;
|
||||
|
||||
// Helper to get active channel object
|
||||
const activeChannelObj = channels.find(c => c.id === activeChannel);
|
||||
|
||||
const { room, voiceStates } = useVoice();
|
||||
|
||||
return (
|
||||
<div className="app-container">
|
||||
@@ -26,11 +88,33 @@ const Chat = () => {
|
||||
channels={channels}
|
||||
activeChannel={activeChannel}
|
||||
onSelectChannel={setActiveChannel}
|
||||
username={username}
|
||||
channelKeys={channelKeys}
|
||||
onChannelCreated={refreshData} // Use refresh instead of reload
|
||||
view={view}
|
||||
onViewChange={setView}
|
||||
/>
|
||||
<ChatArea
|
||||
channelId={activeChannel}
|
||||
channelName={channels.find(c => c.id === activeChannel)?.name || activeChannel}
|
||||
/>
|
||||
{view === 'me' ? (
|
||||
<FriendsView />
|
||||
) : activeChannel ? (
|
||||
activeChannelObj?.type === 'voice' ? (
|
||||
<VoiceStage room={room} channelId={activeChannel} voiceStates={voiceStates} channelName={activeChannelObj?.name} />
|
||||
) : (
|
||||
<ChatArea
|
||||
channelId={activeChannel}
|
||||
channelName={activeChannelObj?.name || activeChannel}
|
||||
channelKey={channelKeys[activeChannel]}
|
||||
username={username}
|
||||
userId={userId}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b9bbbe', flexDirection: 'column' }}>
|
||||
<h2>Welcome to Secure Chat</h2>
|
||||
<p>No channels found.</p>
|
||||
<p>Click the <b>+</b> in the sidebar to create your first encrypted channel.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user