PLEASE IMPLEMENT THIS PLAN: # Sound + haptics redesign ## C...
Prompt
PLEASE IMPLEMENT THIS PLAN: # Sound + haptics redesign ## Context The app's haptics and sounds grew call site by call site: ~70 raw preset calls through `utils/VibrationUtils.ts` (`expo-haptics`), four synthesized chat sounds plus two call mp3s (`expo-audio`), system defaults for pushes, nothing on web, and no sounds switch. Using the `sound-haptics-design` skill (causality, harmony, utility; default is no effect), the goal is one small feedback vocabulary that means the same thing on iOS, Android and web, with fewer and better effects. Decisions from the brainstorm: - First pass = foundation + fixes. New moments come after. - Sound scope: sounds switch, sounds on web, branded push sound, own call sounds. - Custom haptics (Core Haptics / Android compositions): decide after a Motion Lab prototype. ## Audit facts the plan rests on - iOS: `expo-haptics` = UIKit feedback generators. No Core Haptics, no `.ahap`. - Android: `impactAsync` / `notificationAsync` / `selectionAsync` are `Vibrator` waveforms (`HapticsModule.kt:51`); `performAndroidHapticsAsync` uses `view.performHapticFeedback` (line 45). - Usage: selection 23, success 16, light 13, error 11, medium 4, ripple 2, rigid 1. - `VibrationUtils.ripple` = three presets on `setTimeout`. - Voice note cancel fires `error` (`useVoiceNoteRecording.ts:241`). - `chatSounds.ts` returns early on web; `chat-pop` is identical on every bubble. - `DESIGN.md` "Press and haptics" table has stale rows. ## Phase 1: semantic layer (JS only) New `utils/feedback/` (name open): - `feedback.ts`: one function per event. Starting vocabulary, from what the code already does: `selectionChanged`, `thresholdReached` (swipe-to-reply, slide lock, call bar snap), `lifted` (long-press menu), `recordingStarted`, `actionSucceeded`, `actionFailed`, `actionCancelled`, `iraSpoke`, `iraReacted`, `callConnected`, `callEnded`, `rippleSent`. - Per-platform map in one table: - iOS: today's UIKit presets (no feel change except the fixes below). - Android: `Haptics.performAndroidHapticsAsync(AndroidHaptics.*)` equivalents (e.g. Confirm, Reject, Segment_Tick, Long_Press, Gesture_End); pick each on a real phone. - Web: no haptic. - `VibrationUtils` keeps the switch, storage and error handling; the event layer calls it. Components stop calling `light`/`medium` directly. - Migrate the ~70 call sites, `useHapticPick`, `useCallHaptics`, and `tests/hapticsMock.ts`. ## Phase 2: fixes and pruning - Voice note cancel -> `actionCancelled` (a soft/light impact), not the error pattern. - Trace the 16 `success` sites; move any that fire on tap to the confirmed outcome. - Review the 23 `selection` sites and the typing tick against "would removing it lose clarity"; list proposed removals for Saket before deleting any. - `chat-pop`: small bounded pitch/volume variation per bubble (playback rate on the existing player). - Rewrite the `DESIGN.md` table from the event map. ## Phase 3: sound scope - Sounds switch: `AsyncStorageManager` key beside `VIBRATION_ENABLED`, row in `components/IraPage/sections/GeneralSectionView.tsx`, read in `playChatSound`. i18n key in `en.json`. - Web: remove the `IS_WEB` return in `chatSounds.ts` and verify `expo-audio` web playback (browsers block audio until the first user gesture). - Own call sounds: synthesize ringback, connected, dropped and hang-up in `scripts/generate-chat-sounds.py` in the chime's voice; replace `assets/ringing-sound/*`, `assets/call-hang-up/*` and the native copies (`ios/Ira/`, `android/.../res/raw/`). Native rebuild. - Branded push sound: bundle the chime for iOS and Android. Android needs a new notification channel id (a channel's sound cannot change after creation) in `MessageNotificationHelper.java` and `utils/Notifications/NotificationTypes.js`. iOS needs the backend to name the sound in the push payload. Native rebuild + backend change; confirm with Rohan before shipping. ## Phase 4: prototype, then decide custom haptics In the Motion Lab (`components/MotionLab/demos/`), build the theme ripple two ways: presets on timers (today) vs a Core Haptics pattern on iOS / composition on Android. Compare on a real iPhone and a low-end Android. If custom wins, it becomes a small local module in `app/modules/` used only for the signature moments (ripple, payment confirmed, Ira's chime). ## Verification - `npx jest --findRelatedTests <changed> --ci --forceExit`, `npx tsc --noEmit`, `npm run lint` (existing: `tests/utils/VibrationUtils.test.ts`, `tests/hooks/hapticHooks.test.ts`, `tests/utils/Chat/chatSounds.test.ts`). - Haptics cannot be judged on a simulator: Saket checks each event on a real iPhone and an Android phone via a Motion Lab page that lists every event with a play button. - Web: chat sounds play after first interaction; sounds switch mutes them on all three platforms.
Response not available
Response not available
Response not available