@capgo/capacitor-stream-call 0.0.6 → 0.0.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.
@@ -23,9 +23,10 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
23
23
  CAPPluginMethod(name: "setMicrophoneEnabled", returnType: CAPPluginReturnPromise),
24
24
  CAPPluginMethod(name: "setCameraEnabled", returnType: CAPPluginReturnPromise),
25
25
  CAPPluginMethod(name: "acceptCall", returnType: CAPPluginReturnPromise),
26
- CAPPluginMethod(name: "isCameraEnabled", returnType: CAPPluginReturnPromise)
26
+ CAPPluginMethod(name: "isCameraEnabled", returnType: CAPPluginReturnPromise),
27
+ CAPPluginMethod(name: "getCallStatus", returnType: CAPPluginReturnPromise)
27
28
  ]
28
-
29
+
29
30
  private enum State {
30
31
  case notInitialized
31
32
  case initializing
@@ -37,7 +38,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
37
38
  private static let tokenRefreshSemaphore = DispatchSemaphore(value: 1)
38
39
  private var currentToken: String?
39
40
  private var tokenWaitSemaphore: DispatchSemaphore?
40
-
41
+
41
42
  private var overlayView: UIView?
42
43
  private var hostingController: UIHostingController<CallOverlayView>?
43
44
  private var overlayViewModel: CallOverlayViewModel?
@@ -45,19 +46,47 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
45
46
  private var activeCallSubscription: AnyCancellable?
46
47
  private var lastVoIPToken: String?
47
48
  private var touchInterceptView: TouchInterceptView?
48
-
49
+
49
50
  private var streamVideo: StreamVideo?
50
-
51
+
51
52
  // Track the current active call ID
52
53
  private var currentActiveCallId: String?
53
-
54
+
55
+ // Store current call info for getCallStatus
56
+ private var currentCallId: String = ""
57
+ private var currentCallState: String = ""
58
+
54
59
  @Injected(\.callKitAdapter) var callKitAdapter
55
60
  @Injected(\.callKitPushNotificationAdapter) var callKitPushNotificationAdapter
56
61
  private var webviewDelegate: WebviewNavigationDelegate?
57
-
62
+
58
63
  // Add class property to store call states
59
64
  private var callStates: [String: (members: [MemberResponse], participantResponses: [String: String], createdAt: Date, timer: Timer?)] = [:]
60
-
65
+
66
+ // Helper method to update call status and notify listeners
67
+ private func updateCallStatusAndNotify(callId: String, state: String, userId: String? = nil, reason: String? = nil) {
68
+ // Update stored call info
69
+ currentCallId = callId
70
+ currentCallState = state
71
+
72
+ // Create data dictionary with only the fields in the CallEvent interface
73
+ var data: [String: Any] = [
74
+ "callId": callId,
75
+ "state": state
76
+ ]
77
+
78
+ if let userId = userId {
79
+ data["userId"] = userId
80
+ }
81
+
82
+ if let reason = reason {
83
+ data["reason"] = reason
84
+ }
85
+
86
+ // Notify listeners
87
+ notifyListeners("callEvent", data: data)
88
+ }
89
+
61
90
  override public func load() {
62
91
  // Read API key from Info.plist
63
92
  if let apiKey = Bundle.main.object(forInfoDictionaryKey: "CAPACITOR_STREAM_VIDEO_APIKEY") as? String {
@@ -66,7 +95,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
66
95
  if self.apiKey == nil {
67
96
  fatalError("Cannot get apikey")
68
97
  }
69
-
98
+
70
99
  // Check if we have a logged in user for handling incoming calls
71
100
  if let credentials = SecureUserRepository.shared.loadCurrentUser() {
72
101
  print("Loading user for StreamCallPlugin: \(credentials.user.name)")
@@ -74,21 +103,21 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
74
103
  self.initializeStreamVideo()
75
104
  }
76
105
  }
77
-
106
+
78
107
  // Create and set the navigation delegate
79
108
  self.webviewDelegate = WebviewNavigationDelegate(
80
109
  wrappedDelegate: self.webView?.navigationDelegate,
81
110
  onSetupOverlay: { [weak self] in
82
111
  guard let self = self else { return }
83
112
  print("Attempting to setup call view")
84
-
113
+
85
114
  self.setupViews()
86
115
  }
87
116
  )
88
-
117
+
89
118
  self.webView?.navigationDelegate = self.webviewDelegate
90
119
  }
91
-
120
+
92
121
  // private func cleanupStreamVideo() {
93
122
  // // Cancel subscriptions
94
123
  // tokenSubscription?.cancel()
@@ -114,7 +143,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
114
143
  //
115
144
  // state = .notInitialized
116
145
  // }
117
-
146
+
118
147
  private func requireInitialized() throws {
119
148
  guard state == .initialized else {
120
149
  throw NSError(domain: "StreamCallPlugin", code: -1, userInfo: [NSLocalizedDescriptionKey: "StreamVideo not initialized"])
@@ -125,17 +154,17 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
125
154
  call.reject("Missing token parameter")
126
155
  return
127
156
  }
128
-
157
+
129
158
  print("loginMagicToken received token")
130
159
  currentToken = token
131
160
  tokenWaitSemaphore?.signal()
132
161
  call.resolve()
133
162
  }
134
-
163
+
135
164
  private func setupTokenSubscription() {
136
165
  // Cancel existing subscription if any
137
166
  tokenSubscription?.cancel()
138
-
167
+
139
168
  // Create new subscription
140
169
  tokenSubscription = callKitPushNotificationAdapter.$deviceToken.sink { [weak self] (updatedDeviceToken: String) in
141
170
  guard let self = self else { return }
@@ -161,26 +190,23 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
161
190
  }
162
191
  }
163
192
  }
164
-
193
+
165
194
  private func setupActiveCallSubscription() {
166
195
  if let streamVideo = streamVideo {
167
196
  Task {
168
197
  for await event in streamVideo.subscribe() {
169
198
  // print("Event", event)
170
199
  if let ringingEvent = event.rawValue as? CallRingEvent {
171
- notifyListeners("callEvent", data: [
172
- "callId": ringingEvent.callCid,
173
- "state": "ringing"
174
- ])
200
+ updateCallStatusAndNotify(callId: ringingEvent.callCid, state: "ringing")
175
201
  continue
176
202
  }
177
-
203
+
178
204
  if let callCreatedEvent = event.rawValue as? CallCreatedEvent {
179
205
  print("CallCreatedEvent \(String(describing: userId))")
180
-
206
+
181
207
  let callCid = callCreatedEvent.callCid
182
208
  let members = callCreatedEvent.members
183
-
209
+
184
210
  // Create timer on main thread
185
211
  await MainActor.run {
186
212
  // Store in the combined callStates map
@@ -190,20 +216,22 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
190
216
  createdAt: Date(),
191
217
  timer: nil
192
218
  )
193
-
219
+
194
220
  // Start timer to check for timeout every second
195
221
  // Use @objc method as timer target to avoid sendable closure issues
196
222
  let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.checkCallTimeoutTimer(_:)), userInfo: callCid, repeats: true)
197
-
223
+
198
224
  // Update timer in callStates
199
225
  self.callStates[callCid]?.timer = timer
200
226
  }
227
+
228
+ updateCallStatusAndNotify(callId: callCid, state: "created")
201
229
  }
202
-
230
+
203
231
  if let rejectedEvent = event.rawValue as? CallRejectedEvent {
204
232
  let userId = rejectedEvent.user.id
205
233
  let callCid = rejectedEvent.callCid
206
-
234
+
207
235
  // Operate on callStates on the main thread
208
236
  await MainActor.run {
209
237
  // Update the combined callStates map
@@ -212,22 +240,18 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
212
240
  self.callStates[callCid] = callState
213
241
  }
214
242
  }
215
-
243
+
216
244
  print("CallRejectedEvent \(userId)")
217
- notifyListeners("callEvent", data: [
218
- "callId": callCid,
219
- "state": "rejected",
220
- "userId": userId
221
- ])
222
-
245
+ updateCallStatusAndNotify(callId: callCid, state: "rejected", userId: userId)
246
+
223
247
  await checkAllParticipantsResponded(callCid: callCid)
224
248
  continue
225
249
  }
226
-
250
+
227
251
  if let missedEvent = event.rawValue as? CallMissedEvent {
228
252
  let userId = missedEvent.user.id
229
253
  let callCid = missedEvent.callCid
230
-
254
+
231
255
  // Operate on callStates on the main thread
232
256
  await MainActor.run {
233
257
  // Update the combined callStates map
@@ -236,73 +260,74 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
236
260
  self.callStates[callCid] = callState
237
261
  }
238
262
  }
239
-
263
+
240
264
  print("CallMissedEvent \(userId)")
241
- notifyListeners("callEvent", data: [
242
- "callId": callCid,
243
- "state": "missed",
244
- "userId": userId
245
- ])
246
-
265
+ updateCallStatusAndNotify(callId: callCid, state: "missed", userId: userId)
266
+
247
267
  await checkAllParticipantsResponded(callCid: callCid)
248
268
  continue
249
269
  }
250
-
270
+
251
271
  if let participantLeftEvent = event.rawValue as? CallSessionParticipantLeftEvent {
252
272
  let callIdSplit = participantLeftEvent.callCid.split(separator: ":")
253
- if (callIdSplit.count != 2) {
273
+ if callIdSplit.count != 2 {
254
274
  print("CallSessionParticipantLeftEvent invalid cID \(participantLeftEvent.callCid)")
255
275
  continue
256
276
  }
257
-
277
+
258
278
  let callType = callIdSplit[0]
259
279
  let callId = callIdSplit[1]
260
-
280
+
261
281
  let call = streamVideo.call(callType: String(callType), callId: String(callId))
262
- if await MainActor.run(body: { (call.state.session?.participants.count ?? 1) - 1 <= 1 }) {
263
-
264
-
265
- print("We are left solo in a call. Ending. cID: \(participantLeftEvent.callCid)")
266
-
282
+ guard let participantsCount = await MainActor.run(body: {
283
+ if call.id == streamVideo.state.activeCall?.id {
284
+ return (call.state.session?.participants.count) ?? streamVideo.state.activeCall?.state.participants.count
285
+ } else {
286
+ return (call.state.session?.participants.count)
287
+ }
288
+ }) else {
289
+ print("CallSessionParticipantLeftEvent no participantsCount")
290
+ continue
291
+ }
292
+
293
+ if participantsCount - 1 <= 1 {
294
+
295
+ print("We are left solo in a call. Ending. cID: \(participantLeftEvent.callCid). participantsCount: \(participantsCount)")
296
+
267
297
  Task {
268
298
  if let activeCall = streamVideo.state.activeCall {
269
299
  activeCall.leave()
300
+ } else {
301
+ print("Active call isn't the one?")
270
302
  }
271
303
  }
272
304
  }
273
305
  }
274
-
306
+
275
307
  if let acceptedEvent = event.rawValue as? CallAcceptedEvent {
276
308
  let userId = acceptedEvent.user.id
277
309
  let callCid = acceptedEvent.callCid
278
-
310
+
279
311
  // Operate on callStates on the main thread
280
312
  await MainActor.run {
281
313
  // Update the combined callStates map
282
314
  if var callState = self.callStates[callCid] {
283
315
  callState.participantResponses[userId] = "accepted"
284
-
316
+
285
317
  // If someone accepted, invalidate the timer as we don't need to check anymore
286
318
  callState.timer?.invalidate()
287
319
  callState.timer = nil
288
-
320
+
289
321
  self.callStates[callCid] = callState
290
322
  }
291
323
  }
292
-
324
+
293
325
  print("CallAcceptedEvent \(userId)")
294
- notifyListeners("callEvent", data: [
295
- "callId": callCid,
296
- "state": "accepted",
297
- "userId": userId
298
- ])
326
+ updateCallStatusAndNotify(callId: callCid, state: "accepted", userId: userId)
299
327
  continue
300
328
  }
301
-
302
- notifyListeners("callEvent", data: [
303
- "callId": streamVideo.state.activeCall?.callId ?? "",
304
- "state": event.type
305
- ])
329
+
330
+ updateCallStatusAndNotify(callId: streamVideo.state.activeCall?.callId ?? "", state: event.type)
306
331
  }
307
332
  }
308
333
  }
@@ -311,60 +336,54 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
311
336
  // Create new subscription
312
337
  activeCallSubscription = streamVideo?.state.$activeCall.sink { [weak self] newState in
313
338
  guard let self = self else { return }
314
-
339
+
315
340
  Task { @MainActor in
316
341
  do {
317
342
  try self.requireInitialized()
318
343
  print("Call State Update:")
319
344
  print("- Call is nil: \(newState == nil)")
320
-
345
+
321
346
  if let state = newState?.state {
322
347
  print("- state: \(state)")
323
348
  print("- Session ID: \(state.sessionId)")
324
349
  print("- All participants: \(String(describing: state.participants))")
325
350
  print("- Remote participants: \(String(describing: state.remoteParticipants))")
326
-
351
+
327
352
  // Store the active call ID when a call becomes active
328
353
  self.currentActiveCallId = newState?.cId
329
354
  print("Updated current active call ID: \(String(describing: self.currentActiveCallId))")
330
-
355
+
331
356
  // Update overlay and make visible when there's an active call
332
357
  self.overlayViewModel?.updateCall(newState)
333
358
  self.overlayView?.isHidden = false
334
359
  self.webView?.isOpaque = false
335
-
360
+
336
361
  // Notify that a call has started
337
- self.notifyListeners("callEvent", data: [
338
- "callId": newState?.cId ?? "",
339
- "state": "joined"
340
- ])
362
+ self.updateCallStatusAndNotify(callId: newState?.cId ?? "", state: "joined")
341
363
  } else {
342
364
  // Get the call ID that was active before the state changed to nil
343
365
  let endingCallId = self.currentActiveCallId
344
366
  print("Call ending: \(String(describing: endingCallId))")
345
-
367
+
346
368
  // If newState is nil, hide overlay and clear call
347
369
  self.overlayViewModel?.updateCall(nil)
348
370
  self.overlayView?.isHidden = true
349
371
  self.webView?.isOpaque = true
350
-
372
+
351
373
  // Notify that call has ended - use the properly tracked call ID
352
- self.notifyListeners("callEvent", data: [
353
- "callId": endingCallId ?? "",
354
- "state": "left"
355
- ])
356
-
374
+ self.updateCallStatusAndNotify(callId: endingCallId ?? "", state: "left")
375
+
357
376
  // Clean up any resources for this call
358
377
  if let callCid = endingCallId {
359
378
  // Invalidate and remove the timer
360
379
  self.callStates[callCid]?.timer?.invalidate()
361
-
380
+
362
381
  // Remove call from callStates
363
382
  self.callStates.removeValue(forKey: callCid)
364
-
383
+
365
384
  print("Cleaned up resources for ended call: \(callCid)")
366
385
  }
367
-
386
+
368
387
  // Clear the active call ID
369
388
  self.currentActiveCallId = nil
370
389
  }
@@ -374,45 +393,45 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
374
393
  }
375
394
  }
376
395
  }
377
-
396
+
378
397
  @objc private func checkCallTimeoutTimer(_ timer: Timer) {
379
398
  guard let callCid = timer.userInfo as? String else { return }
380
-
399
+
381
400
  Task { [weak self] in
382
401
  guard let self = self else { return }
383
402
  await self.checkCallTimeout(callCid: callCid)
384
403
  }
385
404
  }
386
-
405
+
387
406
  private func checkCallTimeout(callCid: String) async {
388
407
  // Get a local copy of the call state from the main thread
389
408
  let callState: (members: [MemberResponse], participantResponses: [String: String], createdAt: Date, timer: Timer?)? = await MainActor.run {
390
409
  return self.callStates[callCid]
391
410
  }
392
-
411
+
393
412
  guard let callState = callState else { return }
394
-
413
+
395
414
  // Calculate time elapsed since call creation
396
415
  let now = Date()
397
416
  let elapsedSeconds = now.timeIntervalSince(callState.createdAt)
398
-
417
+
399
418
  // Check if 30 seconds have passed
400
419
  if elapsedSeconds >= 30.0 {
401
420
 
402
421
  // Check if anyone has accepted
403
422
  let hasAccepted = callState.participantResponses.values.contains { $0 == "accepted" }
404
-
423
+
405
424
  if !hasAccepted {
406
425
  print("Call \(callCid) has timed out after \(elapsedSeconds) seconds")
407
426
  print("No one accepted call \(callCid), marking all non-responders as missed")
408
-
427
+
409
428
  // Mark all members who haven't responded as "missed"
410
429
  for member in callState.members {
411
430
  let memberId = member.userId
412
431
  let needsToBeMarkedAsMissed = await MainActor.run {
413
432
  return self.callStates[callCid]?.participantResponses[memberId] == nil
414
433
  }
415
-
434
+
416
435
  if needsToBeMarkedAsMissed {
417
436
  // Update callStates map on main thread
418
437
  await MainActor.run {
@@ -422,23 +441,19 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
422
441
  self.callStates[callCid] = updatedCallState
423
442
  }
424
443
  }
425
-
444
+
426
445
  // Notify listeners
427
446
  await MainActor.run {
428
- self.notifyListeners("callEvent", data: [
429
- "callId": callCid,
430
- "state": "missed",
431
- "userId": memberId
432
- ])
447
+ self.updateCallStatusAndNotify(callId: callCid, state: "missed", userId: memberId)
433
448
  }
434
449
  }
435
450
  }
436
-
451
+
437
452
  // End the call
438
453
  if let call = streamVideo?.state.activeCall, call.cId == callCid {
439
454
  call.leave()
440
455
  }
441
-
456
+
442
457
  // Clean up timer on main thread
443
458
  await MainActor.run {
444
459
  self.callStates[callCid]?.timer?.invalidate()
@@ -447,74 +462,66 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
447
462
  if let updatedCallState = updatedCallState {
448
463
  self.callStates[callCid] = updatedCallState
449
464
  }
450
-
465
+
451
466
  // Remove from callStates
452
467
  self.callStates.removeValue(forKey: callCid)
453
468
  }
454
-
469
+
455
470
  // Update UI
456
471
  await MainActor.run {
457
472
  self.overlayViewModel?.updateCall(nil)
458
473
  self.overlayView?.isHidden = true
459
474
  self.webView?.isOpaque = true
460
- self.notifyListeners("callEvent", data: [
461
- "callId": callCid,
462
- "state": "ended",
463
- "reason": "timeout"
464
- ])
475
+ self.updateCallStatusAndNotify(callId: callCid, state: "ended", reason: "timeout")
465
476
  }
466
477
  }
467
478
  }
468
479
  }
469
-
480
+
470
481
  private func checkAllParticipantsResponded(callCid: String) async {
471
482
  // Get a local copy of the call state from the main thread
472
483
  let callState: (members: [MemberResponse], participantResponses: [String: String], createdAt: Date, timer: Timer?)? = await MainActor.run {
473
484
  return self.callStates[callCid]
474
485
  }
475
-
486
+
476
487
  guard let callState = callState else {
477
488
  print("Call state not found for cId: \(callCid)")
478
489
  return
479
490
  }
480
-
491
+
481
492
  let totalParticipants = callState.members.count
482
493
  let responseCount = callState.participantResponses.count
483
-
494
+
484
495
  print("Total participants: \(totalParticipants), Responses: \(responseCount)")
485
-
496
+
486
497
  let allResponded = responseCount >= totalParticipants
487
- let allRejectedOrMissed = allResponded &&
498
+ let allRejectedOrMissed = allResponded &&
488
499
  callState.participantResponses.values.allSatisfy { $0 == "rejected" || $0 == "missed" }
489
-
500
+
490
501
  if allResponded && allRejectedOrMissed {
491
502
  print("All participants have rejected or missed the call")
492
-
493
- // End the call
503
+
504
+ // End the call
494
505
  if let call = streamVideo?.state.activeCall, call.cId == callCid {
495
506
  call.leave()
496
507
  }
497
-
508
+
498
509
  // Clean up timer and remove from callStates on main thread
499
510
  await MainActor.run {
500
511
  // Clean up timer
501
512
  self.callStates[callCid]?.timer?.invalidate()
502
-
513
+
503
514
  // Remove from callStates
504
515
  self.callStates.removeValue(forKey: callCid)
505
-
516
+
506
517
  self.overlayViewModel?.updateCall(nil)
507
518
  self.overlayView?.isHidden = true
508
519
  self.webView?.isOpaque = true
509
- self.notifyListeners("callEvent", data: [
510
- "callId": callCid,
511
- "state": "ended",
512
- "reason": "all_rejected_or_missed"
513
- ])
520
+ self.updateCallStatusAndNotify(callId: callCid, state: "ended", reason: "all_rejected_or_missed")
514
521
  }
515
522
  }
516
523
  }
517
-
524
+
518
525
  @objc func login(_ call: CAPPluginCall) {
519
526
  guard let token = call.getString("token"),
520
527
  let userId = call.getString("userId"),
@@ -522,7 +529,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
522
529
  call.reject("Missing required parameters")
523
530
  return
524
531
  }
525
-
532
+
526
533
  let imageURL = call.getString("imageURL")
527
534
  let user = User(
528
535
  id: userId,
@@ -530,31 +537,31 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
530
537
  imageURL: imageURL.flatMap { URL(string: $0) },
531
538
  customData: [:]
532
539
  )
533
-
540
+
534
541
  let credentials = UserCredentials(user: user, tokenValue: token)
535
542
  SecureUserRepository.shared.save(user: credentials)
536
543
  // Initialize Stream Video with new credentials
537
544
  initializeStreamVideo()
538
-
545
+
539
546
  if state != .initialized {
540
547
  call.reject("Failed to initialize StreamVideo")
541
548
  return
542
549
  }
543
-
550
+
544
551
  // Update the CallOverlayView with new StreamVideo instance
545
552
  Task { @MainActor in
546
553
  self.overlayViewModel?.updateStreamVideo(self.streamVideo)
547
554
  }
548
-
555
+
549
556
  call.resolve([
550
557
  "success": true
551
558
  ])
552
559
  }
553
-
560
+
554
561
  @objc func logout(_ call: CAPPluginCall) {
555
562
  // Remove VOIP token from repository
556
563
  SecureUserRepository.shared.save(voipPushToken: nil)
557
-
564
+
558
565
  // Try to delete the device from Stream if we have the last token
559
566
  if let lastVoIPToken = lastVoIPToken {
560
567
  Task {
@@ -565,16 +572,16 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
565
572
  }
566
573
  }
567
574
  }
568
-
575
+
569
576
  // Cancel subscriptions
570
577
  tokenSubscription?.cancel()
571
578
  tokenSubscription = nil
572
579
  activeCallSubscription?.cancel()
573
580
  activeCallSubscription = nil
574
581
  lastVoIPToken = nil
575
-
582
+
576
583
  SecureUserRepository.shared.removeCurrentUser()
577
-
584
+
578
585
  // Update the CallOverlayView with nil StreamVideo instance
579
586
  Task { @MainActor in
580
587
  self.overlayViewModel?.updateCall(nil)
@@ -582,19 +589,19 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
582
589
  self.overlayView?.isHidden = true
583
590
  self.webView?.isOpaque = true
584
591
  }
585
-
592
+
586
593
  call.resolve([
587
594
  "success": true
588
595
  ])
589
596
  }
590
-
597
+
591
598
  @objc func isCameraEnabled(_ call: CAPPluginCall) {
592
599
  do {
593
600
  try requireInitialized()
594
601
  } catch {
595
602
  call.reject("SDK not initialized")
596
603
  }
597
-
604
+
598
605
  if let activeCall = streamVideo?.state.activeCall {
599
606
  call.resolve([
600
607
  "enabled": activeCall.camera.status == .enabled
@@ -629,6 +636,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
629
636
 
630
637
  let callType = call.getString("type") ?? "default"
631
638
  let shouldRing = call.getBool("ring") ?? true
639
+ let team = call.getString("team")
632
640
 
633
641
  // Generate a unique call ID
634
642
  let callId = UUID().uuidString
@@ -640,6 +648,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
640
648
  print("- Call Type: \(callType)")
641
649
  print("- Users: \(members)")
642
650
  print("- Should Ring: \(shouldRing)")
651
+ print("- Team: \(team)")
643
652
 
644
653
  // Create the call object
645
654
  let streamCall = streamVideo?.call(callType: callType, callId: callId)
@@ -649,7 +658,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
649
658
  try await streamCall?.create(
650
659
  memberIds: members,
651
660
  custom: [:],
652
- ring: shouldRing
661
+ team: team, ring: shouldRing
653
662
  )
654
663
 
655
664
  // Join the call
@@ -788,6 +797,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
788
797
  print("Accepting and joining call \(streamCall!.cId)...")
789
798
  try await streamCall?.accept()
790
799
  try await streamCall?.join(create: false)
800
+ try await streamCall?.get()
791
801
  print("Successfully joined call")
792
802
 
793
803
  // Update the CallOverlayView with the active call
@@ -900,4 +910,17 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
900
910
  ])
901
911
  }
902
912
  }
913
+
914
+ @objc func getCallStatus(_ call: CAPPluginCall) {
915
+ // Use stored state rather than accessing SDK state directly
916
+ if currentCallId.isEmpty || currentCallState == "left" {
917
+ call.reject("Not in a call")
918
+ return
919
+ }
920
+
921
+ call.resolve([
922
+ "callId": currentCallId,
923
+ "state": currentCallState
924
+ ])
925
+ }
903
926
  }