diff --git a/.env b/.env deleted file mode 100644 index 7895789..0000000 --- a/.env +++ /dev/null @@ -1,4 +0,0 @@ -REACT_APP_ACCOUNT_USERNAME=+191xxxxxxx -REACT_APP_ACCOUNT_DISPLAY_NAME=+1919xxxxxx -REACT_APP_ACCOUNT_PASSWORD=+1919xxxxxxx -REACT_APP_AUTH_TOKEN=xxxxxxxx \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..eb91fcd --- /dev/null +++ b/.env.example @@ -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= diff --git a/.gitignore b/.gitignore index d2e8545..ec7d8e9 100644 --- a/.gitignore +++ b/.gitignore @@ -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* diff --git a/README.md b/README.md index 91dfe17..6dc63aa 100644 --- a/README.md +++ b/README.md @@ -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= +REACT_APP_ACCOUNT_USERNAME= ``` -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. diff --git a/package.json b/package.json index 44f9250..2b123db 100644 --- a/package.json +++ b/package.json @@ -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" @@ -59,4 +55,4 @@ "last 1 safari version" ] } -} \ No newline at end of file +} diff --git a/src/components/DialPad.js b/src/components/DialPad.js index 7d32f6d..e4bb6ff 100644 --- a/src/components/DialPad.js +++ b/src/components/DialPad.js @@ -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 }); @@ -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); @@ -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': @@ -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}`);