@capgo/capacitor-stream-call 0.0.2 → 0.0.3
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/{StreamCall.podspec → CapgoCapacitorStreamCall.podspec} +1 -1
- package/README.md +36 -28
- package/android/src/main/java/ee/forgr/capacitor/streamcall/CallOverlayView.kt +0 -53
- package/android/src/main/java/ee/forgr/capacitor/streamcall/CustomNotificationHandler.kt +9 -18
- package/android/src/main/java/ee/forgr/capacitor/streamcall/IncomingCallView.kt +18 -7
- package/android/src/main/java/ee/forgr/capacitor/streamcall/RingtonePlayer.kt +0 -2
- package/android/src/main/java/ee/forgr/capacitor/streamcall/StreamCallPlugin.kt +174 -41
- package/dist/docs.json +36 -37
- package/dist/esm/definitions.d.ts +7 -11
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +2 -1
- package/dist/esm/web.js +10 -2
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +10 -2
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +10 -2
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/StreamCallPlugin/CustomCallView.swift +0 -1
- package/ios/Sources/StreamCallPlugin/StreamCallPlugin.swift +137 -188
- package/package.json +2 -2
|
@@ -22,9 +22,10 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
22
22
|
CAPPluginMethod(name: "endCall", returnType: CAPPluginReturnPromise),
|
|
23
23
|
CAPPluginMethod(name: "setMicrophoneEnabled", returnType: CAPPluginReturnPromise),
|
|
24
24
|
CAPPluginMethod(name: "setCameraEnabled", returnType: CAPPluginReturnPromise),
|
|
25
|
-
CAPPluginMethod(name: "acceptCall", returnType: CAPPluginReturnPromise)
|
|
25
|
+
CAPPluginMethod(name: "acceptCall", returnType: CAPPluginReturnPromise),
|
|
26
|
+
CAPPluginMethod(name: "isCameraEnabled", returnType: CAPPluginReturnPromise)
|
|
26
27
|
]
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
private enum State {
|
|
29
30
|
case notInitialized
|
|
30
31
|
case initializing
|
|
@@ -36,7 +37,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
36
37
|
private static let tokenRefreshSemaphore = DispatchSemaphore(value: 1)
|
|
37
38
|
private var currentToken: String?
|
|
38
39
|
private var tokenWaitSemaphore: DispatchSemaphore?
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
private var overlayView: UIView?
|
|
41
42
|
private var hostingController: UIHostingController<CallOverlayView>?
|
|
42
43
|
private var overlayViewModel: CallOverlayViewModel?
|
|
@@ -44,17 +45,13 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
44
45
|
private var activeCallSubscription: AnyCancellable?
|
|
45
46
|
private var lastVoIPToken: String?
|
|
46
47
|
private var touchInterceptView: TouchInterceptView?
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
private var streamVideo: StreamVideo?
|
|
49
|
-
|
|
50
|
+
|
|
50
51
|
@Injected(\.callKitAdapter) var callKitAdapter
|
|
51
52
|
@Injected(\.callKitPushNotificationAdapter) var callKitPushNotificationAdapter
|
|
52
|
-
|
|
53
|
-
private var refreshTokenURL: String?
|
|
54
|
-
private var refreshTokenHeaders: [String: String]?
|
|
55
|
-
|
|
56
53
|
private var webviewDelegate: WebviewNavigationDelegate?
|
|
57
|
-
|
|
54
|
+
|
|
58
55
|
override public func load() {
|
|
59
56
|
// Read API key from Info.plist
|
|
60
57
|
if let apiKey = Bundle.main.object(forInfoDictionaryKey: "CAPACITOR_STREAM_VIDEO_APIKEY") as? String {
|
|
@@ -63,7 +60,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
63
60
|
if self.apiKey == nil {
|
|
64
61
|
fatalError("Cannot get apikey")
|
|
65
62
|
}
|
|
66
|
-
|
|
63
|
+
|
|
67
64
|
// Check if we have a logged in user for handling incoming calls
|
|
68
65
|
if let credentials = SecureUserRepository.shared.loadCurrentUser() {
|
|
69
66
|
print("Loading user for StreamCallPlugin: \(credentials.user.name)")
|
|
@@ -71,21 +68,21 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
71
68
|
self.initializeStreamVideo()
|
|
72
69
|
}
|
|
73
70
|
}
|
|
74
|
-
|
|
71
|
+
|
|
75
72
|
// Create and set the navigation delegate
|
|
76
73
|
self.webviewDelegate = WebviewNavigationDelegate(
|
|
77
74
|
wrappedDelegate: self.webView?.navigationDelegate,
|
|
78
75
|
onSetupOverlay: { [weak self] in
|
|
79
76
|
guard let self = self else { return }
|
|
80
77
|
print("Attempting to setup call view")
|
|
81
|
-
|
|
78
|
+
|
|
82
79
|
self.setupViews()
|
|
83
80
|
}
|
|
84
81
|
)
|
|
85
|
-
|
|
82
|
+
|
|
86
83
|
self.webView?.navigationDelegate = self.webviewDelegate
|
|
87
84
|
}
|
|
88
|
-
|
|
85
|
+
|
|
89
86
|
// private func cleanupStreamVideo() {
|
|
90
87
|
// // Cancel subscriptions
|
|
91
88
|
// tokenSubscription?.cancel()
|
|
@@ -111,125 +108,28 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
111
108
|
//
|
|
112
109
|
// state = .notInitialized
|
|
113
110
|
// }
|
|
114
|
-
|
|
111
|
+
|
|
115
112
|
private func requireInitialized() throws {
|
|
116
113
|
guard state == .initialized else {
|
|
117
114
|
throw NSError(domain: "StreamCallPlugin", code: -1, userInfo: [NSLocalizedDescriptionKey: "StreamVideo not initialized"])
|
|
118
115
|
}
|
|
119
116
|
}
|
|
120
|
-
|
|
121
|
-
private func refreshToken() throws -> UserToken {
|
|
122
|
-
// Acquire the semaphore in a thread-safe way
|
|
123
|
-
StreamCallPlugin.tokenRefreshQueue.sync {
|
|
124
|
-
StreamCallPlugin.tokenRefreshSemaphore.wait()
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
defer {
|
|
128
|
-
// Always release the semaphore when we're done, even if we throw an error
|
|
129
|
-
StreamCallPlugin.tokenRefreshSemaphore.signal()
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Clear current token
|
|
133
|
-
currentToken = nil
|
|
134
|
-
|
|
135
|
-
// Create a local semaphore for waiting on the token
|
|
136
|
-
let localSemaphore = DispatchSemaphore(value: 0)
|
|
137
|
-
tokenWaitSemaphore = localSemaphore
|
|
138
|
-
|
|
139
|
-
// Capture webView before async context
|
|
140
|
-
guard let webView = self.webView else {
|
|
141
|
-
print("WebView not available")
|
|
142
|
-
tokenWaitSemaphore = nil
|
|
143
|
-
throw NSError(domain: "StreamCallPlugin", code: -1, userInfo: [NSLocalizedDescriptionKey: "WebView not available"])
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
guard let refreshURL = self.refreshTokenURL else {
|
|
147
|
-
print("Refresh URL not configured")
|
|
148
|
-
tokenWaitSemaphore = nil
|
|
149
|
-
throw NSError(domain: "StreamCallPlugin", code: -1, userInfo: [NSLocalizedDescriptionKey: "Refresh URL not configured"])
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
let headersJSON = (self.refreshTokenHeaders ?? [:]).reduce(into: "") { result, pair in
|
|
153
|
-
result += "\n'\(pair.key)': '\(pair.value)',"
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
let script = """
|
|
157
|
-
(function() {
|
|
158
|
-
console.log('Starting token refresh...');
|
|
159
|
-
fetch('\(refreshURL)', {
|
|
160
|
-
headers: {
|
|
161
|
-
\(headersJSON)
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
.then(response => {
|
|
165
|
-
console.log('Got response:', response.status);
|
|
166
|
-
return response.json();
|
|
167
|
-
})
|
|
168
|
-
.then(data => {
|
|
169
|
-
console.log('Got data, token length:', data.token?.length);
|
|
170
|
-
const tokenA = data.token;
|
|
171
|
-
window.Capacitor.Plugins.StreamCall.loginMagicToken({
|
|
172
|
-
token: tokenA
|
|
173
|
-
});
|
|
174
|
-
})
|
|
175
|
-
.catch(error => {
|
|
176
|
-
console.error('Token refresh error:', error);
|
|
177
|
-
});
|
|
178
|
-
})();
|
|
179
|
-
"""
|
|
180
|
-
|
|
181
|
-
if Thread.isMainThread {
|
|
182
|
-
print("Executing script on main thread")
|
|
183
|
-
webView.evaluateJavaScript(script, completionHandler: nil)
|
|
184
|
-
} else {
|
|
185
|
-
print("Executing script from background thread")
|
|
186
|
-
DispatchQueue.main.sync {
|
|
187
|
-
webView.evaluateJavaScript(script, completionHandler: nil)
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Set up a timeout
|
|
192
|
-
let timeoutQueue = DispatchQueue.global()
|
|
193
|
-
let timeoutWork = DispatchWorkItem {
|
|
194
|
-
print("Token refresh timed out")
|
|
195
|
-
self.tokenWaitSemaphore?.signal()
|
|
196
|
-
self.tokenWaitSemaphore = nil
|
|
197
|
-
}
|
|
198
|
-
timeoutQueue.asyncAfter(deadline: .now() + 10.0, execute: timeoutWork) // 10 second timeout
|
|
199
|
-
|
|
200
|
-
// Wait for token to be set via loginMagicToken or timeout
|
|
201
|
-
localSemaphore.wait()
|
|
202
|
-
timeoutWork.cancel()
|
|
203
|
-
tokenWaitSemaphore = nil
|
|
204
|
-
|
|
205
|
-
guard let token = currentToken else {
|
|
206
|
-
print("Failed to get token")
|
|
207
|
-
throw NSError(domain: "StreamCallPlugin", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to get token or timeout occurred"])
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Save the token
|
|
211
|
-
SecureUserRepository.shared.save(token: token)
|
|
212
|
-
|
|
213
|
-
print("Got the token!!!")
|
|
214
|
-
return UserToken(stringLiteral: token)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
117
|
@objc func loginMagicToken(_ call: CAPPluginCall) {
|
|
218
118
|
guard let token = call.getString("token") else {
|
|
219
119
|
call.reject("Missing token parameter")
|
|
220
120
|
return
|
|
221
121
|
}
|
|
222
|
-
|
|
122
|
+
|
|
223
123
|
print("loginMagicToken received token")
|
|
224
124
|
currentToken = token
|
|
225
125
|
tokenWaitSemaphore?.signal()
|
|
226
126
|
call.resolve()
|
|
227
127
|
}
|
|
228
|
-
|
|
128
|
+
|
|
229
129
|
private func setupTokenSubscription() {
|
|
230
130
|
// Cancel existing subscription if any
|
|
231
131
|
tokenSubscription?.cancel()
|
|
232
|
-
|
|
132
|
+
|
|
233
133
|
// Create new subscription
|
|
234
134
|
tokenSubscription = callKitPushNotificationAdapter.$deviceToken.sink { [weak self] (updatedDeviceToken: String) in
|
|
235
135
|
guard let self = self else { return }
|
|
@@ -255,9 +155,12 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
255
155
|
}
|
|
256
156
|
}
|
|
257
157
|
}
|
|
258
|
-
|
|
158
|
+
|
|
259
159
|
private func setupActiveCallSubscription() {
|
|
260
160
|
if let streamVideo = streamVideo {
|
|
161
|
+
// Track participants responses
|
|
162
|
+
var participantResponses: [String: String] = [:]
|
|
163
|
+
|
|
261
164
|
Task {
|
|
262
165
|
for await event in streamVideo.subscribe() {
|
|
263
166
|
print("Event", event)
|
|
@@ -268,6 +171,44 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
268
171
|
])
|
|
269
172
|
return
|
|
270
173
|
}
|
|
174
|
+
|
|
175
|
+
if let rejectedEvent = event.rawValue as? CallRejectedEvent {
|
|
176
|
+
let userId = rejectedEvent.user.id
|
|
177
|
+
participantResponses[userId] = "rejected"
|
|
178
|
+
notifyListeners("callEvent", data: [
|
|
179
|
+
"callId": rejectedEvent.callCid,
|
|
180
|
+
"state": "rejected",
|
|
181
|
+
"userId": userId
|
|
182
|
+
])
|
|
183
|
+
|
|
184
|
+
await checkAllParticipantsResponded(participantResponses: participantResponses)
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if let missedEvent = event.rawValue as? CallMissedEvent {
|
|
189
|
+
let userId = missedEvent.user.id
|
|
190
|
+
participantResponses[userId] = "missed"
|
|
191
|
+
notifyListeners("callEvent", data: [
|
|
192
|
+
"callId": missedEvent.callCid,
|
|
193
|
+
"state": "missed",
|
|
194
|
+
"userId": userId
|
|
195
|
+
])
|
|
196
|
+
|
|
197
|
+
await checkAllParticipantsResponded(participantResponses: participantResponses)
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if let acceptedEvent = event.rawValue as? CallAcceptedEvent {
|
|
202
|
+
let userId = acceptedEvent.user.id
|
|
203
|
+
participantResponses[userId] = "accepted"
|
|
204
|
+
notifyListeners("callEvent", data: [
|
|
205
|
+
"callId": acceptedEvent.callCid,
|
|
206
|
+
"state": "accepted",
|
|
207
|
+
"userId": userId
|
|
208
|
+
])
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
271
212
|
notifyListeners("callEvent", data: [
|
|
272
213
|
"callId": streamVideo.state.activeCall?.callId ?? "",
|
|
273
214
|
"state": event.type
|
|
@@ -290,12 +231,12 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
290
231
|
print("- Session ID: \(state.sessionId)")
|
|
291
232
|
print("- All participants: \(String(describing: state.participants))")
|
|
292
233
|
print("- Remote participants: \(String(describing: state.remoteParticipants))")
|
|
293
|
-
|
|
234
|
+
|
|
294
235
|
// Update overlay and make visible when there's an active call
|
|
295
236
|
self.overlayViewModel?.updateCall(newState)
|
|
296
237
|
self.overlayView?.isHidden = false
|
|
297
238
|
self.webView?.isOpaque = false
|
|
298
|
-
|
|
239
|
+
|
|
299
240
|
// Notify that a call has started
|
|
300
241
|
self.notifyListeners("callEvent", data: [
|
|
301
242
|
"callId": newState?.cId ?? "",
|
|
@@ -306,7 +247,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
306
247
|
self.overlayViewModel?.updateCall(nil)
|
|
307
248
|
self.overlayView?.isHidden = true
|
|
308
249
|
self.webView?.isOpaque = true
|
|
309
|
-
|
|
250
|
+
|
|
310
251
|
// Notify that call has ended
|
|
311
252
|
self.notifyListeners("callEvent", data: [
|
|
312
253
|
"callId": newState?.cId ?? "",
|
|
@@ -319,57 +260,69 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
319
260
|
}
|
|
320
261
|
}
|
|
321
262
|
}
|
|
322
|
-
|
|
263
|
+
|
|
264
|
+
private func checkAllParticipantsResponded(participantResponses: [String: String]) async {
|
|
265
|
+
let call = streamVideo?.state.activeCall
|
|
266
|
+
let totalParticipants = await call?.state.participants.count ?? 0
|
|
267
|
+
let allResponded = participantResponses.count == totalParticipants
|
|
268
|
+
let allRejectedOrMissed = participantResponses.values.allSatisfy { $0 == "rejected" || $0 == "missed" }
|
|
269
|
+
|
|
270
|
+
if allResponded && allRejectedOrMissed {
|
|
271
|
+
print("All participants have rejected or missed the call")
|
|
272
|
+
call?.leave()
|
|
273
|
+
await MainActor.run {
|
|
274
|
+
self.overlayViewModel?.updateCall(nil)
|
|
275
|
+
self.overlayView?.isHidden = true
|
|
276
|
+
self.webView?.isOpaque = true
|
|
277
|
+
self.notifyListeners("callEvent", data: [
|
|
278
|
+
"callId": callCid,
|
|
279
|
+
"state": "ended",
|
|
280
|
+
"reason": "all_rejected_or_missed"
|
|
281
|
+
])
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
323
286
|
@objc func login(_ call: CAPPluginCall) {
|
|
324
287
|
guard let token = call.getString("token"),
|
|
325
288
|
let userId = call.getString("userId"),
|
|
326
|
-
let name = call.getString("name")
|
|
327
|
-
let apiKey = call.getString("apiKey") else {
|
|
289
|
+
let name = call.getString("name") else {
|
|
328
290
|
call.reject("Missing required parameters")
|
|
329
291
|
return
|
|
330
292
|
}
|
|
331
|
-
|
|
293
|
+
|
|
332
294
|
let imageURL = call.getString("imageURL")
|
|
333
|
-
let refreshTokenConfig = call.getObject("refreshToken")
|
|
334
|
-
let refreshTokenURL = refreshTokenConfig?["url"] as? String
|
|
335
|
-
let refreshTokenHeaders = refreshTokenConfig?["headers"] as? [String: String]
|
|
336
|
-
|
|
337
295
|
let user = User(
|
|
338
296
|
id: userId,
|
|
339
297
|
name: name,
|
|
340
298
|
imageURL: imageURL.flatMap { URL(string: $0) },
|
|
341
299
|
customData: [:]
|
|
342
300
|
)
|
|
343
|
-
|
|
301
|
+
|
|
344
302
|
let credentials = UserCredentials(user: user, tokenValue: token)
|
|
345
303
|
SecureUserRepository.shared.save(user: credentials)
|
|
346
|
-
|
|
347
|
-
// Store API key and refresh config for later use
|
|
348
|
-
self.refreshTokenURL = refreshTokenURL
|
|
349
|
-
self.refreshTokenHeaders = refreshTokenHeaders
|
|
350
|
-
|
|
351
304
|
// Initialize Stream Video with new credentials
|
|
352
305
|
initializeStreamVideo()
|
|
353
|
-
|
|
306
|
+
|
|
354
307
|
if state != .initialized {
|
|
355
308
|
call.reject("Failed to initialize StreamVideo")
|
|
356
309
|
return
|
|
357
310
|
}
|
|
358
|
-
|
|
311
|
+
|
|
359
312
|
// Update the CallOverlayView with new StreamVideo instance
|
|
360
313
|
Task { @MainActor in
|
|
361
314
|
self.overlayViewModel?.updateStreamVideo(self.streamVideo)
|
|
362
315
|
}
|
|
363
|
-
|
|
316
|
+
|
|
364
317
|
call.resolve([
|
|
365
318
|
"success": true
|
|
366
319
|
])
|
|
367
320
|
}
|
|
368
|
-
|
|
321
|
+
|
|
369
322
|
@objc func logout(_ call: CAPPluginCall) {
|
|
370
323
|
// Remove VOIP token from repository
|
|
371
324
|
SecureUserRepository.shared.save(voipPushToken: nil)
|
|
372
|
-
|
|
325
|
+
|
|
373
326
|
// Try to delete the device from Stream if we have the last token
|
|
374
327
|
if let lastVoIPToken = lastVoIPToken {
|
|
375
328
|
Task {
|
|
@@ -380,16 +333,16 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
380
333
|
}
|
|
381
334
|
}
|
|
382
335
|
}
|
|
383
|
-
|
|
336
|
+
|
|
384
337
|
// Cancel subscriptions
|
|
385
338
|
tokenSubscription?.cancel()
|
|
386
339
|
tokenSubscription = nil
|
|
387
340
|
activeCallSubscription?.cancel()
|
|
388
341
|
activeCallSubscription = nil
|
|
389
342
|
lastVoIPToken = nil
|
|
390
|
-
|
|
343
|
+
|
|
391
344
|
SecureUserRepository.shared.removeCurrentUser()
|
|
392
|
-
|
|
345
|
+
|
|
393
346
|
// Update the CallOverlayView with nil StreamVideo instance
|
|
394
347
|
Task { @MainActor in
|
|
395
348
|
self.overlayViewModel?.updateCall(nil)
|
|
@@ -397,24 +350,41 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
397
350
|
self.overlayView?.isHidden = true
|
|
398
351
|
self.webView?.isOpaque = true
|
|
399
352
|
}
|
|
400
|
-
|
|
353
|
+
|
|
401
354
|
call.resolve([
|
|
402
355
|
"success": true
|
|
403
356
|
])
|
|
404
357
|
}
|
|
358
|
+
|
|
359
|
+
@objc func isCameraEnabled(_ call: CAPPluginCall) {
|
|
360
|
+
do {
|
|
361
|
+
try requireInitialized()
|
|
362
|
+
} catch {
|
|
363
|
+
call.reject("SDK not initialized")
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if let activeCall = streamVideo?.state.activeCall {
|
|
367
|
+
call.resolve([
|
|
368
|
+
"enabled": activeCall.camera.status == .enabled
|
|
369
|
+
])
|
|
370
|
+
} else {
|
|
371
|
+
call.reject("No active call")
|
|
372
|
+
}
|
|
373
|
+
}
|
|
405
374
|
|
|
406
375
|
@objc func call(_ call: CAPPluginCall) {
|
|
407
|
-
guard let
|
|
408
|
-
call.reject("Missing required parameter:
|
|
376
|
+
guard let members = call.getArray("userIds", String.self) else {
|
|
377
|
+
call.reject("Missing required parameter: userIds (array of user IDs)")
|
|
378
|
+
return
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if members.isEmpty {
|
|
382
|
+
call.reject("userIds array cannot be empty")
|
|
409
383
|
return
|
|
410
384
|
}
|
|
411
385
|
|
|
412
386
|
// Initialize if needed
|
|
413
387
|
if state == .notInitialized {
|
|
414
|
-
guard let credentials = SecureUserRepository.shared.loadCurrentUser() else {
|
|
415
|
-
call.reject("No user credentials found")
|
|
416
|
-
return
|
|
417
|
-
}
|
|
418
388
|
initializeStreamVideo()
|
|
419
389
|
if state != .initialized {
|
|
420
390
|
call.reject("Failed to initialize StreamVideo")
|
|
@@ -436,16 +406,16 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
436
406
|
print("Creating call:")
|
|
437
407
|
print("- Call ID: \(callId)")
|
|
438
408
|
print("- Call Type: \(callType)")
|
|
439
|
-
print("-
|
|
409
|
+
print("- Users: \(members)")
|
|
440
410
|
print("- Should Ring: \(shouldRing)")
|
|
441
411
|
|
|
442
412
|
// Create the call object
|
|
443
413
|
let streamCall = streamVideo?.call(callType: callType, callId: callId)
|
|
444
414
|
|
|
445
|
-
// Start the call with the
|
|
446
|
-
print("Creating call with
|
|
415
|
+
// Start the call with the members
|
|
416
|
+
print("Creating call with members...")
|
|
447
417
|
try await streamCall?.create(
|
|
448
|
-
memberIds:
|
|
418
|
+
memberIds: members,
|
|
449
419
|
custom: [:],
|
|
450
420
|
ring: shouldRing
|
|
451
421
|
)
|
|
@@ -480,26 +450,21 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
480
450
|
try requireInitialized()
|
|
481
451
|
|
|
482
452
|
Task {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
try await activeCall.leave()
|
|
486
|
-
|
|
487
|
-
// Update view state instead of cleaning up
|
|
488
|
-
await MainActor.run {
|
|
489
|
-
self.overlayViewModel?.updateCall(nil)
|
|
490
|
-
self.overlayView?.isHidden = true
|
|
491
|
-
self.webView?.isOpaque = true
|
|
492
|
-
}
|
|
453
|
+
if let activeCall = streamVideo?.state.activeCall {
|
|
454
|
+
activeCall.leave()
|
|
493
455
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
456
|
+
// Update view state instead of cleaning up
|
|
457
|
+
await MainActor.run {
|
|
458
|
+
self.overlayViewModel?.updateCall(nil)
|
|
459
|
+
self.overlayView?.isHidden = true
|
|
460
|
+
self.webView?.isOpaque = true
|
|
499
461
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
462
|
+
|
|
463
|
+
call.resolve([
|
|
464
|
+
"success": true
|
|
465
|
+
])
|
|
466
|
+
} else {
|
|
467
|
+
call.reject("No active call to end")
|
|
503
468
|
}
|
|
504
469
|
}
|
|
505
470
|
} catch {
|
|
@@ -625,26 +590,10 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
625
590
|
}
|
|
626
591
|
print("Initializing with saved credentials for user: \(savedCredentials.user.name)")
|
|
627
592
|
|
|
628
|
-
// Create a local reference to refreshToken to avoid capturing self
|
|
629
|
-
let refreshTokenFn = self.refreshToken
|
|
630
|
-
|
|
631
593
|
self.streamVideo = StreamVideo(
|
|
632
594
|
apiKey: apiKey,
|
|
633
595
|
user: savedCredentials.user,
|
|
634
|
-
token: UserToken(stringLiteral: savedCredentials.tokenValue)
|
|
635
|
-
tokenProvider: { result in
|
|
636
|
-
print("attempt to refresh")
|
|
637
|
-
DispatchQueue.global().async {
|
|
638
|
-
do {
|
|
639
|
-
let newToken = try refreshTokenFn()
|
|
640
|
-
print("Refresh successful")
|
|
641
|
-
result(.success(newToken))
|
|
642
|
-
} catch {
|
|
643
|
-
print("Refresh fail")
|
|
644
|
-
result(.failure(error))
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
}
|
|
596
|
+
token: UserToken(stringLiteral: savedCredentials.tokenValue)
|
|
648
597
|
)
|
|
649
598
|
|
|
650
599
|
state = .initialized
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/capacitor-stream-call",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Uses the https://getstream.io/ SDK to implement calling in Capacitor",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"ios/Sources",
|
|
14
14
|
"ios/Tests",
|
|
15
15
|
"Package.swift",
|
|
16
|
-
"
|
|
16
|
+
"CapgoCapacitorStreamCall.podspec"
|
|
17
17
|
],
|
|
18
18
|
"author": "Martin Donadieu <martin@capgo.app>",
|
|
19
19
|
"license": "MIT",
|