/ utils / haptics.ts
haptics.ts
  1  import * as Haptics from 'expo-haptics';
  2  import { Platform } from 'react-native';
  3  
  4  export enum HapticType {
  5    Light = 'light',
  6    Medium = 'medium',
  7    Heavy = 'heavy',
  8    Success = 'success',
  9    Warning = 'warning',
 10    Error = 'error',
 11    Selection = 'selection',
 12  }
 13  
 14  class HapticFeedbackService {
 15    private static instance: HapticFeedbackService;
 16    private isEnabled: boolean = true;
 17  
 18    private constructor() {}
 19  
 20    static getInstance(): HapticFeedbackService {
 21      if (!HapticFeedbackService.instance) {
 22        HapticFeedbackService.instance = new HapticFeedbackService();
 23      }
 24      return HapticFeedbackService.instance;
 25    }
 26  
 27    setEnabled(enabled: boolean): void {
 28      this.isEnabled = enabled;
 29    }
 30  
 31    async trigger(type: HapticType): Promise<void> {
 32      if (!this.isEnabled || Platform.OS === 'web') {
 33        return;
 34      }
 35  
 36      try {
 37        switch (type) {
 38          case HapticType.Light:
 39            await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
 40            break;
 41          case HapticType.Medium:
 42            await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
 43            break;
 44          case HapticType.Heavy:
 45            await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
 46            break;
 47          case HapticType.Success:
 48            await Haptics.notificationAsync(
 49              Haptics.NotificationFeedbackType.Success
 50            );
 51            break;
 52          case HapticType.Warning:
 53            await Haptics.notificationAsync(
 54              Haptics.NotificationFeedbackType.Warning
 55            );
 56            break;
 57          case HapticType.Error:
 58            await Haptics.notificationAsync(
 59              Haptics.NotificationFeedbackType.Error
 60            );
 61            break;
 62          case HapticType.Selection:
 63            await Haptics.selectionAsync();
 64            break;
 65          default:
 66            await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
 67        }
 68      } catch (error) {
 69        console.warn('Haptic feedback failed:', error);
 70      }
 71    }
 72  
 73    // Convenience methods for common interactions
 74    async onPress(): Promise<void> {
 75      return this.trigger(HapticType.Light);
 76    }
 77  
 78    async onLongPress(): Promise<void> {
 79      return this.trigger(HapticType.Medium);
 80    }
 81  
 82    async onSuccess(): Promise<void> {
 83      return this.trigger(HapticType.Success);
 84    }
 85  
 86    async onError(): Promise<void> {
 87      return this.trigger(HapticType.Error);
 88    }
 89  
 90    async onSelection(): Promise<void> {
 91      return this.trigger(HapticType.Selection);
 92    }
 93  
 94    async onSwipe(): Promise<void> {
 95      return this.trigger(HapticType.Light);
 96    }
 97  
 98    async onBookmark(): Promise<void> {
 99      return this.trigger(HapticType.Medium);
100    }
101  
102    async onChapterComplete(): Promise<void> {
103      return this.trigger(HapticType.Success);
104    }
105  }
106  
107  export const hapticFeedback = HapticFeedbackService.getInstance();
108  
109  // React hook for haptic feedback
110  export function useHapticFeedback() {
111    return {
112      onPress: () => hapticFeedback.onPress(),
113      onLongPress: () => hapticFeedback.onLongPress(),
114      onSuccess: () => hapticFeedback.onSuccess(),
115      onError: () => hapticFeedback.onError(),
116      onSelection: () => hapticFeedback.onSelection(),
117      onSwipe: () => hapticFeedback.onSwipe(),
118      onBookmark: () => hapticFeedback.onBookmark(),
119      onChapterComplete: () => hapticFeedback.onChapterComplete(),
120      trigger: (type: HapticType) => hapticFeedback.trigger(type),
121      setEnabled: (enabled: boolean) => hapticFeedback.setEnabled(enabled),
122    };
123  }