Загрузка данных
import {
Hash,
Bell,
Users,
Smile,
PlusCircle,
Gift,
Pencil,
Trash2
} from "lucide-react";
import {
useEffect,
useState
} from "react";
import socket from "../lib/socket";
import useChatStore from "../store/chatStore";
import useServerStore from "../store/serverStore";
import useAuthStore from "../store/authStore";
export default function ChatPage() {
const user = useAuthStore((s) => s.user);
const [message, setMessage] = useState("");
const [editingId, setEditingId] =
useState(null);
const [editingText, setEditingText] =
useState("");
const selectedChannel = useServerStore(
(s) => s.selectedChannel
);
const {
messages,
addMessage,
deleteMessage,
editMessage,
typingUsers,
setTypingUsers
} = useChatStore();
useEffect(() => {
if (!selectedChannel) return;
socket.emit(
"join-channel",
selectedChannel.id
);
socket.on("new-message", (data) => {
if (
data.channelId === selectedChannel.id
) {
addMessage(data);
}
});
socket.on("user-typing", (data) => {
if (
data.channelId === selectedChannel.id
) {
setTypingUsers([data.username]);
}
});
socket.on("user-stop-typing", () => {
setTypingUsers([]);
});
return () => {
socket.off("new-message");
socket.off("user-typing");
socket.off("user-stop-typing");
};
}, [selectedChannel]);
function sendMessage() {
if (!message.trim()) return;
const data = {
id: Date.now(),
author: user.username,
content: message,
channelId: selectedChannel.id,
createdAt: new Date().toLocaleTimeString()
};
socket.emit(
"send-message",
data
);
socket.emit(
"stop-typing",
{
channelId: selectedChannel.id
}
);
setMessage("");
}
function handleTyping(value) {
setMessage(value);
socket.emit("typing", {
username: user.username,
channelId: selectedChannel.id
});
clearTimeout(window.typingTimeout);
window.typingTimeout = setTimeout(() => {
socket.emit("stop-typing", {
channelId: selectedChannel.id
});
}, 1200);
}
function startEdit(msg) {
setEditingId(msg.id);
setEditingText(msg.content);
}
function saveEdit(id) {
editMessage(id, editingText);
setEditingId(null);
}
return (
<div className="flex-1 flex flex-col bg-[#202225]">
<div className="h-14 border-b border-white/5 bg-[#18191c]/80 backdrop-blur-xl flex items-center justify-between px-5">
<div className="flex items-center gap-2">
<Hash
size={22}
className="text-zinc-500"
/>
<span className="font-semibold text-[15px]">
{selectedChannel?.name || "general"}
</span>
</div>
<div className="flex items-center gap-4 text-zinc-400">
<Bell
size={20}
className="hover:text-white cursor-pointer"
/>
<Users
size={20}
className="hover:text-white cursor-pointer"
/>
</div>
</div>
<div className="flex-1 overflow-y-auto px-6 py-6">
{messages
.filter(
(msg) =>
msg.channelId === selectedChannel?.id
)
.map((msg) => (
<div
key={msg.id}
className="flex gap-4 mb-6 group"
>
<div className="w-11 h-11 rounded-full bg-[#5865f2]" />
<div className="flex-1">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="font-semibold">
{msg.author}
</span>
<span className="text-xs text-zinc-500">
{msg.createdAt}
</span>
{msg.edited && (
<span className="text-xs text-zinc-500 italic">
edited
</span>
)}
</div>
{msg.author === user.username && (
<div className="hidden group-hover:flex items-center gap-2">
<button
onClick={() =>
startEdit(msg)
}
className="text-zinc-400 hover:text-white"
>
<Pencil size={16} />
</button>
<button
onClick={() =>
deleteMessage(msg.id)
}
className="text-zinc-400 hover:text-red-400"
>
<Trash2 size={16} />
</button>
</div>
)}
</div>
{editingId === msg.id ? (
<input
value={editingText}
onChange={(e) =>
setEditingText(
e.target.value
)
}
onKeyDown={(e) => {
if (e.key === "Enter") {
saveEdit(msg.id);
}
}}
className="mt-2 bg-[#2b2d31] px-3 py-2 rounded-lg outline-none w-full"
/>
) : (
<div className="text-zinc-300 mt-1 break-words">
{msg.content}
</div>
)}
</div>
</div>
))}
</div>
{typingUsers.length > 0 && (
<div className="px-6 pb-2 text-sm text-zinc-400 italic">
{typingUsers.join(", ")} typing...
</div>
)}
<div className="p-5">
<div className="bg-[#2b2d31] rounded-2xl flex items-center px-4 py-3 gap-3 border border-white/5 shadow-lg">
<button className="text-zinc-400 hover:text-white">
<PlusCircle size={22} />
</button>
<input
value={message}
onChange={(e) =>
handleTyping(e.target.value)
}
onKeyDown={(e) => {
if (e.key === "Enter") {
sendMessage();
}
}}
className="flex-1 bg-transparent outline-none text-white text-[15px]"
placeholder={`Message #${selectedChannel?.name}`}
/>
<button className="text-zinc-400 hover:text-white">
<Gift size={22} />
</button>
<button className="text-zinc-400 hover:text-white">
<Smile size={22} />
</button>
</div>
</div>
</div>
);
}