139 lines
3.6 KiB
JavaScript
139 lines
3.6 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import { useParams } from 'react-router-dom';
|
|
import { useSocket } from '../../contexts/SocketContext';
|
|
import { useAuth } from '../../contexts/AuthContext';
|
|
import ChannelList from './ChannelList';
|
|
import ChannelView from './ChannelView';
|
|
import './GameForgeChat.css';
|
|
|
|
/**
|
|
* Embedded chat component for GameForge projects
|
|
* Can be embedded in GameForge UI via iframe or direct integration
|
|
*/
|
|
export default function GameForgeChat({ projectId: propProjectId, embedded = false }) {
|
|
const { projectId: paramProjectId } = useParams();
|
|
const projectId = propProjectId || paramProjectId;
|
|
|
|
const { socket } = useSocket();
|
|
const { user } = useAuth();
|
|
|
|
const [channels, setChannels] = useState([]);
|
|
const [activeChannel, setActiveChannel] = useState(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState(null);
|
|
|
|
// Load channels
|
|
useEffect(() => {
|
|
if (projectId) {
|
|
loadChannels();
|
|
}
|
|
}, [projectId]);
|
|
|
|
// Socket listeners for system notifications
|
|
useEffect(() => {
|
|
if (!socket) return;
|
|
|
|
socket.on('gameforge:notification', handleNotification);
|
|
|
|
return () => {
|
|
socket.off('gameforge:notification', handleNotification);
|
|
};
|
|
}, [socket]);
|
|
|
|
const loadChannels = async () => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
const response = await fetch(`/api/gameforge/projects/${projectId}/channels`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
setChannels(data.channels);
|
|
|
|
// Auto-select general channel
|
|
const generalChannel = data.channels.find(c => c.name === 'general');
|
|
if (generalChannel) {
|
|
setActiveChannel(generalChannel);
|
|
}
|
|
} else {
|
|
setError(data.error);
|
|
}
|
|
|
|
} catch (err) {
|
|
console.error('Failed to load channels:', err);
|
|
setError('Failed to load project channels');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleNotification = (notification) => {
|
|
// Update channel with new system message
|
|
const { channelId, message } = notification;
|
|
|
|
setChannels(prev => prev.map(channel => {
|
|
if (channel.id === channelId) {
|
|
return {
|
|
...channel,
|
|
lastMessage: message,
|
|
unreadCount: activeChannel?.id === channelId ? 0 : channel.unreadCount + 1
|
|
};
|
|
}
|
|
return channel;
|
|
}));
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className={`gameforge-chat ${embedded ? 'embedded' : ''}`}>
|
|
<div className="loading">Loading project chat...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className={`gameforge-chat ${embedded ? 'embedded' : ''}`}>
|
|
<div className="error">
|
|
<p>{error}</p>
|
|
<button onClick={loadChannels}>Retry</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={`gameforge-chat ${embedded ? 'embedded' : ''}`}>
|
|
<div className="gameforge-chat-sidebar">
|
|
<div className="project-header">
|
|
<h3>Project Channels</h3>
|
|
</div>
|
|
|
|
<ChannelList
|
|
channels={channels}
|
|
activeChannel={activeChannel}
|
|
onSelectChannel={setActiveChannel}
|
|
/>
|
|
</div>
|
|
|
|
<div className="gameforge-chat-main">
|
|
{activeChannel ? (
|
|
<ChannelView
|
|
channel={activeChannel}
|
|
projectId={projectId}
|
|
/>
|
|
) : (
|
|
<div className="no-channel-selected">
|
|
<p>Select a channel to start chatting</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|