| import { useState, useCallback } from 'react'; |
| import { eq, desc, asc, and, or } from 'drizzle-orm'; |
| import { useDatabase } from './useDatabase'; |
| import { conversations, messages } from '../db/schema'; |
|
|
| |
|
|
| interface ConversationData { |
| id: string; |
| participant1Id: string; |
| participant2Id: string; |
| lastMessage: string | null; |
| lastMessageAt: string | null; |
| unreadCount: number | null; |
| createdAt: string | null; |
| updatedAt: string | null; |
| } |
|
|
| interface MessageData { |
| id: string; |
| conversationId: string; |
| senderId: string; |
| content: string; |
| type: 'text' | 'image' | 'location' | 'system'; |
| status: 'sending' | 'sent' | 'delivered' | 'read' | 'failed'; |
| createdAt: string | null; |
| } |
|
|
| interface UseConversationsReturn { |
| conversations: ConversationData[]; |
| messages: MessageData[]; |
| loadingConversations: boolean; |
| loadingMessages: boolean; |
| loadConversations: () => Promise<void>; |
| loadMessages: (conversationId: string) => Promise<void>; |
| sendMessage: ( |
| conversationId: string, |
| senderId: string, |
| content: string, |
| type?: MessageData['type'], |
| ) => Promise<MessageData | null>; |
| markAsRead: (conversationId: string) => Promise<void>; |
| getUnreadCount: () => Promise<number>; |
| } |
|
|
| |
|
|
| export function useConversations(userId?: string): UseConversationsReturn { |
| const { db } = useDatabase(); |
| const [conversationsList, setConversationsList] = useState<ConversationData[]>( |
| [], |
| ); |
| const [messagesList, setMessagesList] = useState<MessageData[]>([]); |
| const [loadingConversations, setLoadingConversations] = useState(false); |
| const [loadingMessages, setLoadingMessages] = useState(false); |
|
|
| |
|
|
| const loadConversations = useCallback(async () => { |
| try { |
| setLoadingConversations(true); |
|
|
| const results = await db |
| .select() |
| .from(conversations) |
| .orderBy(desc(conversations.lastMessageAt)) |
| .all(); |
|
|
| |
| let filtered = results as ConversationData[]; |
| if (userId) { |
| filtered = results.filter( |
| (c) => c.participant1Id === userId || c.participant2Id === userId, |
| ); |
| } |
|
|
| setConversationsList(filtered); |
| } catch (error) { |
| console.error('[useConversations] Failed to load conversations:', error); |
| setConversationsList([]); |
| } finally { |
| setLoadingConversations(false); |
| } |
| }, [db, userId]); |
|
|
| |
|
|
| const loadMessages = useCallback( |
| async (conversationId: string) => { |
| try { |
| setLoadingMessages(true); |
|
|
| const results = await db |
| .select() |
| .from(messages) |
| .where(eq(messages.conversationId, conversationId)) |
| .orderBy(asc(messages.createdAt)) |
| .all(); |
|
|
| setMessagesList(results as MessageData[]); |
| } catch (error) { |
| console.error('[useConversations] Failed to load messages:', error); |
| setMessagesList([]); |
| } finally { |
| setLoadingMessages(false); |
| } |
| }, |
| [db], |
| ); |
|
|
| |
|
|
| const sendMessage = useCallback( |
| async ( |
| conversationId: string, |
| senderId: string, |
| content: string, |
| type: MessageData['type'] = 'text', |
| ): Promise<MessageData | null> => { |
| const id = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; |
|
|
| try { |
| |
| await db.insert(messages).values({ |
| id, |
| conversationId, |
| senderId, |
| content, |
| type, |
| status: 'sent', |
| }).run(); |
|
|
| |
| await db |
| .update(conversations) |
| .set({ |
| lastMessage: content, |
| lastMessageAt: new Date().toISOString(), |
| updatedAt: new Date().toISOString(), |
| }) |
| .where(eq(conversations.id, conversationId)) |
| .run(); |
|
|
| |
| await loadMessages(conversationId); |
| await loadConversations(); |
|
|
| return { |
| id, |
| conversationId, |
| senderId, |
| content, |
| type, |
| status: 'sent', |
| createdAt: new Date().toISOString(), |
| }; |
| } catch (error) { |
| console.error('[useConversations] Failed to send message:', error); |
| return null; |
| } |
| }, |
| [db, loadMessages, loadConversations], |
| ); |
|
|
| |
|
|
| const markAsRead = useCallback( |
| async (conversationId: string) => { |
| try { |
| await db |
| .update(conversations) |
| .set({ unreadCount: 0, updatedAt: new Date().toISOString() }) |
| .where(eq(conversations.id, conversationId)) |
| .run(); |
|
|
| await loadConversations(); |
| } catch (error) { |
| console.error('[useConversations] Failed to mark as read:', error); |
| } |
| }, |
| [db, loadConversations], |
| ); |
|
|
| |
|
|
| const getUnreadCount = useCallback(async (): Promise<number> => { |
| try { |
| const results = await db.select().from(conversations).all(); |
|
|
| let filtered = results as ConversationData[]; |
| if (userId) { |
| filtered = results.filter( |
| (c) => c.participant1Id === userId || c.participant2Id === userId, |
| ); |
| } |
|
|
| return filtered.reduce( |
| (sum, c) => sum + (c.unreadCount ?? 0), |
| 0, |
| ); |
| } catch (error) { |
| console.error('[useConversations] Failed to get unread count:', error); |
| return 0; |
| } |
| }, [db, userId]); |
|
|
| return { |
| conversations: conversationsList, |
| messages: messagesList, |
| loadingConversations, |
| loadingMessages, |
| loadConversations, |
| loadMessages, |
| sendMessage, |
| markAsRead, |
| getUnreadCount, |
| }; |
| } |
|
|