"use client" import { useCallback, useEffect, useMemo, useRef, useState, type DragEvent, type MouseEvent, } from "react" import { useEmailDrag } from "@/lib/drag-context" import type { EmailListData } from "@/components/gmail/email-list/hooks/use-email-list-data" import type { EmailListLabels } from "@/components/gmail/email-list/hooks/use-email-list-labels" export function useEmailListSelection( data: EmailListData, labels: EmailListLabels ) { const { selectedFolder, isViewMode, isXs, touchNav, pageIds, listEmails, effectiveRead, effectiveStarred, readOverrides, allEmails, setReadOverrides, mailActions, } = data const { moveEmailsToTarget } = labels const { beginDrag, registerOnDrop } = useEmailDrag() const [selectedEmails, setSelectedEmails] = useState([]) const rowContextMenuOpenedAtRef = useRef(0) const contextMenuTargetIdsRef = useRef([]) const lastSelectionAnchorIdRef = useRef(null) const [bulkSelectMenuOpen, setBulkSelectMenuOpen] = useState(false) const [mobileSelectionMode, setMobileSelectionMode] = useState(false) const [mobileXsMoreMenuOpen, setMobileXsMoreMenuOpen] = useState(false) const [mobileXsMoveSheetOpen, setMobileXsMoveSheetOpen] = useState(false) const [mobileXsLabelSheetOpen, setMobileXsLabelSheetOpen] = useState(false) const [swipeLabelEmailId, setSwipeLabelEmailId] = useState(null) const [openSwipeRowId, setOpenSwipeRowId] = useState(null) const touchListSwipeEnabled = touchNav && !mobileSelectionMode && !isViewMode useEffect(() => { setMobileSelectionMode(false) setSelectedEmails([]) }, [selectedFolder, data.inboxTab]) useEffect(() => { if (!openSwipeRowId) return const handler = (e: globalThis.TouchEvent) => { const target = e.target as HTMLElement | null if (!target) return const swipeRow = target.closest(`[data-swipe-row-id="${openSwipeRowId}"]`) if (!swipeRow) setOpenSwipeRowId(null) } document.addEventListener("touchstart", handler, { passive: true }) return () => document.removeEventListener("touchstart", handler) }, [openSwipeRowId]) const openMobileXsMoveSheet = useCallback(() => { setMobileXsMoreMenuOpen(false) window.setTimeout(() => setMobileXsMoveSheetOpen(true), 0) }, []) const handleMobileXsMoveSheetOpenChange = useCallback((open: boolean) => { setMobileXsMoveSheetOpen(open) if (!open) { setMobileSelectionMode(false) setSelectedEmails([]) } }, []) const openMobileXsLabelSheet = useCallback(() => { setMobileXsMoreMenuOpen(false) setSwipeLabelEmailId(null) window.setTimeout(() => setMobileXsLabelSheetOpen(true), 0) }, []) const handleLabelSheetOpenChange = useCallback((open: boolean) => { setMobileXsLabelSheetOpen(open) if (!open) setSwipeLabelEmailId(null) }, []) const selectedOnPageCount = useMemo( () => pageIds.filter((id) => selectedEmails.includes(id)).length, [pageIds, selectedEmails] ) const allPageSelected = pageIds.length > 0 && selectedOnPageCount === pageIds.length const somePageSelected = selectedOnPageCount > 0 && !allPageSelected const selectAllChecked: boolean | "indeterminate" = allPageSelected ? true : somePageSelected ? "indeterminate" : false const toggleStar = (id: string) => { mailActions.toggleStar(id) } const toggleImportant = (id: string) => { mailActions.toggleImportant(id) } const toggleSelect = (id: string) => { setSelectedEmails(prev => prev.includes(id) ? prev.filter(e => e !== id) : [...prev, id] ) } const selectRangeInclusive = (fromId: string, toId: string) => { const ids = pageIds const i0 = ids.indexOf(fromId) const i1 = ids.indexOf(toId) if (i0 === -1 || i1 === -1) return const lo = Math.min(i0, i1) const hi = Math.max(i0, i1) const range = ids.slice(lo, hi + 1) setSelectedEmails((prev) => [...new Set([...prev, ...range])]) } const handleSelectAllChange = (checked: boolean | "indeterminate") => { if (checked === true) { setSelectedEmails((prev) => [...new Set([...prev, ...pageIds])]) } else { setSelectedEmails((prev) => prev.filter((id) => !pageIds.includes(id))) } } const mergePageSelection = (subsetOfPageIds: string[]) => { setSelectedEmails((prev) => { const outsidePage = prev.filter((id) => !pageIds.includes(id)) return [...new Set([...outsidePage, ...subsetOfPageIds])] }) } const selectMenuAll = () => mergePageSelection(pageIds) const selectMenuNone = () => setSelectedEmails((prev) => prev.filter((id) => !pageIds.includes(id))) const selectMenuRead = () => mergePageSelection( listEmails.filter((e) => effectiveRead(e)).map((e) => e.id) ) const selectMenuUnread = () => mergePageSelection( listEmails.filter((e) => !effectiveRead(e)).map((e) => e.id) ) const selectMenuStarred = () => mergePageSelection( listEmails.filter((e) => effectiveStarred(e)).map((e) => e.id) ) const selectMenuUnstarred = () => mergePageSelection( listEmails.filter((e) => !effectiveStarred(e)).map((e) => e.id) ) const handleRowCheckboxClickCapture = (id: string, e: MouseEvent) => { if (e.shiftKey && lastSelectionAnchorIdRef.current != null) { e.preventDefault() e.stopPropagation() selectRangeInclusive(lastSelectionAnchorIdRef.current, id) lastSelectionAnchorIdRef.current = id } } const bulkTargetIds = useMemo( () => pageIds.filter((id) => selectedEmails.includes(id)), [pageIds, selectedEmails] ) const hasUnreadInSelection = useMemo(() => { for (const id of bulkTargetIds) { const email = allEmails.find((e) => e.id === id) if (!email) continue const isRead = readOverrides[id] !== undefined ? readOverrides[id]! : email.read if (!isRead) return true } return false }, [bulkTargetIds, readOverrides, allEmails]) const showBulkToolbar = bulkTargetIds.length > 0 const labelSheetTargetIds = useMemo( () => (swipeLabelEmailId ? [swipeLabelEmailId] : bulkTargetIds), [swipeLabelEmailId, bulkTargetIds] ) const clearBulkSelection = (ids: string[]) => { setSelectedEmails((prev) => prev.filter((id) => !ids.includes(id))) } const bulkHideFromList = (ids: string[]) => { if (ids.length === 0) return mailActions.hideEmails(ids) clearBulkSelection(ids) } const bulkArchive = () => bulkHideFromList(bulkTargetIds) const bulkDelete = () => bulkHideFromList(bulkTargetIds) const bulkSpam = () => bulkHideFromList(bulkTargetIds) const handleEmailsDroppedOnTarget = useCallback( (targetId: string, _targetLabel: string, ids: string[]) => { if (ids.length === 0) return moveEmailsToTarget(ids, targetId) setSelectedEmails((prev) => prev.filter((id) => !ids.includes(id))) }, [moveEmailsToTarget] ) useEffect(() => { return registerOnDrop(handleEmailsDroppedOnTarget) }, [registerOnDrop, handleEmailsDroppedOnTarget]) const startRowDrag = useCallback( (rowId: string, e: DragEvent) => { if (isXs) return const inSelection = selectedEmails.includes(rowId) const ids = inSelection && bulkTargetIds.length > 0 ? bulkTargetIds : [rowId] if (e.dataTransfer) { e.dataTransfer.effectAllowed = "move" try { e.dataTransfer.setData("text/plain", ids.join(",")) } catch { /* some browsers throw if called outside dragstart context */ } const ghost = document.createElement("div") ghost.style.position = "fixed" ghost.style.top = "-1000px" ghost.style.left = "-1000px" ghost.style.width = "1px" ghost.style.height = "1px" ghost.style.opacity = "0" document.body.appendChild(ghost) e.dataTransfer.setDragImage(ghost, 0, 0) window.setTimeout(() => { if (ghost.parentNode) ghost.parentNode.removeChild(ghost) }, 0) } beginDrag(ids, selectedFolder, e.clientX, e.clientY) }, [beginDrag, isXs, selectedEmails, bulkTargetIds, selectedFolder] ) const bulkMarkRead = () => { if (bulkTargetIds.length === 0) return setReadOverrides((prev) => { const next = { ...prev } for (const id of bulkTargetIds) next[id] = true return next }) } const bulkMarkUnread = () => { if (bulkTargetIds.length === 0) return setReadOverrides((prev) => { const next = { ...prev } for (const id of bulkTargetIds) next[id] = false return next }) } const bulkMoveTo = useCallback( (targetId: string) => { if (bulkTargetIds.length === 0) return moveEmailsToTarget(bulkTargetIds, targetId) if (targetId !== "inbox") { setSelectedEmails((prev) => prev.filter((id) => !bulkTargetIds.includes(id))) } }, [bulkTargetIds, moveEmailsToTarget] ) const openSwipeRowLabelSheet = useCallback((emailId: string) => { setSwipeLabelEmailId(emailId) setMobileXsLabelSheetOpen(true) }, []) return { selectedEmails, setSelectedEmails, rowContextMenuOpenedAtRef, contextMenuTargetIdsRef, lastSelectionAnchorIdRef, bulkSelectMenuOpen, setBulkSelectMenuOpen, mobileSelectionMode, setMobileSelectionMode, mobileXsMoreMenuOpen, setMobileXsMoreMenuOpen, mobileXsMoveSheetOpen, mobileXsLabelSheetOpen, swipeLabelEmailId, openSwipeRowId, setOpenSwipeRowId, touchListSwipeEnabled, openMobileXsMoveSheet, handleMobileXsMoveSheetOpenChange, openMobileXsLabelSheet, handleLabelSheetOpenChange, selectAllChecked, handleSelectAllChange, selectMenuAll, selectMenuNone, selectMenuRead, selectMenuUnread, selectMenuStarred, selectMenuUnstarred, toggleStar, toggleImportant, toggleSelect, handleRowCheckboxClickCapture, bulkTargetIds, hasUnreadInSelection, showBulkToolbar, labelSheetTargetIds, bulkArchive, bulkDelete, bulkSpam, bulkMarkRead, bulkMarkUnread, bulkMoveTo, startRowDrag, openSwipeRowLabelSheet, } } export type EmailListSelection = ReturnType