https://github.com/msutkowski/twilio-client-redux
Redux bindings for Twilio Client JSSDK
https://github.com/msutkowski/twilio-client-redux
Last synced: over 1 year ago
JSON representation
Redux bindings for Twilio Client JSSDK
- Host: GitHub
- URL: https://github.com/msutkowski/twilio-client-redux
- Owner: msutkowski
- License: mit
- Created: 2020-04-02T01:41:13.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2023-01-06T02:46:12.000Z (over 3 years ago)
- Last Synced: 2025-03-16T00:27:48.513Z (over 1 year ago)
- Language: TypeScript
- Size: 730 KB
- Stars: 2
- Watchers: 1
- Forks: 2
- Open Issues: 17
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Twilio Client Redux
## Purpose
This package is intended to be used with [`twilio-client.js`](https://github.com/twilio/twilio-client.js) and to make integrating its event emitters with redux very straightforward. We recommend using it along side [Redux Toolkit](https://github.com/reduxjs/redux-toolkit) for several reasons and all code examples are shown with RTK implementations.
The general implementation of this library is currently very opinionated, but should work for every use case - if you have any specific requests, please open an issue or PR and we'd be happy to include it. Our goal is to track `twilio-client` releases and upgrade as necessary.
> Note: As of right now, Twilio does not export types that are easily consumable in their frontend libraries, but we do our best to give you an accurate representation of the serializable **device** and **connection** when appropriate. This is mostly just for logging purposes as you can't call methods on the serialized `Device` or `Connection` objects.
## Installation
Using `npm`:
```
npm i twilio-client-redux
```
Using `yarn`:
```
yarn add twilio-client-redux
```
## Usage
### Add the middleware
At a high level, the implementation looks like this:
1. Add the middleware
2. Create a device instance with your capability token by `dispatch`ing `setup(token, options)`
3. `dispatch` actions to make calls, control the device, etc etc.
4. Respond to events in relevant reducers to handle `Device` and/or `Connection` events
5. Have a phone party!
The middleware accepts the following options:
```ts
export interface MiddlewareOptions {
storeAudioDevices?: boolean; // default = true. store the payload of setInputDevice/setOutputDevice to localStorage. will automatically try to use the values when setting up a future device.
connectOnIncoming?: boolean; // default = true. automatically accept incoming connections
prefix?: string; // default = @tcr. currently unimplemented and may be implemented in the future
}
```
Example implementation:
```ts
// store.ts
import { configureStore, Action, getDefaultMiddleware } from '@reduxjs/toolkit';
import TwilioClientRedux from 'twilio-client-redux';
import rootReducer, { RootState } from './rootReducer';
const store = configureStore({
reducer: rootReducer,
middleware: [...getDefaultMiddleware(), TwilioClientRedux()],
});
export type AppDispatch = typeof store.dispatch;
export default store;
```
### Initialize a device
```ts
// MyPhoneApp.ts
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { setup } from "twilio-client-redux";
export const MyPhoneApp = () => {
const dispatch = useDispatch();
useEffect(() => {
// You could fetch a token here, or pass it in as a prop, or whatever makes sense for your app
dispatch(setup(token, { debug: true }));
}, [token])
});
return ();
};
```
#### Device setup options:
Accepts the standard `twilio-client` `Device` options:
```ts
declare interface DeviceConfigOptions {
allowIncomingWhileBusy?: boolean;
audioConstraints?: MediaTrackConstraints | boolean;
closeProtection?: boolean | string;
codecPreferences?: Codec[];
debug?: boolean;
disableAudioContextSounds?: boolean;
dscp?: boolean;
enableIceRestart?: boolean;
enableRingingState?: boolean;
fakeLocalDTMF?: boolean;
forceAggressiveIceNomination?: boolean;
maxAverageBitrate?: number;
region?: string;
rtcConfiguration?: RTCConfiguration;
sounds?: Partial>;
warnings?: boolean;
}
```
#### Device actions:
> Note: All devices actions take an optional parameter of `deviceId`. By default, this is set to 'default'. This allows you to manage multiple devices if your implementation requires that.
```ts
setup(); // initialize a device.
destroy(); // destroy the device instance and disconnect all connections. you will need to call setup again to create a new device.
setOutputDevice(); // if you passed in storeAudioDevices: true to `setup`, this will store in localStorage. it will attempt to be reused when the device initialized in the future
setInputDevice(); // sets the input device and behaves the same as setOutputDevice
testOutputDevice(); // can be used to play a chime on the currently set output device
```
### Managing connections
We provide every method that is available to `twilio-client.js` as exported actions. All methods take an optional `deviceId` parameter.
```ts
makeCall();
hangupCall();
sendDigit(); // sends a digit to the active connection
setMute(); // mute the current connection on the device
toggleMute(); // toggles the mute state
getMuteStatus(); // returns `{ muted: value }`
getStatus(); // returns the status of the active connection
acceptCall();
rejectCall();
ignoreCall();
```
## Responding to middleware-generated actions in a reducer
By default, the middleware creates actions for every `twilio-client` event listener. Depending on your redux setup, you can append `.type` to the end of the action you're looking for to get it's type value (ex: `onReady.type` === `@tcr::DEVICE_READY`).
In a non-`createSlice` implementation, that might look like:
```js
// phoneReducer.js
// ... reducer code
switch (action.type) {
case onReady.type:
// Do things when the device is ready
}
```
### Included action types that are created after initializing a Device
```ts
onReady;
onCancel;
onConnect;
onError;
onDisconnect;
onIncoming;
onOffline;
```
Example slice responding to common use cases:
```ts
// phoneSlice.ts
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import {
onReady,
onConnect,
onDisconnect,
getMuteStatus,
} from 'twilio-client-redux';
import { RootState } from 'src/store/rootReducer';
import { apiRequest } from 'src/utils';
// in this case, we make an API request to our server to initiate a call. being that
// the default value in the middleware is to automatically accept incoming connections
// our call will be bridged immediately. your implementation will vary.
export const makeCall = createAsyncThunk(
'phone/makeCall',
async (phone: string) => {
const {
data: { data },
} = await apiRequest.post(`agent/calls`, { phone });
return data;
}
);
const initialState = {
call: {},
muted: false,
status: 'idle',
};
const slice = createSlice({
name: 'phone',
initialState,
reducers: {},
extraReducers: builder => {
builder.addCase(onReady, (state, action) => {
// Note that action is type safe here and will return a serialized `Device`
state.status = 'idle';
});
builder.addCase(onConnect, (state, action) => {
// Note that action is type safe here and will return a serialized `Connection`
state.status = 'active';
});
builder.addCase(onDisconnect, (state, action) => {
// Note that action is type safe here and will return a serialized `Device`
return initialState;
});
builder.addCase(makeCall.pending, (state, action) => {
// set state to ringing when we initiate the API request to start the call
state.status = 'ringing';
});
builder.addCase(makeCall.fulfilled, (state, action) => {
// Note that action is type safe here and will return a action.payload will be the type of Call from our API
state.status = 'active';
state.call = action.payload;
});
builder.addCase(getMuteStatus, (state, action) => {
state.muted = action.payload.muted;
});
},
});
export default slice.reducer;
export const selectIsMuted = (state: RootState) => state.phone.muted;
```