"use client" import { useCallback, useEffect, useMemo, useRef, useState, type KeyboardEvent, } from "react" import { useRouter } from "next/navigation" import { ArrowLeft, Search, X, Paperclip, Clock, User, SlidersHorizontal, } from "lucide-react" import { Button } from "@/components/ui/button" import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Checkbox } from "@/components/ui/checkbox" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { cn } from "@/lib/utils" import { emails } from "@/lib/email-data" import { useContactsStore } from "@/lib/contacts/contacts-store" import { useActiveAccount } from "@/lib/stores/account-store" import { matchContacts, matchEmails, bestCompletion, type SearchSuggestion, } from "@/lib/mail-search/search-engine" import { buildSearchUrl, EMPTY_SEARCH_PARAMS, DATE_RANGE_OPTIONS, SEARCH_IN_OPTIONS, type SearchParams, } from "@/lib/mail-search/search-params" import { avatarColor, senderInitial } from "@/lib/sender-display" interface MobileSearchOverlayProps { open: boolean onClose: () => void initialQuery?: string } export function MobileSearchOverlay({ open, onClose, initialQuery = "" }: MobileSearchOverlayProps) { const router = useRouter() const account = useActiveAccount() const contacts = useContactsStore((s) => s.contacts) const [inputValue, setInputValue] = useState(initialQuery) const [selectedIndex, setSelectedIndex] = useState(-1) const [chipAttachment, setChipAttachment] = useState(false) const [chipLast7Days, setChipLast7Days] = useState(false) const [chipFromMe, setChipFromMe] = useState(false) const [advancedMode, setAdvancedMode] = useState(false) const inputRef = useRef(null) useEffect(() => { if (open) { setInputValue(initialQuery) setAdvancedMode(false) setTimeout(() => inputRef.current?.focus(), 50) } else { setSelectedIndex(-1) setChipAttachment(false) setChipLast7Days(false) setChipFromMe(false) setAdvancedMode(false) } }, [open, initialQuery]) const suggestions = useMemo(() => { if (!inputValue.trim()) return [] const contactHits = matchContacts(inputValue, contacts, 4) const emailHits = matchEmails(inputValue, emails, 4) const seen = new Set(contactHits.map((c) => c.email)) const unique = emailHits.filter((e) => !seen.has(e.email)) return [...contactHits, ...unique] }, [inputValue, contacts]) const ghostText = useMemo( () => bestCompletion(inputValue, suggestions), [inputValue, suggestions] ) const totalItems = suggestions.length + 1 const submitSearch = useCallback( (overrideQuery?: string) => { const q = overrideQuery ?? inputValue if (!q.trim() && !chipAttachment && !chipLast7Days && !chipFromMe) return const params: Partial = { q: q.trim() } if (chipAttachment) params.has = ["attachment"] if (chipLast7Days) params.within = "1w" if (chipFromMe) params.from = account.email router.push(buildSearchUrl(params)) onClose() }, [inputValue, chipAttachment, chipLast7Days, chipFromMe, account.email, router, onClose] ) const selectSuggestion = useCallback( (s: SearchSuggestion) => { const params: Partial = { q: s.email } if (chipAttachment) params.has = ["attachment"] if (chipLast7Days) params.within = "1w" if (chipFromMe) params.from = account.email router.push(buildSearchUrl(params)) onClose() }, [chipAttachment, chipLast7Days, chipFromMe, account.email, router, onClose] ) const handleKeyDown = useCallback( (e: KeyboardEvent) => { switch (e.key) { case "ArrowDown": e.preventDefault() setSelectedIndex((i) => (i < totalItems - 1 ? i + 1 : 0)) break case "ArrowUp": e.preventDefault() setSelectedIndex((i) => (i > 0 ? i - 1 : totalItems - 1)) break case "Enter": e.preventDefault() if (selectedIndex >= 0 && selectedIndex < suggestions.length) { selectSuggestion(suggestions[selectedIndex]!) } else { submitSearch() } break case "Tab": if (ghostText) { e.preventDefault() setInputValue(inputValue + ghostText) } break case "Escape": e.preventDefault() onClose() break } }, [selectedIndex, totalItems, suggestions, ghostText, inputValue, submitSearch, selectSuggestion, onClose] ) return ( { if (!isOpen) onClose() }}> Rechercher dans les messages {/* Header */}
{ghostText && !advancedMode && (
{inputValue} {ghostText}
)} { setInputValue(e.target.value) setSelectedIndex(-1) if (advancedMode) setAdvancedMode(false) }} onKeyDown={handleKeyDown} placeholder="Rechercher dans les messages" className="h-10 w-full bg-transparent text-sm outline-none placeholder:text-gray-400" autoComplete="off" />
{inputValue && !advancedMode && ( )}
{advancedMode ? ( { router.push(url); onClose() }} /> ) : ( <> {/* Filter chips */}
{/* Suggestions */}
{inputValue.trim() && ( <> {suggestions.map((s, i) => { const isSelected = i === selectedIndex if (s.kind === "contact") { const initial = senderInitial(s.displayName) const color = avatarColor(s.displayName) return ( ) } return ( ) })} {/* All results row */} )}
)}
) } // ─── Mobile Advanced Search ────────────────────────────────────────────────── function MobileAdvancedSearch({ initialQuery, onSubmit, }: { initialQuery: string onSubmit: (url: string) => void }) { const [from, setFrom] = useState("") const [to, setTo] = useState("") const [subject, setSubject] = useState("") const [hasWords, setHasWords] = useState(initialQuery) const [doesNotHave, setDoesNotHave] = useState("") const [sizeVal, setSizeVal] = useState("") const [sizeOp, setSizeOp] = useState<"gt" | "lt">("gt") const [sizeUnit, setSizeUnit] = useState<"Mo" | "Ko">("Mo") const [within, setWithin] = useState("") const [searchIn, setSearchIn] = useState("all") const [hasAttachment, setHasAttachment] = useState(false) const [excludeChats, setExcludeChats] = useState(false) const handleSubmit = () => { const params: Partial = { ...EMPTY_SEARCH_PARAMS, q: "", from, to, subject, hasWords, doesNotHave, size: sizeVal, sizeOp, sizeUnit, within, in: searchIn, has: hasAttachment ? ["attachment"] : [], excludeChats, } onSubmit(buildSearchUrl(params)) } return (
setFrom(e.target.value)} className="h-9 text-sm" />
setTo(e.target.value)} className="h-9 text-sm" />
setSubject(e.target.value)} className="h-9 text-sm" />
setHasWords(e.target.value)} className="h-9 text-sm" />
setDoesNotHave(e.target.value)} className="h-9 text-sm" />
setSizeVal(e.target.value)} className="h-9 w-20 text-sm" />
) }