/** * Recipients Store */ import { ref } from 'vue' import { defineStore } from 'pinia' import { recipientService } from '../services/recipientService' import type { MessageAddressInterface } from '@MailManager/types/message' /** Minimum characters before a lookup fires. */ const MIN_QUERY_LENGTH = 3 /** Debounce window for keystroke-driven searches. */ const DEBOUNCE_MS = 250 export const useRecipientLookupStore = defineStore('mailRecipientsStore', () => { const suggestions = ref([]) const searching = ref(false) let debounceTimer: ReturnType | null = null // Monotonic token so out-of-order/stale responses are discarded. let requestToken = 0 function cancelPending() { if (debounceTimer) { clearTimeout(debounceTimer) debounceTimer = null } } /** * Clear suggestions and cancel any in-flight lookup. */ function clear() { cancelPending() requestToken++ suggestions.value = [] searching.value = false } /** * Debounced contact search. Queries shorter than MIN_QUERY_LENGTH clear the * suggestion list without hitting the backend. */ function search(query: string) { const trimmed = (query ?? '').trim() cancelPending() if (trimmed.length < MIN_QUERY_LENGTH) { requestToken++ suggestions.value = [] searching.value = false return } searching.value = true const token = ++requestToken debounceTimer = setTimeout(async () => { try { const matches = await recipientService.search(trimmed) if (token !== requestToken) { return } suggestions.value = matches } catch (error: any) { if (token === requestToken) { suggestions.value = [] } console.error('[Mail][Recipient Lookup] - Search failed:', error) } finally { if (token === requestToken) { searching.value = false } } }, DEBOUNCE_MS) } return { suggestions, searching, search, clear, } })