Spaces:
Configuration error
Configuration error
| import { create } from 'zustand' | |
| import { Chat, Message } from '../../../shared/types' | |
| import { chatService } from '../services/chatService' | |
| interface ChatState { | |
| chats: Chat[] | |
| currentChat: Chat | null | |
| messages: Record<string, Message[]> | |
| typingUsers: Record<string, string[]> | |
| onlineUsers: Set<string> | |
| loading: boolean | |
| error: string | null | |
| } | |
| interface ChatActions { | |
| // Chat management | |
| loadChats: () => Promise<void> | |
| selectChat: (chatId: string) => void | |
| createChat: (data: { | |
| type: 'direct' | 'group' | |
| name?: string | |
| participantIds: string[] | |
| }) => Promise<Chat> | |
| updateChat: (chatId: string, data: Partial<Chat>) => Promise<void> | |
| deleteChat: (chatId: string) => Promise<void> | |
| // Message management | |
| loadMessages: (chatId: string, page?: number) => Promise<void> | |
| sendMessage: (chatId: string, content: string, attachments?: File[]) => Promise<void> | |
| editMessage: (messageId: string, content: string) => Promise<void> | |
| deleteMessage: (messageId: string) => Promise<void> | |
| addReaction: (messageId: string, emoji: string) => Promise<void> | |
| removeReaction: (messageId: string, emoji: string) => Promise<void> | |
| // Real-time updates | |
| addMessage: (message: Message) => void | |
| updateMessage: (message: Message) => void | |
| removeMessage: (messageId: string) => void | |
| updateChatFromSocket: (chat: Chat) => void | |
| // Typing indicators | |
| setTyping: (chatId: string, userId: string, isTyping: boolean) => void | |
| // User status | |
| setUserOnline: (userId: string, isOnline: boolean) => void | |
| // Utility | |
| clearError: () => void | |
| reset: () => void | |
| } | |
| export const useChatStore = create<ChatState & ChatActions>((set, get) => ({ | |
| // State | |
| chats: [], | |
| currentChat: null, | |
| messages: {}, | |
| typingUsers: {}, | |
| onlineUsers: new Set(), | |
| loading: false, | |
| error: null, | |
| // Actions | |
| loadChats: async () => { | |
| set({ loading: true, error: null }) | |
| try { | |
| const chats = await chatService.getChats() | |
| set({ chats, loading: false }) | |
| } catch (error: any) { | |
| set({ error: error.message, loading: false }) | |
| } | |
| }, | |
| selectChat: (chatId: string) => { | |
| const { chats } = get() | |
| const chat = chats.find(c => c.id === chatId) | |
| if (chat) { | |
| set({ currentChat: chat }) | |
| // Load messages if not already loaded | |
| if (!get().messages[chatId]) { | |
| get().loadMessages(chatId) | |
| } | |
| } | |
| }, | |
| createChat: async (data) => { | |
| set({ loading: true, error: null }) | |
| try { | |
| const chat = await chatService.createChat(data) | |
| set(state => ({ | |
| chats: [chat, ...state.chats], | |
| currentChat: chat, | |
| loading: false | |
| })) | |
| return chat | |
| } catch (error: any) { | |
| set({ error: error.message, loading: false }) | |
| throw error | |
| } | |
| }, | |
| updateChat: async (chatId: string, data: Partial<Chat>) => { | |
| try { | |
| const updatedChat = await chatService.updateChat(chatId, data) | |
| set(state => ({ | |
| chats: state.chats.map(chat => | |
| chat.id === chatId ? updatedChat : chat | |
| ), | |
| currentChat: state.currentChat?.id === chatId ? updatedChat : state.currentChat | |
| })) | |
| } catch (error: any) { | |
| set({ error: error.message }) | |
| } | |
| }, | |
| deleteChat: async (chatId: string) => { | |
| try { | |
| await chatService.deleteChat(chatId) | |
| set(state => ({ | |
| chats: state.chats.filter(chat => chat.id !== chatId), | |
| currentChat: state.currentChat?.id === chatId ? null : state.currentChat, | |
| messages: Object.fromEntries( | |
| Object.entries(state.messages).filter(([id]) => id !== chatId) | |
| ) | |
| })) | |
| } catch (error: any) { | |
| set({ error: error.message }) | |
| } | |
| }, | |
| loadMessages: async (chatId: string, page = 1) => { | |
| set({ loading: true, error: null }) | |
| try { | |
| const messages = await chatService.getMessages(chatId, page) | |
| set(state => ({ | |
| messages: { | |
| ...state.messages, | |
| [chatId]: page === 1 ? messages : [...(state.messages[chatId] || []), ...messages] | |
| }, | |
| loading: false | |
| })) | |
| } catch (error: any) { | |
| set({ error: error.message, loading: false }) | |
| } | |
| }, | |
| sendMessage: async (chatId: string, content: string, attachments?: File[]) => { | |
| try { | |
| await chatService.sendMessage(chatId, { | |
| content, | |
| type: 'text', | |
| attachments | |
| }) | |
| // Message will be added via socket event | |
| } catch (error: any) { | |
| set({ error: error.message }) | |
| } | |
| }, | |
| editMessage: async (messageId: string, content: string) => { | |
| try { | |
| await chatService.editMessage(messageId, content) | |
| // Message will be updated via socket event | |
| } catch (error: any) { | |
| set({ error: error.message }) | |
| } | |
| }, | |
| deleteMessage: async (messageId: string) => { | |
| try { | |
| await chatService.deleteMessage(messageId) | |
| // Message will be removed via socket event | |
| } catch (error: any) { | |
| set({ error: error.message }) | |
| } | |
| }, | |
| addReaction: async (messageId: string, emoji: string) => { | |
| try { | |
| await chatService.addReaction(messageId, emoji) | |
| // Reaction will be updated via socket event | |
| } catch (error: any) { | |
| set({ error: error.message }) | |
| } | |
| }, | |
| removeReaction: async (messageId: string, emoji: string) => { | |
| try { | |
| await chatService.removeReaction(messageId, emoji) | |
| // Reaction will be updated via socket event | |
| } catch (error: any) { | |
| set({ error: error.message }) | |
| } | |
| }, | |
| // Real-time updates | |
| addMessage: (message: Message) => { | |
| set(state => ({ | |
| messages: { | |
| ...state.messages, | |
| [message.chatId]: [...(state.messages[message.chatId] || []), message] | |
| }, | |
| chats: state.chats.map(chat => | |
| chat.id === message.chatId | |
| ? { ...chat, lastMessage: message, updatedAt: new Date() } | |
| : chat | |
| ) | |
| })) | |
| }, | |
| updateMessage: (message: Message) => { | |
| set(state => ({ | |
| messages: { | |
| ...state.messages, | |
| [message.chatId]: (state.messages[message.chatId] || []).map(msg => | |
| msg.id === message.id ? message : msg | |
| ) | |
| } | |
| })) | |
| }, | |
| removeMessage: (messageId: string) => { | |
| set(state => { | |
| const newMessages = { ...state.messages } | |
| Object.keys(newMessages).forEach(chatId => { | |
| newMessages[chatId] = newMessages[chatId].filter(msg => msg.id !== messageId) | |
| }) | |
| return { messages: newMessages } | |
| }) | |
| }, | |
| updateChatFromSocket: (chat: Chat) => { | |
| set(state => ({ | |
| chats: state.chats.map(c => c.id === chat.id ? chat : c), | |
| currentChat: state.currentChat?.id === chat.id ? chat : state.currentChat | |
| })) | |
| }, | |
| setTyping: (chatId: string, userId: string, isTyping: boolean) => { | |
| set(state => { | |
| const typingUsers = { ...state.typingUsers } | |
| const currentTyping = typingUsers[chatId] || [] | |
| if (isTyping) { | |
| if (!currentTyping.includes(userId)) { | |
| typingUsers[chatId] = [...currentTyping, userId] | |
| } | |
| } else { | |
| typingUsers[chatId] = currentTyping.filter(id => id !== userId) | |
| if (typingUsers[chatId].length === 0) { | |
| delete typingUsers[chatId] | |
| } | |
| } | |
| return { typingUsers } | |
| }) | |
| }, | |
| setUserOnline: (userId: string, isOnline: boolean) => { | |
| set(state => { | |
| const onlineUsers = new Set(state.onlineUsers) | |
| if (isOnline) { | |
| onlineUsers.add(userId) | |
| } else { | |
| onlineUsers.delete(userId) | |
| } | |
| return { onlineUsers } | |
| }) | |
| }, | |
| clearError: () => { | |
| set({ error: null }) | |
| }, | |
| reset: () => { | |
| set({ | |
| chats: [], | |
| currentChat: null, | |
| messages: {}, | |
| typingUsers: {}, | |
| onlineUsers: new Set(), | |
| loading: false, | |
| error: null | |
| }) | |
| } | |
| })) | |