about summary refs log tree commit diff
path: root/src/state/global-gesture-events/index.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/global-gesture-events/index.tsx')
-rw-r--r--src/state/global-gesture-events/index.tsx83
1 files changed, 83 insertions, 0 deletions
diff --git a/src/state/global-gesture-events/index.tsx b/src/state/global-gesture-events/index.tsx
new file mode 100644
index 000000000..f8d87a8f9
--- /dev/null
+++ b/src/state/global-gesture-events/index.tsx
@@ -0,0 +1,83 @@
+import {createContext, useContext, useMemo, useRef, useState} from 'react'
+import {View} from 'react-native'
+import {
+  Gesture,
+  GestureDetector,
+  type GestureStateChangeEvent,
+  type GestureUpdateEvent,
+  type PanGestureHandlerEventPayload,
+} from 'react-native-gesture-handler'
+import EventEmitter from 'eventemitter3'
+
+export type GlobalGestureEvents = {
+  begin: GestureStateChangeEvent<PanGestureHandlerEventPayload>
+  update: GestureUpdateEvent<PanGestureHandlerEventPayload>
+  end: GestureStateChangeEvent<PanGestureHandlerEventPayload>
+  finalize: GestureStateChangeEvent<PanGestureHandlerEventPayload>
+}
+
+const Context = createContext<{
+  events: EventEmitter<GlobalGestureEvents>
+  register: () => void
+  unregister: () => void
+}>({
+  events: new EventEmitter<GlobalGestureEvents>(),
+  register: () => {},
+  unregister: () => {},
+})
+
+export function GlobalGestureEventsProvider({
+  children,
+}: {
+  children: React.ReactNode
+}) {
+  const refCount = useRef(0)
+  const events = useMemo(() => new EventEmitter<GlobalGestureEvents>(), [])
+  const [enabled, setEnabled] = useState(false)
+  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)
+    .simultaneousWithExternalGesture()
+    .onBegin(e => {
+      events.emit('begin', e)
+    })
+    .onUpdate(e => {
+      events.emit('update', e)
+    })
+    .onEnd(e => {
+      events.emit('end', e)
+    })
+    .onFinalize(e => {
+      events.emit('finalize', e)
+    })
+
+  return (
+    <Context.Provider value={ctx}>
+      <GestureDetector gesture={gesture}>
+        <View collapsable={false}>{children}</View>
+      </GestureDetector>
+    </Context.Provider>
+  )
+}
+
+export function useGlobalGestureEvents() {
+  return useContext(Context)
+}