cnmksjs's picture
Upload 60 files
590318c verified
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
})
}
}))