Remove debug mode from chat and optimize image usage

This commit is contained in:
hamid zarghami
2025-10-14 16:45:33 +03:30
parent 4378f0e5ef
commit 4af0082c03
3 changed files with 141 additions and 84 deletions
@@ -1,9 +1,10 @@
'use client' 'use client'
import { useState, useRef, useEffect, useMemo } from 'react' 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 { Trash, More, EmojiHappy, AttachCircle, Send, Shop, Call, VideoPlay } from 'iconsax-react'
import { useSocket } from '../hooks/useSocket' import { useSocket } from '../hooks/useSocket'
import { Message as SocketMessage } from '../service/SocketService' import { Message as SocketMessage } from '../service/SocketService'
import { useChatMessages, useMarkChatAsRead } from '../../hooks/useChatData' import { useChatDetail, useMarkChatAsRead } from '../../hooks/useChatData'
import { isAuthenticated } from '@/config/func' import { isAuthenticated } from '@/config/func'
import { ChatMessage as APIMessage } from '../../types/ProfileTypes' import { ChatMessage as APIMessage } from '../../types/ProfileTypes'
@@ -23,26 +24,13 @@ interface ChatMessagesProps {
export default function ChatMessages({ productId, shopId, chatId }: ChatMessagesProps) { export default function ChatMessages({ productId, shopId, chatId }: ChatMessagesProps) {
const [messageText, setMessageText] = useState('') const [messageText, setMessageText] = useState('')
const [debugInfo, setDebugInfo] = useState<string>('')
const [isTyping, setIsTyping] = useState(false) const [isTyping, setIsTyping] = useState(false)
const [selectedFile, setSelectedFile] = useState<File | null>(null) const [selectedFile, setSelectedFile] = useState<File | null>(null)
const [isUserAuthenticated, setIsUserAuthenticated] = useState<boolean | null>(null) const [isUserAuthenticated, setIsUserAuthenticated] = useState<boolean | null>(null)
const messagesEndRef = useRef<HTMLDivElement>(null) const messagesEndRef = useRef<HTMLDivElement>(null)
const typingTimeoutRef = useRef<NodeJS.Timeout | null>(null) const typingTimeoutRef = useRef<NodeJS.Timeout | null>(null)
const fileInputRef = useRef<HTMLInputElement>(null) const fileInputRef = useRef<HTMLInputElement>(null)
const markedAsReadRef = useRef<Set<string>>(new Set())
// Server capabilities - temporarily disable features not implemented yet
const SERVER_CAPABILITIES = {
socketChat: false, // Socket.IO chat not implemented yet
markAsRead: false, // markAsRead endpoint not implemented yet
}
// Calculate currentChatId from props
const currentChatId = chatId || (productId && shopId ? `chat_${shopId}_${productId}` : '')
const { data: chatData, isLoading, error } = useChatMessages(currentChatId, {
enabled: isUserAuthenticated === true // Only fetch if user is logged in
})
// Check authentication on mount // Check authentication on mount
useEffect(() => { useEffect(() => {
@@ -52,7 +40,6 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
} }
checkAuth() checkAuth()
}, []) }, [])
const markAsRead = useMarkChatAsRead()
const { const {
isConnected, isConnected,
@@ -60,9 +47,14 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
leaveChat, leaveChat,
sendMessage, sendMessage,
onNewMessage, onNewMessage,
onError onError,
connect
} = useSocket() } = useSocket()
const { data: chatData, isLoading, error } = useChatDetail(chatId)
const markAsRead = useMarkChatAsRead()
// Convert API messages to component format // Convert API messages to component format
const messages: Message[] = useMemo(() => const messages: Message[] = useMemo(() =>
chatData?.results?.messages?.map((msg: APIMessage) => ({ chatData?.results?.messages?.map((msg: APIMessage) => ({
@@ -85,31 +77,52 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
scrollToBottom() scrollToBottom()
}, [messages]) }, [messages])
// Join chat room when chatId is set and socket is connected (only if server supports it and user is logged in) // اتصال به چت وقتی chatId تنظیم شده و کاربر لاگین کرده
useEffect(() => { useEffect(() => {
if (currentChatId && currentChatId !== '' && isUserAuthenticated === true) { if (chatId && chatId !== '' && isUserAuthenticated === true) {
if (SERVER_CAPABILITIES.socketChat && isConnected) { // اتصال به سوکت اگر متصل نیست
console.log('🔗 اتصال به چت:', currentChatId) if (!isConnected) {
joinChat(currentChatId) console.log('🔌 اتصال به سوکت برای چت...')
return () => { connect().catch((error) => {
leaveChat(currentChatId) console.error('❌ خطا در اتصال سوکت:', error.message)
// اگر توکن expired است، پیام نمایش بده
if (error.message === 'توکن منقضی شده' || error.message === 'توکن یافت نشد') {
console.log('🔐 توکن نامعتبر برای چت')
} }
})
} }
// Mark messages as read only if server supports it // اتصال به چت
if (SERVER_CAPABILITIES.markAsRead) { if (isConnected) {
markAsRead.mutate(currentChatId) console.log('🔗 اتصال به چت:', chatId)
joinChat(chatId)
return () => {
leaveChat(chatId)
}
}
}
}, [chatId, isConnected, joinChat, leaveChat, connect, isUserAuthenticated])
// Mark messages as read - فقط یک بار برای هر chatId
useEffect(() => {
if (chatId && chatId !== '' && isUserAuthenticated === true && isConnected) {
// بررسی اینکه آیا قبلاً برای این chatId mark as read کرده‌ایم
if (!markedAsReadRef.current.has(chatId)) {
console.log('📖 mark as read برای چت:', chatId)
markedAsReadRef.current.add(chatId)
markAsRead.mutate(chatId)
} else { } else {
console.log('markAsRead غیرفعال است (سرور هنوز پشتیبانی نمی‌کند)') console.log('چت قبلاً mark as read شده:', chatId)
} }
} }
}, [currentChatId, isConnected, joinChat, leaveChat, markAsRead, SERVER_CAPABILITIES.socketChat, SERVER_CAPABILITIES.markAsRead, isUserAuthenticated]) }, [chatId, isUserAuthenticated, isConnected, markAsRead])
// Listen for new messages // Listen for new messages
useEffect(() => { useEffect(() => {
const handleNewMessage = (socketMessage: unknown) => { const handleNewMessage = (socketMessage: unknown) => {
const message = socketMessage as SocketMessage const message = socketMessage as SocketMessage
if (message.chatId === currentChatId) { if (message.chatId === chatId) {
// The message will be added via React Query invalidation // The message will be added via React Query invalidation
// or we can update local state if needed // or we can update local state if needed
console.log('New message received:', message) console.log('New message received:', message)
@@ -119,7 +132,6 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
const handleError = (error: unknown) => { const handleError = (error: unknown) => {
const err = error as { message: string } const err = error as { message: string }
console.error('🚨 خطای چت:', err.message) console.error('🚨 خطای چت:', err.message)
setDebugInfo(`خطا: ${err.message}`)
// You can show a toast notification here // You can show a toast notification here
if (err.message === 'اتصال سوکت برقرار نیست') { if (err.message === 'اتصال سوکت برقرار نیست') {
console.warn('⚠️ راهنمایی: اگر این خطا تکرار می‌شود، احتمالاً سرور سوکت اجرا نمی‌شود یا قابل دسترسی نیست.') console.warn('⚠️ راهنمایی: اگر این خطا تکرار می‌شود، احتمالاً سرور سوکت اجرا نمی‌شود یا قابل دسترسی نیست.')
@@ -132,13 +144,13 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
return () => { return () => {
// Cleanup will be handled by the hook // Cleanup will be handled by the hook
} }
}, [currentChatId, onNewMessage, onError]) }, [chatId, onNewMessage, onError])
const handleTyping = () => { const handleTyping = () => {
if (!isTyping) { if (!isTyping) {
setIsTyping(true) setIsTyping(true)
// Here you could emit a typing event to the server // Here you could emit a typing event to the server
// socketService.emit('typing', { chatId: currentChatId }) // socketService.emit('typing', { chatId: chatId })
} }
// Clear previous timeout // Clear previous timeout
@@ -149,7 +161,7 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
// Set new timeout to stop typing indicator // Set new timeout to stop typing indicator
typingTimeoutRef.current = setTimeout(() => { typingTimeoutRef.current = setTimeout(() => {
setIsTyping(false) setIsTyping(false)
// socketService.emit('stopTyping', { chatId: currentChatId }) // socketService.emit('stopTyping', { chatId: chatId })
}, 2000) }, 2000)
} }
@@ -166,7 +178,7 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
} }
const handleSend = () => { const handleSend = () => {
if ((messageText.trim() || selectedFile) && currentChatId) { if ((messageText.trim() || selectedFile) && chatId) {
const content = messageText.trim() const content = messageText.trim()
setMessageText('') setMessageText('')
setIsTyping(false) setIsTyping(false)
@@ -180,7 +192,7 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
// For now, just send text message // For now, just send text message
// TODO: Implement file upload when server supports it // TODO: Implement file upload when server supports it
if (content) { if (content) {
sendMessage(currentChatId, content) sendMessage(chatId, content)
} }
// Scroll to bottom after sending // Scroll to bottom after sending
@@ -189,23 +201,10 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
} }
const chat = chatData?.results?.chat const chat = chatData?.results?.chat
const seller = chat?.participants?.seller const seller = chat?.shopLogo
return ( return (
<div className="bg-white flex flex-col h-full"> <div className="bg-white flex flex-col h-full">
{/* Debug Info - فقط در محیط توسعه */}
{process.env.NODE_ENV === 'development' && (
<div className="px-4 py-2 bg-yellow-50 border-b border-yellow-200 text-xs text-yellow-800">
<div>🔍 دیباگ چت:</div>
<div>ChatId: {currentChatId || 'خالی'}</div>
<div>ProductId: {productId || 'خالی'}</div>
<div>ShopId: {shopId || 'خالی'}</div>
<div>Socket: {isConnected ? '🟢 متصل' : '🔴 قطع'}</div>
<div>Server Capabilities: Socket={SERVER_CAPABILITIES.socketChat ? '✅' : '❌'}, MarkAsRead={SERVER_CAPABILITIES.markAsRead ? '✅' : '❌'}</div>
{debugInfo && <div className="mt-1 text-red-600">{debugInfo}</div>}
</div>
)}
{/* Header */} {/* Header */}
<div className="px-6 py-4 border-b border-gray-100 bg-white"> <div className="px-6 py-4 border-b border-gray-100 bg-white">
{isLoading ? ( {isLoading ? (
@@ -226,23 +225,22 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
<div className="relative"> <div className="relative">
<div className="w-10 h-10 bg-gradient-to-br from-primary/10 to-primary/20 rounded-full flex items-center justify-center overflow-hidden"> <div className="w-10 h-10 bg-gradient-to-br from-primary/10 to-primary/20 rounded-full flex items-center justify-center overflow-hidden">
{seller.logo ? ( {seller.logo ? (
<img <Image
src={seller.logo} src={seller.logo}
alt={seller.shopName} alt={chat?.sellerRef || 'فروشگاه'}
className="w-full h-full object-cover" fill
className="object-cover"
/> />
) : ( ) : (
<Shop size={18} className="text-primary" variant="Bold" /> <Shop size={18} className="text-primary" variant="Bold" />
)} )}
</div> </div>
{chat?.isOnline && ( {/* وضعیت آنلاین در این API موجود نیست */}
<div className="absolute -bottom-0.5 -right-0.5 w-3 h-3 bg-green-500 border-2 border-white rounded-full"></div>
)}
</div> </div>
<div> <div>
<h3 className="font-semibold text-gray-900 text-sm">{seller.shopName}</h3> <h3 className="font-semibold text-gray-900 text-sm">{chat?.sellerRef || 'فروشگاه'}</h3>
<p className="text-xs text-gray-500"> <p className="text-xs text-gray-500">
{chat?.isOnline ? 'آنلاین' : 'آفلاین'} آفلاین
</p> </p>
</div> </div>
</div> </div>
@@ -257,9 +255,9 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
<button <button
onClick={() => { onClick={() => {
// TODO: Implement delete chat functionality // TODO: Implement delete chat functionality
console.log('🗑️ درخواست حذف چت:', currentChatId) console.log('🗑️ درخواست حذف چت:', chatId)
if (confirm('آیا از حذف این گفتگو مطمئن هستید؟')) { if (confirm('آیا از حذف این گفتگو مطمئن هستید؟')) {
// deleteChatMutation.mutate(currentChatId) // deleteChatMutation.mutate(chatId)
alert('این قابلیت هنوز پیاده‌سازی نشده است.') alert('این قابلیت هنوز پیاده‌سازی نشده است.')
} }
}} }}
@@ -281,19 +279,15 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
{/* Messages Area */} {/* Messages Area */}
<div className="flex-1 overflow-y-auto bg-gray-50/30"> <div className="flex-1 overflow-y-auto bg-gray-50/30">
{/* Server Status Alert */} {/* Socket Status Alert */}
{(!SERVER_CAPABILITIES.socketChat || !isConnected) && ( {!isConnected && (
<div className="px-6 py-3"> <div className="px-6 py-3">
<div className="bg-blue-50 border border-blue-200 rounded-lg px-4 py-3"> <div className="bg-blue-50 border border-blue-200 rounded-lg px-4 py-3">
<div className="text-xs text-blue-800 leading-relaxed"> <div className="text-xs text-blue-800 leading-relaxed">
<div className="font-semibold mb-2"> راهاندازی سرویس چت</div> <div className="font-semibold mb-2"> اتصال به چت</div>
<div className="space-y-1"> <div className="space-y-1">
<div> سرور Socket.IO: پیاده‌سازی نشده</div> <div> در حال اتصال به سرور چت...</div>
<div> API markAsRead: پیاده‌سازی نشده</div> <div> لطفاً چند لحظه صبر کنید</div>
<div> وضعیت: در حال توسعه توسط تیم بکند</div>
</div>
<div className="mt-2 text-xs opacity-75">
چت در حالت آزمایشی کار میکند
</div> </div>
</div> </div>
</div> </div>
@@ -332,7 +326,7 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
messages.map((message, index) => { messages.map((message, index) => {
const isFirstInGroup = index === 0 || messages[index - 1].sender !== message.sender const isFirstInGroup = index === 0 || messages[index - 1].sender !== message.sender
const senderName = message.sender === 'admin' ? const senderName = message.sender === 'admin' ?
(seller?.shopName || 'فروشگاه') : 'شما' (chat?.sellerRef || 'فروشگاه') : 'شما'
return ( return (
<div <div
@@ -391,7 +385,7 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
<div className="max-w-[70%]"> <div className="max-w-[70%]">
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center gap-2 mb-2">
<span className="text-xs text-gray-500 font-medium"> <span className="text-xs text-gray-500 font-medium">
{seller?.shopName || 'فروشگاه'} {chat?.sellerRef || 'فروشگاه'}
</span> </span>
</div> </div>
<div className="bg-white border border-gray-100 rounded-2xl px-4 py-3 shadow-sm"> <div className="bg-white border border-gray-100 rounded-2xl px-4 py-3 shadow-sm">
@@ -477,8 +471,8 @@ export default function ChatMessages({ productId, shopId, chatId }: ChatMessages
<button <button
onClick={handleSend} onClick={handleSend}
disabled={(!messageText.trim() && !selectedFile) || (!SERVER_CAPABILITIES.socketChat && !isConnected)} disabled={(!messageText.trim() && !selectedFile) || !isConnected}
className={`p-3 rounded-xl transition-all flex-shrink-0 ${(messageText.trim() || selectedFile) && (SERVER_CAPABILITIES.socketChat || isConnected) className={`p-3 rounded-xl transition-all flex-shrink-0 ${(messageText.trim() || selectedFile) && isConnected
? 'bg-primary hover:bg-primary/90 text-primary-foreground shadow-lg' ? 'bg-primary hover:bg-primary/90 text-primary-foreground shadow-lg'
: 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-gray-100 text-gray-400 cursor-not-allowed'
}`} }`}
+7 -6
View File
@@ -7,10 +7,11 @@ import {
UpdateAddressType, UpdateAddressType,
WishlistResponse, WishlistResponse,
CommentsResponse, CommentsResponse,
ChatListResponse,
ChatMessagesResponse, ChatMessagesResponse,
CreateChatRequest, CreateChatRequest,
CreateChatResponse, CreateChatResponse,
GetChatListResponse,
ChatDetailResponse,
} from "../types/ProfileTypes"; } from "../types/ProfileTypes";
export const getProfile = async () => { export const getProfile = async () => {
@@ -54,15 +55,15 @@ export const changeEmail = async (email: string) => {
}; };
// Chat Services // Chat Services
export const getChatList = async (): Promise<ChatListResponse> => { export const getChatList = async (): Promise<GetChatListResponse> => {
const { data } = await axios.get("/chat/list"); const { data } = await axios.get("/chat/user");
return data; return data;
}; };
export const getChatMessages = async ( export const getChatDetail = async (
chatId: string chatId: string
): Promise<ChatMessagesResponse> => { ): Promise<ChatDetailResponse> => {
const { data } = await axios.get(`/chat/${chatId}/messages`); const { data } = await axios.get(`/chat/user/${chatId}`);
return data; return data;
}; };
+65 -3
View File
@@ -359,9 +359,7 @@ export interface ChatMessagesResponse {
} }
export interface CreateChatRequest { export interface CreateChatRequest {
productId: number; sellerId: string;
shopId: string;
initialMessage?: string;
} }
export interface CreateChatResponse { export interface CreateChatResponse {
@@ -372,3 +370,67 @@ export interface CreateChatResponse {
message?: ChatMessage; message?: ChatMessage;
}; };
} }
// New Chat List Types
export interface ChatListSeller {
_id: string;
fullName: string;
}
export interface ChatListShopLogo {
_id: string;
address: string | null;
logo: string;
owner: string;
}
export interface ChatListItem {
_id: string;
seller: ChatListSeller;
sellerRef: string;
user: string;
createdAt: string;
updatedAt: string;
shortId: number;
lastMessage: string | null;
shopLogo: ChatListShopLogo;
}
export interface GetChatListResults {
chats: ChatListItem[];
}
export interface GetChatListResponse {
status: number;
success: boolean;
results: GetChatListResults;
}
// Chat Detail Types
export interface ChatDetailShopLogo {
_id: string;
address: string | null;
logo: string;
owner: string;
}
export interface ChatDetailItem {
_id: string;
seller: string;
sellerRef: string;
user: string;
createdAt: string;
updatedAt: string;
shortId: number;
lastMessage: string | null;
shopLogo: ChatDetailShopLogo;
}
export interface ChatDetailResponse {
status: number;
success: boolean;
results: {
chat: ChatDetailItem;
messages: ChatMessage[];
};
}