Files
relay/client/src/components/chat/rightSidebar/ParticipantsBar.tsx

308 lines
9.6 KiB
TypeScript

import { useEffect, useMemo, useState } from 'react';
import { socket } from '@/socket/socket.tsx';
import { Crown, Sword } from 'lucide-react';
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from '@/components/ui/context-menu.tsx';
import { useOutletContext } from 'react-router-dom';
import zdjecie from '../../../../assets/turtleProfileImg3.webp';
import { ParticipantsProps, UsernameType } from '@/types/types.ts';
import { useChat } from '@/context/chat/useChat.ts';
import { axiosClient } from '@/utils/axiosClient.ts';
function ParticipantsBar() {
const { setMe, initializeContact, setGroupOwner, currentContact, me } =
useChat();
const [participants, setParticipants] = useState<ParticipantsProps[]>([]);
const user: UsernameType = useOutletContext();
const getParticipants = async () => {
try {
const response = await axiosClient.get(
`/api/chat/groups/getMembers/${currentContact?.conversation_id}`,
);
console.log(
'getParticipants for: ',
currentContact?.conversation_id,
'response: ',
response.data,
);
setParticipants(response.data);
} catch (e) {
console.error('Failed to get chat participants: ', e);
}
};
const handleRemoveUser = async (userId: string) => {
socket?.emit(
'remove user from group',
{
group_id: currentContact?.conversation_id,
user_id: userId,
},
(response: { status: 'ok' | 'error'; message: string }) => {
if (response.status == 'ok') {
console.log(response.message);
} else {
console.error('Failed to remove user from group: ', response.message);
}
},
);
// setParticipants((prevMembers) =>
// prevMembers.filter((member) => member.user_id !== userId),
// );
};
const handleAddToAdministrators = async (userId: string) => {
socket?.emit(
'added administrator',
{ group_id: currentContact?.conversation_id, user_id: userId },
(response: { status: 'ok' | 'error'; message: string }) => {
if (response.status == 'ok') {
console.log(response.message);
} else {
console.error(
'Failed to add user to administrators: ',
response.message,
);
}
},
);
};
const handleRemoveFromAdministrators = async (userId: string) => {
socket?.emit(
'removed administrator',
{ group_id: currentContact?.conversation_id, user_id: userId },
(response: { status: 'ok' | 'error'; message: string }) => {
if (response.status == 'ok') {
console.log(response.message);
} else {
console.error(
'Failed to remove user from administrators: ',
response.message,
);
}
},
);
};
useEffect(() => {
if (participants.length > 0 && user?.user_id) {
const userIsAdmin = participants.some(
(participant) =>
participant.user_id === user.user_id && participant.isadmin,
);
const userIsOwner = participants.some(
(participant) =>
participant.user_id === user.user_id && participant.isowner,
);
const whoIsOwner = participants.find(
(participant) => participant.isowner,
);
setGroupOwner(whoIsOwner?.user_id);
setMe({ isGroupAdmin: userIsAdmin, isGroupOwner: userIsOwner });
}
}, [participants, user?.user_id]);
useEffect(() => {
if (currentContact) {
getParticipants();
}
}, [currentContact]);
const sortedParticipants = useMemo(() => {
return [...participants].sort((a, b) => {
if (a.isowner !== b.isowner) {
return b.isowner ? 1 : -1;
}
if (a.isadmin !== b.isadmin) {
return b.isadmin ? 1 : -1;
}
return a.username.localeCompare(b.username);
});
}, [participants]);
useEffect(() => {
if (!socket || !currentContact) return;
const handleAddedToGroup = (msg: {
username: string;
user_id: string;
group_id: string;
isadmin: boolean;
isowner: boolean;
}) => {
const { group_id } = msg;
if (
msg.group_id == currentContact?.conversation_id &&
msg.user_id == user?.user_id
) {
initializeContact({
read: true,
username: msg.username,
user_id: msg.user_id,
id: currentContact?.id,
type: 'group',
conversation_id: msg.group_id,
last_active: new Date().toString(),
last_message: '',
last_message_sender: '',
last_message_time: '',
});
}
if (group_id === currentContact.conversation_id) {
setParticipants((prevMembers) => {
const existingMember = prevMembers.some(
(m) => m.user_id === msg.user_id,
);
if (existingMember) {
return prevMembers;
}
return [...prevMembers, msg];
});
}
};
const handleLeftGroup = (msg: { user_id: string; group_id: string }) => {
if (
msg.group_id == currentContact?.conversation_id &&
msg.user_id == user?.user_id
) {
setParticipants([]);
initializeContact(currentContact);
}
setParticipants((prevMembers) =>
prevMembers.filter((member) => member.user_id !== msg.user_id),
);
};
const handleAddToAdmins = (msg: { user_id: string; group_id: string }) => {
if (msg.group_id === currentContact.conversation_id) {
setParticipants((prevMembers) =>
prevMembers.map((member) =>
member.user_id === msg.user_id
? { ...member, isadmin: true }
: member,
),
);
}
};
const handleRemoveFromAdmins = (msg: {
user_id: string;
group_id: string;
}) => {
console.log('(socket) removed administrator: ', msg);
if (msg.group_id === currentContact.conversation_id) {
if (msg.user_id === user?.user_id) {
setMe({ isGroupAdmin: false, isGroupOwner: false });
}
setParticipants((prevMembers) =>
prevMembers.map((member) =>
member.user_id === msg.user_id
? { ...member, isadmin: false }
: member,
),
);
}
};
socket.on('added administrator', handleAddToAdmins);
socket.on('removed administrator', handleRemoveFromAdmins);
socket.on('added to group', handleAddedToGroup);
socket.on('left group', handleLeftGroup);
return () => {
socket?.off('added to group');
socket?.off('left group');
};
}, [socket, currentContact, currentContact, user?.user_id]);
const ParticipantsList = sortedParticipants?.map(
(participant: ParticipantsProps) => (
<ContextMenu key={participant.user_id}>
<ContextMenuTrigger>
<li className="p-2 hover:bg-zinc-800 rounded-2xl flex items-center justify-between group m-2">
<span className="flex items-center gap-2">
<div className="flex items-center justify-center w-8 h-8 overflow-hidden rounded-full bg-gray-100">
<img
src={zdjecie}
alt="Profile image"
className="h-full w-full object-cover"
/>
</div>
{participant.username}
{participant.isowner ? (
<span className="flex items-center text-yellow-400 text-xs">
<Crown className="h-3 w-3" />
</span>
) : null}
{participant.isadmin && (
<span className="flex items-center text-green-300 text-xs">
<Sword className="h-3 w-3" />
<span className="ml-1 opacity-0 group-hover:opacity-100 transition-opacity">
admin
</span>
</span>
)}
</span>
</li>
</ContextMenuTrigger>
{user.user_id !== participant.user_id &&
me.isGroupAdmin &&
(!participant.isadmin || me.isGroupOwner) ? (
<ContextMenuContent className="p-0">
<ContextMenuItem
className="bg-zinc-900 text-white outline-1 hover:bg-zinc-800 hover:cursor-pointer"
onClick={() => handleRemoveUser(participant.user_id)}
>
<p>Remove from group</p>
</ContextMenuItem>
{!participant.isadmin ? (
<ContextMenuItem
className="bg-zinc-900 text-white outline-1 hover:bg-zinc-800 hover:cursor-pointer"
onClick={() => handleAddToAdministrators(participant.user_id)}
>
<p>Add to group administrator</p>
</ContextMenuItem>
) : null}
{participant.isadmin ? (
<ContextMenuItem
className="bg-zinc-900 text-white outline-1 hover:bg-zinc-800 hover:cursor-pointer"
onClick={() =>
handleRemoveFromAdministrators(participant.user_id)
}
>
<p>Remove from administrator</p>
</ContextMenuItem>
) : null}
</ContextMenuContent>
) : null}
</ContextMenu>
),
);
if (!currentContact) {
return null;
}
return (
<div className="border-l border-zinc-800 bg-zinc-950 h-full w-full flex flex-col ml-auto">
<h1 className="text-xl font-semibold text-gray-200 text-left p-4 border-b border-zinc-800">
Members
</h1>
<ul className=" rounded">{ParticipantsList}</ul>
</div>
);
}
export default ParticipantsBar;