File tree Expand file tree Collapse file tree
packages/react-native-sortables/src/integrations/haptics Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ /**
2+ * Optional expo-haptics adapter.
3+ *
4+ * We never import `expo-haptics` directly, because a static import would break
5+ * Metro bundling for bare React Native apps that don't have it installed.
6+ * Instead we read its native module from the Expo runtime registry, so it's
7+ * picked up automatically in any Expo app (including Expo Go) and ignored
8+ * everywhere else.
9+ */
10+
11+ /* eslint-disable @typescript-eslint/no-explicit-any */
12+ /* eslint-disable @typescript-eslint/no-unsafe-call */
13+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
14+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
15+
16+ import { runOnJS } from 'react-native-reanimated' ;
17+
18+ const load = ( ) => {
19+ const expoHaptics = ( globalThis as any ) . expo ?. modules ?. ExpoHaptics ;
20+
21+ if ( ! expoHaptics ?. impactAsync ) {
22+ return null ;
23+ }
24+
25+ const impact = ( style : string ) => {
26+ try {
27+ // expo-haptics' native method is async; fire-and-forget and swallow
28+ // rejections (e.g. when haptics are unsupported on the device)
29+ const result = expoHaptics . impactAsync ( style ) ;
30+ result ?. catch ?.( ( ) => {
31+ // ignore rejection
32+ } ) ;
33+ } catch {
34+ // ignore
35+ }
36+ } ;
37+
38+ const trigger = ( type = 'impactLight' ) => {
39+ 'worklet' ;
40+ // expo-haptics runs on the JS thread, so hop over from the UI worklet
41+ runOnJS ( impact ) ( type === 'impactMedium' ? 'medium' : 'light' ) ;
42+ } ;
43+
44+ return trigger ;
45+ } ;
46+
47+ const ExpoHaptics = { load } ;
48+
49+ export default ExpoHaptics ;
Original file line number Diff line number Diff line change 1- export { default as ReactNativeHapticFeedback } from './react-native-haptic-feedback' ;
1+ import ExpoHaptics from './expo-haptics' ;
2+ import ReactNativeHapticFeedback from './react-native-haptic-feedback' ;
3+
4+ export const Haptics = {
5+ // Prefer expo-haptics (available in any Expo app, including Expo Go) and
6+ // fall back to react-native-haptic-feedback for bare React Native apps.
7+ load : ( ) => ExpoHaptics . load ( ) ?? ReactNativeHapticFeedback . load ( )
8+ } ;
Original file line number Diff line number Diff line change 1- import type { HapticOptions } from 'react-native-haptic-feedback' ;
2-
3- export const ReactNativeHapticFeedback = {
4- load : ( ) => ( _type : string , _options ?: HapticOptions ) => {
5- // noop
1+ export const Haptics = {
2+ load : ( ) => ( _type ?: string ) => {
3+ // noop on web
64 }
75} ;
Original file line number Diff line number Diff line change @@ -2,22 +2,21 @@ import { useCallback, useMemo } from 'react';
22import { useDerivedValue } from 'react-native-reanimated' ;
33
44import { IS_WEB } from '../../../constants' ;
5- import { ReactNativeHapticFeedback } from '../adapters' ;
5+ import { Haptics } from '../adapters' ;
66
77type HapticImpact = {
88 light ( ) : void ;
99 medium ( ) : void ;
1010} ;
1111
12- let hapticFeedback : null | ReturnType < typeof ReactNativeHapticFeedback . load > =
13- null ;
12+ let hapticFeedback : null | ReturnType < typeof Haptics . load > = null ;
1413
1514export default function useHaptics ( enabled : boolean ) : HapticImpact {
1615 const isEnabled = ! IS_WEB && enabled ;
1716 const enabledValue = useDerivedValue ( ( ) => isEnabled ) ;
1817
1918 if ( isEnabled && ! hapticFeedback ) {
20- hapticFeedback = ReactNativeHapticFeedback . load ( ) ;
19+ hapticFeedback = Haptics . load ( ) ;
2120 }
2221
2322 const light = useCallback ( ( ) => {
You can’t perform that action at this time.
0 commit comments