134 lines
4.6 KiB
JavaScript
134 lines
4.6 KiB
JavaScript
import React, { useEffect, useRef } from "react";
|
|
import { useMemberStore } from "../../stores/memberStore";
|
|
import { useModalStore } from "../../stores/modalStore";
|
|
import { Settings } from "lucide-react";
|
|
|
|
export default function MemberSidebar() {
|
|
const members = useMemberStore((state) => state.members);
|
|
const getCurrentUser = useMemberStore((state) => state.getCurrentUser);
|
|
const onOpen = useModalStore((state) => state.onOpen);
|
|
|
|
const currentUser = getCurrentUser();
|
|
const isAdmin = currentUser?.role === "ADMIN";
|
|
|
|
// Group members by section
|
|
const groupedMembers = members.reduce((acc, member) => {
|
|
const section = member.division || "Others";
|
|
if (!acc[section]) acc[section] = [];
|
|
acc[section].push(member);
|
|
return acc;
|
|
}, {});
|
|
|
|
const joined = false;
|
|
const peers = [];
|
|
const localStream = null;
|
|
|
|
function RemoteAudio({ stream }) {
|
|
const audioRef = useRef();
|
|
useEffect(() => {
|
|
if (audioRef.current && stream) {
|
|
audioRef.current.srcObject = stream;
|
|
}
|
|
}, [stream]);
|
|
return <audio ref={audioRef} autoPlay playsInline />;
|
|
}
|
|
|
|
function LocalAudio() {
|
|
const audioRef = useRef();
|
|
useEffect(() => {
|
|
if (audioRef.current && localStream) {
|
|
audioRef.current.srcObject = localStream;
|
|
}
|
|
}, [localStream]);
|
|
return <audio ref={audioRef} autoPlay playsInline muted />;
|
|
}
|
|
|
|
return (
|
|
<div className="member-sidebar">
|
|
<div className="member-header">
|
|
<span>Members — {members.length}</span>
|
|
{isAdmin && (
|
|
<button
|
|
onClick={() => onOpen("manageMembers")}
|
|
className="manage-members-button"
|
|
title="Manage members"
|
|
>
|
|
<Settings className="w-4 h-4" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
<div className="member-list">
|
|
{/* Show all connected voice users */}
|
|
{(joined || (peers && peers.length > 0)) && (
|
|
<div className="member-section">
|
|
<div className="member-section-title" style={{ color: '#0066ff' }}>
|
|
Voice Channel — Nexus Lounge
|
|
</div>
|
|
{joined && (
|
|
<div className="member-item voice-active">
|
|
<div className="member-avatar-small">
|
|
<span role="img" aria-label="mic">🎤</span>
|
|
<div className="online-indicator"></div>
|
|
</div>
|
|
<div className="member-name">You (Voice Connected)</div>
|
|
<div className="member-activity">Live</div>
|
|
<LocalAudio />
|
|
</div>
|
|
)}
|
|
{peers && peers.map(({ peerId, stream }) => (
|
|
<div key={peerId} className="member-item voice-peer">
|
|
<div className="member-avatar-small">
|
|
<span role="img" aria-label="mic">🎤</span>
|
|
<div className="online-indicator"></div>
|
|
</div>
|
|
<div className="member-name">{peerId}</div>
|
|
<div className="member-activity">Live</div>
|
|
<RemoteAudio stream={stream} />
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Members by division */}
|
|
{Object.entries(groupedMembers).map(([section, sectionMembers]) => (
|
|
<div key={section} className="member-section">
|
|
<div className="member-section-title">
|
|
{section} — {sectionMembers.length}
|
|
</div>
|
|
{sectionMembers.map((member) => (
|
|
<div
|
|
key={member.id}
|
|
className="member-item"
|
|
onClick={() => onOpen("userProfile", { member })}
|
|
>
|
|
<div className={`member-avatar-small ${member.avatarBg}`}>
|
|
{member.avatar}
|
|
{member.status !== "offline" && (
|
|
<div
|
|
className={`online-indicator ${
|
|
member.status === "online"
|
|
? "online"
|
|
: member.status === "in-game"
|
|
? "in-game"
|
|
: member.status === "labs"
|
|
? "labs"
|
|
: "idle"
|
|
}`}
|
|
></div>
|
|
)}
|
|
</div>
|
|
<div className="member-info">
|
|
<div className="member-name">{member.name}</div>
|
|
{member.activity && (
|
|
<div className="member-activity">{member.activity}</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|