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:
Bryan1029384756
2026-01-06 17:58:56 -06:00
parent f531301863
commit abedd78893
3795 changed files with 10981 additions and 229 deletions

View File

@@ -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>
);
};