diff --git a/components/map/MapboxMap.tsx b/components/map/MapboxMap.tsx index a16b5f2..4dcf5ad 100644 --- a/components/map/MapboxMap.tsx +++ b/components/map/MapboxMap.tsx @@ -3,8 +3,12 @@ import { styled } from '@mui/material'; import type * as mapboxgl from 'mapbox-gl'; import 'mapbox-gl/dist/mapbox-gl.css'; import { useEffect, useRef } from 'react'; +import { preconnect, prefetchDNS } from 'react-dom'; // Component +preconnect('https://api.mapbox.com'); +prefetchDNS('https://events.mapbox.com'); + export interface MapboxMapProps { readonly zoom: number; readonly onMapCreated: (map: mapboxgl.Map) => void; diff --git a/components/search/AnimalSearchOptions.tsx b/components/search/AnimalSearchOptions.tsx index e46d8c3..96850f9 100644 --- a/components/search/AnimalSearchOptions.tsx +++ b/components/search/AnimalSearchOptions.tsx @@ -3,20 +3,23 @@ import { SearchContext, useLoadingSearchOptions } from '@/components/search/search.context'; import SearchOption from '@/components/search/SearchOption'; import { fetchAnimalTracking } from '@/data/club-ocean'; +import PetsIcon from '@mui/icons-material/Pets'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; -import PetsIcon from '@mui/icons-material/Pets'; -import { use, useMemo } from 'react'; -import useSWR from 'swr'; import { AnimatePresence } from 'motion/react'; +import { use } from 'react'; +import useSWR from 'swr'; +// Component export default function AnimalSearchOptions() { const { inputValue } = use(SearchContext); - const isAnimal = useMemo(() => ANIMAL_RE.test(inputValue), [inputValue]); - const key = isAnimal ? `animal:${inputValue.toLowerCase()}` : null; - - const { data, isValidating } = useSWR(key, { fetcher: animalFetcher }); + const { data, isValidating } = useSWR( + ANIMAL_RE.test(inputValue) + ? ['animal', inputValue.toLowerCase()] + : null, + { fetcher: animalFetcher } + ); useLoadingSearchOptions(isValidating); return ( @@ -36,6 +39,8 @@ export default function AnimalSearchOptions() { // Utils const ANIMAL_RE = /^[a-z]{3,}$/i; -function animalFetcher(key: string) { - return fetchAnimalTracking(key.slice(7)); +type AnimalKey = readonly ['animal', string]; + +function animalFetcher([, name]: AnimalKey) { + return fetchAnimalTracking(name); } diff --git a/components/search/DnsSearchOptions.tsx b/components/search/DnsSearchOptions.tsx index 021000d..5dbf85d 100644 --- a/components/search/DnsSearchOptions.tsx +++ b/components/search/DnsSearchOptions.tsx @@ -18,7 +18,7 @@ export default function DnsSearchOptions() { return ( { ips.map((ip) => ( - + diff --git a/components/search/SearchListBox.tsx b/components/search/SearchListBox.tsx index c46a742..8713197 100644 --- a/components/search/SearchListBox.tsx +++ b/components/search/SearchListBox.tsx @@ -10,7 +10,16 @@ export interface SearchListBoxProps { export default function SearchListBox({ ref, listBoxId, children }: SearchListBoxProps) { return ( - + { children } ); diff --git a/components/search/SearchOption.tsx b/components/search/SearchOption.tsx index 427b439..71f17a2 100644 --- a/components/search/SearchOption.tsx +++ b/components/search/SearchOption.tsx @@ -4,6 +4,7 @@ import { SearchContext } from '@/components/search/search.context'; import SearchListItem from '@/components/search/SearchListItem'; import ListItemButton from '@mui/material/ListItemButton'; import { AnimatePresence, usePresence } from 'motion/react'; +import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { type MouseEvent, type ReactNode, use, useCallback, useEffect, useId, useMemo } from 'react'; @@ -16,11 +17,16 @@ export default function SearchOption({ href, children }: SearchOptionProps) { const id = useId(); const [isPresent, safeToRemove] = usePresence(); - const { activeOption, isOpen, search, setActiveOption, registerOption, unregisterOption } = use(SearchContext); + const { activeOption, isOpen, inputValue, search, setActiveOption, registerOption, unregisterOption } = use(SearchContext); const pathname = usePathname(); const isSelected = pathname === href; - const url = useMemo(() => new URL(href, location.origin + pathname), [href, pathname]); + const url = useMemo(() => { + const url = new URL(href, location.origin + pathname); + url.searchParams.set('search', inputValue); + + return url; + }, [href, inputValue, pathname]); useEffect(() => { registerOption(id, url); @@ -39,6 +45,7 @@ export default function SearchOption({ href, children }: SearchOptionProps) { }, [id, setActiveOption]); const handleClick = useCallback((event: MouseEvent) => { + event.preventDefault(); search(url); }, [url, search]); @@ -55,7 +62,7 @@ export default function SearchOption({ href, children }: SearchOptionProps) { > (name ? `https://dns.google.com/resolve?type=${type}&name=${name}` : null, jsonFetch); + const url = `https://dns.google.com/resolve?type=${type}&name=${name}`; + + if (name) { + preload(url, { as: 'fetch', crossOrigin: 'anonymous' }); + } + + return useSWR(name ? url : null, jsonFetch); } function extractIps(type: number, data?: DnsResponse) {