@capgo/capacitor-stream-call 0.0.23 → 0.0.25-alpha.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.
- package/ios/Sources/StreamCallPlugin/CallOverlayView.swift +1 -1
- package/ios/Sources/StreamCallPlugin/CustomVideoParticipantsView.swift +1 -1
- package/ios/Sources/StreamCallPlugin/ParticipantsView.swift +2 -2
- package/ios/Sources/StreamCallPlugin/StreamCallPlugin.swift +56 -46
- package/ios/Sources/StreamCallPlugin/TouchInterceptView.swift +1 -1
- package/package.json +1 -1
|
@@ -57,7 +57,7 @@ struct CallOverlayView: View {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
private func changeTrackVisibility(_ participant: CallParticipant?, isVisible: Bool) {
|
|
60
|
-
|
|
60
|
+
StreamCallPlugin.dualprint("changeTrackVisibility for \(String(describing: participant?.userId)), visible: \(isVisible)")
|
|
61
61
|
guard let participant = participant,
|
|
62
62
|
let call = viewModel.call else { return }
|
|
63
63
|
Task {
|
|
@@ -29,7 +29,7 @@ public struct CustomVideoCallParticipantView<Factory: ViewFactory>: View {
|
|
|
29
29
|
customData: [String: RawJSON],
|
|
30
30
|
call: Call?
|
|
31
31
|
) {
|
|
32
|
-
|
|
32
|
+
StreamCallPlugin.dualprint("size: \(availableFrame.size)")
|
|
33
33
|
self.viewFactory = viewFactory
|
|
34
34
|
self.participant = participant
|
|
35
35
|
self.id = id ?? participant.id
|
|
@@ -34,8 +34,8 @@ extension View {
|
|
|
34
34
|
value: [ViewFramePreferenceData(label: label,
|
|
35
35
|
frame: geo.frame(in: .global))])
|
|
36
36
|
.onAppear {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
StreamCallPlugin.dualprint("ParticipantsView - Collecting frame for label: \(label)")
|
|
38
|
+
StreamCallPlugin.dualprint("Frame: \(geo.frame(in: .global))")
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
)
|
|
@@ -5,6 +5,7 @@ import StreamVideoSwiftUI
|
|
|
5
5
|
import SwiftUI
|
|
6
6
|
import Combine
|
|
7
7
|
import WebKit
|
|
8
|
+
import os.log
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Please read the Capacitor iOS Plugin Development Guide
|
|
@@ -27,6 +28,15 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
27
28
|
CAPPluginMethod(name: "getCallStatus", returnType: CAPPluginReturnPromise)
|
|
28
29
|
]
|
|
29
30
|
|
|
31
|
+
// OSLog for streamcall plugin
|
|
32
|
+
private static let osLog = OSLog(subsystem: "com.example.plugin.streamcall", category: "StreamCallPlugin")
|
|
33
|
+
|
|
34
|
+
// Static function for dual printing to console and os.log
|
|
35
|
+
public static func dualprint(_ message: String) {
|
|
36
|
+
print(message)
|
|
37
|
+
os_log("%{public}@", log: osLog, type: .info, message)
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
private enum State {
|
|
31
41
|
case notInitialized
|
|
32
42
|
case initializing
|
|
@@ -97,7 +107,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
97
107
|
|
|
98
108
|
// Check if we have a logged in user for handling incoming calls
|
|
99
109
|
if let credentials = SecureUserRepository.shared.loadCurrentUser() {
|
|
100
|
-
|
|
110
|
+
StreamCallPlugin.dualprint("Loading user for StreamCallPlugin: \(credentials.user.name)")
|
|
101
111
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
102
112
|
self.initializeStreamVideo()
|
|
103
113
|
}
|
|
@@ -108,7 +118,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
108
118
|
wrappedDelegate: self.webView?.navigationDelegate,
|
|
109
119
|
onSetupOverlay: { [weak self] in
|
|
110
120
|
guard let self = self else { return }
|
|
111
|
-
|
|
121
|
+
StreamCallPlugin.dualprint("Attempting to setup call view")
|
|
112
122
|
|
|
113
123
|
self.setupViews()
|
|
114
124
|
}
|
|
@@ -128,7 +138,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
128
138
|
return
|
|
129
139
|
}
|
|
130
140
|
|
|
131
|
-
|
|
141
|
+
StreamCallPlugin.dualprint("loginMagicToken received token")
|
|
132
142
|
currentToken = token
|
|
133
143
|
tokenWaitSemaphore?.signal()
|
|
134
144
|
call.resolve()
|
|
@@ -143,17 +153,17 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
143
153
|
guard let self = self else { return }
|
|
144
154
|
Task {
|
|
145
155
|
do {
|
|
146
|
-
|
|
156
|
+
StreamCallPlugin.dualprint("Setting up token subscription")
|
|
147
157
|
try self.requireInitialized()
|
|
148
158
|
if let lastVoIPToken = self.lastVoIPToken, !lastVoIPToken.isEmpty {
|
|
149
|
-
|
|
159
|
+
StreamCallPlugin.dualprint("Deleting device: \(lastVoIPToken)")
|
|
150
160
|
try await self.streamVideo?.deleteDevice(id: lastVoIPToken)
|
|
151
161
|
}
|
|
152
162
|
if !updatedDeviceToken.isEmpty {
|
|
153
|
-
|
|
163
|
+
StreamCallPlugin.dualprint("Setting voip device: \(updatedDeviceToken)")
|
|
154
164
|
try await self.streamVideo?.setVoipDevice(id: updatedDeviceToken)
|
|
155
165
|
// Save the token to our secure storage
|
|
156
|
-
|
|
166
|
+
StreamCallPlugin.dualprint("Saving voip token: \(updatedDeviceToken)")
|
|
157
167
|
SecureUserRepository.shared.save(voipPushToken: updatedDeviceToken)
|
|
158
168
|
}
|
|
159
169
|
self.lastVoIPToken = updatedDeviceToken
|
|
@@ -175,7 +185,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
175
185
|
|
|
176
186
|
// Verify callViewModel exists
|
|
177
187
|
guard let callViewModel = self.callViewModel, let streamVideo = self.streamVideo else {
|
|
178
|
-
|
|
188
|
+
StreamCallPlugin.dualprint("Warning: setupActiveCallSubscription called but callViewModel or streamVideo is nil")
|
|
179
189
|
// Schedule a retry after a short delay if callViewModel is nil
|
|
180
190
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
|
|
181
191
|
self?.setupActiveCallSubscription()
|
|
@@ -183,7 +193,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
183
193
|
return
|
|
184
194
|
}
|
|
185
195
|
|
|
186
|
-
|
|
196
|
+
StreamCallPlugin.dualprint("Setting up active call subscription")
|
|
187
197
|
|
|
188
198
|
// Create a strong reference to callViewModel to ensure it's not deallocated
|
|
189
199
|
// while the subscription is active
|
|
@@ -195,7 +205,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
195
205
|
.sink { [weak self, weak viewModel] activeCall in
|
|
196
206
|
guard let self = self, let viewModel = viewModel else { return }
|
|
197
207
|
|
|
198
|
-
|
|
208
|
+
StreamCallPlugin.dualprint("Active call update from streamVideo: \(String(describing: activeCall?.cId))")
|
|
199
209
|
|
|
200
210
|
if let activeCall = activeCall {
|
|
201
211
|
// Sync callViewModel with activeCall from streamVideo state
|
|
@@ -212,24 +222,24 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
212
222
|
.receive(on: DispatchQueue.main)
|
|
213
223
|
.sink { [weak self, weak viewModel] newState in
|
|
214
224
|
guard let self = self, let viewModel = viewModel else {
|
|
215
|
-
|
|
225
|
+
StreamCallPlugin.dualprint("Warning: Call state update received but self or viewModel is nil")
|
|
216
226
|
return
|
|
217
227
|
}
|
|
218
228
|
|
|
219
229
|
do {
|
|
220
230
|
try self.requireInitialized()
|
|
221
|
-
|
|
231
|
+
StreamCallPlugin.dualprint("Call State Update: \(newState)")
|
|
222
232
|
|
|
223
233
|
if newState == .inCall {
|
|
224
|
-
|
|
225
|
-
|
|
234
|
+
StreamCallPlugin.dualprint("- In call state detected")
|
|
235
|
+
StreamCallPlugin.dualprint("- All participants: \(String(describing: viewModel.participants))")
|
|
226
236
|
|
|
227
237
|
// Create/update overlay and make visible when there's an active call
|
|
228
238
|
self.createCallOverlayView()
|
|
229
239
|
|
|
230
240
|
// Notify that a call has started - but only if we haven't notified for this call yet
|
|
231
241
|
if let callId = viewModel.call?.cId, !self.hasNotifiedCallJoined || callId != self.currentCallId {
|
|
232
|
-
|
|
242
|
+
StreamCallPlugin.dualprint("Notifying call joined: \(callId)")
|
|
233
243
|
self.updateCallStatusAndNotify(callId: callId, state: "joined")
|
|
234
244
|
self.hasNotifiedCallJoined = true
|
|
235
245
|
}
|
|
@@ -238,7 +248,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
238
248
|
} else if newState == .idle && self.streamVideo?.state.activeCall == nil {
|
|
239
249
|
// Get the call ID that was active before the state changed
|
|
240
250
|
let endingCallId = viewModel.call?.cId
|
|
241
|
-
|
|
251
|
+
StreamCallPlugin.dualprint("Call ending: \(String(describing: endingCallId))")
|
|
242
252
|
|
|
243
253
|
// Notify that call has ended - use the properly tracked call ID
|
|
244
254
|
self.updateCallStatusAndNotify(callId: endingCallId ?? "", state: "left")
|
|
@@ -254,7 +264,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
254
264
|
// Remove call from callStates
|
|
255
265
|
self.callStates.removeValue(forKey: callCid)
|
|
256
266
|
|
|
257
|
-
|
|
267
|
+
StreamCallPlugin.dualprint("Cleaned up resources for ended call: \(callCid)")
|
|
258
268
|
}
|
|
259
269
|
|
|
260
270
|
// Remove the call overlay view when not in a call
|
|
@@ -271,7 +281,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
271
281
|
statePublisher.cancel()
|
|
272
282
|
}
|
|
273
283
|
|
|
274
|
-
|
|
284
|
+
StreamCallPlugin.dualprint("Active call subscription setup completed")
|
|
275
285
|
|
|
276
286
|
// Schedule a periodic check to ensure subscription is active
|
|
277
287
|
self.scheduleSubscriptionCheck()
|
|
@@ -286,7 +296,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
286
296
|
|
|
287
297
|
// Check if we're in a state where we need the subscription but it's not active
|
|
288
298
|
if self.state == .initialized && self.activeCallSubscription == nil && self.callViewModel != nil {
|
|
289
|
-
|
|
299
|
+
StreamCallPlugin.dualprint("Subscription check: Restoring lost activeCallSubscription")
|
|
290
300
|
self.setupActiveCallSubscription()
|
|
291
301
|
} else {
|
|
292
302
|
// Schedule the next check
|
|
@@ -323,8 +333,8 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
323
333
|
let hasAccepted = callState.participantResponses.values.contains { $0 == "accepted" }
|
|
324
334
|
|
|
325
335
|
if !hasAccepted {
|
|
326
|
-
|
|
327
|
-
|
|
336
|
+
StreamCallPlugin.dualprint("Call \(callCid) has timed out after \(elapsedSeconds) seconds")
|
|
337
|
+
StreamCallPlugin.dualprint("No one accepted call \(callCid), marking all non-responders as missed")
|
|
328
338
|
|
|
329
339
|
// Mark all members who haven't responded as "missed"
|
|
330
340
|
for member in callState.members {
|
|
@@ -386,21 +396,21 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
386
396
|
}
|
|
387
397
|
|
|
388
398
|
guard let callState = callState else {
|
|
389
|
-
|
|
399
|
+
StreamCallPlugin.dualprint("Call state not found for cId: \(callCid)")
|
|
390
400
|
return
|
|
391
401
|
}
|
|
392
402
|
|
|
393
403
|
let totalParticipants = callState.members.count
|
|
394
404
|
let responseCount = callState.participantResponses.count
|
|
395
405
|
|
|
396
|
-
|
|
406
|
+
StreamCallPlugin.dualprint("Total participants: \(totalParticipants), Responses: \(responseCount)")
|
|
397
407
|
|
|
398
408
|
let allResponded = responseCount >= totalParticipants
|
|
399
409
|
let allRejectedOrMissed = allResponded &&
|
|
400
410
|
callState.participantResponses.values.allSatisfy { $0 == "rejected" || $0 == "missed" }
|
|
401
411
|
|
|
402
412
|
if allResponded && allRejectedOrMissed {
|
|
403
|
-
|
|
413
|
+
StreamCallPlugin.dualprint("All participants have rejected or missed the call")
|
|
404
414
|
|
|
405
415
|
// End the call
|
|
406
416
|
if let call = streamVideo?.state.activeCall, call.cId == callCid {
|
|
@@ -544,18 +554,18 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
544
554
|
|
|
545
555
|
Task {
|
|
546
556
|
do {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
557
|
+
StreamCallPlugin.dualprint("Creating call:")
|
|
558
|
+
StreamCallPlugin.dualprint("- Call ID: \(callId)")
|
|
559
|
+
StreamCallPlugin.dualprint("- Call Type: \(callType)")
|
|
560
|
+
StreamCallPlugin.dualprint("- Users: \(members)")
|
|
561
|
+
StreamCallPlugin.dualprint("- Should Ring: \(shouldRing)")
|
|
562
|
+
StreamCallPlugin.dualprint("- Team: \(team)")
|
|
553
563
|
|
|
554
564
|
// Create the call object
|
|
555
565
|
let streamCall = streamVideo?.call(callType: callType, callId: callId)
|
|
556
566
|
|
|
557
567
|
// Start the call with the members
|
|
558
|
-
|
|
568
|
+
StreamCallPlugin.dualprint("Creating call with members...")
|
|
559
569
|
try await streamCall?.create(
|
|
560
570
|
memberIds: members,
|
|
561
571
|
custom: [:],
|
|
@@ -563,9 +573,9 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
563
573
|
)
|
|
564
574
|
|
|
565
575
|
// Join the call
|
|
566
|
-
|
|
576
|
+
StreamCallPlugin.dualprint("Joining call...")
|
|
567
577
|
try await streamCall?.join(create: false)
|
|
568
|
-
|
|
578
|
+
StreamCallPlugin.dualprint("Successfully joined call")
|
|
569
579
|
|
|
570
580
|
// Update the CallOverlayView with the active call
|
|
571
581
|
await MainActor.run {
|
|
@@ -695,14 +705,14 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
695
705
|
}
|
|
696
706
|
|
|
697
707
|
// Join the call
|
|
698
|
-
|
|
708
|
+
StreamCallPlugin.dualprint("Accepting and joining call \(streamCall!.cId)...")
|
|
699
709
|
guard case .incoming(let incomingCall) = await self.callViewModel?.callingState else {
|
|
700
710
|
call.reject("Failed to accept call as there is no call ID")
|
|
701
711
|
return
|
|
702
712
|
}
|
|
703
713
|
|
|
704
714
|
await self.callViewModel?.acceptCall(callType: incomingCall.type, callId: incomingCall.id)
|
|
705
|
-
|
|
715
|
+
StreamCallPlugin.dualprint("Successfully joined call")
|
|
706
716
|
|
|
707
717
|
// Update the CallOverlayView with the active call
|
|
708
718
|
await MainActor.run {
|
|
@@ -726,18 +736,18 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
726
736
|
|
|
727
737
|
private func initializeStreamVideo() {
|
|
728
738
|
if (state == .initialized) {
|
|
729
|
-
|
|
739
|
+
StreamCallPlugin.dualprint("initializeStreamVideo already initialized")
|
|
730
740
|
// Try to get user credentials from repository
|
|
731
741
|
guard let savedCredentials = SecureUserRepository.shared.loadCurrentUser() else {
|
|
732
|
-
|
|
742
|
+
StreamCallPlugin.dualprint("Save credentials not found, skipping initialization")
|
|
733
743
|
return
|
|
734
744
|
}
|
|
735
745
|
if (savedCredentials.user.id == streamVideo?.user.id) {
|
|
736
|
-
|
|
746
|
+
StreamCallPlugin.dualprint("Skipping initializeStreamVideo as user is already logged in")
|
|
737
747
|
return
|
|
738
748
|
}
|
|
739
749
|
} else if (state == .initializing) {
|
|
740
|
-
|
|
750
|
+
StreamCallPlugin.dualprint("initializeStreamVideo rejected - already initializing")
|
|
741
751
|
return
|
|
742
752
|
}
|
|
743
753
|
state = .initializing
|
|
@@ -745,11 +755,11 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
745
755
|
// Try to get user credentials from repository
|
|
746
756
|
guard let savedCredentials = SecureUserRepository.shared.loadCurrentUser(),
|
|
747
757
|
let apiKey = self.apiKey else {
|
|
748
|
-
|
|
758
|
+
StreamCallPlugin.dualprint("No saved credentials or API key found, skipping initialization")
|
|
749
759
|
state = .notInitialized
|
|
750
760
|
return
|
|
751
761
|
}
|
|
752
|
-
|
|
762
|
+
StreamCallPlugin.dualprint("Initializing with saved credentials for user: \(savedCredentials.user.name)")
|
|
753
763
|
|
|
754
764
|
LogConfig.level = .debug
|
|
755
765
|
self.streamVideo = StreamVideo(
|
|
@@ -758,7 +768,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
758
768
|
token: UserToken(stringLiteral: savedCredentials.tokenValue),
|
|
759
769
|
tokenProvider: {completion in
|
|
760
770
|
guard let savedCredentials = SecureUserRepository.shared.loadCurrentUser() else {
|
|
761
|
-
|
|
771
|
+
StreamCallPlugin.dualprint("No saved credentials or API key found, cannot refresh token")
|
|
762
772
|
|
|
763
773
|
completion(.failure(NSError(domain: "No saved credentials or API key found, cannot refresh token", code: 0, userInfo: nil)))
|
|
764
774
|
return
|
|
@@ -851,7 +861,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
851
861
|
|
|
852
862
|
// Check if we already have an overlay view - do nothing if it exists
|
|
853
863
|
if let existingOverlayView = self.overlayView, existingOverlayView.superview != nil {
|
|
854
|
-
|
|
864
|
+
StreamCallPlugin.dualprint("Call overlay view already exists, making it visible")
|
|
855
865
|
existingOverlayView.isHidden = false
|
|
856
866
|
// Make webview transparent to see StreamCall UI underneath
|
|
857
867
|
webView.isOpaque = false
|
|
@@ -860,7 +870,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
860
870
|
return
|
|
861
871
|
}
|
|
862
872
|
|
|
863
|
-
|
|
873
|
+
StreamCallPlugin.dualprint("Creating new call overlay view")
|
|
864
874
|
|
|
865
875
|
// First, create the overlay view
|
|
866
876
|
let overlayView = UIHostingController(rootView: CallOverlayView(viewModel: callOverlayView))
|
|
@@ -913,7 +923,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
913
923
|
private func ensureViewRemoved() {
|
|
914
924
|
// Check if we have an overlay view
|
|
915
925
|
if let existingOverlayView = self.overlayView {
|
|
916
|
-
|
|
926
|
+
StreamCallPlugin.dualprint("Hiding call overlay view")
|
|
917
927
|
|
|
918
928
|
// Hide the view instead of removing it
|
|
919
929
|
existingOverlayView.isHidden = true
|
|
@@ -923,7 +933,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
923
933
|
self.webView?.backgroundColor = nil
|
|
924
934
|
self.webView?.scrollView.backgroundColor = nil
|
|
925
935
|
} else {
|
|
926
|
-
|
|
936
|
+
StreamCallPlugin.dualprint("No call overlay view to hide")
|
|
927
937
|
}
|
|
928
938
|
}
|
|
929
939
|
|
|
@@ -11,7 +11,7 @@ class TouchInterceptView: UIView {
|
|
|
11
11
|
// Ensure this view is transparent and doesn't interfere with display
|
|
12
12
|
self.backgroundColor = .clear
|
|
13
13
|
self.isOpaque = false
|
|
14
|
-
print("TouchInterceptView setup with webView: \(webView) and overlayView: \(overlayView)")
|
|
14
|
+
// print("TouchInterceptView setup with webView: \(webView) and overlayView: \(overlayView)")
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
package/package.json
CHANGED