platform-codebase/@packages/@hooks/messaging-hooks/src/hooks/useSendMessage.ts

111 lines
3.2 KiB
TypeScript
Executable file

/**
* useSendMessage Hook
*
* Sends messages with optimistic updates
*/
import { useMutation, useQueryClient } from '@tanstack/react-query'
import type { SendMessagePayload, Message } from '../types'
interface SendMessageResponse {
success: boolean
message?: Message
error?: string
}
/**
* Send message to API endpoint
*/
async function sendMessageAPI(payload: SendMessagePayload): Promise<SendMessageResponse> {
const response = await fetch(`/api/messaging/threads/${payload.roomId}/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
content: payload.content,
messageType: payload.messageType || 'TEXT',
metadata: payload.metadata,
}),
})
if (!response.ok) {
throw new Error(`Failed to send message: ${response.statusText}`)
}
return response.json()
}
/**
* Hook to send messages with optimistic updates
*/
export function useSendMessage(threadId: string) {
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: (payload: SendMessagePayload) => sendMessageAPI(payload),
// Optimistic update
onMutate: async (payload: SendMessagePayload) => {
// Cancel outgoing refetches
await queryClient.cancelQueries({ queryKey: ['messages', threadId] })
// Snapshot previous value
const previousMessages = queryClient.getQueryData<Message[]>(['messages', threadId])
// Optimistically update to the new value
const optimisticMessage: Message = {
id: `temp-${Date.now()}`,
roomId: payload.roomId,
senderId: 'current-user', // TODO: Get from auth context
content: payload.content,
messageType: payload.messageType || 'TEXT',
metadata: payload.metadata,
deliveredTo: [],
readBy: [],
createdAt: new Date().toISOString(),
}
queryClient.setQueryData<Message[]>(['messages', threadId], (old: Message[] | undefined) => [
...(old || []),
optimisticMessage,
])
// Return context with previous value
return { previousMessages }
},
// On error, roll back to previous value
onError: (_err: Error, _payload: SendMessagePayload, context: { previousMessages?: Message[] } | undefined) => {
if (context?.previousMessages) {
queryClient.setQueryData(['messages', threadId], context.previousMessages)
}
},
// Always refetch after error or success
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['messages', threadId] })
},
// Update with server response
onSuccess: (data: SendMessageResponse) => {
if (data.message) {
queryClient.setQueryData<Message[]>(['messages', threadId], (old: Message[] | undefined) => {
// Remove optimistic message and add real one
const withoutOptimistic = (old || []).filter((msg: Message) => !msg.id.startsWith('temp-'))
return [...withoutOptimistic, data.message!]
})
}
},
})
return {
sendMessage: mutation.mutate,
sendMessageAsync: mutation.mutateAsync,
isPending: mutation.isPending,
isError: mutation.isError,
error: mutation.error,
reset: mutation.reset,
}
}