Commit Graph
6148 Commits
Author SHA1 Message Date
Tanmay Deep Sharma 0b1ad1a854 chore(voice): trim dead exports, fix duplicate-OR bug, drop redundant comments
Branch review pass:
- useWhatsappCallSession: drop unused `error` ref and unused exports
  (hasActiveWhatsappCall, isWhatsappCallMuted), fold sendWhatsappCallBeacon
  into a private beaconTerminate helper since only sendWhatsappTerminateBeacon
  consumed it externally. Trim WHAT-comments; keep WHY-comments
  (browser-quirk explanations, race-condition notes, auth-cookie rationale).
- VoiceCall.vue bubble: fix `data || data` short-circuit that did nothing —
  upstream key transform was the same on both branches; collapse to one read.
- calls/useCallSession/actionCable/FloatingCallWidget/VoiceCallButton:
  drop comments that just describe what the next line already says.

Net diff: -63 lines across 7 files. No behavior change.
2026-05-04 15:16:04 +07:00
Tanmay Deep Sharma 6179adac22 fix(voice): keep ringing WhatsApp calls alive across page refresh
Inverted the previous decision: only the active (accepted) call gets
terminated on pagehide, since its WebRTC session dies with the page and
genuinely can't be rejoined. Ringing calls have no WebRTC state yet —
they're just a row on the backend and a state on Meta — so killing them
on refresh would needlessly hang up on the customer when the agent
intended to recover their UI, not reject the call.

After this, the flow on refresh during a ringing inbound is:
  - beforeunload prompt fires (warning the agent)
  - pagehide does nothing for ringing calls; active calls still terminate
  - new page loads, conversation messages fetch
  - seedCallsFromHydratedMessages picks up status='ringing' voice_call
    messages and populates the calls store
  - FloatingCallWidget reappears with Accept; agent clicks; acceptIncomingCall
    fetches the SDP via /whatsapp_calls/:id and the call connects
2026-05-03 16:49:00 +07:00
Tanmay Deep Sharma 3c959f0604 fix(voice): authenticate page-close terminate beacon
The previous beacon used navigator.sendBeacon, which can't set custom
request headers — and Chatwoot's API requires devise-token-auth headers
(access-token / client / uid) on every call. So the beacon hit the
terminate endpoint, got 401, and Meta kept the call alive until the
carrier-side timeout (~60s) — exactly the bug reported: refresh during
an in-progress call leaves it ringing on Meta's side.

Switch to fetch with keepalive:true so the request survives page unload
AND lets us rehydrate the auth headers from the cw_d_session_info cookie
the dashboard sets at login. credentials:'same-origin' keeps any session
cookies along for the ride. Empty body is well under the 64KB keepalive
quota so it actually flushes before the tab dies.
2026-05-03 16:11:43 +07:00
Tanmay Deep Sharma 772734840d fix(voice): terminate ringing WhatsApp calls on page close
Browser-direct WebRTC has no rejoin path, so any call that outlives the
agent's tab becomes permanently orphaned on Meta's side. The existing
pagehide beacon only covered the active (accepted) call — extend it to
every ringing inbound too, so a hard refresh during ringing releases the
call instead of leaving Meta to time it out.

- useWhatsappCallSession: factor sendWhatsappCallBeacon(callId) out of
  the active-call wrapper so any caller can post terminate for any callId
  without going through the activeCallId / intentionallyClosing guard.
- useCallSession.handlePageHide: after the active-call beacon, iterate
  the calls store for any ringing WhatsApp call and beacon /terminate
  for each. terminate is the right endpoint because the backend records
  it as 'no_answer' when the call was still ringing — accurate UX shape.
2026-05-03 16:07:55 +07:00
Tanmay Deep Sharma 77c57acffa feat(voice): move WhatsApp calling toggle to its own Calls tab
Mirror the Twilio voice configuration flow: split the WhatsApp Cloud
calling-enabled toggle out of the generic Configuration tab into a
dedicated Calls tab so the surface for telephony settings is consistent
across providers.

- Added WhatsappCallingPage.vue with the toggle, business phone display,
  and a how-it-works blurb (no extra credentials needed since the
  embedded-signup token already grants the call scopes).
- Settings.vue: register the page and surface a Calls tab on
  WhatsApp Cloud + embedded-signup inboxes.
- ConfigurationPage.vue: drop the inline calling-enabled section, the
  watcher that auto-saved on every toggle flip, and the now-orphan
  updateWhatsAppCallingEnabled method.
- en/inboxMgmt.json: TABS.CALLS label + WHATSAPP_CALLING.* strings.
2026-05-03 15:52:43 +07:00
Tanmay Deep Sharma 499ac053ee chore(voice): drop debug toasts; appease rubocop on inbox jbuilder
- Remove the [debug] useAlert breadcrumbs that surfaced the accept-call
  silent-fail (the !conversation guard). The actual fix from
  6ed9500792 stays.
- Rubocop Style/IfUnlessModifier on the WhatsApp voice_enabled jbuilder
  block — convert to modifier form.
2026-05-03 15:40:30 +07:00
Tanmay Deep Sharma 5aa1df81a8 debug(voice): surface accept-call diagnostics via toast alerts
Console logs aren't visible to the agent on staging — switch to
useAlert toasts so the debug breadcrumbs appear directly in the UI.
Will revert once root cause is identified.
2026-05-03 14:12:22 +07:00
Tanmay Deep Sharma 81464a9087 debug(voice): add temporary console logs around accept-call path
Trace every checkpoint between the green Accept button click and the
backend POST so we can see exactly where the silent failure happens
(0 /accept requests landing on backend, no console errors, no network
requests reported by the user). Logs prefixed [CW Voice] for grep.
Will revert once the root cause is identified.
2026-05-03 13:49:33 +07:00
Tanmay Deep Sharma 6ed9500792 fix(voice): accept calls without conversation hydration; warn on refresh during ringing
- FloatingCallWidget: drop !conversation early-return in handleJoinCall —
  on a fresh account or after a hard refresh the inbound call's conversation
  may not be in the Vuex store yet, which silently no-op'd Accept while
  Reject worked (Reject doesn't read the conversation).
- useCallSession: seed the calls store from already-loaded voice_call
  messages with status='ringing' on mount and whenever the conversation
  list changes. Cable events (voice_call.incoming, message.created) are
  one-shot and not replayed on reconnect, so without seeding a refresh
  during a ringing call leaves the FloatingCallWidget empty.
- useCallSession: extend the beforeunload warning to fire while a call is
  ringing too — losing a ringing inbound to refresh is the same UX hit as
  losing an active one.
2026-05-02 17:35:53 +07:00
Tanmay Deep Sharma 6835f9c85b feat(voice): WhatsApp Cloud Calling — UI 2026-05-02 16:06:48 +07:00
Tanmay Deep Sharma 442631102c fix(voice): stub Channel::Voice for legacy inbox rows after DropChannelVoice migration 2026-05-02 16:06:36 +07:00
Tanmay Deep Sharma 78c615fe42 chore(voice): align whatsapp_spec to incoming-pipeline (specs go to specs PR) 2026-05-02 14:49:21 +07:00
Tanmay Deep Sharma 907011e280 chore(voice): split spec changes out to feat/whatsapp-call-specs (sibling PR) 2026-05-02 14:48:41 +07:00
Tanmay Deep Sharma a766ebf642 chore(voice): align FE files to incoming-pipeline base (UI changes consolidated to PR #14346) 2026-05-02 14:40:06 +07:00
Tanmay Deep Sharma 0e0e0868d7 chore(voice): split FE changes out to feat/whatsapp-call-ui (PR #14346) 2026-05-02 14:31:13 +07:00
Tanmay Deep Sharma d9077c64d3 fix(voice): robust accept-with-fetch + remote-end recording race + single-duration bubble + audio playback signals 2026-05-01 18:29:54 +07:00
Tanmay Deep Sharma 1abb51992b fix(voice): fall back to content_attributes.data for duration when call payload lacks it 2026-05-01 17:50:02 +07:00
Tanmay Deep Sharma 0282156821 fix(audio): allow audio attachments to serve inline so <audio> can play call recordings 2026-05-01 17:47:04 +07:00
Tanmay Deep Sharma 257a9129d7 fix(voice): create missed-call record when terminate webhook arrives before connect 2026-05-01 17:33:05 +07:00
Tanmay Deep Sharma 1ce316efc7 ui(voice): use ButtonV4 for header WhatsApp call button to match action toolbar styling 2026-05-01 17:27:57 +07:00
Tanmay Deep Sharma ba94d8c2b0 fix(transcription): drop Whisper temperature to 0 to suppress hallucinations 2026-05-01 16:31:48 +07:00
Tanmay Deep Sharma 6e11bebeae fix(audio): resolve real duration for MediaRecorder WebM blobs via end-seek trick 2026-05-01 11:16:55 +07:00
Tanmay Deep Sharma 069f25f88a fix(voice): await remote-end upload before store removeCall to prevent sync cleanup race 2026-05-01 11:15:09 +07:00
Tanmay Deep Sharma 63c193358c fix(voice): render audio + transcript + duration on call bubble; upload recording on remote-end too 2026-05-01 11:06:53 +07:00
Tanmay Deep Sharma 7663e2ac30 fix(voice): merge call store entries so cable + message.created races don't drop provider/sdp/caller 2026-05-01 10:54:47 +07:00
Tanmay Deep Sharma 0567655840 fix(voice): outbound STUN, mute toggle, skip voice_call bubble in send pipeline 2026-04-30 20:43:35 +07:00
Tanmay Deep Sharma c25ed094fa fix(voice): pipe Meta's remote audio to a hidden audio element so the agent hears the contact 2026-04-30 20:17:19 +07:00
Tanmay Deep Sharma 112a8f9e9e feat(voice): branch contact-panel call button on inbox provider for WhatsApp 2026-04-30 20:14:21 +07:00
Tanmay Deep Sharma 723d416ce3 fix(voice): bump WhatsApp Cloud Calling endpoints to v22 (OSS phone_id_path is locked at v13) 2026-04-30 19:54:31 +07:00
Tanmay Deep Sharma f3df486ce6 feat(voice): add WhatsApp calling enable toggle to inbox configuration 2026-04-30 19:39:19 +07:00
Tanmay Deep Sharma c7b6a87e77 feat(voice): wire WhatsApp call UI on top of existing Twilio session flow 2026-04-30 19:30:43 +07:00
Tanmay Deep Sharma 19d91e47f1 fix(voice): rescue transport errors during permission_request send 2026-04-30 19:11:14 +07:00
Tanmay Deep Sharma 1c945899c7 fix(whatsapp): restrict voice_enabled? to embedded-signup whatsapp_cloud channels 2026-04-30 19:10:06 +07:00
Tanmay Deep Sharma 2c025755e2 fix(voice): wrap Meta transport errors during accept as CallFailed 2026-04-30 18:52:51 +07:00
Tanmay Deep Sharma 0618e6e229 fix(voice): close lock-window gaps in accept, throttle, and call_attributes_changed? 2026-04-30 18:46:09 +07:00
Tanmay Deep Sharma cbef2d4e66 refactor(voice): memoize provider_service accessor in whatsapp calls controller 2026-04-30 18:26:43 +07:00
Tanmay Deep Sharma cb93aba55c refactor(voice): split call validations into single-purpose before_actions 2026-04-30 18:14:29 +07:00
Tanmay Deep Sharma beef43f2bf refactor(voice): hoist validations to before_action and centralize call-error rendering 2026-04-30 18:04:25 +07:00
Tanmay Deep Sharma cbd3f759b5 fix(voice): order accept guards correctly and validate contact phone upstream 2026-04-30 17:46:24 +07:00
Tanmay Deep Sharma a7fdc45977 fix(voice): bail on Meta failures and restrict initiate to WhatsApp inboxes 2026-04-30 17:26:01 +07:00
Tanmay Deep Sharma 41e3ea0d87 fix(voice): record permission_request wamid for context-based reply matching 2026-04-30 17:11:55 +07:00
Tanmay Deep Sharma fd22017292 Merge feat/whatsapp-call-incoming-pipeline into feature/pla-150 2026-04-30 17:10:45 +07:00
Tanmay Deep Sharma 555894793e fix(voice): match permission reply to originating conversation via context.id 2026-04-30 17:10:34 +07:00
Tanmay Deep Sharma 7c87d3d150 fix(voice): lock call state transitions, branch terminate on connection state, atomic recording upload, rescue CallFailed in initiate 2026-04-30 16:54:47 +07:00
Tanmay Deep Sharma c37d1b1d6b Merge feat/whatsapp-call-incoming-pipeline into feature/pla-150 2026-04-30 16:51:42 +07:00
Tanmay Deep Sharma e5a03d08ea fix(voice): resolve contact via source_id and i18n call message bubble 2026-04-30 16:51:29 +07:00
Tanmay Deep Sharma bc80985f55 refactor(voice): align WhatsappCallsController with Chatwoot jbuilder + concern conventions 2026-04-30 16:38:08 +07:00
Tanmay Deep Sharma 83cd568ca2 feat(voice): add WhatsApp Cloud Calling agent service, controller and routes 2026-04-30 16:24:39 +07:00
Tanmay Deep Sharma f32a7b3f77 refactor(voice): drop redundant CallMessageBuilder.update_status! class delegate 2026-04-30 16:13:53 +07:00
Tanmay Deep Sharma ba0fcbfc7f refactor(voice): tighten WhatsApp call pipeline and broadcast hyphenated terminate status 2026-04-30 16:04:39 +07:00