gilded / client /src /components /Layout.jsx
OmegaOne
Upload 41 files
0a8fe79 verified
import React, { useState, useEffect, useCallback } from 'react';
import { useAuth } from '../context/AuthContext';
import { useSocket } from '../context/SocketContext';
import ServerList from './ServerList';
import ChannelSidebar from './ChannelSidebar';
// Lazy-loaded modals — these will be built in a separate batch
const CreateServerModal = React.lazy(() => import('./modals/CreateServerModal'));
const ServerSettings = React.lazy(() => import('./modals/ServerSettings'));
const UserSettings = React.lazy(() => import('./modals/UserSettings'));
const InviteModal = React.lazy(() => import('./modals/InviteModal'));
// Chat area — will be built separately
const ChatArea = React.lazy(() => import('./ChatArea'));
const MemberList = React.lazy(() => import('./MemberList'));
export default function Layout() {
const { user, token } = useAuth();
const { socket } = useSocket();
const [servers, setServers] = useState([]);
const [activeServer, setActiveServer] = useState(null);
const [activeChannel, setActiveChannel] = useState(null);
const [serverDetail, setServerDetail] = useState(null);
const [showServerSettings, setShowServerSettings] = useState(false);
const [showUserSettings, setShowUserSettings] = useState(false);
const [showCreateServer, setShowCreateServer] = useState(false);
const [showInviteModal, setShowInviteModal] = useState(false);
const [showMemberList, setShowMemberList] = useState(true);
// Fetch user's servers
const fetchServers = useCallback(async () => {
try {
const res = await fetch('/api/servers', {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) return;
const data = await res.json();
const list = data.servers || data;
setServers(list);
// Auto-select first server if none active
if (!activeServer && list.length > 0) {
setActiveServer(list[0]);
}
} catch (err) {
console.error('Failed to fetch servers:', err);
}
}, [token, activeServer]);
useEffect(() => {
fetchServers();
}, [fetchServers]);
// Fetch full server detail when active server changes
useEffect(() => {
if (!activeServer) {
setServerDetail(null);
setActiveChannel(null);
return;
}
const fetchDetail = async () => {
try {
const res = await fetch(`/api/servers/${activeServer.id}`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) return;
const data = await res.json();
const detail = data.server || data;
setServerDetail(detail);
// Auto-select first channel
if (detail.channels && detail.channels.length > 0) {
setActiveChannel(detail.channels[0]);
} else if (detail.categories) {
for (const cat of detail.categories) {
if (cat.channels && cat.channels.length > 0) {
setActiveChannel(cat.channels[0]);
break;
}
}
}
} catch (err) {
console.error('Failed to fetch server detail:', err);
}
};
fetchDetail();
// Join server room via socket
if (socket) {
socket.emit('server:join', activeServer.id);
}
return () => {
if (socket && activeServer) {
socket.emit('server:leave', activeServer.id);
}
};
}, [activeServer?.id, token, socket]);
// Socket listeners for server updates
useEffect(() => {
if (!socket) return;
const handleServerUpdate = (data) => {
setServers((prev) =>
prev.map((s) => (s.id === data.id ? { ...s, ...data } : s))
);
if (activeServer?.id === data.id) {
setActiveServer((prev) => (prev ? { ...prev, ...data } : prev));
}
};
socket.on('server:updated', handleServerUpdate);
return () => {
socket.off('server:updated', handleServerUpdate);
};
}, [socket, activeServer?.id]);
const handleSelectServer = (server) => {
setActiveServer(server);
};
const handleOpenDMs = () => {
setActiveServer(null);
setServerDetail(null);
setActiveChannel(null);
};
const handleSelectChannel = (channel) => {
setActiveChannel(channel);
};
const handleServerCreated = (newServer) => {
setServers((prev) => [...prev, newServer]);
setActiveServer(newServer);
setShowCreateServer(false);
};
const dmMode = activeServer === null;
return (
<div className="flex h-screen bg-[#0e0e10] overflow-hidden">
{/* Server List */}
<ServerList
servers={servers}
activeServer={activeServer}
onSelectServer={handleSelectServer}
onOpenDMs={handleOpenDMs}
onCreateServer={() => setShowCreateServer(true)}
onOpenUserSettings={() => setShowUserSettings(true)}
user={user}
/>
{/* Channel Sidebar */}
<ChannelSidebar
server={serverDetail}
activeChannel={activeChannel}
onSelectChannel={handleSelectChannel}
onOpenServerSettings={() => setShowServerSettings(true)}
onOpenInvite={() => setShowInviteModal(true)}
dmMode={dmMode}
user={user}
token={token}
/>
{/* Chat Area */}
<div className="flex-1 flex flex-col min-w-0">
<React.Suspense
fallback={
<div className="flex-1 flex items-center justify-center bg-[#1e1e22]">
<div className="w-8 h-8 border-3 border-[#FFD700] border-t-transparent rounded-full animate-spin" />
</div>
}
>
<ChatArea
server={activeServer}
channel={activeChannel}
token={token}
user={user}
socket={socket}
onToggleMemberList={() => setShowMemberList((v) => !v)}
showMemberList={showMemberList}
/>
</React.Suspense>
</div>
{/* Member List */}
{showMemberList && activeServer && (
<React.Suspense fallback={null}>
<MemberList server={activeServer} token={token} />
</React.Suspense>
)}
{/* Modals */}
<React.Suspense fallback={null}>
{showCreateServer && (
<CreateServerModal
onClose={() => setShowCreateServer(false)}
onCreated={handleServerCreated}
token={token}
/>
)}
{showServerSettings && serverDetail && (
<ServerSettings
server={serverDetail}
onClose={() => setShowServerSettings(false)}
token={token}
onUpdate={(updated) => {
setServerDetail((prev) => ({ ...prev, ...updated }));
setServers((prev) =>
prev.map((s) => (s.id === updated.id ? { ...s, ...updated } : s))
);
}}
/>
)}
{showUserSettings && (
<UserSettings onClose={() => setShowUserSettings(false)} />
)}
{showInviteModal && activeServer && (
<InviteModal
server={activeServer}
onClose={() => setShowInviteModal(false)}
token={token}
/>
)}
</React.Suspense>
</div>
);
}