@@ -14,13 +14,13 @@ import {
1414 Virtualizer ,
1515} from "@pythnetwork/component-library/Virtualizer" ;
1616import type { Button as UnstyledButton } from "@pythnetwork/component-library/unstyled/Button" ;
17+ import type { ListBoxItemProps } from "@pythnetwork/component-library/unstyled/ListBox" ;
1718import {
1819 ListBox ,
1920 ListBoxItem ,
2021} from "@pythnetwork/component-library/unstyled/ListBox" ;
2122import { useDrawer } from "@pythnetwork/component-library/useDrawer" ;
2223import { useLogger } from "@pythnetwork/component-library/useLogger" ;
23- import { useDetectBrowserInfo } from '@pythnetwork/react-hooks/use-detect-browser-info' ;
2424import { matchSorter } from "match-sorter" ;
2525import type { ReactNode } from "react" ;
2626import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
@@ -58,9 +58,9 @@ type ResolvedSearchButtonProps = {
5858 averageScore ?: number | undefined ;
5959 cluster : Cluster ;
6060 } & (
61- | { name : string ; icon : ReactNode }
62- | { name ?: undefined ; icon ?: undefined }
63- ) ) [ ] ;
61+ | { name : string ; icon : ReactNode }
62+ | { name ?: undefined ; icon ?: undefined }
63+ ) ) [ ] ;
6464} ;
6565
6666const ResolvedSearchButton = ( props : ResolvedSearchButtonProps ) => {
@@ -136,22 +136,59 @@ const SearchDialogContents = ({
136136 /** hooks */
137137 const drawer = useDrawer ( ) ;
138138 const logger = useLogger ( ) ;
139- const browserInfo = useDetectBrowserInfo ( ) ;
140139
141140 /** refs */
141+ const closeDrawerDebounceRef = useRef < NodeJS . Timeout | undefined > ( undefined ) ;
142142 const openTabModifierActiveRef = useRef ( false ) ;
143143 const middleMousePressedRef = useRef ( false ) ;
144144
145145 /** state */
146146 const [ search , setSearch ] = useState ( "" ) ;
147147 const [ type , setType ] = useState < ResultType | "" > ( "" ) ;
148148
149+ /** callbacks */
149150 const closeDrawer = useCallback ( ( ) => {
150- drawer . close ( ) . catch ( ( error : unknown ) => {
151- logger . error ( error ) ;
152- } ) ;
151+ if ( closeDrawerDebounceRef . current ) {
152+ clearTimeout ( closeDrawerDebounceRef . current ) ;
153+ closeDrawerDebounceRef . current = undefined ;
154+ }
155+
156+ // we debounce the drawer closure because, if we don't,
157+ // mobile browsers (at least on iOS) may squash the native <a />
158+ // click, resulting in no price feed loading for the user
159+ closeDrawerDebounceRef . current = setTimeout ( ( ) => {
160+ drawer . close ( ) . catch ( ( error : unknown ) => {
161+ logger . error ( error ) ;
162+ } ) ;
163+ } , 250 ) ;
153164 } , [ drawer , logger ] ) ;
165+ const onLinkPointerDown = useCallback <
166+ NonNullable < ListBoxItemProps < never > [ "onPointerDown" ] >
167+ > ( ( e ) => {
168+ const { button, ctrlKey, metaKey } = e ;
169+
170+ middleMousePressedRef . current = button === 1 ;
171+
172+ // on press is too abstracted and doesn't give us the native event
173+ // for determining if the user clicked their middle mouse button,
174+ // so we need to use the native onClick directly
175+ middleMousePressedRef . current = button === 1 ;
176+ openTabModifierActiveRef . current = metaKey || ctrlKey ;
177+ } , [ ] ) ;
178+ const onLinkPointerUp = useCallback <
179+ NonNullable < ListBoxItemProps < never > [ "onPointerUp" ] >
180+ > ( ( ) => {
181+ const userWantsNewTab =
182+ middleMousePressedRef . current || openTabModifierActiveRef . current ;
183+
184+ // // they want a new tab, the search popover stays open
185+ if ( ! userWantsNewTab ) closeDrawer ( ) ;
186+
187+ middleMousePressedRef . current = false ;
188+ openTabModifierActiveRef . current = false ;
189+ } , [ closeDrawer ] ) ;
154190
191+ /** memos */
155192 const results = useMemo ( ( ) => {
156193 const filteredFeeds = matchSorter ( feeds , search , {
157194 keys : [ "displaySymbol" , "symbol" , "description" , "priceAccount" ] ,
@@ -178,6 +215,7 @@ const SearchDialogContents = ({
178215 }
179216 return [ ...filteredFeeds , ...filteredPublishers ] ;
180217 } , [ feeds , publishers , search , type ] ) ;
218+
181219 return (
182220 < div className = { styles . searchDialogContents } >
183221 < div className = { styles . searchBar } >
@@ -247,23 +285,8 @@ const SearchDialogContents = ({
247285 : `/publishers/${ ClusterToName [ result . cluster ] } /${ encodeURIComponent ( result . publisherKey ) } `
248286 }
249287 data-is-first = { result . id === results [ 0 ] ?. id ? "" : undefined }
250- onPointerDown = { e => {
251- middleMousePressedRef . current = e . button === 1 ;
252- // on press is too abstracted and doesn't give us the native event
253- // for determining if the user clicked their middle mouse button,
254- // so we need to use the native onClick directly
255- middleMousePressedRef . current = e . button === 1 ;
256- openTabModifierActiveRef . current = browserInfo ?. isMacOS ? e . metaKey : e . ctrlKey ;
257- } }
258- onPointerUp = { ( ) => {
259- const userWantsNewTab = middleMousePressedRef . current || openTabModifierActiveRef . current ;
260-
261- // they want a new tab, the search popover stays open
262- if ( ! userWantsNewTab ) closeDrawer ( ) ;
263-
264- middleMousePressedRef . current = false ;
265- openTabModifierActiveRef . current = false ;
266- } }
288+ onPointerDown = { onLinkPointerDown }
289+ onPointerUp = { onLinkPointerUp }
267290 >
268291 < div className = { styles . smallScreen } >
269292 { result . type === ResultType . PriceFeed ? (
0 commit comments