refactor: centralise outbound dial into useSip().dialOutbound()

- Single dialOutbound() in sip-provider handles all outbound state:
  callState, callerNumber, outboundPending, API call, error recovery
- ClickToCallButton, PhoneActionCell, Dialler all use dialOutbound()
- Removed direct Jotai atom manipulation from calling components
- Removed setOutboundPending imports from components
- SIP disconnects on provider unmount + auth logout
- Dialler input is now editable (type or numpad)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 18:49:10 +05:30
parent 13e81ba9fb
commit 710609dfee
5 changed files with 48 additions and 55 deletions

View File

@@ -2,14 +2,10 @@ import type { FC } from 'react';
import { useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPhone } from '@fortawesome/pro-duotone-svg-icons';
import { useSetAtom } from 'jotai';
const Phone01: FC<{ className?: string } & Record<string, any>> = ({ className, ...rest }) => <FontAwesomeIcon icon={faPhone} className={className} {...rest} />;
import { Button } from '@/components/base/buttons/button';
import { useSip } from '@/providers/sip-provider';
import { sipCallStateAtom, sipCallerNumberAtom, sipCallUcidAtom } from '@/state/sip-state';
import { setOutboundPending } from '@/state/sip-manager';
import { apiClient } from '@/lib/api-client';
import { notify } from '@/lib/toast';
interface ClickToCallButtonProps {
@@ -20,33 +16,14 @@ interface ClickToCallButtonProps {
}
export const ClickToCallButton = ({ phoneNumber, label, size = 'sm' }: ClickToCallButtonProps) => {
const { isRegistered, isInCall } = useSip();
const { isRegistered, isInCall, dialOutbound } = useSip();
const [dialing, setDialing] = useState(false);
const setCallState = useSetAtom(sipCallStateAtom);
const setCallerNumber = useSetAtom(sipCallerNumberAtom);
const setCallUcid = useSetAtom(sipCallUcidAtom);
const handleDial = async () => {
setDialing(true);
// Show call UI immediately
setCallState('ringing-out');
setCallerNumber(phoneNumber);
setOutboundPending(true);
// Safety: reset flag if SIP INVITE doesn't arrive within 30s
const safetyTimer = setTimeout(() => setOutboundPending(false), 30000);
try {
const result = await apiClient.post<{ ucid?: string; status?: string }>('/api/ozonetel/dial', { phoneNumber });
if (result?.ucid) {
setCallUcid(result.ucid);
}
await dialOutbound(phoneNumber);
} catch {
clearTimeout(safetyTimer);
setCallState('idle');
setCallerNumber(null);
setOutboundPending(false);
setCallUcid(null);
notify.error('Dial Failed', 'Could not place the call');
} finally {
setDialing(false);