@zyratalk1/zyra-twilio-wrapper 1.2.0 → 1.2.1

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.
package/.eslint.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "env": {
3
+ "node": true,
4
+ "es2021": true
5
+ },
6
+ "extends": "eslint:recommended",
7
+ "parserOptions": {
8
+ "ecmaVersion": "latest",
9
+ "sourceType": "commonjs"
10
+ },
11
+ "rules": {
12
+ "semi": ["error", "always"],
13
+ "quotes": ["error", "double"]
14
+ }
15
+ }
package/dist/index.d.mts CHANGED
@@ -4,6 +4,7 @@ interface Config {
4
4
  waitUrl?: string;
5
5
  sdkToken: string;
6
6
  accessToken?: string;
7
+ enableAndroidVoiceRegister?: boolean;
7
8
  requestTimeoutMs?: number;
8
9
  retry?: {
9
10
  maxRetries?: number;
@@ -287,14 +288,6 @@ interface NativeModule {
287
288
  };
288
289
  }
289
290
  type SDKEventName = "onReady" | "onCallRinging" | "onCallConnected" | "onParticipantJoined" | "onParticipantLeft" | "onCallEnded" | "onCallStateChanged" | "onAuthStateChanged" | "onMissedCall" | "onError";
290
- /**
291
- * Options for {@link ReactNativeVoipSdk.makeCall}.
292
- * - `parallel`: start a second Twilio `voice.connect` leg while another call is active (independent lifecycle).
293
- * - Omitted / false: if a call is already active, add a conference participant via `addNewCallee` (existing behavior).
294
- */
295
- interface MakeCallOptions {
296
- parallel?: boolean;
297
- }
298
291
 
299
292
  type EventPayload = Record<string, unknown>;
300
293
  type Listener = (payload: EventPayload) => void;
@@ -327,9 +320,6 @@ declare class ReactNativeVoipSdk {
327
320
  private readonly configService;
328
321
  private initialized;
329
322
  protected activeCallId: string | null;
330
- /** Tracks every in-flight call leg (parallel or single); kept in sync with native `onCallEnded`. */
331
- protected activeCallIds: Set<string>;
332
- private unsubscribeOnCallEnded;
333
323
  protected internalConferenceName: string;
334
324
  constructor(config: Config, nativeModule?: NativeModule);
335
325
  on(eventName: SDKEventName | string, listener: (payload: Record<string, unknown>) => void): () => void;
@@ -345,23 +335,14 @@ declare class ReactNativeVoipSdk {
345
335
  disconnectParticipant(participantId: string): Promise<void>;
346
336
  init(): Promise<void>;
347
337
  verifySDKToken(): Promise<boolean>;
348
- /**
349
- * Start a call or add to a conference.
350
- * Use `makeCall(to, { parallel: true })` (or `addCall(to)`) while a call is active to open a second
351
- * independent leg; failures on that leg are isolated to its `callId` in `onError` / `onCallEnded`.
352
- */
353
- makeCall(to: string, options?: MakeCallOptions): Promise<CallResponse<CallSession>>;
354
- /** Start a parallel outbound call while another call is active (alias for `makeCall(to, { parallel: true })`). */
355
- addCall(to: string): Promise<CallResponse<CallSession>>;
356
- /** All active call legs (parallel-aware). */
357
- getActiveCallIds(): string[];
338
+ makeCall(to: string): Promise<CallResponse<CallSession>>;
358
339
  acceptCall(callId?: string): Promise<CallResponse<null>>;
359
340
  rejectCall(callId: string): Promise<CallResponse<null>>;
360
341
  hangup(callId?: string): Promise<CallResponse<null>>;
361
342
  hold(callId: string): Promise<CallResponse<null>>;
362
343
  resume(callId: string): Promise<CallResponse<null>>;
363
344
  merge(callId: string): Promise<CallResponse<CallSession>>;
364
- toggleMute(mute: boolean, callId?: string): Promise<CallResponse<null>>;
345
+ toggleMute(mute: boolean): Promise<CallResponse<null>>;
365
346
  makeCallIncoming(to: string, callSid: string): Promise<CallResponse<CallSession>>;
366
347
  holdAndAddParticipant(callSid: string, number: string): Promise<CallResponse<unknown>>;
367
348
  holdAndAddParticipantIncoming(callSid: string, number: string): Promise<CallResponse<unknown>>;
@@ -380,7 +361,6 @@ declare class ReactNativeVoipSdk {
380
361
  isWebhookConfigured(): Promise<CallResponse<WebhookConfigResult>>;
381
362
  welcomeMessageConfig(welcomeType: "audio" | "tts", welcomeMessage: string): Promise<CallResponse<WelcomeConfigResult>>;
382
363
  destroy(): void;
383
- private pickNextActiveCallId;
384
364
  private ensureAuthenticated;
385
365
  }
386
366
 
@@ -404,4 +384,4 @@ declare class ZyraTwilioWrapper extends ReactNativeVoipSdk {
404
384
  destroy(): void;
405
385
  }
406
386
 
407
- export { type AuthPayload, type CallSession, type ConsumerAuth, type MakeCallOptions, type NativeModule, type Participant, ReactNativeVoipSdk, type SDKConfig, type SDKEventName, ZyraTwilioWrapper as default };
387
+ export { type AuthPayload, type CallSession, type ConsumerAuth, type NativeModule, type Participant, ReactNativeVoipSdk, type SDKConfig, type SDKEventName, ZyraTwilioWrapper as default };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ interface Config {
4
4
  waitUrl?: string;
5
5
  sdkToken: string;
6
6
  accessToken?: string;
7
+ enableAndroidVoiceRegister?: boolean;
7
8
  requestTimeoutMs?: number;
8
9
  retry?: {
9
10
  maxRetries?: number;
@@ -287,14 +288,6 @@ interface NativeModule {
287
288
  };
288
289
  }
289
290
  type SDKEventName = "onReady" | "onCallRinging" | "onCallConnected" | "onParticipantJoined" | "onParticipantLeft" | "onCallEnded" | "onCallStateChanged" | "onAuthStateChanged" | "onMissedCall" | "onError";
290
- /**
291
- * Options for {@link ReactNativeVoipSdk.makeCall}.
292
- * - `parallel`: start a second Twilio `voice.connect` leg while another call is active (independent lifecycle).
293
- * - Omitted / false: if a call is already active, add a conference participant via `addNewCallee` (existing behavior).
294
- */
295
- interface MakeCallOptions {
296
- parallel?: boolean;
297
- }
298
291
 
299
292
  type EventPayload = Record<string, unknown>;
300
293
  type Listener = (payload: EventPayload) => void;
@@ -327,9 +320,6 @@ declare class ReactNativeVoipSdk {
327
320
  private readonly configService;
328
321
  private initialized;
329
322
  protected activeCallId: string | null;
330
- /** Tracks every in-flight call leg (parallel or single); kept in sync with native `onCallEnded`. */
331
- protected activeCallIds: Set<string>;
332
- private unsubscribeOnCallEnded;
333
323
  protected internalConferenceName: string;
334
324
  constructor(config: Config, nativeModule?: NativeModule);
335
325
  on(eventName: SDKEventName | string, listener: (payload: Record<string, unknown>) => void): () => void;
@@ -345,23 +335,14 @@ declare class ReactNativeVoipSdk {
345
335
  disconnectParticipant(participantId: string): Promise<void>;
346
336
  init(): Promise<void>;
347
337
  verifySDKToken(): Promise<boolean>;
348
- /**
349
- * Start a call or add to a conference.
350
- * Use `makeCall(to, { parallel: true })` (or `addCall(to)`) while a call is active to open a second
351
- * independent leg; failures on that leg are isolated to its `callId` in `onError` / `onCallEnded`.
352
- */
353
- makeCall(to: string, options?: MakeCallOptions): Promise<CallResponse<CallSession>>;
354
- /** Start a parallel outbound call while another call is active (alias for `makeCall(to, { parallel: true })`). */
355
- addCall(to: string): Promise<CallResponse<CallSession>>;
356
- /** All active call legs (parallel-aware). */
357
- getActiveCallIds(): string[];
338
+ makeCall(to: string): Promise<CallResponse<CallSession>>;
358
339
  acceptCall(callId?: string): Promise<CallResponse<null>>;
359
340
  rejectCall(callId: string): Promise<CallResponse<null>>;
360
341
  hangup(callId?: string): Promise<CallResponse<null>>;
361
342
  hold(callId: string): Promise<CallResponse<null>>;
362
343
  resume(callId: string): Promise<CallResponse<null>>;
363
344
  merge(callId: string): Promise<CallResponse<CallSession>>;
364
- toggleMute(mute: boolean, callId?: string): Promise<CallResponse<null>>;
345
+ toggleMute(mute: boolean): Promise<CallResponse<null>>;
365
346
  makeCallIncoming(to: string, callSid: string): Promise<CallResponse<CallSession>>;
366
347
  holdAndAddParticipant(callSid: string, number: string): Promise<CallResponse<unknown>>;
367
348
  holdAndAddParticipantIncoming(callSid: string, number: string): Promise<CallResponse<unknown>>;
@@ -380,7 +361,6 @@ declare class ReactNativeVoipSdk {
380
361
  isWebhookConfigured(): Promise<CallResponse<WebhookConfigResult>>;
381
362
  welcomeMessageConfig(welcomeType: "audio" | "tts", welcomeMessage: string): Promise<CallResponse<WelcomeConfigResult>>;
382
363
  destroy(): void;
383
- private pickNextActiveCallId;
384
364
  private ensureAuthenticated;
385
365
  }
386
366
 
@@ -404,4 +384,4 @@ declare class ZyraTwilioWrapper extends ReactNativeVoipSdk {
404
384
  destroy(): void;
405
385
  }
406
386
 
407
- export { type AuthPayload, type CallSession, type ConsumerAuth, type MakeCallOptions, type NativeModule, type Participant, ReactNativeVoipSdk, type SDKConfig, type SDKEventName, ZyraTwilioWrapper as default };
387
+ export { type AuthPayload, type CallSession, type ConsumerAuth, type NativeModule, type Participant, ReactNativeVoipSdk, type SDKConfig, type SDKEventName, ZyraTwilioWrapper as default };
package/dist/index.js CHANGED
@@ -209,15 +209,14 @@ var TwilioVoiceAdapter = class {
209
209
  error: "Twilio access token missing. Pass accessToken in constructor or authenticate({ accessToken })."
210
210
  };
211
211
  }
212
- const conferenceName = `conf_${this.sdkConfig.consumerId}_${input.callId}`;
213
212
  console.log("[VOIP SDK] voice.connect() params", {
214
213
  To: input.number,
215
- conferenceName
214
+ conferenceName: this.defaultConferenceName
216
215
  });
217
216
  const call = await this.voice.connect(token, {
218
217
  params: {
219
218
  To: input.number,
220
- conferenceName
219
+ conferenceName: this.defaultConferenceName
221
220
  }
222
221
  });
223
222
  const callSid = this.readCallSid(call);
@@ -226,7 +225,7 @@ var TwilioVoiceAdapter = class {
226
225
  callSid,
227
226
  call,
228
227
  direction: "OUTBOUND",
229
- conferenceName,
228
+ conferenceName: this.defaultConferenceName,
230
229
  conferenceDetail: null
231
230
  };
232
231
  this.calls.set(input.callId, tracked);
@@ -268,10 +267,26 @@ var TwilioVoiceAdapter = class {
268
267
  };
269
268
  }
270
269
  }
270
+ async waitForInvite(callId, timeoutMs = 8e3) {
271
+ const existing = this.callInvites.get(callId);
272
+ if (existing) return existing;
273
+ console.log("[VOIP DEBUG] waitForInvite polling\u2026", { callId, timeoutMs });
274
+ const start = Date.now();
275
+ while (Date.now() - start < timeoutMs) {
276
+ await new Promise((r) => setTimeout(r, 300));
277
+ const inv = this.callInvites.get(callId);
278
+ if (inv) {
279
+ console.log("[VOIP DEBUG] waitForInvite found invite", { callId, elapsed: Date.now() - start });
280
+ return inv;
281
+ }
282
+ }
283
+ console.warn("[VOIP DEBUG] waitForInvite timed out", { callId });
284
+ return null;
285
+ }
271
286
  async answerCall(input) {
272
287
  console.log("[VOIP DEBUG] call accepted flow start", { callId: input.callId });
273
288
  try {
274
- const invite = this.callInvites.get(input.callId);
289
+ const invite = await this.waitForInvite(input.callId);
275
290
  if (!invite) {
276
291
  return { success: false, error: "Incoming call invite not found" };
277
292
  }
@@ -1177,6 +1192,54 @@ async function setRoutingConfig(deps, input) {
1177
1192
  }
1178
1193
  }
1179
1194
 
1195
+ // src/twilioConfig.ts
1196
+ async function setTwilioVoiceConfig(deps) {
1197
+ try {
1198
+ const res = await deps.axiosInstance.post(
1199
+ `${deps.serverUrl}/voipSdk.setTwilioVoiceConfig`,
1200
+ {},
1201
+ {
1202
+ headers: {
1203
+ Authorization: `Bearer ${deps.sdkToken}`
1204
+ }
1205
+ }
1206
+ );
1207
+ const result = res?.data?.result ?? res?.data;
1208
+ return createSuccessResult(
1209
+ result?.message || "Twilio voice config updated",
1210
+ result
1211
+ );
1212
+ } catch (error) {
1213
+ return createErrorResult(
1214
+ error instanceof Error ? error : "Failed to set Twilio voice config"
1215
+ );
1216
+ }
1217
+ }
1218
+ async function getVoipProviderConfigStatus(deps) {
1219
+ try {
1220
+ const res = await deps.axiosInstance.post(
1221
+ `${deps.serverUrl}/voipSdk.getVoipProviderConfigStatus`,
1222
+ {},
1223
+ {
1224
+ headers: {
1225
+ Authorization: `Bearer ${deps.sdkToken}`
1226
+ }
1227
+ }
1228
+ );
1229
+ const data = res?.data?.result ?? res?.data;
1230
+ const raw = data?.data?.isConfiguredVoipProvider;
1231
+ const isConfiguredVoipProvider = raw === true || raw === 1 || raw === "1";
1232
+ return createSuccessResult(
1233
+ "VoIP provider config status fetched",
1234
+ { isConfiguredVoipProvider }
1235
+ );
1236
+ } catch (error) {
1237
+ return createErrorResult(
1238
+ error instanceof Error ? error : "Failed to fetch VoIP provider config status"
1239
+ );
1240
+ }
1241
+ }
1242
+
1180
1243
  // src/webhook.ts
1181
1244
  function validateWebhookUrl(webhookUrl) {
1182
1245
  const trimmed = (webhookUrl ?? "").trim();
@@ -1348,6 +1411,12 @@ var ConfigService = class {
1348
1411
  async welcomeMessageConfig(welcomeType, welcomeMessage) {
1349
1412
  return welcomeMessageConfig(this.withDeps(), { welcomeType, welcomeMessage });
1350
1413
  }
1414
+ async getVoipProviderConfigStatus() {
1415
+ return getVoipProviderConfigStatus(this.withDeps());
1416
+ }
1417
+ async setTwilioVoiceConfig() {
1418
+ return setTwilioVoiceConfig(this.withDeps());
1419
+ }
1351
1420
  withDeps() {
1352
1421
  return {
1353
1422
  axiosInstance: this.axiosInstance,
@@ -1505,9 +1574,6 @@ var ReactNativeVoipSdk = class {
1505
1574
  constructor(config, nativeModule) {
1506
1575
  this.initialized = false;
1507
1576
  this.activeCallId = null;
1508
- /** Tracks every in-flight call leg (parallel or single); kept in sync with native `onCallEnded`. */
1509
- this.activeCallIds = /* @__PURE__ */ new Set();
1510
- this.unsubscribeOnCallEnded = null;
1511
1577
  logVoipDebug("constructor() invoked", {
1512
1578
  hasServerUrl: Boolean(config.serverUrl),
1513
1579
  hasIdentity: Boolean(config.identity),
@@ -1521,7 +1587,8 @@ var ReactNativeVoipSdk = class {
1521
1587
  identity: config.identity,
1522
1588
  sdkToken: config.sdkToken,
1523
1589
  accessToken: config.accessToken,
1524
- waitUrl: config.waitUrl
1590
+ waitUrl: config.waitUrl,
1591
+ enableAndroidVoiceRegister: config.enableAndroidVoiceRegister
1525
1592
  };
1526
1593
  this.bridge = new BridgeClient(nativeModule);
1527
1594
  this.retry = new RetryPolicy(config.retry);
@@ -1551,14 +1618,6 @@ var ReactNativeVoipSdk = class {
1551
1618
  sdkToken: this.sdkConfig.sdkToken,
1552
1619
  ensureAuthenticated: this.ensureAuthenticated.bind(this)
1553
1620
  });
1554
- this.unsubscribeOnCallEnded = this.bridge.on("onCallEnded", (payload) => {
1555
- const callId = typeof payload.callId === "string" ? payload.callId : null;
1556
- if (!callId) return;
1557
- this.activeCallIds.delete(callId);
1558
- if (this.activeCallId === callId) {
1559
- this.activeCallId = this.pickNextActiveCallId();
1560
- }
1561
- });
1562
1621
  }
1563
1622
  on(eventName, listener) {
1564
1623
  return this.bridge.on(eventName, listener);
@@ -1566,16 +1625,37 @@ var ReactNativeVoipSdk = class {
1566
1625
  async initializeSDK(config) {
1567
1626
  logVoipDebug("initializeSDK() called");
1568
1627
  if (this.initialized) return;
1569
- const mergedConfig = { ...this.sdkConfig, ...config };
1570
- const result = await this.bridge.nativeModule.initializeSDK(mergedConfig);
1571
- if (!result.success) {
1572
- logVoipDebug("initializeSDK() failed", {
1573
- error: result.error ?? "Failed to initialize native VOIP SDK"
1628
+ try {
1629
+ const configStatus = await this.configService.getVoipProviderConfigStatus();
1630
+ if (!configStatus.success) {
1631
+ throw new Error("Failed to fetch VoIP provider config status");
1632
+ }
1633
+ const isConfigured = configStatus.data?.isConfiguredVoipProvider;
1634
+ logVoipDebug("VoIP config status checked", { isConfigured });
1635
+ if (!isConfigured) {
1636
+ logVoipDebug("VoIP not configured. Setting Twilio config");
1637
+ const setupRes = await this.configService.setTwilioVoiceConfig();
1638
+ if (!setupRes.success) {
1639
+ throw new Error("Failed to set Twilio Voice Config");
1640
+ }
1641
+ logVoipDebug("VoIP configuration completed");
1642
+ }
1643
+ const mergedConfig = { ...this.sdkConfig, ...config };
1644
+ const result = await this.bridge.nativeModule.initializeSDK(mergedConfig);
1645
+ if (!result.success) {
1646
+ logVoipDebug("initializeSDK() failed", {
1647
+ error: result.error ?? "Failed to initialize native VOIP SDK"
1648
+ });
1649
+ throw new Error(result.error ?? "Failed to initialize native VOIP SDK");
1650
+ }
1651
+ this.initialized = true;
1652
+ logVoipDebug("initializeSDK() success");
1653
+ } catch (error) {
1654
+ logVoipDebug("initializeSDK() config check failure", {
1655
+ message: error instanceof Error ? error.message : String(error)
1574
1656
  });
1575
- throw new Error(result.error ?? "Failed to initialize native VOIP SDK");
1657
+ throw error;
1576
1658
  }
1577
- this.initialized = true;
1578
- logVoipDebug("initializeSDK() success");
1579
1659
  }
1580
1660
  async authenticate(payload) {
1581
1661
  logVoipDebug("authenticate() start", {
@@ -1593,7 +1673,6 @@ var ReactNativeVoipSdk = class {
1593
1673
  logVoipDebug("startCall() invoked", { number });
1594
1674
  this.ensureAuthenticated();
1595
1675
  const session = await this.callEngine.startCall(number);
1596
- this.activeCallIds.add(session.callId);
1597
1676
  this.activeCallId = session.callId;
1598
1677
  logVoipDebug("startCall() success", { callId: session.callId });
1599
1678
  return session;
@@ -1606,9 +1685,8 @@ var ReactNativeVoipSdk = class {
1606
1685
  logVoipDebug("endCall() invoked", { callId });
1607
1686
  this.ensureAuthenticated();
1608
1687
  await this.callEngine.endCall(callId);
1609
- this.activeCallIds.delete(callId);
1610
1688
  if (this.activeCallId === callId) {
1611
- this.activeCallId = this.pickNextActiveCallId();
1689
+ this.activeCallId = null;
1612
1690
  }
1613
1691
  logVoipDebug("endCall() success", { callId });
1614
1692
  }
@@ -1644,17 +1722,8 @@ var ReactNativeVoipSdk = class {
1644
1722
  return false;
1645
1723
  }
1646
1724
  }
1647
- /**
1648
- * Start a call or add to a conference.
1649
- * Use `makeCall(to, { parallel: true })` (or `addCall(to)`) while a call is active to open a second
1650
- * independent leg; failures on that leg are isolated to its `callId` in `onError` / `onCallEnded`.
1651
- */
1652
- async makeCall(to, options) {
1653
- logVoipDebug("makeCall() start", {
1654
- to,
1655
- hasActiveCall: Boolean(this.activeCallId),
1656
- parallel: Boolean(options?.parallel)
1657
- });
1725
+ async makeCall(to) {
1726
+ logVoipDebug("makeCall() start", { to, hasActiveCall: Boolean(this.activeCallId) });
1658
1727
  try {
1659
1728
  const destination = to.trim();
1660
1729
  if (!destination) {
@@ -1665,11 +1734,6 @@ var ReactNativeVoipSdk = class {
1665
1734
  logVoipDebug("makeCall() new call started", { callId: data.callId });
1666
1735
  return createSuccessResult("Call started", data);
1667
1736
  }
1668
- if (options?.parallel) {
1669
- const data = await this.startCall(destination);
1670
- logVoipDebug("makeCall() parallel call started", { callId: data.callId });
1671
- return createSuccessResult("Parallel call started", data);
1672
- }
1673
1737
  if (!this.bridge.nativeModule.addNewCallee) {
1674
1738
  return createErrorResult("addNewCallee not supported by native module");
1675
1739
  }
@@ -1701,14 +1765,6 @@ var ReactNativeVoipSdk = class {
1701
1765
  return createErrorResult(error instanceof Error ? error : "Failed to start call");
1702
1766
  }
1703
1767
  }
1704
- /** Start a parallel outbound call while another call is active (alias for `makeCall(to, { parallel: true })`). */
1705
- async addCall(to) {
1706
- return this.makeCall(to, { parallel: true });
1707
- }
1708
- /** All active call legs (parallel-aware). */
1709
- getActiveCallIds() {
1710
- return [...this.activeCallIds];
1711
- }
1712
1768
  async acceptCall(callId) {
1713
1769
  try {
1714
1770
  const id = callId ?? this.activeCallId;
@@ -1767,10 +1823,10 @@ var ReactNativeVoipSdk = class {
1767
1823
  return createErrorResult(error instanceof Error ? error : "Failed to merge call");
1768
1824
  }
1769
1825
  }
1770
- async toggleMute(mute, callId) {
1771
- const id = callId ?? this.activeCallId;
1772
- logVoipDebug("toggleMute() invoked", { mute, callId: id, hasActiveCall: Boolean(this.activeCallId) });
1826
+ async toggleMute(mute) {
1827
+ logVoipDebug("toggleMute() invoked", { mute, hasActiveCall: Boolean(this.activeCallId) });
1773
1828
  try {
1829
+ const id = this.activeCallId;
1774
1830
  if (!id) return createErrorResult("No active call");
1775
1831
  await this.callEngine.toggleMute(id, mute);
1776
1832
  return createSuccessResult(`Mute ${mute}`, null);
@@ -1969,19 +2025,12 @@ var ReactNativeVoipSdk = class {
1969
2025
  }
1970
2026
  destroy() {
1971
2027
  logVoipDebug("destroy() invoked");
1972
- this.unsubscribeOnCallEnded?.();
1973
- this.unsubscribeOnCallEnded = null;
1974
2028
  this.authManager.destroy();
1975
2029
  this.bridge.destroy();
1976
2030
  this.initialized = false;
1977
2031
  this.activeCallId = null;
1978
- this.activeCallIds.clear();
1979
2032
  logVoipDebug("destroy() completed");
1980
2033
  }
1981
- pickNextActiveCallId() {
1982
- if (this.activeCallIds.size === 0) return null;
1983
- return this.activeCallIds.values().next().value ?? null;
1984
- }
1985
2034
  ensureAuthenticated() {
1986
2035
  if (this.authManager.getState() !== "AUTHENTICATED") {
1987
2036
  throw new Error("SDK not authenticated.");
@@ -2011,10 +2060,8 @@ var ZyraTwilioWrapper = class extends ReactNativeVoipSdk {
2011
2060
  this.onConnect?.(payload);
2012
2061
  });
2013
2062
  this.on("onCallEnded", () => {
2014
- if (this.getActiveCallIds().length === 0) {
2015
- this.activeCallSid = null;
2016
- this.onDisconnect?.();
2017
- }
2063
+ this.activeCallSid = null;
2064
+ this.onDisconnect?.();
2018
2065
  });
2019
2066
  this.on("onMissedCall", () => {
2020
2067
  this.activeCallSid = null;