import Avatar from '@/components/ui/Avatar' import Button from '@/components/ui/Button' import Input from '@/components/ui/Input' import Notification from '@/components/ui/Notification' import Tooltip from '@/components/ui/Tooltip' import { toast } from '@/components/ui' import { AVATAR_URL } from '@/constants/app.constant' import { getMessengerContacts, MessengerAttachmentDto, MessengerContactDto, uploadMessengerAttachment, } from '@/services/messenger.service' import { messengerSignalR, MessengerMessageDto } from '@/services/messenger.signalr' import { useStoreState } from '@/store' import dayjs from 'dayjs' import EmojiPicker, { EmojiClickData } from 'emoji-picker-react' import { useEffect, useMemo, useRef, useState } from 'react' import { FaArrowLeft, FaPaperclip, FaRegSmile, FaSearch, FaTimes, FaTrash } from 'react-icons/fa' import { IoCheckmarkDone, IoChatbubbleEllipsesOutline, IoSend } from 'react-icons/io5' const MAX_UPLOAD_SIZE = 10 * 1024 * 1024 const formatFileSize = (size: number) => { if (size < 1024 * 1024) return `${Math.max(1, Math.round(size / 1024))} KB` return `${(size / (1024 * 1024)).toFixed(1)} MB` } const MessengerWidget = () => { const auth = useStoreState((state) => state.auth) const tenantId = auth.user.tenantId || auth.tenant?.tenantId const fileInputRef = useRef(null) const messagesEndRef = useRef(null) const [open, setOpen] = useState(false) const [contacts, setContacts] = useState([]) const [selectedIds, setSelectedIds] = useState([]) const [messages, setMessages] = useState([]) const [text, setText] = useState('') const [filter, setFilter] = useState('') const [attachments, setAttachments] = useState([]) const [connected, setConnected] = useState(false) const [uploading, setUploading] = useState(false) const [showEmoji, setShowEmoji] = useState(false) const [multiSelect, setMultiSelect] = useState(false) const [unread, setUnread] = useState(0) useEffect(() => { if (!auth.session.signedIn) return const unsubscribeMessage = messengerSignalR.onMessage((message) => { setMessages((prev) => (prev.some((item) => item.id === message.id) ? prev : [...prev, message])) if (!open && message.senderId !== auth.user.id) { setUnread((count) => count + 1) } }) const unsubscribeState = messengerSignalR.onStateChange(setConnected) messengerSignalR.start() return () => { unsubscribeMessage() unsubscribeState() } }, [auth.session.signedIn, auth.user.id, open]) useEffect(() => { if (!auth.session.signedIn) { messengerSignalR.stop() } }, [auth.session.signedIn]) useEffect(() => { if (!open) return setUnread(0) getMessengerContacts(filter) .then((response) => setContacts(response.data)) .catch(() => setContacts([])) }, [filter, open]) useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [messages, open]) const selectedContacts = useMemo( () => contacts.filter((contact) => selectedIds.includes(contact.id)), [contacts, selectedIds], ) const visibleMessages = useMemo(() => { if (selectedIds.length === 0) return messages.slice(-30) return messages.filter((message) => { const isOwn = message.senderId === auth.user.id const participants = new Set([message.senderId, ...message.recipientIds]) return selectedIds.some((id) => participants.has(id)) && (isOwn || selectedIds.includes(message.senderId)) }) }, [auth.user.id, messages, selectedIds]) const toggleContact = (id: string) => { if (!multiSelect) { setSelectedIds([id]) return } setSelectedIds((prev) => (prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id])) } const toggleMultiSelect = () => { setMultiSelect((value) => { if (value) { setSelectedIds((prev) => prev.slice(0, 1)) } return !value }) } const handleFiles = async (files: FileList | null) => { if (!files?.length) return const nextFiles = Array.from(files) const tooLarge = nextFiles.find((file) => file.size > MAX_UPLOAD_SIZE) if (tooLarge) { toast.push(, { placement: 'top-end', }) return } setUploading(true) try { const uploaded = await Promise.all( nextFiles.map((file) => uploadMessengerAttachment(file).then((response) => response.data)), ) setAttachments((prev) => [...prev, ...uploaded]) } catch { toast.push(, { placement: 'top-end' }) } finally { setUploading(false) if (fileInputRef.current) fileInputRef.current.value = '' } } const sendMessage = async () => { const trimmedText = text.trim() if (selectedIds.length === 0 || (!trimmedText && attachments.length === 0)) return try { await messengerSignalR.sendMessage({ recipientIds: selectedIds, text: trimmedText, attachments, }) setText('') setAttachments([]) setShowEmoji(false) } catch { toast.push(, { placement: 'top-end' }) } } const onEmojiClick = (emoji: EmojiClickData) => { setText((current) => `${current}${emoji.emoji}`) } if (!auth.session.signedIn) return null return (
{open && (
{visibleMessages.length === 0 && (
Henüz mesaj yok
)} {visibleMessages.map((message) => { const own = message.senderId === auth.user.id return (
{!own &&
{message.senderName}
} {message.text &&
{message.text}
} {message.attachments.length > 0 && (
{message.attachments.map((file) => ( {file.fileName} {formatFileSize(file.fileSize)} ))}
)}
{dayjs(message.sentAt).format('HH:mm')}
) })}
{selectedIds.length > 0 && attachments.length > 0 && (
{attachments.map((file) => ( {file.fileName} ))}
)} {selectedIds.length > 0 && (
{showEmoji && (
)}
handleFiles(event.target.files)} />