101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect, useRef, useState } from 'react';
|
||
import { useParams } from 'next/navigation';
|
||
import { io, Socket } from 'socket.io-client';
|
||
import { Send } from 'lucide-react';
|
||
import { Button } from '@/components/ui/button';
|
||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||
import { api } from '@/lib/api';
|
||
import { getWsBaseUrl } from '@/lib/ws';
|
||
|
||
export default function InviteGroupPage() {
|
||
const params = useParams();
|
||
const groupId = params?.groupId as string;
|
||
const [messages, setMessages] = useState<any[]>([]);
|
||
const [members, setMembers] = useState<any[]>([]);
|
||
const [message, setMessage] = useState('');
|
||
const socketRef = useRef<Socket | null>(null);
|
||
|
||
useEffect(() => {
|
||
if (!groupId) return;
|
||
(async () => {
|
||
await api.joinGroupByInvite(groupId).catch(() => null);
|
||
const [msgs, mbrs] = await Promise.all([
|
||
api.getGroupMessages(groupId).catch(() => []),
|
||
api.getGroupMembers(groupId).catch(() => []),
|
||
]);
|
||
setMessages(msgs);
|
||
setMembers(mbrs);
|
||
})();
|
||
}, [groupId]);
|
||
|
||
useEffect(() => {
|
||
if (!groupId) return;
|
||
const token =
|
||
typeof window !== 'undefined' ? sessionStorage.getItem('coursecraft_api_token') || undefined : undefined;
|
||
const socket = io(`${getWsBaseUrl()}/ws/course-groups`, {
|
||
transports: ['websocket'],
|
||
auth: { token },
|
||
});
|
||
socketRef.current = socket;
|
||
socket.emit('groups:join', { groupId });
|
||
socket.on('groups:new-message', (msg: any) => setMessages((prev) => [...prev, msg]));
|
||
return () => {
|
||
socket.disconnect();
|
||
socketRef.current = null;
|
||
};
|
||
}, [groupId]);
|
||
|
||
const send = async () => {
|
||
if (!groupId || !message.trim()) return;
|
||
await api.sendGroupMessage(groupId, message.trim());
|
||
setMessage('');
|
||
};
|
||
|
||
return (
|
||
<div className="grid gap-4 lg:grid-cols-[1fr_320px]">
|
||
<Card className="min-h-[460px]">
|
||
<CardHeader>
|
||
<CardTitle>Групповой чат курса</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="flex h-[400px] flex-col">
|
||
<div className="flex-1 overflow-auto space-y-2 pr-2">
|
||
{messages.map((msg) => (
|
||
<div key={msg.id} className="rounded-md border p-2 text-sm">
|
||
<p className="font-medium">{msg.user?.name || 'Участник'}</p>
|
||
<p>{msg.content}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div className="mt-3 flex gap-2">
|
||
<input
|
||
value={message}
|
||
onChange={(e) => setMessage(e.target.value)}
|
||
className="flex-1 rounded-md border bg-background px-3 py-2 text-sm"
|
||
placeholder="Сообщение"
|
||
/>
|
||
<Button onClick={send}>
|
||
<Send className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle>Участники ({members.length})</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-2">
|
||
{members.map((member) => (
|
||
<div key={member.id} className="rounded-md border p-2 text-sm">
|
||
<p className="font-medium">{member.user?.name || member.user?.email}</p>
|
||
<p className="text-xs text-muted-foreground">{member.role}</p>
|
||
</div>
|
||
))}
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
);
|
||
}
|