Hook Isiyo na Kiolesura

Hook ya useThunderPhone hukupa udhibiti kamili wa kiolesura cha mtumiaji huku ThunderPhone ikisimamia kipindi cha sauti, uelekezaji wa sauti na hali ya muunganisho. Itumie unapohitaji UI iliyobinafsishwa kikamilifu -- vitufe, mipangilio, uhuishaji na utambulisho wako wa chapa -- huku ThunderPhone ikishughulikia kila kitu chinichini.

Wakati wa Kutumia Hook Isiyo na Kiolesura

Kijenzi kilichojengwa awali cha ThunderPhoneWidget kinatosheleza hali nyingi za matumizi, lakini tumia hook isiyo na kiolesura unapohitaji:


Usakinishaji

npm install @thunderphone/widget

Matumizi ya Msingi

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}
    </>
  )
}

Chaguo

Pitisha chaguo hizi kwa useThunderPhone kupitia UseThunderPhoneOptions:

ChaguoAinaInahitajikaChaguomsingiMaelezo
publishableKeystringNdiyo--Ufunguo wa API unaoweza kuchapishwa (pk_live_...). Ejenti hutambuliwa kiotomatiki kutoka kwenye usanidi wa wijeti wa ufunguo.
apiBasestringHapana'https://api.thunderphone.com/v1'Badilisha URL ya msingi ya API.
languagestringHapana--Badilisha lugha kwa kila kipindi -- msimbo wa lugha au eneo kama en, es, au fr-FR. Isipowekwa, lugha iliyosanidiwa ya ejenti hutumika.
voicestringHapana--Badilisha sauti kwa kila kipindi -- jina la sauti kama maria. Isipowekwa, sauti iliyosanidiwa ya ejenti hutumika.
contextstringHapana--Muktadha wa ukweli wa ukurasa au tovuti kwa kila kipindi unaopitishwa kwa ejenti. Hukatwa upande wa seva hadi herufi 12,000.
onConnect() => voidHapana--Huitwa kipindi cha sauti kinapounganishwa.
onDisconnect() => voidHapana--Huitwa kipindi kinapoisha.
onError(error) => voidHapana--Huitwa kunapotokea hitilafu. Hitilafu ina sehemu za error (msimbo) na message.
ringtoneboolean | stringHapanafalseCheza mlio wa simu wakati wa kuunganisha. true kwa mlio chaguomsingi wa simu, au mfuatano wa URL kwa sauti maalum.

Thamani ya Kurejesha

Hook hurejesha objekti ya UseThunderPhoneReturn:

SifaAinaMaelezo
state'idle' | 'connecting' | 'connected' | 'disconnected' | 'error'Hali ya sasa ya muunganisho.
connect() => voidAnzisha kipindi cha sauti.
disconnect() => voidMaliza kipindi cha sasa.
toggleMute() => voidWasha au zima kunyamazisha maikrofoni.
isMutedbooleanIkiwa maikrofoni imenyamazishwa kwa sasa.
errorstring | undefinedUjumbe wa hitilafu wakati hali ni 'error'.
agentNamestring | undefinedJina la kuonyesha la ejenti aliyeunganishwa.
audioLevelnumberImeondolewa -- daima 0. Kishikilia nafasi kisichobadilika kinachohifadhiwa kwa uoanifu wa nyuma; hakisasishwi kamwe. Soma audioLevelRef.current badala yake.
audioLevelRefReact.RefObject<number>Ref inayoweza kubadilishwa iliyo na kiwango cha sauti cha wakati halisi (0--1) -- kilicho juu zaidi kati ya sauti ya ejenti na maikrofoni ya mgeni -- inayosasishwa katika kila fremu ya uhuishaji, nje ya mzunguko wa render wa React. Soma audioLevelRef.current ndani ya mizunguko ya requestAnimationFrame kwa uhuishaji laini usio na kukwama, au ichukue kwa vipindi unapohitaji thamani katika state ya React.
audioReactNodeKipengele kisichoonekana kinachoshughulikia muunganisho wa sauti -- lazima kirendwe.

Kiolesura Kinachoathiriwa na Sauti

ref ya audioLevelRef hukupa viwango vya sauti kwa kasi ya fremu bila kusababisha React kujitoa upya, hivyo ni bora kwa kuendesha vielelezo laini vya muundo wa mawimbi, duara zinazodunda, au uhuishaji wowote unaohusishwa na mazungumzo. Kiwango huonyesha sauti iliyo kubwa zaidi: sauti ya ejenti au maikrofoni ya mgeni.

Mfano wa Muundo wa Mawimbi

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>
  )
}

Mfano wa Duara Linalodunda

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>
  )
}

Mfano wa Kiashiria cha Kuzungumza

Kwa UI inayotolewa na React ambayo hubadilika kulingana na sauti -- kama beji ya "inayozungumza" inayotegemea kizingiti -- chukua sampuli ya audioLevelRef.current kwa vipindi na uhifadhi matokeo katika state:

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>
  )
}

Mashine ya Hali

Sifa ya state hufuata mzunguko huu wa maisha:

idle --> connecting --> connected --> disconnected --> idle (after 1.5s)
                  \
                   --> error (stays until connect() is called again)
HaliMaelezo
idleHakuna kipindi kinachoendelea. Tayari kuita connect().
connectingKipindi kinaanzishwa. Zima kitufe cha kupiga simu wakati wa hali hii.
connectedKipindi cha sauti kinaendelea. Mtumiaji anazungumza na ejenti.
disconnectedKipindi kimeisha vizuri. Hubadilika kurudi kuwa idle kiotomatiki baada ya sekunde 1.5.
errorKitu kimeharibika. Angalia phone.error kwa ujumbe. Hali hii haijifuti yenyewe -- kuita connect() tena huanzisha jaribio jipya na kuweka upya hitilafu.

Mifano

Kwa Udhibiti wa Kunyamazisha

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>
  )
}

Kwa Mlio wa Simu

Cheza sauti ya mlio wa simu wakati wa kuunganisha ili kuiga simu:

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}
    </>
  )
}

Mlio wa simu hurudiwa katika hali ya connecting na hupungua sauti ejenti anapounganishwa. Pitisha true kwa mlio chaguomsingi uliojengewa ndani, au mfuatano wa URL ili kutumia faili yako ya sauti.

Kwa Callback za Matukio

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}
    </>
  )
}

Kiolesura Maalum Kabisa

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>
  )
}

Vidokezo

Render phone.audio kila wakati

Kipengele cha phone.audio hakionekani lakini kinahitajika. Kiweke mahali popote katika JSX yako -- hakirender DOM inayoonekana lakini kinasimamia muunganisho wa sauti wa WebRTC ndani kwa ndani.

Zima kitufe wakati wa kuunganisha

Hali ya connecting inaweza kudumu sekunde 1-3. Zima kitufe cha simu wakati wa hali hii ili kuzuia majaribio ya muunganisho yanayojirudia.

Shughulikia hali ya hitilafu kwa ustadi

Hali inapokuwa error, onyesha phone.error kwa mtumiaji na uache kitufe chako cha simu kikiwa kimewashwa. Hook haitoki kwenye hali ya error yenyewe -- kuita connect() tena huanzisha jaribio jipya na kufuta hitilafu ya awali.

Tumia callbacks kwa athari za kando

Callbacks za onConnect, onDisconnect, na onError zinafaa kwa uchanganuzi, uandishi wa kumbukumbu, au kuanzisha mantiki nyingine ya programu bila kufuatilia hali mara kwa mara.

Soma viwango vya sauti kutoka audioLevelRef

audioLevelRef ndicho chanzo pekee cha viwango vya sauti vya moja kwa moja. Soma audioLevelRef.current ndani ya requestAnimationFrame kwa uhuishaji laini kama maumbo ya mawimbi (kusoma ref hakusababishi render upya), au ichukue sampuli kwa vipindi na uhifadhi matokeo katika state kwa kiolesura kinachorendwa na React. Nambari ya audioLevel imepitwa na wakati na daima ni 0 -- usijenge mantiki juu yake.