From 80b7c761a900d53a338742a255a297eead75f866 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Wed, 15 Oct 2025 10:57:25 +0330 Subject: [PATCH] fix bug message --- .../product/components/ShopInformation.tsx | 2 +- .../profile/chat/components/ChatMessages.tsx | 315 +++++++++--------- .../profile/chat/components/ChatSidebar.tsx | 94 +++--- src/app/profile/chat/hooks/useSocket.ts | 154 ++++----- src/app/profile/chat/page.tsx | 65 ++-- src/app/profile/chat/service/SocketService.ts | 253 ++++++-------- src/app/profile/hooks/useChatData.ts | 41 ++- src/app/profile/types/ProfileTypes.ts | 1 + 8 files changed, 455 insertions(+), 470 deletions(-) diff --git a/src/app/product/components/ShopInformation.tsx b/src/app/product/components/ShopInformation.tsx index bf2a09e..2254952 100644 --- a/src/app/product/components/ShopInformation.tsx +++ b/src/app/product/components/ShopInformation.tsx @@ -31,7 +31,7 @@ const ShopInformation = ({ product }: ShopInformationProps) => { const handleChatWithSeller = () => { // Navigate to chat page with product and shop context - router.push(`/profile/chat?productId=${product._id}&shopId=${shop._id}`) + router.push(`/profile/chat?productId=${product._id}&shopId=${shop.owner}`) } return ( diff --git a/src/app/profile/chat/components/ChatMessages.tsx b/src/app/profile/chat/components/ChatMessages.tsx index 8575083..e6cb972 100644 --- a/src/app/profile/chat/components/ChatMessages.tsx +++ b/src/app/profile/chat/components/ChatMessages.tsx @@ -1,10 +1,11 @@ 'use client' import { useState, useRef, useEffect, useMemo } from 'react' import Image from 'next/image' -import { Trash, More, EmojiHappy, AttachCircle, Send, Shop, Call, VideoPlay } from 'iconsax-react' +import { Send, Shop } from 'iconsax-react' import { useSocket } from '../hooks/useSocket' import { Message as SocketMessage } from '../service/SocketService' import { useChatDetail, useMarkChatAsRead } from '../../hooks/useChatData' +import { useQueryClient } from '@tanstack/react-query' import { isAuthenticated } from '@/config/func' import { ChatMessage as APIMessage } from '../../types/ProfileTypes' @@ -17,17 +18,16 @@ interface Message { } interface ChatMessagesProps { - productId?: string | null - shopId?: string | null chatId?: string } -export default function ChatMessages({ productId, shopId, chatId }: ChatMessagesProps) { +export default function ChatMessages({ chatId }: ChatMessagesProps) { const [messageText, setMessageText] = useState('') const [isTyping, setIsTyping] = useState(false) const [selectedFile, setSelectedFile] = useState(null) const [isUserAuthenticated, setIsUserAuthenticated] = useState(null) const messagesEndRef = useRef(null) + const messagesContainerRef = useRef(null) const typingTimeoutRef = useRef(null) const fileInputRef = useRef(null) const markedAsReadRef = useRef>(new Set()) @@ -51,58 +51,64 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages connect } = useSocket() + const queryClient = useQueryClient() const { data: chatData, isLoading, error } = useChatDetail(chatId) const markAsRead = useMarkChatAsRead() // Convert API messages to component format - const messages: Message[] = useMemo(() => - chatData?.results?.messages?.map((msg: APIMessage) => ({ - id: msg._id, - text: msg.content, - time: new Date(msg.createdAt).toLocaleTimeString('fa-IR', { - hour: '2-digit', - minute: '2-digit' - }), - sender: msg.sender === 'customer' ? 'user' : 'admin', - status: msg.status - })) || [] - , [chatData?.results?.messages]) + const messages: Message[] = useMemo(() => { + const apiMessages = chatData?.results?.messages + if (Array.isArray(apiMessages)) { + return apiMessages.map((msg: APIMessage) => ({ + id: msg._id, + text: msg.content, + time: new Date(msg.createdAt).toLocaleTimeString('fa-IR', { + hour: '2-digit', + minute: '2-digit' + }), + sender: msg.senderRef === 'User' ? 'user' : 'admin', + status: msg.status + })) + } + return [] + }, [chatData?.results?.messages]) const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }) + if (messagesContainerRef.current) { + messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight + } } useEffect(() => { scrollToBottom() }, [messages]) - // اتصال به چت وقتی chatId تنظیم شده و کاربر لاگین کرده + // اتصال به سوکت وقتی chatId تنظیم شده و کاربر لاگین کرده useEffect(() => { - if (chatId && chatId !== '' && isUserAuthenticated === true) { - // اتصال به سوکت اگر متصل نیست - if (!isConnected) { - console.log('🔌 اتصال به سوکت برای چت...') - connect().catch((error) => { - console.error('❌ خطا در اتصال سوکت:', error.message) + if (chatId && chatId !== '' && isUserAuthenticated === true && !isConnected) { + console.log('🔌 اتصال به سوکت برای چت...') + connect().catch((error) => { + console.error('❌ خطا در اتصال سوکت:', error.message) - // اگر توکن expired است، پیام نمایش بده - if (error.message === 'توکن منقضی شده' || error.message === 'توکن یافت نشد') { - console.log('🔐 توکن نامعتبر برای چت') - } - }) - } - - // اتصال به چت - if (isConnected) { - console.log('🔗 اتصال به چت:', chatId) - joinChat(chatId) - return () => { - leaveChat(chatId) + // اگر توکن expired است، پیام نمایش بده + if (error.message === 'توکن منقضی شده' || error.message === 'توکن یافت نشد') { + console.log('🔐 توکن نامعتبر برای چت') } + }) + } + }, [chatId, isUserAuthenticated, isConnected, connect]) + + // اتصال به چت وقتی سوکت متصل شد + useEffect(() => { + if (chatId && chatId !== '' && isConnected) { + console.log('🔗 اتصال به چت:', chatId) + joinChat(chatId) + return () => { + leaveChat(chatId) } } - }, [chatId, isConnected, joinChat, leaveChat, connect, isUserAuthenticated]) + }, [chatId, isConnected, joinChat, leaveChat]) // Mark messages as read - فقط یک بار برای هر chatId useEffect(() => { @@ -123,9 +129,12 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages const handleNewMessage = (socketMessage: unknown) => { const message = socketMessage as SocketMessage if (message.chatId === chatId) { - // The message will be added via React Query invalidation - // or we can update local state if needed console.log('New message received:', message) + // Invalidate chat detail query to refetch messages + // This will handle both sent and received messages + queryClient.invalidateQueries({ queryKey: ['chat', 'detail', chatId] }) + // Scroll to bottom after a short delay to ensure new message is rendered + setTimeout(scrollToBottom, 100) } } @@ -144,7 +153,7 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages return () => { // Cleanup will be handled by the hook } - }, [chatId, onNewMessage, onError]) + }, [chatId, onNewMessage, onError, queryClient]) const handleTyping = () => { if (!isTyping) { @@ -173,9 +182,9 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages } } - const handleAttachClick = () => { - fileInputRef.current?.click() - } + // const handleAttachClick = () => { + // fileInputRef.current?.click() + // } const handleSend = () => { if ((messageText.trim() || selectedFile) && chatId) { @@ -192,7 +201,17 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages // For now, just send text message // TODO: Implement file upload when server supports it if (content) { - sendMessage(chatId, content) + const receiver = chat?.seller + if (receiver) { + sendMessage(chatId, content, receiver) + // Invalidate query to show sent message immediately after socket event + // Note: This might cause duplicate if socket event also invalidates + setTimeout(() => { + queryClient.invalidateQueries({ queryKey: ['chat', 'detail', chatId] }) + }, 500) + } else { + console.error('❌ receiver یافت نشد - نمی‌توان پیام ارسال کرد') + } } // Scroll to bottom after sending @@ -232,7 +251,7 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages className="object-cover" /> ) : ( - + )} {/* وضعیت آنلاین در این API موجود نیست */} @@ -245,30 +264,6 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages -
- - - - -
) : (
@@ -278,7 +273,7 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
{/* Messages Area */} -
+
{/* Socket Status Alert */} {!isConnected && (
@@ -286,7 +281,6 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
ℹ️ اتصال به چت
-
• در حال اتصال به سرور چت...
• لطفاً چند لحظه صبر کنید
@@ -311,7 +305,7 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
-
+
@@ -325,17 +319,17 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages ) : messages.length > 0 ? ( messages.map((message, index) => { const isFirstInGroup = index === 0 || messages[index - 1].sender !== message.sender - const senderName = message.sender === 'admin' ? - (chat?.sellerRef || 'فروشگاه') : 'شما' + const senderName = message.sender === 'user' ? 'شما' : + (typeof chat?.sellerRef === 'string' ? chat.sellerRef : 'فروشگاه') return (
-
+
{isFirstInGroup && ( -
+
{senderName} @@ -344,16 +338,16 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages )}
-

{message.text}

+

{typeof message.text === 'string' ? message.text : 'پیام نامعتبر'}

{message.sender === 'user' && ( -
+
{message.time} {message.status === 'read' && ( @@ -381,21 +375,21 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages {/* Typing Indicator */} {isTyping && ( -
+
-
+
{chat?.sellerRef || 'فروشگاه'}
-
+
-
-
-
+
+
+
- در حال تایپ... + در حال تایپ...
@@ -406,85 +400,84 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
- {/* Input Area */} -
-
- + {/* Input Area - Only show when chat is selected */} + {chatId && chatId !== '' && ( +
+
+ {/* */} - {/* Hidden file input */} - - -
- {/* File Preview */} - {selectedFile && ( -
-
-
- 📎 -
-
-

{selectedFile.name}

-

{(selectedFile.size / 1024).toFixed(1)} KB

-
-
- -
- )} - -