@wazo/sdk 1.0.0-alpha.17 → 1.0.0-alpha.19

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.
@@ -3,7 +3,7 @@ import { Inviter, UserAgent, type URI } from 'sip.js';
3
3
 
4
4
  import softphone from '../../voice/softphone';
5
5
  import Call from '../../voice/call';
6
- import { Actions, EstablishedStates, States } from '../../state-machine/call-state-machine';
6
+ import { Actions, EstablishedStates, EstablishedSubStates, States } from '../../state-machine/call-state-machine';
7
7
  import { Actions as SoftphoneActions } from '../../state-machine/softphone-state-machine';
8
8
  import ApiCall from '../../domain/Call';
9
9
 
@@ -32,6 +32,9 @@ jest.mock('../..', () => ({
32
32
 
33
33
  jest.mock('../../web-rtc-client', () => jest.fn().mockImplementation(() => ({
34
34
  answer: jest.fn(() => Promise.resolve()),
35
+ mute: jest.fn(() => Promise.resolve()),
36
+ hold: jest.fn(() => Promise.resolve()),
37
+ unhold: jest.fn(() => Promise.resolve()),
35
38
  hangup: jest.fn(() => Promise.resolve()),
36
39
  register: jest.fn(() => Promise.resolve()),
37
40
  onCallEnded: jest.fn(() => Promise.resolve()),
@@ -76,9 +79,16 @@ describe('Call', () => {
76
79
  expect(call.id).toBe(apiCall.sipCallId);
77
80
  expect(call.apiId).toBe(apiCall.id);
78
81
  expect(call.number).toBe(apiCall.calleeNumber);
79
- expect(call.answerTime).toStrictEqual(apiCall.startingTime);
82
+ expect(call.answerTime).toStrictEqual(undefined);
83
+ expect(call.creationTime).toStrictEqual(apiCall.startingTime);
80
84
  expect(call.isMuted()).toBeTruthy();
81
- expect(call.state).toStrictEqual({ [States.ESTABLISHED]: EstablishedStates.MUTED });
85
+ expect(call.state).toStrictEqual({
86
+ [States.ESTABLISHED]: {
87
+ [EstablishedStates.ONGOING]: {},
88
+ [EstablishedSubStates.MUTE]: EstablishedStates.MUTED,
89
+ [EstablishedSubStates.HOLD]: EstablishedStates.UN_HELD,
90
+ },
91
+ });
82
92
  expect(call.isEstablished()).toBeTruthy();
83
93
  });
84
94
  });
@@ -141,11 +151,43 @@ describe('Call', () => {
141
151
  call._sendAction(Actions.MAKE_CALL);
142
152
 
143
153
  call.on('remotelyAccepted', () => {
144
- expect(call.state).toStrictEqual({ [States.ESTABLISHED]: EstablishedStates.ONGOING });
154
+ expect(call.state).toStrictEqual({
155
+ [States.ESTABLISHED]: {
156
+ [EstablishedStates.ONGOING]: {},
157
+ [EstablishedSubStates.MUTE]: EstablishedStates.UN_MUTED,
158
+ [EstablishedSubStates.HOLD]: EstablishedStates.UN_HELD,
159
+ },
160
+ });
145
161
  done();
146
162
  });
147
163
 
148
164
  call.onAccepted();
149
165
  });
150
166
  });
167
+
168
+ describe('Multiple sub state', () => {
169
+ it('Should mute and hold a call', () => {
170
+ const call = new Call(sipCall, softphone);
171
+ // @ts-ignore: private method
172
+ call._sendAction(Actions.MAKE_CALL);
173
+
174
+ expect(call.isMuted()).toBeFalsy();
175
+ expect(call.isHeld()).toBeFalsy();
176
+
177
+ // @ts-ignore: private method
178
+ call._sendAction(Actions.REMOTLY_ACCEPTED);
179
+
180
+ call.mute();
181
+ expect(call.isMuted()).toBeTruthy();
182
+ expect(call.isHeld()).toBeFalsy();
183
+
184
+ call.hold();
185
+ expect(call.isMuted()).toBeTruthy();
186
+ expect(call.isHeld()).toBeTruthy();
187
+
188
+ call.resume();
189
+ expect(call.isMuted()).toBeTruthy();
190
+ expect(call.isHeld()).toBeFalsy();
191
+ });
192
+ });
151
193
  });
@@ -15,11 +15,19 @@ export const EstablishedStates = {
15
15
  ONGOING: 'ongoing',
16
16
  HELD: 'held',
17
17
  MUTED: 'muted',
18
+ UN_MUTED: 'unMuted',
19
+ UN_HELD: 'unHeld',
20
+ } as const;
21
+
22
+ export const EstablishedSubStates = {
23
+ MUTE: 'muteState',
24
+ HOLD: 'holdState',
18
25
  } as const;
19
26
 
20
27
  export type StateTypes = typeof States[keyof typeof States];
21
28
 
22
29
  export type EstablishedStateTypes = typeof EstablishedStates[keyof typeof EstablishedStates];
30
+ export type EstablishedSubStateTypes = typeof EstablishedSubStates[keyof typeof EstablishedSubStates];
23
31
 
24
32
  export const Actions = {
25
33
  INCOMING_CALL: 'incomingCall',
@@ -88,35 +96,46 @@ const callMachine = setup({
88
96
  },
89
97
  [States.ESTABLISHED]: {
90
98
  initial: EstablishedStates.ONGOING,
99
+ type: 'parallel',
91
100
  on: {
92
101
  [Actions.HANGUP]: {
93
102
  guard: 'isRegistered',
94
103
  target: States.TERMINATED,
95
104
  },
96
- [EstablishedActions.HOLD]: {
97
- guard: 'isRegistered',
98
- target: `.${EstablishedStates.HELD}`,
99
- },
100
- [EstablishedActions.MUTE]: {
101
- guard: 'isRegistered',
102
- target: `.${EstablishedStates.MUTED}`,
103
- },
104
105
  },
105
106
  states: {
106
107
  [EstablishedStates.ONGOING]: { },
107
- [EstablishedStates.HELD]: {
108
- on: {
109
- [EstablishedActions.RESUME]: {
110
- guard: 'isRegistered',
111
- target: EstablishedStates.ONGOING,
108
+
109
+ // Mute / Unmute
110
+ [EstablishedSubStates.MUTE]: {
111
+ initial: EstablishedStates.UN_MUTED,
112
+ states: {
113
+ [EstablishedStates.MUTED]: {
114
+ on: {
115
+ unMute: EstablishedStates.UN_MUTED,
116
+ },
117
+ },
118
+ [EstablishedStates.UN_MUTED]: {
119
+ on: {
120
+ mute: EstablishedStates.MUTED,
121
+ },
112
122
  },
113
123
  },
114
124
  },
115
- [EstablishedStates.MUTED]: {
116
- on: {
117
- [EstablishedActions.UN_MUTE]: {
118
- guard: 'isRegistered',
119
- target: EstablishedStates.ONGOING,
125
+
126
+ // Hold / Resume
127
+ [EstablishedSubStates.HOLD]: {
128
+ initial: EstablishedStates.UN_HELD,
129
+ states: {
130
+ [EstablishedStates.HELD]: {
131
+ on: {
132
+ [EstablishedActions.RESUME]: EstablishedStates.UN_HELD,
133
+ },
134
+ },
135
+ [EstablishedStates.UN_HELD]: {
136
+ on: {
137
+ [EstablishedActions.HOLD]: EstablishedStates.HELD,
138
+ },
120
139
  },
121
140
  },
122
141
  },
@@ -253,6 +253,9 @@ export const toggleVideo = (sipCall: SipCall, muteCamera: boolean): void => {
253
253
  export const isVideoMuted = (sipCall: SipCall): boolean => {
254
254
  const sdh = sipCall.sessionDescriptionHandler as SessionDescriptionHandler;
255
255
  const pc = sdh?.peerConnection as PeerConnection;
256
+ if (!pc) {
257
+ return true;
258
+ }
256
259
 
257
260
  if (pc?.getSenders) {
258
261
  for (const sender of pc.getSenders()) {
package/src/voice/call.ts CHANGED
@@ -9,7 +9,7 @@ import { SessionDescriptionHandler } from 'sip.js/lib/platform/web';
9
9
  import { SessionDescriptionHandlerOptions } from 'sip.js/lib/api';
10
10
  import type { IncomingRequestMessage } from 'sip.js/lib/core/messages/incoming-request-message';
11
11
 
12
- import callStateMachine, { type ActionTypes, Actions, type EstablishedActionTypes, EstablishedActions, States, type StateTypes, EstablishedStates, type EstablishedStateTypes, type CallActorRef } from '../state-machine/call-state-machine';
12
+ import callStateMachine, { type ActionTypes, Actions, type EstablishedActionTypes, EstablishedActions, States, type StateTypes, EstablishedStates, type EstablishedStateTypes, type CallActorRef, EstablishedSubStateTypes } from '../state-machine/call-state-machine';
13
13
  import { SipCall, PeerConnection } from '../domain/types';
14
14
  import ApiCall from '../domain/Call';
15
15
  import newFrom from '../utils/new-from';
@@ -129,11 +129,23 @@ class Call extends EventEmitter {
129
129
  const snapshot = call.callActor.getSnapshot();
130
130
 
131
131
  if (apiCall.isUp()) {
132
- snapshot.value = { [States.ESTABLISHED]: EstablishedStates.ONGOING };
132
+ snapshot.value = {
133
+ [States.ESTABLISHED]: {
134
+ [EstablishedStates.ONGOING]: {},
135
+ muteState: EstablishedStates.UN_MUTED,
136
+ holdState: EstablishedStates.UN_HELD,
137
+ },
138
+ };
133
139
  }
134
140
 
135
141
  if (apiCall.muted) {
136
- snapshot.value = { [States.ESTABLISHED]: EstablishedStates.MUTED };
142
+ snapshot.value = {
143
+ [States.ESTABLISHED]: {
144
+ [EstablishedStates.ONGOING]: {},
145
+ muteState: EstablishedStates.MUTED,
146
+ holdState: EstablishedStates.UN_HELD,
147
+ },
148
+ };
137
149
  }
138
150
 
139
151
  if (apiCall.isRinging()) {
@@ -345,7 +357,7 @@ class Call extends EventEmitter {
345
357
  }
346
358
 
347
359
  isMuted() {
348
- return this._hasEstablishedState(EstablishedStates.MUTED);
360
+ return this._hasEstablishedState('muteState', EstablishedStates.MUTED);
349
361
  }
350
362
 
351
363
  isCameraMuted() {
@@ -353,7 +365,7 @@ class Call extends EventEmitter {
353
365
  }
354
366
 
355
367
  isHeld() {
356
- return this._hasEstablishedState(EstablishedStates.HELD);
368
+ return this._hasEstablishedState('holdState', EstablishedStates.HELD);
357
369
  }
358
370
 
359
371
  isEstablishing() {
@@ -662,9 +674,17 @@ class Call extends EventEmitter {
662
674
  return getState(this.callActor) as StateTypes | Record<StateTypes, EstablishedStateTypes>;
663
675
  }
664
676
 
665
- private _hasEstablishedState(establishedState: EstablishedStateTypes) {
666
- const state = this.state as Record<StateTypes, EstablishedStateTypes>;
667
- return typeof state === 'object' && States.ESTABLISHED in state && state[States.ESTABLISHED] === establishedState;
677
+ private _hasEstablishedState(subState: EstablishedSubStateTypes, establishedState: EstablishedStateTypes) {
678
+ const state = this.state as Record<StateTypes, EstablishedStateTypes | Record<EstablishedSubStateTypes, EstablishedStateTypes>>;
679
+ if (typeof state !== 'object') {
680
+ return false;
681
+ }
682
+
683
+ if (!(States.ESTABLISHED in state)) {
684
+ return false;
685
+ }
686
+
687
+ return (state[States.ESTABLISHED] as Record<EstablishedSubStateTypes, EstablishedStateTypes>)[subState] === establishedState;
668
688
  }
669
689
 
670
690
  private _bindEvents() {