2021-09-01 22:13:41 +00:00
import { useCallback , useEffect , useMemo } from "react" ;
2021-08-31 22:42:15 +00:00
import { useMutation } from "blitz" ;
import type { TwilioError } from "@twilio/voice-sdk" ;
import { Call , Device } from "@twilio/voice-sdk" ;
import { atom , useAtom } from "jotai" ;
2021-09-01 21:54:14 +00:00
import getToken , { ttl } from "../mutations/get-token" ;
import appLogger from "../../../integrations/logger" ;
const logger = appLogger . child ( { module : "use-device" } ) ;
2021-08-31 22:42:15 +00:00
export default function useDevice() {
const [ device , setDevice ] = useAtom ( deviceAtom ) ;
const [ getTokenMutation ] = useMutation ( getToken ) ;
2021-09-01 21:54:14 +00:00
const refreshToken = useCallback ( async ( ) = > {
if ( ! device ) {
logger . error ( "Tried refreshing accessToken for an uninitialized device" ) ;
return ;
}
const token = await getTokenMutation ( ) ;
device . updateToken ( token ) ;
} , [ device , getTokenMutation ] ) ;
2021-09-01 22:13:41 +00:00
const isDeviceReady = useMemo ( ( ) = > device ? . state === Device . State . Registered , [ device ] ) ;
2021-09-01 21:54:14 +00:00
useEffect ( ( ) = > {
if ( ! isDeviceReady ) {
return ;
}
2021-09-01 22:13:41 +00:00
const intervalId = setInterval ( refreshToken , ( ttl - 30 ) * 1000 ) ;
2021-09-01 21:54:14 +00:00
return ( ) = > clearInterval ( intervalId ) ;
} , [ isDeviceReady , refreshToken ] ) ;
2021-08-31 22:42:15 +00:00
useEffect ( ( ) = > {
( async ( ) = > {
const token = await getTokenMutation ( ) ;
const device = new Device ( token , {
codecPreferences : [ Call . Codec . Opus , Call . Codec . PCMU ] ,
sounds : {
[ Device . SoundName . Disconnect ] : undefined , // TODO
} ,
} ) ;
device . register ( ) ;
setDevice ( device ) ;
} ) ( ) ;
2021-09-01 21:54:14 +00:00
} , [ getTokenMutation , setDevice ] ) ;
2021-08-31 22:42:15 +00:00
useEffect ( ( ) = > {
if ( ! device ) {
return ;
}
2021-09-01 21:54:14 +00:00
console . log ( "ok" ) ;
// @ts-ignore
window . device = device ;
2021-08-31 22:42:15 +00:00
device . on ( "error" , onDeviceError ) ;
device . on ( "incoming" , onDeviceIncoming ) ;
return ( ) = > {
device . off ( "error" , onDeviceError ) ;
device . off ( "incoming" , onDeviceIncoming ) ;
} ;
} , [ device ] ) ;
2021-09-01 21:54:14 +00:00
// @ts-ignore
window . refreshToken = refreshToken ;
return {
device ,
isDeviceReady ,
refreshToken ,
} ;
2021-08-31 22:42:15 +00:00
function onDeviceError ( error : TwilioError.TwilioError , call? : Call ) {
// TODO gracefully handle errors: possibly hang up the call, redirect to keypad
2021-09-01 22:13:41 +00:00
console . error ( "device error" , JSON . parse ( JSON . stringify ( error ) ) ) ;
alert ( error . code ) ;
2021-08-31 22:42:15 +00:00
}
function onDeviceIncoming ( call : Call ) {
// TODO show alert to accept/reject the incoming call /!\ it should persist between screens /!\ prevent making a new call when there is a pending incoming call
console . log ( "call" , call ) ;
console . log ( "Incoming connection from " + call . parameters . From ) ;
let archEnemyPhoneNumber = "+12093373517" ;
if ( call . parameters . From === archEnemyPhoneNumber ) {
call . reject ( ) ;
console . log ( "It's your nemesis. Rejected call." ) ;
} else {
// accept the incoming connection and start two-way audio
call . accept ( ) ;
}
}
}
const deviceAtom = atom < Device | null > ( null ) ;
2021-09-01 22:13:41 +00:00
let e = {
message :
"ConnectionError (53000): Raised whenever a signaling connection error occurs that is not covered by a more specific error code." ,
causes : [ ] ,
code : 53000 ,
description : "Signaling connection error" ,
explanation :
"Raised whenever a signaling connection error occurs that is not covered by a more specific error code." ,
name : "ConnectionError" ,
solutions : [ ] ,
} ;