@telegenta/webclient 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -245,6 +245,9 @@
245
245
 
246
246
  // Enable call initialized tone
247
247
  playCallInitializedTone: true,
248
+
249
+ // Enable call ended tone
250
+ playCallEndedTone: true,
248
251
  };
249
252
 
250
253
  try {
package/dist/telegenta.js CHANGED
@@ -1567,6 +1567,7 @@ const options = {
1567
1567
  autoRemoveSessionTime: 3000,
1568
1568
  playRingOnIncoming: true,
1569
1569
  playCallInitializedTone: false,
1570
+ playCallEndedTone: false,
1570
1571
  };
1571
1572
 
1572
1573
  // DTMF Frequency table
@@ -1603,6 +1604,11 @@ const ST_CALLINIT = [
1603
1604
  { d: 200, f1: 500, f2: 0, g: 0 },
1604
1605
  { d: 200, f1: 700, f2: 0, g: 5000 },
1605
1606
  ];
1607
+ const ST_CALLENDED = [
1608
+ { d: 150, f1: 600, f2: 0, g: 50 },
1609
+ { d: 150, f1: 500, f2: 0, g: 50 },
1610
+ { d: 150, f1: 400, f2: 0, g: 0 },
1611
+ ];
1606
1612
  const ST_RINGBACK = [{ d: 1000, f1: 425, f2: 0, g: 4000 }];
1607
1613
  const ST_INCOMING = [
1608
1614
  { d: 150, f1: 400, f2: 0, g: 0 },
@@ -1783,6 +1789,7 @@ class Phone extends EventEmitter {
1783
1789
  * @param {number} [phoneOptions.autoRemoveSessionTime=3000] Automatically remove session after interval specified
1784
1790
  * @param {number} [phoneOptions.playRingOnIncoming=true] Play ringing tones on incoming calls
1785
1791
  * @param {number} [phoneOptions.playCallInitializedTone=false] Play tone when call has been initiated (before progress/ringback tones)
1792
+ * @param {boolean} [phoneOptions.playCallEndedTone=false] Play tone once when an answered call ends without the agent hanging up (remote hangup, server hangup, connection loss, timeouts)
1786
1793
  *
1787
1794
  * @throws MediaNotPrepared - If {@link prepareMedia} was not called and resolved prior to calling this method
1788
1795
  * @throws InvalidOptions - If one or more options are invalid
@@ -2218,14 +2225,18 @@ class Phone extends EventEmitter {
2218
2225
 
2219
2226
  // Set timer to stop tone again
2220
2227
  const self = this;
2221
- this._toneTimer = setTimeout(function () {
2222
- self.stopTone();
2228
+ const timer = setTimeout(function () {
2229
+ // A later playTone() has taken over the oscillators; don't silence it or cancel its timer
2230
+ if (self._toneTimer === timer) {
2231
+ self.stopTone();
2232
+ }
2223
2233
 
2224
2234
  // Handle callback now
2225
2235
  if (callback) {
2226
2236
  callback();
2227
2237
  }
2228
2238
  }, duration);
2239
+ this._toneTimer = timer;
2229
2240
  }
2230
2241
 
2231
2242
  /**
@@ -2268,6 +2279,18 @@ class Phone extends EventEmitter {
2268
2279
  }
2269
2280
  }
2270
2281
 
2282
+ // Play a tone sequence once. A repeat tone starting meanwhile (e.g. a new call's ringing) cancels the rest.
2283
+ _playToneSequence(sequence, pos = 0) {
2284
+ const toneDef = sequence[pos];
2285
+ if (!toneDef || this._repeatToneActive) {
2286
+ return;
2287
+ }
2288
+ const self = this;
2289
+ this.playTone(toneDef.d, toneDef.f1, toneDef.f2, function () {
2290
+ setTimeout(self._playToneSequence.bind(self, sequence, pos + 1), toneDef.g);
2291
+ });
2292
+ }
2293
+
2271
2294
  // End repeating tones
2272
2295
  _endRepeatTone() {
2273
2296
  if (this._repeatToneActive) {
@@ -2597,6 +2620,7 @@ class Session extends EventEmitter {
2597
2620
  this._isListenCall = false;
2598
2621
  this._whisperMode = null;
2599
2622
  this._manualDisposition = null;
2623
+ this._transferInitiated = false;
2600
2624
 
2601
2625
  // Quality report
2602
2626
  this._rtcpStatsTimer = null;
@@ -2863,10 +2887,17 @@ class Session extends EventEmitter {
2863
2887
  self._setSessionState('ACTIVE');
2864
2888
  });
2865
2889
 
2866
- this._webRTCSession.on('ended', function (/* data */) {
2890
+ this._webRTCSession.on('ended', function (data) {
2867
2891
  // End any ringsbacks if needed
2868
2892
  self._phone._endRepeatTone();
2869
2893
 
2894
+ // jssip fires 'ended' once a call is answered. Its own errors (connection loss, timeouts, ICE failure)
2895
+ // also end as 'local' but with their own cause, so only a local BYE is the agent hanging up.
2896
+ const agentHangup = data.originator === 'local' && data.cause === JsSIP.C.causes.BYE;
2897
+ if (self._phone.options.playCallEndedTone && !agentHangup && !self._transferInitiated) {
2898
+ self._phone._playToneSequence(ST_CALLENDED);
2899
+ }
2900
+
2870
2901
  self._callEndTime = Date.now();
2871
2902
 
2872
2903
  // Clear answer call timeout timer if needed
@@ -3308,6 +3339,10 @@ class Session extends EventEmitter {
3308
3339
  transfer(dstSession) {
3309
3340
  this.log.info(`Transferring ${this.uuid} => ${dstSession.uuid}`);
3310
3341
 
3342
+ // The server hangs up both legs once the transfer completes - that's the agent's doing, no call-ended tone
3343
+ this._transferInitiated = true;
3344
+ dstSession._transferInitiated = true;
3345
+
3311
3346
  // Create ReferSubscriber event class
3312
3347
  const referSubscriber = this._webRTCSession.refer(this._webRTCSession._request.ruri, {
3313
3348
  replaces: dstSession._webRTCSession,
@@ -3322,10 +3357,15 @@ class Session extends EventEmitter {
3322
3357
 
3323
3358
  referSubscriber.on('requestFailed', function (cause) {
3324
3359
  self.log.error(`Transfer rejected: ${cause}`);
3360
+ // Legs stay up, so a later hangup by the other party should get the cue
3361
+ self._transferInitiated = false;
3362
+ dstSession._transferInitiated = false;
3325
3363
  });
3326
3364
 
3327
3365
  referSubscriber.on('failed', function (cause) {
3328
3366
  self.log.error(`Transfer failed: ${cause}`);
3367
+ self._transferInitiated = false;
3368
+ dstSession._transferInitiated = false;
3329
3369
  });
3330
3370
  }
3331
3371