Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .env

This file was deleted.

11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Required: Your OAuth / Identity token
REACT_APP_AUTH_TOKEN=

# Required: Source phone number (E.164, e.g. +15551234567) — used as the from number for outbound calls
REACT_APP_ACCOUNT_USERNAME=

# Optional: Voice application ID — skips server-side lookup when provided
# REACT_APP_APP_ID=

# Optional: Override WebRTC gateway URL (non-production testing only)
# REACT_APP_GATEWAY_URL=
7 changes: 3 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
.env
.env.*
!.env.example

npm-debug.log*
yarn-debug.log*
Expand Down
159 changes: 25 additions & 134 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,164 +1,55 @@
# In-App Calling Dialpad

# Table of Contents
A simple dial pad application used to create calls using the Bandwidth WebRTC SDK.

## Table of Contents

* [Description](#description)
* [Pre-Requisites](#pre-requisites)
* [Initialization](#initialization)
* [Setup](#setup)
* [Running the Application](#running-the-application)
* [SDK Documentation](#sdk-documentation)

# Description

A simple dial pad application used to create calls using our WebRTC SDK.

# Pre-Requisites

In order to use this sample app, your account must have In-App Calling enabled. You will also have to generate an auth token using our Identity API.

For more information about API credentials see our [Account Credentials](https://dev.bandwidth.com/docs/account/credentials) page.
## Description

### Environmental Variables
This sample app demonstrates how to build a simple dialing interface with the Bandwidth WebRTC SDK. It includes call controls for mute, hold, and hangup functionality.

The sample app uses the below environmental variables.
## Pre-Requisites

```sh
REACT_APP_IN_APP_CALLING_TOKEN # You Identity Token
REACT_APP_ACCOUNT_USERNAME # Put from number here
REACT_APP_ACCOUNT_DISPLAY_NAME # Put from number/display name here
REACT_APP_ACCOUNT_PASSWORD # use some password or leave it empty
```
Your account must have In-App Calling enabled. For more information about API credentials, see the [Account Credentials](https://dev.bandwidth.com/docs/account/credentials) page.

# Initialization

- **BandwidthUA**: The instance is available from the outset, Initialization required before making the call, follow the below code snippet for initialization
```sh
const serverConfig = {
domain: 'gw.webrtc-app.bandwidth.com',
addresses: ['wss://gw.webrtc-app.bandwidth.com:10081'],
iceServers: [
'stun.l.google.com:19302',
'stun1.l.google.com:19302',
'stun2.l.google.com:19302',
],
};
const phone = new BandwidthUA();

phone.setServerConfig(
serverConfig.addresses,
serverConfig.domain,
serverConfig.iceServers
);
phone.setWebSocketKeepAlive(5, false, false);
phone.checkAvailableDevices();
phone.setAccount(`${sourceNumber}`, 'In-App Calling Sample', '');
phone.setOAuthToken(authToken);
phone.init();
```
You'll need a Signum JWT token (OAuth token) to authenticate with the SDK.

# Usage
## SDK Defaults

### Making a Call
This sample connects using your account ID and Signum OAuth token. The SDK authenticates directly with the Bandwidth WebRTC gateway — no REST endpoint creation call is made. The gateway URL uses the SDK's built-in production default; do not override it unless Bandwidth support has specifically directed you to a different endpoint.

Making a call using the Bandwidth services involves a series of steps to ensure the call's proper initiation and management.
## Setup

```sh
var activeCall = async phone.makeCall(`${destNumber}`, extraHeaders);
cp .env.example .env
```

Keep the `activeCall` instance global in persistant state in order to reuse this instance for call termination, hold & mute.
### Terminating a Call
Edit `.env` and populate:

```sh
activeCall.terminate();
REACT_APP_AUTH_TOKEN=<your-signum-jwt-token>
REACT_APP_ACCOUNT_USERNAME=<source-phone-number-e164>
```

This method is responsible for correctly signaling the termination of the call session. After invoking this method, it's a good practice to handle UI transitions and take any other post-call actions that may be necessary in your application's context.

### Listeners and Implementation

Listeners are pivotal in monitoring and responding to real-time events during the call.

In the provided code, the `BandwidthUA.setListeners` is used. This listener has multiple callback methods.

**Implementation**:

To use the listener, you implement it as an anonymous class and provide logic inside each method:
## Running the Application

```sh
phone.setListeners({
loginStateChanged: function (isLogin, cause) {
console.log(cause);
switch (cause) {
case 'connected':
console.log('phone>>> loginStateChanged: connected');
break;
case 'disconnected':
console.log('phone>>> loginStateChanged: disconnected');
break;
case 'login failed':
console.log('phone>>> loginStateChanged: login failed');
break;
case 'login':
console.log('phone>>> loginStateChanged: login');
break;
case 'logout':
console.log('phone>>> loginStateChanged: logout');
break;
}
},

outgoingCallProgress: function (call, response) {
updateFBStatus("Call-Initiate");
console.log('phone>>> outgoing call progress');
},

callTerminated: function (call, message, cause) {
console.log(`phone>>> call terminated callback, cause=${cause}`);
},

callConfirmed: function (call, message, cause) {
console.log('phone>>> callConfirmed');
},

callShowStreams: function (call, localStream, remoteStream) {
console.log('phone>>> callShowStreams');
let remoteVideo = document.getElementById('remote-video-container');
if (remoteVideo != undefined) {
remoteVideo.srcObject = remoteStream;
}
},

incomingCall: function (call, invite) {
console.log('phone>>> incomingCall');
},

callHoldStateChanged: function (call, isHold, isRemote) {
console.log(`phone>>> callHoldStateChanged to ${isHold ? 'hold' : 'unhold'} `);
}
});
npm install
npm start
```

### Configuring Inbound Calls

- **Overview:** We have used two major capabilities to make the inbound call

- Caller to Callee & Callback from Callee to Caller
- Bridging the both calls to connect caller and callee in a single call
The app will open at `http://localhost:3000`.

- **Sequence Diagram:** Follow sequence diagram to implement the in call using the SDK
![InboundFLow](bandwidth-inbound-react.drawio.svg)
## SDK Documentation

- **Notification Handler Service Sample:**
https://github.com/Bandwidth-Samples/in-app-calling-inbound-demo

# Running the Application

Use the following command/s to run the application:

```sh
yarn start
```
For detailed SDK usage and API documentation, refer to the [Bandwidth WebRTC SDK documentation](https://dev.bandwidth.com/sdks/webrtc/).

# Error Handling
## Error Handling

Errors, especially in networked operations, are inevitable. Ensure you catch, manage, and inform users about these, fostering a seamless experience.
Errors are logged to the console. Ensure that your environment variables are correctly set and that your account has In-App Calling enabled.
8 changes: 2 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,18 @@
"@babel/plugin-proposal-private-property-in-object": "^7.16.7"
},
"dependencies": {
"@bandwidth/bw-webrtc-sdk": "^1.1.4",
"@bandwidth/bw-webrtc-sdk": "file:../javascript-webrtc-sdk",
"@emotion/react": "^11.8.1",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.14.0",
"@mui/material": "^5.14.0",
"@mui/styles": "^5.14.0",
"@okta/okta-auth-js": "^7.4.0",
"@okta/okta-react": "^6.7.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"axios": "^1.5.0",
"firebase": "^10.12.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-query": "^3.39.2",
"react-router-dom": "^6.15.0",
"react-scripts": "^5.0.1",
"react-timer-hook": "^3.0.0"
Expand Down Expand Up @@ -59,4 +55,4 @@
"last 1 safari version"
]
}
}
}
37 changes: 12 additions & 25 deletions src/components/DialPad.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import { useStopwatch } from 'react-timer-hook';
import { Button } from '@mui/material';

export default function DialPad() {
const userId = process.env.REACT_APP_ACCOUNT_USERNAME;
const authToken = process.env.REACT_APP_AUTH_TOKEN;
const sourceNumber = userId;
const userId = process.env.REACT_APP_ACCOUNT_USERNAME;

const { totalSeconds, seconds, minutes, hours, start, pause, reset } = useStopwatch({ autoStart: false });

Expand Down Expand Up @@ -71,31 +70,20 @@ export default function DialPad() {
}, [])

useEffect(() => {
const serverConfig = {
domain: 'gw.webrtc-app.bandwidth.com',
addresses: ['wss://gw.webrtc-app.bandwidth.com:10081'],
iceServers: [
'stun.l.google.com:19302',
'stun1.l.google.com:19302',
'stun2.l.google.com:19302',
],
};
const newPhone = new BandwidthUA();
const newPhone = new BandwidthUA({
fromNumber: userId,
// Optional: skip server-side app lookup when appId is already known.
// gatewayUrl is for non-production testing only — leave unset in real deployments.
...(process.env.REACT_APP_APP_ID && { applicationId: process.env.REACT_APP_APP_ID }),
...(process.env.REACT_APP_GATEWAY_URL && { gatewayUrl: process.env.REACT_APP_GATEWAY_URL }),
});
console.log(`version: `, newPhone.version());
newPhone.setWebSocketKeepAlive(5, false, false, 5, true);
newPhone.setServerConfig(
serverConfig.addresses,
serverConfig.domain,
serverConfig.iceServers,
);

//overriding the SDK logs

newPhone.setBWLogger((...e) => {
console.log(...e);
});

newPhone.checkAvailableDevices();
newPhone.setAccount(`${sourceNumber}`, 'In-App Calling Sample', '');
newPhone.setOAuthToken(authToken);
newPhone.init();
setPhone(newPhone);
Expand All @@ -111,8 +99,8 @@ export default function DialPad() {
case 'disconnected':
console.log('phone>>> loginStateChanged: disconnected');
if (phone.isInitialized()) {
// after deinit() phone will disconnect SBC.
console.log('Cannot connect to SBC server');
// after deinit() phone will disconnect from gateway.
console.log('Cannot connect to WebRTC gateway server');
}
break;
case 'login failed':
Expand Down Expand Up @@ -267,9 +255,8 @@ export default function DialPad() {
updateFBStatus("Calling");
setCallStatus('Calling');
setWebRtcStatus('Ringing');
let extraHeaders = [`User-to-User:eyJhbGciOiJIUzI1NiJ9.WyJoaSJd.-znkjYyCkgz4djmHUPSXl9YrJ6Nix_XvmlwKGFh5ERM;encoding=jwt;aGVsbG8gd29ybGQ;encoding=base64`];
console.log("Dialed number: ", destNumber);
phone.makeCall(`${destNumber}`, extraHeaders).then((value) => {
phone.makeCall(`${destNumber}`).then((value) => {
setActiveCall(value);
});
setDialedNumber(`+${destNumber}`);
Expand Down