npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

twilio-client-redux

v1.0.16

Published

Redux middleware for managing twilio client devices and connections

Downloads

34

Readme

Twilio Client Redux

Purpose

This package is intended to be used with twilio-client.js and to make integrating its event emitters with redux very straightforward. We recommend using it along side 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 dispatching 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:

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:

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

// 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 (<PhoneUI />);

};

Device setup options:

Accepts the standard twilio-client Device options:

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<Record<SoundName, string>>;
  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.

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.

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:

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

onReady;
onCancel;
onConnect;
onError;
onDisconnect;
onIncoming;
onOffline;

Example slice responding to common use cases:

// 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<Call>(`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;