"use client"; import { useCallback, useState, DragEvent } from "react"; import { useEmailStore } from "@/stores/email-store"; import { useAuthStore } from "@/stores/auth-store"; import { useDragDropContext } from "@/contexts/drag-drop-context"; interface UseTagDropOptions { tagId: string; onSuccess?: (count: number, tagLabel: string) => void; onError?: (error: string) => void; } interface UseTagDropReturn { dropHandlers: { onDragOver: (e: DragEvent) => void; onDragEnter: (e: DragEvent) => void; onDragLeave: (e: DragEvent) => void; onDrop: (e: DragEvent) => void; }; isDropTarget: boolean; isValidDropTarget: boolean; } export function useTagDrop({ tagId, onSuccess, onError }: UseTagDropOptions): UseTagDropReturn { const [isOver, setIsOver] = useState(false); const { client } = useAuthStore(); const { fetchEmails, selectedMailbox } = useEmailStore(); const { isDragging, endDrag } = useDragDropContext(); const handleDragOver = useCallback((e: DragEvent) => { e.preventDefault(); if (isDragging) { e.dataTransfer.dropEffect = "copy"; } else { e.dataTransfer.dropEffect = "none"; } }, [isDragging]); const handleDragEnter = useCallback((e: DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsOver(true); }, []); const handleDragLeave = useCallback((e: DragEvent) => { e.preventDefault(); e.stopPropagation(); const relatedTarget = e.relatedTarget as Node | null; if (!e.currentTarget.contains(relatedTarget)) { setIsOver(false); } }, []); const handleDrop = useCallback(async (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsOver(false); if (!client || !isDragging) { endDrag(); return; } try { const emailIdsJson = e.dataTransfer.getData("application/x-email-ids"); if (!emailIdsJson) { endDrag(); return; } const emailIds: string[] = JSON.parse(emailIdsJson); for (const emailId of emailIds) { // Read fresh state to avoid stale closures const currentEmails = useEmailStore.getState().emails; const email = currentEmails.find(em => em.id === emailId); const keywords = { ...(email?.keywords || {}) }; // Remove old label/color keywords Object.keys(keywords).forEach(key => { if (key.startsWith("$label:") || key.startsWith("$color:")) { keywords[key] = false; } }); // Add the new tag keywords[`$label:${tagId}`] = true; await client.updateEmailKeywords(emailId, keywords); } // Refresh the email list await fetchEmails(client, selectedMailbox); onSuccess?.(emailIds.length, tagId); } catch (error) { console.error("Failed to tag emails:", error); onError?.(error instanceof Error ? error.message : "Unknown error"); } finally { endDrag(); } }, [client, isDragging, tagId, fetchEmails, selectedMailbox, endDrag, onSuccess, onError]); return { dropHandlers: { onDragOver: handleDragOver, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDrop: handleDrop, }, isDropTarget: isOver && isDragging, isValidDropTarget: isOver && isDragging, }; }