From 2b23499a1510d787e4dfb4ae77d525b50efc51ce Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Sun, 22 Jun 2025 17:19:14 -0500 Subject: [PATCH] Cleanup --- src/App.native.tsx | 6 +- src/components/Tooltip/index.tsx | 26 +++---- src/components/hooks/useOnGesture/index.ts | 24 +++++++ .../hooks/useOnGesture/index.web.ts | 1 + .../index.tsx} | 72 +++++++++---------- src/state/global-gesture-events/index.web.tsx | 9 +++ 6 files changed, 83 insertions(+), 55 deletions(-) create mode 100644 src/components/hooks/useOnGesture/index.ts create mode 100644 src/components/hooks/useOnGesture/index.web.ts rename src/state/{shell/GlobalGestureEvents.tsx => global-gesture-events/index.tsx} (55%) create mode 100644 src/state/global-gesture-events/index.web.tsx diff --git a/src/App.native.tsx b/src/App.native.tsx index dc8d29fee2..c42b11746f 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -33,6 +33,7 @@ import { ensureGeolocationResolved, Provider as GeolocationProvider, } from '#/state/geolocation' +import {GlobalGestureEventsProvider} from '#/state/global-gesture-events' import {Provider as HomeBadgeProvider} from '#/state/home-badge' import {Provider as InvitesStateProvider} from '#/state/invites' import {Provider as LightboxStateProvider} from '#/state/lightbox' @@ -73,7 +74,6 @@ import {Splash} from '#/Splash' import {BottomSheetProvider} from '../modules/bottom-sheet' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottomBarBorder' -import {GlobalGestureEvents} from '#/state/shell/GlobalGestureEvents' SplashScreen.preventAutoHideAsync() if (isIOS) { @@ -155,13 +155,13 @@ function InnerApp() { - + - + diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index f73f2ef232..5892338369 100644 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -1,17 +1,17 @@ import { + createContext, useCallback, + useContext, useMemo, useRef, - createContext, - useContext, useState, } from 'react' -import {View, Dimensions} from 'react-native' -import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {Dimensions, View} from 'react-native' -import {Portal} from '#/components/Portal' import {atoms as a, useTheme} from '#/alf' -import {useOnInteract} from '#/state/shell/GlobalGestureEvents' +import {useOnGesture} from '#/components/hooks/useOnGesture' +// import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {Portal} from '#/components/Portal' import {TIP_SIZE} from '#/components/Tooltip/const' type TooltipContextType = { @@ -160,7 +160,7 @@ function Bubble({ > }) { const t = useTheme() - const insets = useSafeAreaInsets() + // const insets = useSafeAreaInsets() const [bubbleMeasurements, setBubbleMeasurements] = useState< | { width: number @@ -180,9 +180,9 @@ function Bubble({ } const win = Dimensions.get('window') - const {width: ww, height: wh} = win - const maxTop = insets.top - const maxBottom = wh - insets.bottom + const {width: ww, height: _wh} = win + // const maxTop = insets.top + // const maxBottom = wh - insets.bottom const {width: cw, height: ch} = bubbleMeasurements const minLeft = a.px_xl.paddingLeft const maxLeft = ww - minLeft @@ -211,14 +211,14 @@ function Bubble({ tipTop, tipLeft, } - }, [targetMeasurements, bubbleMeasurements, insets]) + }, [targetMeasurements, bubbleMeasurements]) const requestCloseWrapped = useCallback(() => { setBubbleMeasurements(undefined) requestClose() }, [requestClose]) - useOnInteract( + useOnGesture( useCallback( e => { const {x, y} = e @@ -243,7 +243,7 @@ function Bubble({ a.align_start, { width: 200, - opacity: !!bubbleMeasurements ? 1 : 0, + opacity: bubbleMeasurements ? 1 : 0, top: coords.top, left: coords.left, }, diff --git a/src/components/hooks/useOnGesture/index.ts b/src/components/hooks/useOnGesture/index.ts new file mode 100644 index 0000000000..6f05606612 --- /dev/null +++ b/src/components/hooks/useOnGesture/index.ts @@ -0,0 +1,24 @@ +import {useEffect} from 'react' + +import { + type GlobalGestureEvents, + useGlobalGestureEvents, +} from '#/state/global-gesture-events' + +/** + * Listen for global gesture events. Callback should be wrapped with + * `useCallback` or otherwise memoized to avoid unnecessary re-renders. + */ +export function useOnGesture( + onGestureCallback: (e: GlobalGestureEvents['begin']) => void, +) { + const ctx = useGlobalGestureEvents() + useEffect(() => { + ctx.register() + ctx.events.on('begin', onGestureCallback) + return () => { + ctx.unregister() + ctx.events.off('begin', onGestureCallback) + } + }, [ctx, onGestureCallback]) +} diff --git a/src/components/hooks/useOnGesture/index.web.ts b/src/components/hooks/useOnGesture/index.web.ts new file mode 100644 index 0000000000..6129fde106 --- /dev/null +++ b/src/components/hooks/useOnGesture/index.web.ts @@ -0,0 +1 @@ +export function useOnGesture() {} diff --git a/src/state/shell/GlobalGestureEvents.tsx b/src/state/global-gesture-events/index.tsx similarity index 55% rename from src/state/shell/GlobalGestureEvents.tsx rename to src/state/global-gesture-events/index.tsx index 8c3c4eb0c7..8202c4b49d 100644 --- a/src/state/shell/GlobalGestureEvents.tsx +++ b/src/state/global-gesture-events/index.tsx @@ -1,49 +1,56 @@ -import {createContext, useContext, useMemo, useEffect, useRef, useState} from 'react' -import EventEmitter from 'eventemitter3' +import {createContext, useContext, useMemo, useRef, useState} from 'react' import { - GestureDetector, Gesture, - GestureStateChangeEvent, - GestureUpdateEvent, - PanGestureHandlerEventPayload, + GestureDetector, + type GestureStateChangeEvent, + type GestureUpdateEvent, + type PanGestureHandlerEventPayload, } from 'react-native-gesture-handler' +import EventEmitter from 'eventemitter3' -const events = new EventEmitter<{ +export type GlobalGestureEvents = { begin: GestureStateChangeEvent update: GestureUpdateEvent end: GestureStateChangeEvent finalize: GestureStateChangeEvent -}>() +} const Context = createContext<{ + events: EventEmitter register: () => void unregister: () => void }>({ + events: new EventEmitter(), register: () => {}, unregister: () => {}, }) -export function GlobalGestureEvents({ +export function GlobalGestureEventsProvider({ children, }: { children: React.ReactNode -}): React.ReactElement { +}) { const refCount = useRef(0) + const events = useMemo(() => new EventEmitter(), []) const [enabled, setEnabled] = useState(false) - const ctx = useMemo(() => ({ - register() { - refCount.current += 1 - if (refCount.current === 1) { - setEnabled(true) - } - }, - unregister() { - refCount.current -= 1 - if (refCount.current === 0) { - setEnabled(false) - } - }, - }), [setEnabled]) + const ctx = useMemo( + () => ({ + events, + register() { + refCount.current += 1 + if (refCount.current === 1) { + setEnabled(true) + } + }, + unregister() { + refCount.current -= 1 + if (refCount.current === 0) { + setEnabled(false) + } + }, + }), + [events, setEnabled], + ) const gesture = Gesture.Pan() .runOnJS(true) .enabled(enabled) @@ -68,19 +75,6 @@ export function GlobalGestureEvents({ ) } -export function useOnInteract( - onInteract: ( - e: GestureStateChangeEvent, - ) => void, -) { - const ctx = useContext(Context) - useEffect(() => { - ctx.register() - events.on('begin', onInteract) - - return () => { - ctx.unregister() - events.off('begin', onInteract) - } - }, [ctx, onInteract]) +export function useGlobalGestureEvents() { + return useContext(Context) } diff --git a/src/state/global-gesture-events/index.web.tsx b/src/state/global-gesture-events/index.web.tsx new file mode 100644 index 0000000000..5d6d53369c --- /dev/null +++ b/src/state/global-gesture-events/index.web.tsx @@ -0,0 +1,9 @@ +export function GlobalGestureEventsProvider(_props: { + children: React.ReactNode +}) { + throw new Error('GlobalGestureEventsProvider is not supported on web.') +} + +export function useGlobalGestureEvents() { + throw new Error('useGlobalGestureEvents is not supported on web.') +}