"use client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { apiClient, API_BASE_URL } from "@/lib/api/client"; import { useAuth } from "@/hooks/useAuth"; // ============================================ // Types // ============================================ export interface Room { id: string; name: string; type: "dm" | "group" | "channel"; lastMessage?: string; lastMessageAt?: string; unreadCount: number; participants: Participant[]; description?: string; } export interface Participant { userId: string; userName: string; photo?: string | null; } export interface Message { id: string; roomId: string; senderId: string; senderName: string; senderPhoto?: string | null; content: string; type: "text" | "file" | "system"; fileUrl?: string; fileName?: string; fileMimeType?: string | null; reactions: Reaction[]; threadCount?: number; parentId?: string | null; isDeleted: boolean; createdAt: string; } export interface Reaction { emoji: string; users: { userId: string; userName: string }[]; } export interface CompanyUser { userId: string; userName: string; deptName?: string; positionName?: string; photo?: string | null; } // ============================================ // API helpers // ============================================ async function fetchApi(url: string): Promise { const res = await apiClient.get(url); return res.data?.data ?? res.data; } async function postApi(url: string, data?: unknown): Promise { const res = await apiClient.post(url, data); return res.data?.data ?? res.data; } // ============================================ // Hooks // ============================================ export function useRooms() { const { user } = useAuth(); const companyCode = user?.companyCode || user?.company_code; return useQuery({ queryKey: ["messenger", "rooms", companyCode], queryFn: async () => { const data = await fetchApi("/messenger/rooms"); return data.map((r) => ({ id: String(r.id), name: r.room_name ?? r.name ?? "", type: r.room_type ?? r.type, lastMessage: r.last_message ?? r.lastMessage, lastMessageAt: r.last_message_at ?? r.lastMessageAt, unreadCount: r.unread_count ?? r.unreadCount ?? 0, description: r.description, participants: (r.participants ?? []).map((p: any) => ({ userId: p.user_id ?? p.userId, userName: p.user_name ?? p.userName, photo: p.photo ? `data:image/jpeg;base64,${p.photo}` : null, })), })); }, refetchInterval: 30000, }); } export function useMessages(roomId: string | null) { return useQuery({ queryKey: ["messenger", "messages", roomId], queryFn: async () => { const data = await fetchApi(`/messenger/rooms/${roomId}/messages`); return data.map((m) => ({ id: String(m.id), roomId: String(m.room_id ?? m.roomId), senderId: m.sender_id ?? m.senderId, senderName: m.sender_name ?? m.senderName ?? m.sender_id, senderPhoto: m.sender_photo ? `data:image/jpeg;base64,${m.sender_photo}` : (m.senderPhoto ?? null), content: m.content ?? "", type: m.message_type ?? m.type ?? "text", fileUrl: m.files?.[0]?.id ? `${API_BASE_URL}/messenger/files/${m.files[0].id}` : (m.file_url ?? m.fileUrl), fileName: m.files?.[0]?.original_name ?? m.file_name ?? m.fileName, fileMimeType: m.files?.[0]?.mime_type ?? null, reactions: m.reactions ?? [], threadCount: m.thread_count ?? m.threadCount ?? 0, parentId: m.parent_message_id ?? m.parentId ?? null, isDeleted: m.is_deleted ?? m.isDeleted ?? false, createdAt: m.created_at ?? m.createdAt, })); }, enabled: !!roomId, }); } export function useCompanyUsers() { const { user } = useAuth(); const companyCode = user?.companyCode || user?.company_code; return useQuery({ queryKey: ["messenger", "users", companyCode], queryFn: async () => { const data = await fetchApi("/messenger/users"); return data.map((u) => ({ userId: u.user_id ?? u.userId, userName: u.user_name ?? u.userName, deptName: u.dept_name ?? u.deptName, photo: u.photo ? `data:image/jpeg;base64,${u.photo}` : null, })); }, }); } export function useUnreadCount() { return useQuery({ queryKey: ["messenger", "unread"], queryFn: () => fetchApi("/messenger/unread"), refetchInterval: 15000, }); } export function useSendMessage() { const qc = useQueryClient(); const { user } = useAuth(); return useMutation({ mutationFn: (payload: { roomId: string; content: string; type?: string; parentId?: string | null; fileUrl?: string; fileName?: string }) => postApi(`/messenger/rooms/${payload.roomId}/messages`, { content: payload.content, type: payload.type, parentId: payload.parentId, file_url: payload.fileUrl, file_name: payload.fileName, }), onMutate: async (variables) => { const queryKey = ["messenger", "messages", variables.roomId]; await qc.cancelQueries({ queryKey }); const previous = qc.getQueryData(queryKey); const optimistic: Message = { id: `optimistic-${Date.now()}`, roomId: variables.roomId, senderId: user?.userId ?? "me", senderName: "", content: variables.content, type: (variables.type as Message["type"]) ?? "text", reactions: [], isDeleted: false, createdAt: new Date().toISOString(), }; qc.setQueryData(queryKey, (old) => [...(old ?? []), optimistic]); return { previous, queryKey }; }, onError: (_err, _vars, context) => { if (context) qc.setQueryData(context.queryKey, context.previous); }, onSuccess: (_data, variables) => { qc.invalidateQueries({ queryKey: ["messenger", "messages", variables.roomId] }); qc.invalidateQueries({ queryKey: ["messenger", "rooms"] }); }, }); } export function useCreateRoom() { const qc = useQueryClient(); return useMutation({ mutationFn: (payload: { type: "dm" | "group" | "channel"; name?: string; description?: string; participantIds: string[] }) => postApi("/messenger/rooms", payload), onSuccess: () => { qc.invalidateQueries({ queryKey: ["messenger", "rooms"] }); }, }); } export function useMarkAsRead() { const qc = useQueryClient(); return useMutation({ mutationFn: (roomId: string) => postApi(`/messenger/rooms/${roomId}/read`), onSuccess: () => { qc.invalidateQueries({ queryKey: ["messenger", "unread"] }); qc.invalidateQueries({ queryKey: ["messenger", "rooms"] }); }, }); } export function useAddReaction() { const qc = useQueryClient(); return useMutation({ mutationFn: (payload: { messageId: string; roomId: string; emoji: string }) => postApi(`/messenger/messages/${payload.messageId}/reactions`, { emoji: payload.emoji }), onSuccess: (_data, variables) => { qc.invalidateQueries({ queryKey: ["messenger", "messages", variables.roomId] }); }, }); } export function useUpdateRoom() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ roomId, name }: { roomId: string; name: string }) => apiClient.put(`/messenger/rooms/${roomId}`, { room_name: name }).then((res) => res.data?.data ?? res.data), onSuccess: () => { qc.invalidateQueries({ queryKey: ["messenger", "rooms"] }); }, }); } export function useUploadFile() { const qc = useQueryClient(); return useMutation({ mutationFn: async ({ file, roomId }: { file: File; roomId: string }) => { const formData = new FormData(); formData.append("files", file); formData.append("room_id", roomId); const res = await apiClient.post("/messenger/files/upload", formData, { headers: { "Content-Type": "multipart/form-data" }, }); return res.data?.data ?? res.data; }, onSuccess: (_data, variables) => { qc.invalidateQueries({ queryKey: ["messenger", "messages", variables.roomId] }); qc.invalidateQueries({ queryKey: ["messenger", "rooms"] }); }, }); }