3

I'm using Twilio in an Angular app. I'm initializing the Twilio device whenever the user visits a certain page (let's call it the customer page) so that the user can call a specific customer. This initialize function is called in the customer controller:

function _initializeDevice(token, connectHandler, disconnectHandler) {
    console.log('CALLED INITIALIZE DEVICE');
    var device = Twilio.Device;
    device.setup(token, {debug: true});
    console.log(device);

    device.connect(connectHandler);
    device.disconnect(disconnectHandler);

    device.offline(function() {
        _getToken().then(function(result) {
            device.setup(result.data.token, {debug: true});
        });
    });

    device.error(_handleTwilioError);
}

And this is the disconnect handler that gets passed in above:

function onDisconnect() {
    console.log('SAVING CALL');
    // code to save call
}

The problem is, whenever the user navigates away from the customer page and back (without refreshing the page), the customer controller runs again, causing _initializeDevice function to also run again. Multiple connect/disconnect/etc. handlers end up getting registered to the same device, which causes things that should only be run once to run multiple times.

Here's a sample of my console logs to illustrate the issue...

So here's what happens when I first go to the customer page and call _initializeDevice the first time:

CALLED INITIALIZE DEVICE
[Device] Setting up PStream
[WSTransport] Opening socket
[WSTransport] attempting to connect
[WSTransport] Socket opened
[PStream] Setting token and publishing listen
[Device] Stream is ready
[Device] Found existing Device; using new token but ignoring options
[PStream] Setting token and publishing listen
[Twilio.PeerConnection] signalingState is "have-local-offer"
[Twilio.PeerConnection] signalingState is "stable"
[Twilio.PeerConnection] iceConnectionState is "checking"
[Twilio.PeerConnection] iceConnectionState is "connected"
[Twilio.PeerConnection] iceConnectionState is "completed"
[Connection] Disconnecting...
[Twilio.PeerConnection] iceConnectionState is "closed"
[Twilio.PeerConnection] signalingState is "closed"
SAVING CALL

Then I navigate away from the customer page and back again, without refreshing, so the controller runs the initialize code again and duplicates the handlers:

CALLED INITIALIZE DEVICE
[Device] Found existing Device; using new token but ignoring options
[PStream] Setting token and publishing listen
CALLED INITIALIZE DEVICE
[Device] Found existing Device; using new token but ignoring options
[PStream] Setting token and publishing listen
[Device] Found existing Device; using new token but ignoring options
[PStream] Setting token and publishing listen
[Twilio.PeerConnection] signalingState is "have-local-offer"
[Twilio.PeerConnection] signalingState is "stable"
[Twilio.PeerConnection] iceConnectionState is "checking"
[Twilio.PeerConnection] iceConnectionState is "connected"
[Twilio.PeerConnection] iceConnectionState is "completed"
[Connection] Received HANGUP from gateway
[Connection] Disconnecting...
[Twilio.PeerConnection] iceConnectionState is "closed"
[Twilio.PeerConnection] signalingState is "closed"
SAVING CALL
SAVING CALL
SAVING CALL

I tried usingTwilio.Device.destroy(), but the handlers are still there.

How can I check if handlers have already been attached to the Twilio device? Or, am I supposed to be attaching the event handlers somewhere else in my Angular app?

Edit: for reference, here's how I'm disconnecting calls (attached to a button):

function hangUp() {
    Twilio.Device.disconnectAll();
}
chinaowl
  • 532
  • 5
  • 15

2 Answers2

4

Twilio.Device currently does not support un-registering listeners. Seems like this was due to its singleton behavior. This may change in the future, but for now you can remove the listeners directly using the following for each event you've bound:

Twilio.Device.instance.removeListener('eventName', handlerFn);

Take care not to removeAllListeners as the Device instance is listening for some of its own events.

rrowland
  • 2,734
  • 2
  • 17
  • 32
  • Sorry I'm getting back to you a bit late, but I tried `Twilio.Device.instance.removeListener('disconnect', disconnectHandler);` (the same `disconnectHandler` from the code above) and it didn't seem to work. The handler was still called twice. – chinaowl Sep 06 '16 at 22:40
  • 1
    Never mind, I figured it out! Had to get an instance of the handler first using `Twilio.Device.instance._events.connect`. – chinaowl Sep 06 '16 at 22:48
1

If when you call Twilio.Device.destroy() your handlers are not being unregistered, you may be experiencing token expiration issues because you need to call Device.setup() with a new token after calling Device.destroy() in order to use Device again.

Another method of ensuring connections terminate would be to call Twilio.Device.disconnectAll();. Perhaps your handler function isn't actually handling the termination.

Please tell me how it goes after you check in on your token setup and provide any relevant log information that will help me see the issue more clearly.

Megan Speir
  • 3,745
  • 1
  • 15
  • 25
  • I'm going to look into the token thing and get back to you (though I don't think that's the issue, since I'm using the default timeout of one hour) but for now, I wanted to confirm that `Twilio.Device.destroy()` is indeed supposed to unregister all handlers? And is that the only way to unregister a handler? – chinaowl Aug 30 '16 at 22:26
  • It should do as the documentation says. I've added an edit, that I did not see in your code above. Is a small change, but hopefully it does the trick. Please keep me posted. – Megan Speir Aug 31 '16 at 15:52
  • The documentation doesn't say anything about unregistering handlers: "Destroys the device. Terminates active and pending connections. This will trigger the offline event handler. Device will not be able to make or receive new connections until you call Twilio.Device.setup() again." I'm asking about how I can go about unregistering/deleting the `connect` and `disconnect` handlers, or for a way to check if those handlers already exist so the controller doesn't register duplicate handlers. It looks like `destroy()` isn't what I'm looking for. – chinaowl Aug 31 '16 at 18:32
  • I'm disconnecting the call just fine (see added the code above). I don't think that's related to unregistering handlers. – chinaowl Aug 31 '16 at 18:33