import type { ISelectItem } from '../types'; import { isoCodeMap } from '../utils/iso-code-map'; import { DictionariesApiService } from '../services/api/dictionaries.service'; /** Ручной выбор страны переживает перезагрузку год — читает его server/middleware/country.ts */ const COOKIE_OPTS = { maxAge: 60 * 60 * 24 * 365, path: '/', secure: true, sameSite: 'lax' as const }; /** * Единый источник страны клиента: ISO-код (гео-заголовок прокси или ручной выбор * в селекторе), иконка флага и локализованное имя. * * Имя всегда резолвится ОТ ТЕКУЩЕГО кода — своего кеша у него нет. Раньше имя * лежало в отдельной куке `clientCountryName` рядом с парной `clientCountryNameCode`, * и пара могла разъехаться: заголовок писал одну страну, флаг и селектор показывали * другую (реальный кейс на проде: `clientCountryName=USA` при коде `ad`). */ export const useClientCountry = () => { const code = useState('clientCountryCode', () => ''); const resolvedName = useState('clientCountryName', () => ''); const countriesItems = useState('countriesItems', () => []); const codeCookie = useCookie('clientCountryCode', COOKIE_OPTS); /** ISO-код → имя файла флага (у части стран код локали отличается от кода страны) */ const toIcon = (value: string) => { const iso = value.trim().toLowerCase(); return iso ? isoCodeMap[iso] || iso : ''; }; const icon = computed(() => toIcon(code.value)); // Список стран догружается лениво на клиенте (см. layout/language.vue) — если он // уже есть, имя берём из него: там оно точно совпадает с текущим кодом. const name = computed(() => { if (!icon.value) return ''; return countriesItems.value.find(item => item.icon === icon.value)?.title || resolvedName.value; }); // Флаг только для страны, которую смогли назвать: гео-заголовок умеет отдавать // служебные коды (Cloudflare `XX` для неизвестного IP и `T1` для Tor, MaxMind `AP`), // их нет ни в словаре стран, ни среди файлов флагов — вышла бы битая картинка. const flagSrc = computed(() => (icon.value && name.value ? `/icons/flags/${icon.value}.svg` : '')); /** Ручной выбор страны в селекторе: наружу переживает перезагрузку только код. */ const setCountry = (item: ISelectItem) => { code.value = item.key || ''; resolvedName.value = item.title; codeCookie.value = code.value || null; }; const isLoading = ref(false); /** * Полный список стран в payload не кладётся (см. plugins/countries.server.ts) — * догружаем лениво на клиенте, когда открыли селектор. */ const loadCountries = async () => { if (countriesItems.value.length || isLoading.value) return; isLoading.value = true; try { const data = await DictionariesApiService.getCountries(); countriesItems.value = (data?.countries ?? []).map((item) => { const iso = item.isoCodes[0].toLowerCase(); return { title: item.title, value: item.title, icon: toIcon(iso), key: iso }; }); } catch { // оставляем пустым — следующее открытие попробует снова } finally { isLoading.value = false; } }; return { code, icon, name, flagSrc, countriesItems, setCountry, loadCountries }; };