Add Twilio client wrapper and types
Add voice state manager
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
"react-scripts": "3.3.1",
|
"react-scripts": "3.3.1",
|
||||||
|
"twilio-client": "^1.9.7",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
34
src/App.tsx
34
src/App.tsx
@@ -1,26 +1,24 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import logo from './logo.svg';
|
import logo from './logo.svg';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
import useVoice from './Wrappers/voice';
|
||||||
|
import { voiceToken } from './Wrappers/default-config';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
const { startDevice, answer, hangup, voiceState } = useVoice();
|
||||||
<div className="App">
|
|
||||||
<header className="App-header">
|
window.setTimeout(() => startDevice(voiceToken), 5000);
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
|
||||||
<p>
|
return (
|
||||||
Edit <code>src/App.tsx</code> and save to reload.
|
<div className="App">
|
||||||
</p>
|
<header className="App-header">
|
||||||
<a
|
<img src={logo} className="App-logo" alt="logo" />
|
||||||
className="App-link"
|
<p>
|
||||||
href="https://reactjs.org"
|
Device is currently {voiceState.deviceState}.
|
||||||
target="_blank"
|
</p>
|
||||||
rel="noopener noreferrer"
|
</header>
|
||||||
>
|
</div>
|
||||||
Learn React
|
);
|
||||||
</a>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
53
src/Hooks/voice-state.ts
Normal file
53
src/Hooks/voice-state.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
export type DeviceState = "Ringing" | "Connected" | "Muted" | "Disconnected" | "Offline";
|
||||||
|
|
||||||
|
export interface VoiceState
|
||||||
|
{
|
||||||
|
deviceState: DeviceState;
|
||||||
|
callCreatedTime: number;
|
||||||
|
callEstablishedTime: number;
|
||||||
|
callPhoneNumber: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VoiceStateManager
|
||||||
|
{
|
||||||
|
onDeviceStateUpdate(state: DeviceState, phoneNumber?: string): void;
|
||||||
|
voiceState: VoiceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useVoiceState(): VoiceStateManager
|
||||||
|
{
|
||||||
|
const [deviceState, setDeviceState] = useState<DeviceState>("Offline");
|
||||||
|
const [callCreatedTime, setCallCreatedime] = useState<number>(0);
|
||||||
|
const [callEstablishedTime, setCallEstablishedTime] = useState<number>(0);
|
||||||
|
const [callPhoneNumber, setCallPhoneNumber] = useState<string>("");
|
||||||
|
|
||||||
|
const onDeviceStateUpdate = (state: DeviceState, phoneNumber?: string) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
if (state === "Ringing" || (state === "Connected" && deviceState === "Disconnected"))
|
||||||
|
{
|
||||||
|
setCallCreatedime(Date.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (state === "Connected" && (deviceState === "Ringing" || deviceState === "Disconnected"))
|
||||||
|
{
|
||||||
|
setCallEstablishedTime(Date.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
setDeviceState(state);
|
||||||
|
if (phoneNumber) { setCallPhoneNumber(phoneNumber); }
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
onDeviceStateUpdate,
|
||||||
|
voiceState: {
|
||||||
|
deviceState,
|
||||||
|
callCreatedTime,
|
||||||
|
callEstablishedTime,
|
||||||
|
callPhoneNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/Wrappers/default-config.ts
Normal file
1
src/Wrappers/default-config.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const voiceToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6InNjb3BlOmNsaWVudDppbmNvbWluZz9jbGllbnROYW1lPXNwZW5jZXIgc2NvcGU6Y2xpZW50Om91dGdvaW5nP2FwcFNpZD1BUGJmNzMyOTg4YjcxYTMzZDFiOGY1ZjY1MmI1NDU4MmQ0JmNsaWVudE5hbWU9c3BlbmNlciIsImlzcyI6IkFDOWQyYzcxOTc0NmIxZTg5MWU2ZjVlZmQwMmE5Yjg0MzAiLCJleHAiOjE1ODA5NjA0NTAsImlhdCI6MTU4MDk1Njg1MH0.LOlWmq52LjPLM4ONlMdnUybZf6NDk1Xeye-qjVRI_O4";
|
||||||
53
src/Wrappers/voice.ts
Normal file
53
src/Wrappers/voice.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { Device, Connection } from "twilio-client";
|
||||||
|
import useVoiceState, { VoiceState } from "../Hooks/voice-state";
|
||||||
|
|
||||||
|
export interface Voice
|
||||||
|
{
|
||||||
|
startDevice(token: string): void;
|
||||||
|
answer(): void;
|
||||||
|
hangup(): void;
|
||||||
|
voiceState: VoiceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useVoice()
|
||||||
|
{
|
||||||
|
const state = useVoiceState();
|
||||||
|
let device: Device;
|
||||||
|
let connection: Connection;
|
||||||
|
|
||||||
|
const startDevice = (token: string) =>
|
||||||
|
{
|
||||||
|
device = new Device(token);
|
||||||
|
|
||||||
|
device.on("ready", () => state.onDeviceStateUpdate("Disconnected"));
|
||||||
|
device.on("offline", () => state.onDeviceStateUpdate("Offline"));
|
||||||
|
device.on("disconnect", () => state.onDeviceStateUpdate("Disconnected"));
|
||||||
|
device.on("cancel", () => state.onDeviceStateUpdate("Disconnected"));
|
||||||
|
|
||||||
|
device.on("incoming", (incomingConnection: Connection) =>
|
||||||
|
{
|
||||||
|
state.onDeviceStateUpdate("Ringing", incomingConnection.parameters.From);
|
||||||
|
|
||||||
|
incomingConnection.on("accept", () => state.onDeviceStateUpdate("Connected"));
|
||||||
|
incomingConnection.on("mute", muted => state.onDeviceStateUpdate(muted ? "Muted" : "Connected"));
|
||||||
|
connection = incomingConnection;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const answer = () =>
|
||||||
|
{
|
||||||
|
connection.accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
const hangup = () =>
|
||||||
|
{
|
||||||
|
device.disconnectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
startDevice,
|
||||||
|
answer,
|
||||||
|
hangup,
|
||||||
|
voiceState: state.voiceState,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"jsx": "react"
|
"jsx": "react"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src"
|
"src",
|
||||||
|
"types"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
103
types/twilio-client.d.ts
vendored
Normal file
103
types/twilio-client.d.ts
vendored
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
declare module "twilio-client"
|
||||||
|
{
|
||||||
|
interface SetupParams
|
||||||
|
{
|
||||||
|
allowIncomingWhileBusy: boolean;
|
||||||
|
audioConstraints: boolean;
|
||||||
|
backoffMaxMs: number;
|
||||||
|
codecPreferences: ("pcmu" | "opus")[];
|
||||||
|
closeProtection: boolean;
|
||||||
|
debug: boolean;
|
||||||
|
dscp: boolean;
|
||||||
|
enableRingingState: boolean;
|
||||||
|
fakeLocalDTMF: boolean;
|
||||||
|
iceServers: string[];
|
||||||
|
region: "au1" | "br1" | "ie1" | "de1" | "jp1" | "sg1" | "us1" | "us2" | "gll";
|
||||||
|
rtcConfiguration: object;
|
||||||
|
sounds: object;
|
||||||
|
warnings: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConnectionParameters
|
||||||
|
{
|
||||||
|
CallSid: string;
|
||||||
|
AccountSid: string;
|
||||||
|
From: string;
|
||||||
|
To: string;
|
||||||
|
ApiVersion: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionStatus = "pending" | "connecting" | "ringing" | "open" | "closed";
|
||||||
|
type ConnectionEvent = "accept" | "disconnect" | "error" | "mute" | "ringing" | "sample" | "volume" | "warning" | "warning-cleared";
|
||||||
|
|
||||||
|
interface Connection
|
||||||
|
{
|
||||||
|
parameters: ConnectionParameters;
|
||||||
|
customParameters: Map<string, string>;
|
||||||
|
options: object;
|
||||||
|
accept(audioConstraints?: any): void;
|
||||||
|
reject(): void;
|
||||||
|
ignore(): void;
|
||||||
|
disconnect(): void;
|
||||||
|
mute(mute: boolean): void;
|
||||||
|
isMuted(): boolean;
|
||||||
|
getRemoteStream(): any;
|
||||||
|
sendDigits(digits: string): void;
|
||||||
|
status(): ConnectionStatus;
|
||||||
|
on(event: ConnectionEvent, handler: (...args: any[]) => void): void;
|
||||||
|
on(event: "mute", handler: (muted: boolean, connection: Connection) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MediaDevicesInfo
|
||||||
|
{
|
||||||
|
deviceId: string;
|
||||||
|
groupId: string;
|
||||||
|
kind: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AudioOutputCollection
|
||||||
|
{
|
||||||
|
get(): Set<MediaDevicesInfo>;
|
||||||
|
set(deviceId: string | string[]): void;
|
||||||
|
test(soundUrl: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AudioDeviceEvent = "deviceChange" | "inputVolume";
|
||||||
|
|
||||||
|
interface DeviceAudio
|
||||||
|
{
|
||||||
|
setAudioConstraints(audioConstraints: object): void;
|
||||||
|
setInputDevice(id: string): void;
|
||||||
|
unsetAudioContraints(): void;
|
||||||
|
unsetInputDevice(id: string): void;
|
||||||
|
on(event: AudioDeviceEvent, callback: (data: any) => void): void;
|
||||||
|
audioContraits: object;
|
||||||
|
|
||||||
|
availableOutputDevices: Map<string, MediaDevicesInfo>;
|
||||||
|
availableInputDevices: Map<string, MediaDevicesInfo>;
|
||||||
|
inputDevice: MediaDevicesInfo;
|
||||||
|
speakerDevices: AudioOutputCollection;
|
||||||
|
ringtoneDevices: AudioOutputCollection;
|
||||||
|
|
||||||
|
isOutputSelectionSupported: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceStatus = "ready" | "offline" | "busy";
|
||||||
|
type DeviceEvent = "cancel" | "connect" | "disconnect" | "error" | "incoming" | "offline" | "ready";
|
||||||
|
|
||||||
|
class Device
|
||||||
|
{
|
||||||
|
constructor(token: string, params?: SetupParams);
|
||||||
|
public setup(token: string, params?: SetupParams): void;
|
||||||
|
public connect(params: any, audioConstraints?: any): Connection;
|
||||||
|
public activeConnection(): Connection;
|
||||||
|
public destroy(): void;
|
||||||
|
public disconnectAll(): void;
|
||||||
|
public status(): DeviceStatus;
|
||||||
|
public on(event: DeviceEvent, handler: (...args: any[]) => void): void;
|
||||||
|
public audio: DeviceAudio;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.Device = Device;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user