about summary refs log tree commit diff
path: root/src/state/global-gesture-events/index.tsx
blob: 8941d9ef460d42f2be81b82905fa36279379a4dd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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: () => {},
})
Context.displayName = 'GlobalGestureEventsContext'

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)
}