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.')
+}