AeThex-Connect/astro-site/src/react-app/components/GameForgeChat/ChannelView.jsx
MrPiglr de54903c15
new file: astro-site/src/components/auth/SupabaseLogin.jsx
new file:   astro-site/src/components/auth/SupabaseLogin.jsx
2026-02-03 09:09:36 +00:00

172 lines
4.6 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import { useSocket } from '../../contexts/SocketContext';
import MessageList from '../Chat/MessageList';
import MessageInput from '../Chat/MessageInput';
import { encryptMessage, decryptMessage } from '../../utils/crypto';
import { useAuth } from '../../contexts/AuthContext';
import './ChannelView.css';
export default function ChannelView({ channel, projectId }) {
const { socket } = useSocket();
const { user } = useAuth();
const [messages, setMessages] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (channel) {
loadMessages();
}
}, [channel]);
// Socket listeners
useEffect(() => {
if (!socket || !channel) return;
const handleNewMessage = async (data) => {
if (data.conversationId === channel.id) {
const message = data.message;
// Decrypt if not system message
if (message.contentType !== 'system') {
try {
const decrypted = await decryptMessage(
JSON.parse(message.content),
user.password
);
message.content = decrypted;
} catch (error) {
console.error('Failed to decrypt message:', error);
message.content = '[Failed to decrypt]';
}
}
setMessages(prev => [message, ...prev]);
}
};
socket.on('message:new', handleNewMessage);
return () => {
socket.off('message:new', handleNewMessage);
};
}, [socket, channel]);
const loadMessages = async () => {
try {
setLoading(true);
const response = await fetch(`/api/conversations/${channel.id}/messages`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
const data = await response.json();
if (data.success) {
// Decrypt messages
const decryptedMessages = await Promise.all(
data.messages.map(async (msg) => {
// System messages are not encrypted
if (msg.contentType === 'system') {
return msg;
}
try {
const decrypted = await decryptMessage(
JSON.parse(msg.content),
user.password
);
return {
...msg,
content: decrypted
};
} catch (error) {
console.error('Failed to decrypt message:', error);
return {
...msg,
content: '[Failed to decrypt]'
};
}
})
);
setMessages(decryptedMessages);
}
} catch (error) {
console.error('Failed to load messages:', error);
} finally {
setLoading(false);
}
};
const sendMessage = async (content) => {
if (!content.trim()) return;
try {
// Get recipient public keys (all channel participants)
const participantsResponse = await fetch(`/api/conversations/${channel.id}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
const participantsData = await participantsResponse.json();
if (!participantsData.success) {
throw new Error('Failed to get participants');
}
const recipientKeys = participantsData.conversation.participants
.map(p => p.publicKey)
.filter(Boolean);
// Encrypt message
const encrypted = await encryptMessage(content, recipientKeys);
// Send via WebSocket
socket.emit('message:send', {
conversationId: channel.id,
content: JSON.stringify(encrypted),
contentType: 'text',
clientId: `temp-${Date.now()}`
});
} catch (error) {
console.error('Failed to send message:', error);
}
};
if (loading) {
return <div className="channel-view loading">Loading messages...</div>;
}
return (
<div className="channel-view">
<div className="channel-header">
<h3>#{channel.name}</h3>
<p className="channel-description">{channel.description}</p>
{channel.permissions && !channel.permissions.includes('all') && (
<span className="restricted-badge">
Restricted to: {channel.permissions.join(', ')}
</span>
)}
</div>
<MessageList
messages={messages}
currentUserId={user.id}
typingUsers={new Set()}
showSystemMessages={true}
/>
<MessageInput
onSend={sendMessage}
onTyping={() => {}}
onStopTyping={() => {}}
placeholder={`Message #${channel.name}`}
/>
</div>
);
}