---
title: "ਹੈੱਡਲੈੱਸ ਹੁੱਕ"
description: "useThunderPhone React ਹੁੱਕ ਨਾਲ ਪੂਰੀ ਤਰ੍ਹਾਂ ਕਸਟਮ ਵੌਇਸ UI ਬਣਾਓ"
---

`useThunderPhone` ਹੁੱਕ ਤੁਹਾਨੂੰ ਯੂਜ਼ਰ ਇੰਟਰਫੇਸ ਉੱਤੇ ਪੂਰਾ ਕੰਟਰੋਲ ਦਿੰਦਾ ਹੈ, ਜਦੋਂ ਕਿ ThunderPhone ਵੌਇਸ ਸੈਸ਼ਨ, ਆਡੀਓ ਰੂਟਿੰਗ ਅਤੇ ਕਨੈਕਸ਼ਨ ਸਟੇਟ ਨੂੰ ਮੈਨੇਜ ਕਰਦਾ ਹੈ। ਇਸਨੂੰ ਤਦ ਵਰਤੋ ਜਦੋਂ ਤੁਸੀਂ ਪੂਰੀ ਤਰ੍ਹਾਂ ਕਸਟਮ UI ਚਾਹੁੰਦੇ ਹੋ -- ਆਪਣੇ ਬਟਨ, ਲੇਆਉਟ, ਐਨੀਮੇਸ਼ਨ ਅਤੇ ਬ੍ਰਾਂਡਿੰਗ -- ਜਦੋਂ ਕਿ ThunderPhone ਅੰਦਰੂਨੀ ਤੌਰ 'ਤੇ ਸਭ ਕੁਝ ਸੰਭਾਲਦਾ ਹੈ।

## ਹੈਡਲੈੱਸ ਹੁੱਕ ਕਦੋਂ ਵਰਤਣਾ ਹੈ

ਪਹਿਲਾਂ ਤੋਂ ਬਣਿਆ `ThunderPhoneWidget` ਕੰਪੋਨੈਂਟ ਜ਼ਿਆਦਾਤਰ ਵਰਤੋਂ ਮਾਮਲਿਆਂ ਨੂੰ ਕਵਰ ਕਰਦਾ ਹੈ, ਪਰ ਜਦੋਂ ਤੁਹਾਨੂੰ ਇਹ ਚਾਹੀਦਾ ਹੋਵੇ ਤਾਂ ਹੈਡਲੈੱਸ ਹੁੱਕ ਵਰਤੋ:

- ਪੂਰੀ ਤਰ੍ਹਾਂ ਕਸਟਮ ਕਾਲ UI ਜੋ ਤੁਹਾਡੀ ਐਪ ਦੇ ਡਿਜ਼ਾਈਨ ਸਿਸਟਮ ਨਾਲ ਮੇਲ ਖਾਂਦਾ ਹੋਵੇ
- ਰੀਅਲ-ਟਾਈਮ ਆਡੀਓ ਲੈਵਲਾਂ ਦੁਆਰਾ ਚਲਾਏ ਜਾਣ ਵਾਲੇ ਆਡੀਓ-ਰੀਐਕਟਿਵ ਵਿਜ਼ੁਅਲਾਈਜ਼ੇਸ਼ਨ (ਵੇਵਫਾਰਮ, ਔਰਬ, ਪਲਸਿੰਗ ਇੰਡੀਕੇਟਰ)
- ਕਸਟਮ ਕਾਲ ਫਲੋ, ਜਿਵੇਂ ਕਾਲ ਤੋਂ ਪਹਿਲਾਂ ਦੇ ਫਾਰਮ, ਕਾਲ ਤੋਂ ਬਾਅਦ ਦੇ ਸਰਵੇਖਣ ਜਾਂ ਵੌਇਸ ਦੇ ਨਾਲ ਇਨਲਾਈਨ ਚੈਟ
- ਮੌਜੂਦਾ ਕੰਪੋਨੈਂਟ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਇੰਟੀਗ੍ਰੇਸ਼ਨ (Material UI, Chakra, Radix, ਆਦਿ)

---

## ਇੰਸਟਾਲੇਸ਼ਨ

```bash
npm install @thunderphone/widget
```

<Note>
  ਹੈਡਲੈੱਸ ਹੁੱਕ ਲਈ `@thunderphone/widget/style.css` ਇੰਪੋਰਟ ਕਰਨ ਦੀ **ਲੋੜ ਨਹੀਂ** ਹੈ ਕਿਉਂਕਿ ਤੁਸੀਂ ਆਪਣਾ UI ਪ੍ਰਦਾਨ ਕਰ ਰਹੇ ਹੋ। ਹਾਲਾਂਕਿ, ਤੁਹਾਨੂੰ ਫਿਰ ਵੀ ਉਹੀ `@thunderphone/widget` ਪੈਕੇਜ ਇੰਸਟਾਲ ਕਰਨਾ ਲਾਜ਼ਮੀ ਹੈ।
</Note>

---

## ਮੁੱਢਲੀ ਵਰਤੋਂ

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function CustomCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  const handleClick = () => {
    if (phone.state === 'connected') {
      phone.disconnect()
    } else {
      phone.connect()
    }
  }

  return (
    <>
      <button onClick={handleClick} disabled={phone.state === 'connecting'}>
        {phone.state === 'connecting'
          ? 'Connecting...'
          : phone.state === 'connected'
            ? 'End call'
            : 'Start call'}
      </button>
      {phone.audio}
    </>
  )
}
```

<Warning>
  **ਤੁਹਾਨੂੰ ਆਪਣੇ ਕੰਪੋਨੈਂਟ ਟ੍ਰੀ ਵਿੱਚ ਕਿਤੇ ਨਾ ਕਿਤੇ `phone.audio` ਰੈਂਡਰ ਕਰਨਾ ਲਾਜ਼ਮੀ ਹੈ।** ਇਹ ਇੱਕ ਅਦਿੱਖ React ਐਲੀਮੈਂਟ ਹੈ ਜੋ ਅੰਦਰੂਨੀ ਆਡੀਓ ਕਨੈਕਸ਼ਨ ਨੂੰ ਮੈਨੇਜ ਕਰਦਾ ਹੈ। ਜੇ ਤੁਸੀਂ ਇਸਨੂੰ ਛੱਡ ਦਿੰਦੇ ਹੋ, ਤਾਂ ਕੋਈ ਆਡੀਓ ਨਹੀਂ ਚੱਲੇਗਾ ਅਤੇ ਸੈਸ਼ਨ ਕੰਮ ਨਹੀਂ ਕਰੇਗਾ।
</Warning>

---

## ਵਿਕਲਪ

`UseThunderPhoneOptions` ਰਾਹੀਂ ਇਹ ਵਿਕਲਪ `useThunderPhone` ਨੂੰ ਪਾਸ ਕਰੋ:

| ਵਿਕਲਪ | ਕਿਸਮ | ਲਾਜ਼ਮੀ | ਡਿਫੌਲਟ | ਵੇਰਵਾ |
|--------|------|----------|---------|-------------|
| `publishableKey` | `string` | ਹਾਂ | -- | ਪਬਲਿਸ਼ੇਬਲ API ਕੀ (`pk_live_...`)। ਏਜੰਟ ਨੂੰ ਕੀ ਦੀ ਵਿਜਿਟ ਕਨਫਿਗਰੇਸ਼ਨ ਤੋਂ ਆਪਣੇ ਆਪ ਨਿਰਧਾਰਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। |
| `apiBase` | `string` | ਨਹੀਂ | `'https://api.thunderphone.com/v1'` | API ਬੇਸ URL ਓਵਰਰਾਈਡ। |
| `language` | `string` | ਨਹੀਂ | -- | ਪ੍ਰਤੀ-ਸੈਸ਼ਨ ਭਾਸ਼ਾ ਓਵਰਰਾਈਡ -- ਇੱਕ ਭਾਸ਼ਾ ਕੋਡ ਜਾਂ ਲੋਕੇਲ ਜਿਵੇਂ `en`, `es`, ਜਾਂ `fr-FR`। ਸੈੱਟ ਨਾ ਹੋਣ 'ਤੇ, ਏਜੰਟ ਦੀ ਕਨਫਿਗਰ ਕੀਤੀ ਭਾਸ਼ਾ ਲਾਗੂ ਹੁੰਦੀ ਹੈ। |
| `voice` | `string` | ਨਹੀਂ | -- | ਪ੍ਰਤੀ-ਸੈਸ਼ਨ ਵੌਇਸ ਓਵਰਰਾਈਡ -- ਇੱਕ ਵੌਇਸ ਨਾਮ ਜਿਵੇਂ `maria`। ਸੈੱਟ ਨਾ ਹੋਣ 'ਤੇ, ਏਜੰਟ ਦੀ ਕਨਫਿਗਰ ਕੀਤੀ ਵੌਇਸ ਲਾਗੂ ਹੁੰਦੀ ਹੈ। |
| `context` | `string` | ਨਹੀਂ | -- | ਏਜੰਟ ਨੂੰ ਪਾਸ ਕੀਤਾ ਜਾਣ ਵਾਲਾ ਪ੍ਰਤੀ-ਸੈਸ਼ਨ ਤੱਥਾਤਮਕ ਪੇਜ ਜਾਂ ਸਾਈਟ ਸੰਦਰਭ। ਸਰਵਰ-ਸਾਈਡ 'ਤੇ 12,000 ਅੱਖਰਾਂ ਤੱਕ ਸੰਖੇਪ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। |
| `onConnect` | `() => void` | ਨਹੀਂ | -- | ਵੌਇਸ ਸੈਸ਼ਨ ਕਨੈਕਟ ਹੋਣ 'ਤੇ ਕਾਲ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। |
| `onDisconnect` | `() => void` | ਨਹੀਂ | -- | ਸੈਸ਼ਨ ਖਤਮ ਹੋਣ 'ਤੇ ਕਾਲ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। |
| `onError` | `(error) => void` | ਨਹੀਂ | -- | ਗਲਤੀਆਂ ਹੋਣ 'ਤੇ ਕਾਲ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। Error ਵਿੱਚ `error` (ਕੋਡ) ਅਤੇ `message` ਫੀਲਡ ਹੁੰਦੇ ਹਨ। |
| `ringtone` | `boolean \| string` | ਨਹੀਂ | `false` | ਕਨੈਕਟ ਕਰਦੇ ਸਮੇਂ ਰਿੰਗਟੋਨ ਚਲਾਓ। ਡਿਫੌਲਟ ਰਿੰਗਟੋਨ ਲਈ `true`, ਜਾਂ ਕਸਟਮ ਆਡੀਓ ਲਈ URL ਸਟ੍ਰਿੰਗ। |

<Note>
  ਹੁੱਕ ਹੈਡਲੈੱਸ ਹੈ: ਇਹ `ThunderPhoneWidget` ਦੇ ਦਿੱਖ ਪ੍ਰੌਪਸ (`theme`, `primaryColor`, `title`, `position`, `className`) ਨੂੰ **ਸਵੀਕਾਰ ਨਹੀਂ** ਕਰਦਾ। ਇਹਨਾਂ ਨੂੰ ਪਾਸ ਕਰਨਾ TypeScript ਗਲਤੀ ਹੈ -- ਪ੍ਰਜ਼ੈਂਟੇਸ਼ਨ ਪੂਰੀ ਤਰ੍ਹਾਂ ਤੁਹਾਡੇ ਬਣਾਉਣ ਲਈ ਹੈ।
</Note>

---

## ਵਾਪਸੀ ਮੁੱਲ

ਹੁੱਕ ਇੱਕ `UseThunderPhoneReturn` ਆਬਜੈਕਟ ਵਾਪਸ ਕਰਦਾ ਹੈ:

| ਪ੍ਰਾਪਰਟੀ | ਕਿਸਮ | ਵੇਰਵਾ |
|----------|------|-------------|
| `state` | `'idle' \| 'connecting' \| 'connected' \| 'disconnected' \| 'error'` | ਮੌਜੂਦਾ ਕਨੈਕਸ਼ਨ ਸਥਿਤੀ। |
| `connect` | `() => void` | ਵੌਇਸ ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਕਰੋ। |
| `disconnect` | `() => void` | ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਖਤਮ ਕਰੋ। |
| `toggleMute` | `() => void` | ਮਾਈਕ੍ਰੋਫੋਨ ਮਿਊਟ ਨੂੰ ਚਾਲੂ/ਬੰਦ ਕਰੋ। |
| `isMuted` | `boolean` | ਕੀ ਮਾਈਕ੍ਰੋਫੋਨ ਇਸ ਵੇਲੇ ਮਿਊਟ ਹੈ। |
| `error` | `string \| undefined` | ਜਦੋਂ ਸਥਿਤੀ `'error'` ਹੋਵੇ ਤਾਂ ਗਲਤੀ ਸੁਨੇਹਾ। |
| `agentName` | `string \| undefined` | ਕਨੈਕਟ ਕੀਤੇ ਏਜੰਟ ਦਾ ਡਿਸਪਲੇ ਨਾਮ। |
| `audioLevel` | `number` | **ਬੰਦ ਕੀਤਾ ਗਿਆ -- ਹਮੇਸ਼ਾ `0`।** ਪਿੱਛਲੀ ਅਨੁਕੂਲਤਾ ਲਈ ਰੱਖਿਆ ਗਿਆ ਇੱਕ ਸਥਿਰ ਪਲੇਸਹੋਲਡਰ; ਇਹ ਕਦੇ ਅੱਪਡੇਟ ਨਹੀਂ ਹੁੰਦਾ। ਇਸ ਦੀ ਬਜਾਏ `audioLevelRef.current` ਪੜ੍ਹੋ। |
| `audioLevelRef` | `React.RefObject<number>` | ਇੱਕ ਬਦਲਣਯੋਗ ਰੈਫ ਜਿਸ ਵਿੱਚ ਰੀਅਲ-ਟਾਈਮ ਆਡੀਓ ਪੱਧਰ (0--1) ਹੁੰਦਾ ਹੈ -- ਏਜੰਟ ਦੀ ਆਵਾਜ਼ ਅਤੇ ਵਿਜ਼ਿਟਰ ਦੇ ਮਾਈਕ੍ਰੋਫੋਨ ਵਿੱਚੋਂ ਵੱਧ ਉੱਚਾ -- ਜੋ ਹਰ ਐਨੀਮੇਸ਼ਨ ਫਰੇਮ 'ਤੇ, React ਦੇ ਰੈਂਡਰ ਚੱਕਰ ਤੋਂ ਬਾਹਰ, ਅੱਪਡੇਟ ਹੁੰਦਾ ਹੈ। ਨਿਰਵਿਘਨ, ਬਿਨਾਂ ਅਟਕਣ ਵਾਲੀਆਂ ਐਨੀਮੇਸ਼ਨਾਂ ਲਈ `requestAnimationFrame` ਲੂਪਾਂ ਦੇ ਅੰਦਰ `audioLevelRef.current` ਪੜ੍ਹੋ, ਜਾਂ ਜਦੋਂ ਤੁਹਾਨੂੰ React ਸਥਿਤੀ ਵਿੱਚ ਮੁੱਲ ਦੀ ਲੋੜ ਹੋਵੇ ਤਾਂ ਇਸਨੂੰ ਕਿਸੇ ਅੰਤਰਾਲ 'ਤੇ ਸੈਂਪਲ ਕਰੋ। |
| `audio` | `ReactNode` | ਅਦਿੱਖ ਐਲੀਮੈਂਟ ਜੋ ਆਡੀਓ ਕਨੈਕਸ਼ਨ ਸੰਭਾਲਦਾ ਹੈ -- **ਰੈਂਡਰ ਕਰਨਾ ਲਾਜ਼ਮੀ ਹੈ**। |

---

## ਆਡੀਓ-ਪ੍ਰਤੀਕਿਰਿਆਸ਼ੀਲ UI

`audioLevelRef` ref ਤੁਹਾਨੂੰ React re-render ਟ੍ਰਿਗਰ ਕੀਤੇ ਬਿਨਾਂ ਫ੍ਰੇਮ-ਰੇਟ ਆਡੀਓ ਪੱਧਰ ਦਿੰਦਾ ਹੈ, ਜਿਸ ਨਾਲ ਇਹ ਸੁਚਾਰੂ ਵੇਵਫਾਰਮ ਵਿਜ਼ੁਅਲਾਈਜ਼ੇਸ਼ਨਾਂ, ਧੜਕਦੇ ਔਰਬਾਂ ਜਾਂ ਗੱਲਬਾਤ ਨਾਲ ਜੁੜੀ ਕਿਸੇ ਵੀ ਐਨੀਮੇਸ਼ਨ ਨੂੰ ਚਲਾਉਣ ਲਈ ਆਦਰਸ਼ ਬਣਦਾ ਹੈ। ਪੱਧਰ ਉਸ ਸਰੋਤ ਨੂੰ ਦਰਸਾਉਂਦਾ ਹੈ ਜੋ ਵੱਧ ਉੱਚਾ ਹੋਵੇ: ਏਜੰਟ ਦੀ ਆਵਾਜ਼ ਜਾਂ ਵਿਜ਼ਟਰ ਦਾ ਮਾਈਕ੍ਰੋਫ਼ੋਨ।

### ਵੇਵਫਾਰਮ ਉਦਾਹਰਨ

```tsx
import { useRef, useEffect } from 'react'
import { useThunderPhone } from '@thunderphone/widget'

function WaveformCall() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })
  const canvasRef = useRef<HTMLCanvasElement>(null)

  useEffect(() => {
    if (phone.state !== 'connected') return
    const canvas = canvasRef.current
    if (!canvas) return
    const ctx = canvas.getContext('2d')!

    let animId: number
    const draw = () => {
      const level = phone.audioLevelRef.current ?? 0
      ctx.clearRect(0, 0, canvas.width, canvas.height)

      // Draw bars that react to audio level
      const barCount = 24
      const barWidth = canvas.width / barCount
      for (let i = 0; i < barCount; i++) {
        const distance = Math.abs(i - barCount / 2) / (barCount / 2)
        const height = level * canvas.height * (1 - distance * 0.6)
        const y = (canvas.height - height) / 2
        ctx.fillStyle = '#0ea5e9'
        ctx.fillRect(i * barWidth + 1, y, barWidth - 2, height)
      }

      animId = requestAnimationFrame(draw)
    }
    animId = requestAnimationFrame(draw)
    return () => cancelAnimationFrame(animId)
  }, [phone.state, phone.audioLevelRef])

  return (
    <div>
      {phone.state === 'connected' && (
        <canvas ref={canvasRef} width={240} height={80} />
      )}
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connected' ? 'End call' : 'Start call'}
      </button>
      {phone.audio}
    </div>
  )
}
```

### ਧੜਕਦੇ ਔਰਬ ਦੀ ਉਦਾਹਰਨ

```tsx
import { useRef, useEffect } from 'react'
import { useThunderPhone } from '@thunderphone/widget'

function PulsingOrb() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })
  const orbRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    if (phone.state !== 'connected') return
    let animId: number
    const animate = () => {
      const level = phone.audioLevelRef.current ?? 0
      if (orbRef.current) {
        const scale = 1 + level * 0.5
        orbRef.current.style.transform = `scale(${scale})`
        orbRef.current.style.opacity = `${0.6 + level * 0.4}`
      }
      animId = requestAnimationFrame(animate)
    }
    animId = requestAnimationFrame(animate)
    return () => cancelAnimationFrame(animId)
  }, [phone.state, phone.audioLevelRef])

  return (
    <div style={{ textAlign: 'center' }}>
      <div
        ref={orbRef}
        style={{
          width: 80,
          height: 80,
          borderRadius: '50%',
          background: '#0ea5e9',
          margin: '20px auto',
          transition: 'transform 0.05s ease-out',
        }}
      />
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connected' ? 'End call' : 'Call'}
      </button>
      {phone.audio}
    </div>
  )
}
```

### ਬੋਲਣ ਦੇ ਸੂਚਕ ਦੀ ਉਦਾਹਰਨ

ਵਾਲਿਊਮ ਦੇ ਨਾਲ ਬਦਲਣ ਵਾਲੇ React-rendered UI ਲਈ -- ਜਿਵੇਂ ਥ੍ਰੈਸ਼ਹੋਲਡ-ਅਧਾਰਿਤ "ਬੋਲ ਰਿਹਾ ਹੈ" ਬੈਜ -- `audioLevelRef.current` ਨੂੰ ਕਿਸੇ ਅੰਤਰਾਲ 'ਤੇ ਸੈਂਪਲ ਕਰੋ ਅਤੇ ਨਤੀਜਾ state ਵਿੱਚ ਸਟੋਰ ਕਰੋ:

```tsx
import { useEffect, useState } from 'react'
import { useThunderPhone } from '@thunderphone/widget'

function SpeakingBadge() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })
  const [speaking, setSpeaking] = useState(false)

  useEffect(() => {
    if (phone.state !== 'connected') {
      setSpeaking(false)
      return
    }
    const interval = setInterval(() => {
      setSpeaking((phone.audioLevelRef.current ?? 0) > 0.1)
    }, 100)
    return () => clearInterval(interval)
  }, [phone.state, phone.audioLevelRef])

  return (
    <div>
      {phone.state === 'connected' && (
        <span>{speaking ? 'Speaking' : 'Listening'}</span>
      )}
      <button onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}>
        {phone.state === 'connected' ? 'End call' : 'Start call'}
      </button>
      {phone.audio}
    </div>
  )
}
```

<Warning>
  ਪੱਧਰ ਹਮੇਸ਼ਾ `audioLevelRef.current` ਤੋਂ ਪੜ੍ਹੋ। return object ਵਿੱਚਲਾ `audioLevel` ਨੰਬਰ **deprecated ਹੈ ਅਤੇ ਹਮੇਸ਼ਾ `0` ਹੁੰਦਾ ਹੈ** -- ਇਸ 'ਤੇ ਬਣਿਆ ਕੋਈ ਵੀ ਲੌਜਿਕ ਬਿਨਾਂ ਕਿਸੇ ਸੰਕੇਤ ਦੇ ਜ਼ੀਰੋ ਪੜ੍ਹੇਗਾ।
</Warning>

---

## ਸਟੇਟ ਮਸ਼ੀਨ

`state` ਪ੍ਰਾਪਰਟੀ ਇਸ ਲਾਈਫਸਾਈਕਲ ਦੀ ਪਾਲਣਾ ਕਰਦੀ ਹੈ:

```
idle --> connecting --> connected --> disconnected --> idle (after 1.5s)
                  \
                   --> error (stays until connect() is called again)
```

| ਸਟੇਟ | ਵੇਰਵਾ |
|-------|-------------|
| `idle` | ਕੋਈ ਸਰਗਰਮ ਸੈਸ਼ਨ ਨਹੀਂ। `connect()` ਨੂੰ ਕਾਲ ਕਰਨ ਲਈ ਤਿਆਰ। |
| `connecting` | ਸੈਸ਼ਨ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ। ਇਸ ਸਟੇਟ ਦੌਰਾਨ ਕਾਲ ਬਟਨ ਨੂੰ ਅਯੋਗ ਕਰੋ। |
| `connected` | ਵੌਇਸ ਸੈਸ਼ਨ ਸਰਗਰਮ ਹੈ। ਯੂਜ਼ਰ ਏਜੰਟ ਨਾਲ ਗੱਲ ਕਰ ਰਿਹਾ ਹੈ। |
| `disconnected` | ਸੈਸ਼ਨ ਸਹੀ ਢੰਗ ਨਾਲ ਸਮਾਪਤ ਹੋ ਗਿਆ ਹੈ। 1.5 ਸਕਿੰਟਾਂ ਬਾਅਦ ਆਪਣੇ ਆਪ ਵਾਪਸ `idle` ਵਿੱਚ ਤਬਦੀਲ ਹੋ ਜਾਂਦਾ ਹੈ। |
| `error` | ਕੁਝ ਗਲਤ ਹੋਇਆ। ਸੁਨੇਹੇ ਲਈ `phone.error` ਦੀ ਜਾਂਚ ਕਰੋ। ਸਟੇਟ ਆਪਣੇ ਆਪ ਸਾਫ਼ **ਨਹੀਂ** ਹੁੰਦੀ -- `connect()` ਨੂੰ ਦੁਬਾਰਾ ਕਾਲ ਕਰਨ ਨਾਲ ਇੱਕ ਨਵੀਂ ਕੋਸ਼ਿਸ਼ ਸ਼ੁਰੂ ਹੁੰਦੀ ਹੈ ਅਤੇ ਗਲਤੀ ਰੀਸੈਟ ਹੋ ਜਾਂਦੀ ਹੈ। |

---

## ਉਦਾਹਰਨਾਂ

### ਮਿਊਟ ਕੰਟਰੋਲ ਨਾਲ

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function CallWithMute() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  return (
    <div>
      {phone.state === 'connected' && (
        <div>
          <p>Talking to {phone.agentName ?? 'Agent'}</p>
          <button onClick={phone.toggleMute}>
            {phone.isMuted ? 'Unmute' : 'Mute'}
          </button>
          <button onClick={phone.disconnect}>End call</button>
        </div>
      )}

      {phone.state !== 'connected' && (
        <button
          onClick={phone.connect}
          disabled={phone.state === 'connecting'}
        >
          {phone.state === 'connecting' ? 'Connecting...' : 'Call support'}
        </button>
      )}

      {phone.state === 'error' && (
        <p style={{ color: 'red' }}>{phone.error}</p>
      )}

      {phone.audio}
    </div>
  )
}
```

### ਰਿੰਗਟੋਨ ਨਾਲ

ਫ਼ੋਨ ਕਾਲ ਦਾ ਅਨੁਕਰਣ ਕਰਨ ਲਈ ਕਨੈਕਟ ਹੋਣ ਦੌਰਾਨ ਰਿੰਗਿੰਗ ਆਵਾਜ਼ ਚਲਾਓ:

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function PhoneCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
    ringtone: true, // or a custom URL: 'https://example.com/ringtone.mp3'
  })

  return (
    <>
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connecting'
          ? 'Ringing...'
          : phone.state === 'connected'
            ? 'Hang up'
            : 'Call'}
      </button>
      {phone.audio}
    </>
  )
}
```

ਰਿੰਗਟੋਨ `connecting` ਸਥਿਤੀ ਦੌਰਾਨ ਲੂਪ ਹੁੰਦੀ ਹੈ ਅਤੇ ਏਜੰਟ ਦੇ ਕਨੈਕਟ ਹੋਣ 'ਤੇ ਹੌਲੀ-ਹੌਲੀ ਬੰਦ ਹੋ ਜਾਂਦੀ ਹੈ। ਬਿਲਟ-ਇਨ ਡਿਫਾਲਟ ਰਿੰਗਟੋਨ ਲਈ `true` ਦਿਓ ਜਾਂ ਆਪਣੀ ਆਡੀਓ ਫ਼ਾਈਲ ਵਰਤਣ ਲਈ URL ਸਟਰਿੰਗ ਦਿਓ।

### ਇਵੈਂਟ ਕਾਲਬੈਕਸ ਨਾਲ

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function TrackedCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
    onConnect: () => {
      analytics.track('call_started')
    },
    onDisconnect: () => {
      analytics.track('call_ended')
    },
    onError: (error) => {
      analytics.track('call_error', { code: error.error, message: error.message })
    },
  })

  return (
    <>
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connected' ? 'Hang up' : 'Talk to AI'}
      </button>
      {phone.audio}
    </>
  )
}
```

### ਪੂਰਾ ਕਸਟਮ UI

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function FullCustomUI() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  return (
    <div className="call-panel">
      <div className="call-status">
        {phone.state === 'idle' && <span>Ready</span>}
        {phone.state === 'connecting' && <span className="pulse">Connecting...</span>}
        {phone.state === 'connected' && (
          <span>On call with {phone.agentName}</span>
        )}
        {phone.state === 'error' && <span className="error">{phone.error}</span>}
      </div>

      <div className="call-controls">
        {phone.state === 'connected' ? (
          <>
            <button className="mute-btn" onClick={phone.toggleMute}>
              {phone.isMuted ? 'Unmute' : 'Mute'}
            </button>
            <button className="end-btn" onClick={phone.disconnect}>
              End
            </button>
          </>
        ) : (
          <button
            className="start-btn"
            onClick={phone.connect}
            disabled={phone.state === 'connecting'}
          >
            Start call
          </button>
        )}
      </div>

      {/* Required -- handles audio under the hood */}
      {phone.audio}
    </div>
  )
}
```

---

## ਸੁਝਾਅ

<AccordionGroup>
  <Accordion title="phone.audio ਨੂੰ ਹਮੇਸ਼ਾ ਰੈਂਡਰ ਕਰੋ">
    `phone.audio` ਐਲੀਮੈਂਟ ਦਿਖਾਈ ਨਹੀਂ ਦਿੰਦਾ, ਪਰ ਲਾਜ਼ਮੀ ਹੈ। ਇਸਨੂੰ ਆਪਣੇ JSX ਵਿੱਚ ਕਿਤੇ ਵੀ ਰੱਖੋ -- ਇਹ ਕੋਈ ਦਿੱਖਣਯੋਗ DOM ਰੈਂਡਰ ਨਹੀਂ ਕਰਦਾ, ਪਰ ਅੰਦਰੂਨੀ ਤੌਰ 'ਤੇ WebRTC ਆਡੀਓ ਕਨੈਕਸ਼ਨ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ।
  </Accordion>

  <Accordion title="ਕਨੈਕਟ ਹੋਣ ਦੌਰਾਨ ਬਟਨ ਅਯੋਗ ਕਰੋ">
    `connecting` ਸਥਿਤੀ 1-3 ਸਕਿੰਟ ਤੱਕ ਰਹਿ ਸਕਦੀ ਹੈ। ਡੁਪਲੀਕੇਟ ਕਨੈਕਸ਼ਨ ਕੋਸ਼ਿਸ਼ਾਂ ਨੂੰ ਰੋਕਣ ਲਈ ਇਸ ਸਥਿਤੀ ਦੌਰਾਨ ਕਾਲ ਬਟਨ ਅਯੋਗ ਕਰੋ।
  </Accordion>

  <Accordion title="ਗਲਤੀ ਸਥਿਤੀ ਨੂੰ ਸੁਚੱਜੇ ਢੰਗ ਨਾਲ ਸੰਭਾਲੋ">
    ਜਦੋਂ ਸਥਿਤੀ `error` ਹੋਵੇ, ਤਾਂ ਵਰਤੋਂਕਾਰ ਨੂੰ `phone.error` ਦਿਖਾਓ ਅਤੇ ਆਪਣਾ ਕਾਲ ਬਟਨ ਸਮਰੱਥ ਰੱਖੋ। ਹੂਕ ਆਪਣੇ ਆਪ `error` ਸਥਿਤੀ ਤੋਂ ਬਾਹਰ ਨਹੀਂ ਨਿਕਲਦਾ -- `connect()` ਨੂੰ ਦੁਬਾਰਾ ਕਾਲ ਕਰਨ ਨਾਲ ਨਵੀਂ ਕੋਸ਼ਿਸ਼ ਸ਼ੁਰੂ ਹੁੰਦੀ ਹੈ ਅਤੇ ਪਿਛਲੀ ਗਲਤੀ ਸਾਫ਼ ਹੋ ਜਾਂਦੀ ਹੈ।
  </Accordion>

  <Accordion title="ਸਾਈਡ ਇਫੈਕਟਸ ਲਈ ਕਾਲਬੈਕ ਵਰਤੋ">
    `onConnect`, `onDisconnect`, ਅਤੇ `onError` ਕਾਲਬੈਕ, ਸਥਿਤੀ ਨੂੰ ਪੋਲ ਕੀਤੇ ਬਿਨਾਂ ਐਨਾਲਿਟਿਕਸ, ਲੌਗਿੰਗ, ਜਾਂ ਐਪਲੀਕੇਸ਼ਨ ਦੇ ਹੋਰ ਲੌਜਿਕ ਨੂੰ ਟ੍ਰਿਗਰ ਕਰਨ ਲਈ ਆਦਰਸ਼ ਹਨ।
  </Accordion>

  <Accordion title="audioLevelRef ਤੋਂ ਆਡੀਓ ਪੱਧਰ ਪੜ੍ਹੋ">
    `audioLevelRef` ਇਕੱਲਾ ਲਾਈਵ ਆਡੀਓ-ਪੱਧਰ ਸਰੋਤ ਹੈ। ਵੇਵਫਾਰਮਾਂ ਵਰਗੀਆਂ ਸੁਚੱਜੀਆਂ ਐਨੀਮੇਸ਼ਨਾਂ ਲਈ `requestAnimationFrame` ਦੇ ਅੰਦਰ `audioLevelRef.current` ਪੜ੍ਹੋ (ref ਪੜ੍ਹਨ ਨਾਲ ਰੀ-ਰੈਂਡਰ ਨਹੀਂ ਹੁੰਦੇ), ਜਾਂ ਇਸਨੂੰ ਕਿਸੇ ਇੰਟਰਵਲ 'ਤੇ ਸੈਂਪਲ ਕਰੋ ਅਤੇ React-ਰੈਂਡਰ ਕੀਤੇ UI ਲਈ ਨਤੀਜਾ ਸਥਿਤੀ ਵਿੱਚ ਸਟੋਰ ਕਰੋ। `audioLevel` ਨੰਬਰ ਹੁਣ ਅਪ੍ਰਚਲਿਤ ਹੈ ਅਤੇ ਹਮੇਸ਼ਾ `0` ਰਹਿੰਦਾ ਹੈ -- ਇਸ 'ਤੇ ਲੌਜਿਕ ਨਾ ਬਣਾਓ।
  </Accordion>
</AccordionGroup>
