308 lines
9.6 KiB
TypeScript
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;
|