Headless Hook
useThunderPhone-hook annab sulle täieliku kontrolli kasutajaliidese üle, samal ajal kui ThunderPhone haldab häälesessiooni, heli suunamist ja ühenduse olekut. Kasuta seda siis, kui soovid täielikult kohandatud kasutajaliidest — oma nuppe, paigutusi, animatsioone ja brändingut — ning ThunderPhone haldab kõike taustal.
Millal kasutada headless-hooki
Valmis ThunderPhoneWidget-komponent katab enamiku kasutusjuhtudest, kuid kasuta headless-hooki, kui vajad järgmist:
- Täielikult kohandatud kõneliidest, mis sobib sinu rakenduse disainisüsteemiga
- Reaalajas helitasemetest juhitud helireaktiivseid visualiseeringuid (lainekujud, sfäärid, pulseerivad indikaatorid)
- Kohandatud kõnevooge, näiteks kõneeelseid vorme, kõnejärgseid küsitlusi või häälega koos kasutatavat tekstivestlust
- Integreerimist olemasolevasse komponenditeeki (Material UI, Chakra, Radix jne)
Paigaldamine
npm install @thunderphone/widget
Põhikasutus
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}
</>
)
}
Valikud
Anna need valikud useThunderPhone-ile UseThunderPhoneOptions kaudu:
| Valik | Tüüp | Kohustuslik | Vaikeväärtus | Kirjeldus |
|---|---|---|---|---|
publishableKey | string | Jah | -- | Avalik API-võti (pk_live_...). Häälagent määratakse automaatselt võtme vidina konfiguratsiooni alusel. |
apiBase | string | Ei | 'https://api.thunderphone.com/v1' | API baas-URL-i alistamine. |
language | string | Ei | -- | Seansipõhine keele alistamine — keelekood või lokaat, näiteks en, es või fr-FR. Kui see on määramata, kasutatakse häälagendi seadistatud keelt. |
voice | string | Ei | -- | Seansipõhine hääle alistamine — hääle nimi, näiteks maria. Kui see on määramata, kasutatakse häälagendi seadistatud häält. |
context | string | Ei | -- | Häälagendile edastatav seansipõhine faktiline lehe või saidi kontekst. Serveris kärbitakse 12 000 tähemärgini. |
onConnect | () => void | Ei | -- | Kutsutakse välja, kui häälesessioon ühendub. |
onDisconnect | () => void | Ei | -- | Kutsutakse välja, kui seanss lõpeb. |
onError | (error) => void | Ei | -- | Kutsutakse välja vea korral. Vea väljad on error (kood) ja message. |
ringtone | boolean | string | Ei | false | Esita ühenduse loomise ajal helinat. Vaikehelina jaoks kasuta true või kohandatud heli jaoks URL-i stringi. |
Tagastusväärtus
Hook tagastab objekti UseThunderPhoneReturn:
| Omadus | Tüüp | Kirjeldus |
|---|---|---|
state | 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error' | Praegune ühenduse olek. |
connect | () => void | Käivita häälseanss. |
disconnect | () => void | Lõpeta praegune seanss. |
toggleMute | () => void | Lülita mikrofoni vaigistus sisse või välja. |
isMuted | boolean | Kas mikrofon on praegu vaigistatud. |
error | string | undefined | Veateade, kui olek on 'error'. |
agentName | string | undefined | Ühendatud agendi kuvatav nimi. |
audioLevel | number | Aegunud -- alati 0. Staatiline kohatäide, mis on säilitatud tagasiühilduvuse tagamiseks; seda ei värskendata kunagi. Loe selle asemel audioLevelRef.current. |
audioLevelRef | React.RefObject<number> | Muudetav ref, mis sisaldab reaalajas helitaset (0--1) -- agendi hääle ja külastaja mikrofoni valjemat taset -- ning mida värskendatakse igal animatsioonikaadril väljaspool Reacti renderdamistsüklit. Sujuvate, tõrgeteta animatsioonide jaoks loe audioLevelRef.current requestAnimationFrame-i tsüklites või võta väärtusest proov intervalliga, kui vajad seda Reacti olekus. |
audio | ReactNode | Nähtamatu element, mis haldab heliühendust -- see tuleb renderdada. |
Helile reageeriv kasutajaliides
Ref audioLevelRef annab sulle kaadrisagedusega helitasemed ilma Reacti uuesti renderdamist käivitamata, mistõttu sobib see ideaalselt sujuvate lainekuju visualiseeringute, pulseerivate kerade või mis tahes vestlusega seotud animatsioonide juhtimiseks. Tase kajastab seda, kumb on valjem: agendi hääl või külastaja mikrofon.
Lainekuju näide
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>
)
}
Pulseeriva kera näide
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>
)
}
Rääkimise indikaatori näide
Reactiga renderdatava kasutajaliidese jaoks, mis muutub helitugevuse järgi — näiteks lävendipõhine „rääkimise” märk — võta audioLevelRef.current väärtusest regulaarsete ajavahemike järel näidiseid ja salvesta tulemus olekusse:
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>
)
}
Olekuautomaat
Atribuut state järgib seda elutsüklit:
idle --> connecting --> connected --> disconnected --> idle (after 1.5s)
\
--> error (stays until connect() is called again)
| Olek | Kirjeldus |
|---|---|
idle | Aktiivset seanssi pole. Valmis kutsuma connect(). |
connecting | Seanssi luuakse. Keela selles olekus kõnenupud. |
connected | Häälseanss on aktiivne. Kasutaja räägib agendiga. |
disconnected | Seanss lõppes korrektselt. Naaseb 1,5 sekundi pärast automaatselt olekusse idle. |
error | Midagi läks valesti. Kontrolli teadet phone.error kaudu. Olek ei tühjene iseenesest -- connect() uuesti kutsumine alustab uut katset ja lähtestab vea. |
Näited
Vaigistuse juhtimisega
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>
)
}
Helinaga
Esita ühenduse loomise ajal helinat, et jäljendada telefonikõnet:
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}
</>
)
}
Helin kordub oleku connecting ajal ja vaibub, kui häälagent ühendub. Sisseehitatud vaikehelina kasutamiseks edasta true või oma helifaili kasutamiseks URL-string.
Sündmuste tagasihelistustega
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}
</>
)
}
Täielikult kohandatud kasutajaliides
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>
)
}
Näpunäited
Renderda alati phone.audio
Element phone.audio on nähtamatu, kuid vajalik. Paiguta see JSX-is ükskõik kuhu -- see ei renderda nähtavat DOM-i, kuid haldab WebRTC-audioühendust sisemiselt.
Keela nupp ühenduse loomise ajal
Olek connecting võib kesta 1–3 sekundit. Keela selles olekus helistamisnupp, et vältida ühenduse loomise korduskatseid.
Käsitle veaolekut sujuvalt
Kui olek on error, kuva kasutajale phone.error ja hoia helistamisnupp lubatuna. Hook ei lahku olekust error iseseisvalt -- connect() uuesti kutsumine alustab uut katset ja kustutab eelmise vea.
Kasuta kõrvaltoimete jaoks tagasihelistusi
Tagasihelistused onConnect, onDisconnect ja onError sobivad ideaalselt analüütikaks, logimiseks või muu rakendusloogika käivitamiseks ilma olekut küsitlemata.
Loe helitasemeid audioLevelRef-ist
audioLevelRef on ainus reaalajas helitaseme allikas. Sujuvate animatsioonide, näiteks lainekujude jaoks loe audioLevelRef.current seest requestAnimationFrame (ref-i lugemine ei põhjusta uuesti renderdamist) või võta sellest väärtus kindla intervalliga ning salvesta tulemus olekusse Reacti renderdatud kasutajaliidese jaoks. Arv audioLevel on aegunud ja alati 0 -- ära raja sellele loogikat.