@capgo/capacitor-stream-call 0.0.2
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/Package.swift +31 -0
- package/README.md +340 -0
- package/StreamCall.podspec +19 -0
- package/android/build.gradle +74 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/CallOverlayView.kt +281 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/CustomNotificationHandler.kt +142 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/IncomingCallView.kt +147 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/RingtonePlayer.kt +164 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/StreamCallPlugin.kt +1014 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/TouchInterceptWrapper.kt +31 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/UserRepository.kt +111 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/android/src/main/res/values/strings.xml +7 -0
- package/dist/docs.json +533 -0
- package/dist/esm/definitions.d.ts +169 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +32 -0
- package/dist/esm/web.js +323 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +337 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +339 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/StreamCallPlugin/CallOverlayView.swift +147 -0
- package/ios/Sources/StreamCallPlugin/CustomCallParticipantImageView.swift +60 -0
- package/ios/Sources/StreamCallPlugin/CustomCallView.swift +257 -0
- package/ios/Sources/StreamCallPlugin/CustomVideoParticipantsView.swift +107 -0
- package/ios/Sources/StreamCallPlugin/ParticipantsView.swift +206 -0
- package/ios/Sources/StreamCallPlugin/StreamCallPlugin.swift +722 -0
- package/ios/Sources/StreamCallPlugin/TouchInterceptView.swift +177 -0
- package/ios/Sources/StreamCallPlugin/UserRepository.swift +96 -0
- package/ios/Sources/StreamCallPlugin/WebviewNavigationDelegate.swift +68 -0
- package/ios/Tests/StreamCallPluginTests/StreamCallPluginTests.swift +15 -0
- package/package.json +96 -0
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
var capacitorStreamCall = (function (exports, core, videoClient) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const StreamCall = core.registerPlugin('StreamCall', {
|
|
5
|
+
web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.StreamCallWeb()),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
class StreamCallWeb extends core.WebPlugin {
|
|
9
|
+
constructor() {
|
|
10
|
+
super(...arguments);
|
|
11
|
+
this.videoBindings = new Map();
|
|
12
|
+
this.audioBindings = new Map();
|
|
13
|
+
this.ringCallback = (event) => {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
console.log('Call ringing', event, this.currentCall);
|
|
16
|
+
this.incomingCall = event.call;
|
|
17
|
+
if (!this.currentCall) {
|
|
18
|
+
console.log('Creating new call', event.call.id);
|
|
19
|
+
this.currentCall = (_a = this.client) === null || _a === void 0 ? void 0 : _a.call(event.call.type, event.call.id);
|
|
20
|
+
this.notifyListeners('callEvent', { callId: event.call.id, state: videoClient.CallingState.RINGING });
|
|
21
|
+
}
|
|
22
|
+
if (this.currentCall) {
|
|
23
|
+
console.log('Call found', this.currentCall.id);
|
|
24
|
+
this.callStateSubscription = (_b = this.currentCall) === null || _b === void 0 ? void 0 : _b.state.callingState$.subscribe((s) => {
|
|
25
|
+
var _a;
|
|
26
|
+
console.log('Call state', s);
|
|
27
|
+
if (s === videoClient.CallingState.JOINED) {
|
|
28
|
+
this.setupParticipantListener();
|
|
29
|
+
}
|
|
30
|
+
else if (s === videoClient.CallingState.LEFT || s === videoClient.CallingState.RECONNECTING_FAILED) {
|
|
31
|
+
this.cleanupCall();
|
|
32
|
+
}
|
|
33
|
+
if (this.outgoingCall && s === videoClient.CallingState.RINGING) {
|
|
34
|
+
this.outgoingCall = undefined;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.notifyListeners('callEvent', { callId: (_a = this.currentCall) === null || _a === void 0 ? void 0 : _a.id, state: s });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
setupCallRingListener() {
|
|
44
|
+
var _a, _b;
|
|
45
|
+
(_a = this.client) === null || _a === void 0 ? void 0 : _a.off('call.ring', this.ringCallback);
|
|
46
|
+
(_b = this.client) === null || _b === void 0 ? void 0 : _b.on('call.ring', this.ringCallback);
|
|
47
|
+
}
|
|
48
|
+
setupParticipantListener() {
|
|
49
|
+
// Subscribe to participant changes
|
|
50
|
+
this.incomingCall = undefined;
|
|
51
|
+
if (!this.currentCall)
|
|
52
|
+
return;
|
|
53
|
+
this.participantJoinedListener = (event) => {
|
|
54
|
+
if (this.magicDivId && event.participant) {
|
|
55
|
+
const magicDiv = document.getElementById(this.magicDivId);
|
|
56
|
+
if (magicDiv && this.currentCall) {
|
|
57
|
+
this.setupParticipantVideo(this.currentCall, event.participant, magicDiv);
|
|
58
|
+
this.setupParticipantAudio(this.currentCall, event.participant, magicDiv);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
this.participantLeftListener = (event) => {
|
|
63
|
+
if (this.magicDivId && event.participant) {
|
|
64
|
+
const videoId = `video-${event.participant.sessionId}`;
|
|
65
|
+
const audioId = `audio-${event.participant.sessionId}`;
|
|
66
|
+
// Remove video element
|
|
67
|
+
const videoEl = document.getElementById(videoId);
|
|
68
|
+
if (videoEl) {
|
|
69
|
+
const unbindVideo = this.videoBindings.get(videoId);
|
|
70
|
+
if (unbindVideo) {
|
|
71
|
+
unbindVideo();
|
|
72
|
+
this.videoBindings.delete(videoId);
|
|
73
|
+
}
|
|
74
|
+
const tracks = videoEl.srcObject;
|
|
75
|
+
if (tracks) {
|
|
76
|
+
tracks.getTracks().forEach((track) => {
|
|
77
|
+
track.stop();
|
|
78
|
+
track.enabled = false;
|
|
79
|
+
});
|
|
80
|
+
videoEl.srcObject = null;
|
|
81
|
+
}
|
|
82
|
+
videoEl.remove();
|
|
83
|
+
}
|
|
84
|
+
// Remove audio element
|
|
85
|
+
const audioEl = document.getElementById(audioId);
|
|
86
|
+
if (audioEl) {
|
|
87
|
+
const unbindAudio = this.audioBindings.get(audioId);
|
|
88
|
+
if (unbindAudio) {
|
|
89
|
+
unbindAudio();
|
|
90
|
+
this.audioBindings.delete(audioId);
|
|
91
|
+
}
|
|
92
|
+
const tracks = audioEl.srcObject;
|
|
93
|
+
if (tracks) {
|
|
94
|
+
tracks.getTracks().forEach((track) => {
|
|
95
|
+
track.stop();
|
|
96
|
+
track.enabled = false;
|
|
97
|
+
});
|
|
98
|
+
audioEl.srcObject = null;
|
|
99
|
+
}
|
|
100
|
+
audioEl.remove();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
this.currentCall.on('participantJoined', this.participantJoinedListener);
|
|
105
|
+
this.currentCall.on('participantLeft', this.participantLeftListener);
|
|
106
|
+
// Setup initial participants
|
|
107
|
+
const participants = this.currentCall.state.participants;
|
|
108
|
+
if (this.magicDivId) {
|
|
109
|
+
const magicDiv = document.getElementById(this.magicDivId);
|
|
110
|
+
if (magicDiv) {
|
|
111
|
+
participants.forEach((participant) => {
|
|
112
|
+
if (this.currentCall) {
|
|
113
|
+
this.setupParticipantVideo(this.currentCall, participant, magicDiv);
|
|
114
|
+
this.setupParticipantAudio(this.currentCall, participant, magicDiv);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
setupParticipantVideo(call, participant, container) {
|
|
121
|
+
const id = `video-${participant.sessionId}`;
|
|
122
|
+
if (!document.getElementById(id)) {
|
|
123
|
+
const videoEl = document.createElement('video');
|
|
124
|
+
videoEl.id = id;
|
|
125
|
+
videoEl.style.width = '100%';
|
|
126
|
+
videoEl.style.maxWidth = '300px';
|
|
127
|
+
videoEl.style.aspectRatio = '16/9';
|
|
128
|
+
container.appendChild(videoEl);
|
|
129
|
+
const unbind = call.bindVideoElement(videoEl, participant.sessionId, 'videoTrack');
|
|
130
|
+
if (unbind)
|
|
131
|
+
this.videoBindings.set(id, unbind);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
setupParticipantAudio(call, participant, container) {
|
|
135
|
+
if (participant.isLocalParticipant)
|
|
136
|
+
return;
|
|
137
|
+
const id = `audio-${participant.sessionId}`;
|
|
138
|
+
if (!document.getElementById(id)) {
|
|
139
|
+
const audioEl = document.createElement('audio');
|
|
140
|
+
audioEl.id = id;
|
|
141
|
+
container.appendChild(audioEl);
|
|
142
|
+
const unbind = call.bindAudioElement(audioEl, participant.sessionId);
|
|
143
|
+
if (unbind)
|
|
144
|
+
this.audioBindings.set(id, unbind);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
cleanupCall() {
|
|
148
|
+
var _a;
|
|
149
|
+
// First cleanup the call listeners
|
|
150
|
+
if (this.currentCall) {
|
|
151
|
+
if (this.participantJoinedListener) {
|
|
152
|
+
this.currentCall.off('participantJoined', this.participantJoinedListener);
|
|
153
|
+
this.participantJoinedListener = undefined;
|
|
154
|
+
}
|
|
155
|
+
if (this.participantLeftListener) {
|
|
156
|
+
this.currentCall.off('participantLeft', this.participantLeftListener);
|
|
157
|
+
this.participantLeftListener = undefined;
|
|
158
|
+
}
|
|
159
|
+
(_a = this.callStateSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
|
|
160
|
+
}
|
|
161
|
+
if (this.magicDivId) {
|
|
162
|
+
const magicDiv = document.getElementById(this.magicDivId);
|
|
163
|
+
if (magicDiv) {
|
|
164
|
+
// Remove all video elements
|
|
165
|
+
const videoElements = magicDiv.querySelectorAll('video');
|
|
166
|
+
videoElements.forEach((video) => {
|
|
167
|
+
const id = video.id;
|
|
168
|
+
const unbind = this.videoBindings.get(id);
|
|
169
|
+
if (unbind) {
|
|
170
|
+
unbind();
|
|
171
|
+
this.videoBindings.delete(id);
|
|
172
|
+
}
|
|
173
|
+
// Stop all tracks
|
|
174
|
+
const tracks = video.srcObject;
|
|
175
|
+
if (tracks) {
|
|
176
|
+
tracks.getTracks().forEach((track) => {
|
|
177
|
+
track.stop();
|
|
178
|
+
track.enabled = false;
|
|
179
|
+
});
|
|
180
|
+
video.srcObject = null;
|
|
181
|
+
}
|
|
182
|
+
video.remove();
|
|
183
|
+
});
|
|
184
|
+
// Remove all audio elements
|
|
185
|
+
const audioElements = magicDiv.querySelectorAll('audio');
|
|
186
|
+
audioElements.forEach((audio) => {
|
|
187
|
+
const id = audio.id;
|
|
188
|
+
const unbind = this.audioBindings.get(id);
|
|
189
|
+
if (unbind) {
|
|
190
|
+
unbind();
|
|
191
|
+
this.audioBindings.delete(id);
|
|
192
|
+
}
|
|
193
|
+
// Stop all tracks
|
|
194
|
+
const tracks = audio.srcObject;
|
|
195
|
+
if (tracks) {
|
|
196
|
+
tracks.getTracks().forEach((track) => {
|
|
197
|
+
track.stop();
|
|
198
|
+
track.enabled = false;
|
|
199
|
+
});
|
|
200
|
+
audio.srcObject = null;
|
|
201
|
+
}
|
|
202
|
+
audio.remove();
|
|
203
|
+
});
|
|
204
|
+
// Clear the container
|
|
205
|
+
while (magicDiv.firstChild) {
|
|
206
|
+
magicDiv.removeChild(magicDiv.firstChild);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Clear all bindings
|
|
211
|
+
this.videoBindings.clear();
|
|
212
|
+
this.audioBindings.clear();
|
|
213
|
+
// Clear call references
|
|
214
|
+
this.currentCall = undefined;
|
|
215
|
+
this.incomingCall = undefined;
|
|
216
|
+
}
|
|
217
|
+
async login(options) {
|
|
218
|
+
this.client = videoClient.StreamVideoClient.getOrCreateInstance({
|
|
219
|
+
apiKey: options.apiKey,
|
|
220
|
+
user: { id: options.userId, name: options.name, image: options.imageURL },
|
|
221
|
+
token: options.token,
|
|
222
|
+
});
|
|
223
|
+
this.magicDivId = options.magicDivId;
|
|
224
|
+
this.setupCallRingListener();
|
|
225
|
+
return { success: true };
|
|
226
|
+
}
|
|
227
|
+
async logout() {
|
|
228
|
+
var _a;
|
|
229
|
+
if (!this.client) {
|
|
230
|
+
console.log('No client', this.client);
|
|
231
|
+
throw new Error('Client not initialized');
|
|
232
|
+
}
|
|
233
|
+
// Cleanup subscription
|
|
234
|
+
(_a = this.callStateSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
|
|
235
|
+
this.callStateSubscription = undefined;
|
|
236
|
+
await this.client.disconnectUser();
|
|
237
|
+
this.client = undefined;
|
|
238
|
+
this.currentCall = undefined;
|
|
239
|
+
return { success: true };
|
|
240
|
+
}
|
|
241
|
+
async call(options) {
|
|
242
|
+
if (!this.client) {
|
|
243
|
+
console.log('No client', this.client);
|
|
244
|
+
throw new Error('Client not initialized - Please login first');
|
|
245
|
+
}
|
|
246
|
+
const call = this.client.call(options.type || 'default', crypto.randomUUID());
|
|
247
|
+
const members = [{ user_id: options.userId }];
|
|
248
|
+
if (this.client.streamClient.userID && options.userId !== this.client.streamClient.userID) {
|
|
249
|
+
members.push({ user_id: this.client.streamClient.userID });
|
|
250
|
+
}
|
|
251
|
+
await call.getOrCreate({ data: { members } });
|
|
252
|
+
this.currentCall = call;
|
|
253
|
+
if (options.ring) {
|
|
254
|
+
this.outgoingCall = call.cid;
|
|
255
|
+
await call.ring();
|
|
256
|
+
}
|
|
257
|
+
await call.join();
|
|
258
|
+
return { success: true };
|
|
259
|
+
}
|
|
260
|
+
async endCall() {
|
|
261
|
+
if (!this.currentCall) {
|
|
262
|
+
console.log('No active call', this.currentCall);
|
|
263
|
+
throw new Error('No active call');
|
|
264
|
+
}
|
|
265
|
+
await this.currentCall.leave();
|
|
266
|
+
this.currentCall = undefined;
|
|
267
|
+
this.cleanupCall();
|
|
268
|
+
return { success: true };
|
|
269
|
+
}
|
|
270
|
+
async setMicrophoneEnabled(options) {
|
|
271
|
+
if (!this.currentCall) {
|
|
272
|
+
console.log('No active call', this.currentCall);
|
|
273
|
+
throw new Error('No active call');
|
|
274
|
+
}
|
|
275
|
+
if (options.enabled) {
|
|
276
|
+
await this.currentCall.microphone.enable();
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
await this.currentCall.microphone.disable();
|
|
280
|
+
}
|
|
281
|
+
return { success: true };
|
|
282
|
+
}
|
|
283
|
+
async setCameraEnabled(options) {
|
|
284
|
+
if (!this.currentCall) {
|
|
285
|
+
console.log('No active call', this.currentCall);
|
|
286
|
+
throw new Error('No active call');
|
|
287
|
+
}
|
|
288
|
+
if (options.enabled) {
|
|
289
|
+
await this.currentCall.camera.enable();
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
await this.currentCall.camera.disable();
|
|
293
|
+
}
|
|
294
|
+
return { success: true };
|
|
295
|
+
}
|
|
296
|
+
async acceptCall() {
|
|
297
|
+
if (!this.incomingCall || !this.client) {
|
|
298
|
+
console.log('No incoming call to accept', this.incomingCall, this.client);
|
|
299
|
+
throw new Error('No incoming call to accept');
|
|
300
|
+
}
|
|
301
|
+
console.log('Accepting call', this.incomingCall);
|
|
302
|
+
const call = this.client.call(this.incomingCall.type, this.incomingCall.id);
|
|
303
|
+
this.currentCall = call;
|
|
304
|
+
console.log('Joining call', call);
|
|
305
|
+
await call.accept();
|
|
306
|
+
await call.join();
|
|
307
|
+
console.log('Joined call', call);
|
|
308
|
+
this.notifyListeners('callEvent', { callId: call.id, state: videoClient.CallingState.JOINED });
|
|
309
|
+
this.setupParticipantListener();
|
|
310
|
+
return { success: true };
|
|
311
|
+
}
|
|
312
|
+
async rejectCall() {
|
|
313
|
+
if (!this.incomingCall || !this.client) {
|
|
314
|
+
console.log('No incoming call to reject', this.incomingCall, this.client);
|
|
315
|
+
throw new Error('No incoming call to reject');
|
|
316
|
+
}
|
|
317
|
+
console.log('Rejecting call', this.incomingCall);
|
|
318
|
+
const call = this.client.call(this.incomingCall.type, this.incomingCall.id);
|
|
319
|
+
console.log('Leaving call', call);
|
|
320
|
+
await call.leave();
|
|
321
|
+
this.incomingCall = undefined;
|
|
322
|
+
console.log('Rejected call', call);
|
|
323
|
+
this.notifyListeners('callEvent', { callId: call.id, state: videoClient.CallingState.LEFT });
|
|
324
|
+
this.cleanupCall();
|
|
325
|
+
return { success: true };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
var web = /*#__PURE__*/Object.freeze({
|
|
330
|
+
__proto__: null,
|
|
331
|
+
StreamCallWeb: StreamCallWeb
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
exports.StreamCall = StreamCall;
|
|
335
|
+
|
|
336
|
+
return exports;
|
|
337
|
+
|
|
338
|
+
})({}, capacitorExports, videoClient);
|
|
339
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst StreamCall = registerPlugin('StreamCall', {\n web: () => import('./web').then((m) => new m.StreamCallWeb()),\n});\nexport * from './definitions';\nexport { StreamCall };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nimport { CallingState, StreamVideoClient } from '@stream-io/video-client';\nexport class StreamCallWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.videoBindings = new Map();\n this.audioBindings = new Map();\n this.ringCallback = (event) => {\n var _a, _b;\n console.log('Call ringing', event, this.currentCall);\n this.incomingCall = event.call;\n if (!this.currentCall) {\n console.log('Creating new call', event.call.id);\n this.currentCall = (_a = this.client) === null || _a === void 0 ? void 0 : _a.call(event.call.type, event.call.id);\n this.notifyListeners('callEvent', { callId: event.call.id, state: CallingState.RINGING });\n }\n if (this.currentCall) {\n console.log('Call found', this.currentCall.id);\n this.callStateSubscription = (_b = this.currentCall) === null || _b === void 0 ? void 0 : _b.state.callingState$.subscribe((s) => {\n var _a;\n console.log('Call state', s);\n if (s === CallingState.JOINED) {\n this.setupParticipantListener();\n }\n else if (s === CallingState.LEFT || s === CallingState.RECONNECTING_FAILED) {\n this.cleanupCall();\n }\n if (this.outgoingCall && s === CallingState.RINGING) {\n this.outgoingCall = undefined;\n }\n else {\n this.notifyListeners('callEvent', { callId: (_a = this.currentCall) === null || _a === void 0 ? void 0 : _a.id, state: s });\n }\n });\n }\n };\n }\n setupCallRingListener() {\n var _a, _b;\n (_a = this.client) === null || _a === void 0 ? void 0 : _a.off('call.ring', this.ringCallback);\n (_b = this.client) === null || _b === void 0 ? void 0 : _b.on('call.ring', this.ringCallback);\n }\n setupParticipantListener() {\n // Subscribe to participant changes\n this.incomingCall = undefined;\n if (!this.currentCall)\n return;\n this.participantJoinedListener = (event) => {\n if (this.magicDivId && event.participant) {\n const magicDiv = document.getElementById(this.magicDivId);\n if (magicDiv && this.currentCall) {\n this.setupParticipantVideo(this.currentCall, event.participant, magicDiv);\n this.setupParticipantAudio(this.currentCall, event.participant, magicDiv);\n }\n }\n };\n this.participantLeftListener = (event) => {\n if (this.magicDivId && event.participant) {\n const videoId = `video-${event.participant.sessionId}`;\n const audioId = `audio-${event.participant.sessionId}`;\n // Remove video element\n const videoEl = document.getElementById(videoId);\n if (videoEl) {\n const unbindVideo = this.videoBindings.get(videoId);\n if (unbindVideo) {\n unbindVideo();\n this.videoBindings.delete(videoId);\n }\n const tracks = videoEl.srcObject;\n if (tracks) {\n tracks.getTracks().forEach((track) => {\n track.stop();\n track.enabled = false;\n });\n videoEl.srcObject = null;\n }\n videoEl.remove();\n }\n // Remove audio element\n const audioEl = document.getElementById(audioId);\n if (audioEl) {\n const unbindAudio = this.audioBindings.get(audioId);\n if (unbindAudio) {\n unbindAudio();\n this.audioBindings.delete(audioId);\n }\n const tracks = audioEl.srcObject;\n if (tracks) {\n tracks.getTracks().forEach((track) => {\n track.stop();\n track.enabled = false;\n });\n audioEl.srcObject = null;\n }\n audioEl.remove();\n }\n }\n };\n this.currentCall.on('participantJoined', this.participantJoinedListener);\n this.currentCall.on('participantLeft', this.participantLeftListener);\n // Setup initial participants\n const participants = this.currentCall.state.participants;\n if (this.magicDivId) {\n const magicDiv = document.getElementById(this.magicDivId);\n if (magicDiv) {\n participants.forEach((participant) => {\n if (this.currentCall) {\n this.setupParticipantVideo(this.currentCall, participant, magicDiv);\n this.setupParticipantAudio(this.currentCall, participant, magicDiv);\n }\n });\n }\n }\n }\n setupParticipantVideo(call, participant, container) {\n const id = `video-${participant.sessionId}`;\n if (!document.getElementById(id)) {\n const videoEl = document.createElement('video');\n videoEl.id = id;\n videoEl.style.width = '100%';\n videoEl.style.maxWidth = '300px';\n videoEl.style.aspectRatio = '16/9';\n container.appendChild(videoEl);\n const unbind = call.bindVideoElement(videoEl, participant.sessionId, 'videoTrack');\n if (unbind)\n this.videoBindings.set(id, unbind);\n }\n }\n setupParticipantAudio(call, participant, container) {\n if (participant.isLocalParticipant)\n return;\n const id = `audio-${participant.sessionId}`;\n if (!document.getElementById(id)) {\n const audioEl = document.createElement('audio');\n audioEl.id = id;\n container.appendChild(audioEl);\n const unbind = call.bindAudioElement(audioEl, participant.sessionId);\n if (unbind)\n this.audioBindings.set(id, unbind);\n }\n }\n cleanupCall() {\n var _a;\n // First cleanup the call listeners\n if (this.currentCall) {\n if (this.participantJoinedListener) {\n this.currentCall.off('participantJoined', this.participantJoinedListener);\n this.participantJoinedListener = undefined;\n }\n if (this.participantLeftListener) {\n this.currentCall.off('participantLeft', this.participantLeftListener);\n this.participantLeftListener = undefined;\n }\n (_a = this.callStateSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();\n }\n if (this.magicDivId) {\n const magicDiv = document.getElementById(this.magicDivId);\n if (magicDiv) {\n // Remove all video elements\n const videoElements = magicDiv.querySelectorAll('video');\n videoElements.forEach((video) => {\n const id = video.id;\n const unbind = this.videoBindings.get(id);\n if (unbind) {\n unbind();\n this.videoBindings.delete(id);\n }\n // Stop all tracks\n const tracks = video.srcObject;\n if (tracks) {\n tracks.getTracks().forEach((track) => {\n track.stop();\n track.enabled = false;\n });\n video.srcObject = null;\n }\n video.remove();\n });\n // Remove all audio elements\n const audioElements = magicDiv.querySelectorAll('audio');\n audioElements.forEach((audio) => {\n const id = audio.id;\n const unbind = this.audioBindings.get(id);\n if (unbind) {\n unbind();\n this.audioBindings.delete(id);\n }\n // Stop all tracks\n const tracks = audio.srcObject;\n if (tracks) {\n tracks.getTracks().forEach((track) => {\n track.stop();\n track.enabled = false;\n });\n audio.srcObject = null;\n }\n audio.remove();\n });\n // Clear the container\n while (magicDiv.firstChild) {\n magicDiv.removeChild(magicDiv.firstChild);\n }\n }\n }\n // Clear all bindings\n this.videoBindings.clear();\n this.audioBindings.clear();\n // Clear call references\n this.currentCall = undefined;\n this.incomingCall = undefined;\n }\n async login(options) {\n this.client = StreamVideoClient.getOrCreateInstance({\n apiKey: options.apiKey,\n user: { id: options.userId, name: options.name, image: options.imageURL },\n token: options.token,\n });\n this.magicDivId = options.magicDivId;\n this.setupCallRingListener();\n return { success: true };\n }\n async logout() {\n var _a;\n if (!this.client) {\n console.log('No client', this.client);\n throw new Error('Client not initialized');\n }\n // Cleanup subscription\n (_a = this.callStateSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();\n this.callStateSubscription = undefined;\n await this.client.disconnectUser();\n this.client = undefined;\n this.currentCall = undefined;\n return { success: true };\n }\n async call(options) {\n if (!this.client) {\n console.log('No client', this.client);\n throw new Error('Client not initialized - Please login first');\n }\n const call = this.client.call(options.type || 'default', crypto.randomUUID());\n const members = [{ user_id: options.userId }];\n if (this.client.streamClient.userID && options.userId !== this.client.streamClient.userID) {\n members.push({ user_id: this.client.streamClient.userID });\n }\n await call.getOrCreate({ data: { members } });\n this.currentCall = call;\n if (options.ring) {\n this.outgoingCall = call.cid;\n await call.ring();\n }\n await call.join();\n return { success: true };\n }\n async endCall() {\n if (!this.currentCall) {\n console.log('No active call', this.currentCall);\n throw new Error('No active call');\n }\n await this.currentCall.leave();\n this.currentCall = undefined;\n this.cleanupCall();\n return { success: true };\n }\n async setMicrophoneEnabled(options) {\n if (!this.currentCall) {\n console.log('No active call', this.currentCall);\n throw new Error('No active call');\n }\n if (options.enabled) {\n await this.currentCall.microphone.enable();\n }\n else {\n await this.currentCall.microphone.disable();\n }\n return { success: true };\n }\n async setCameraEnabled(options) {\n if (!this.currentCall) {\n console.log('No active call', this.currentCall);\n throw new Error('No active call');\n }\n if (options.enabled) {\n await this.currentCall.camera.enable();\n }\n else {\n await this.currentCall.camera.disable();\n }\n return { success: true };\n }\n async acceptCall() {\n if (!this.incomingCall || !this.client) {\n console.log('No incoming call to accept', this.incomingCall, this.client);\n throw new Error('No incoming call to accept');\n }\n console.log('Accepting call', this.incomingCall);\n const call = this.client.call(this.incomingCall.type, this.incomingCall.id);\n this.currentCall = call;\n console.log('Joining call', call);\n await call.accept();\n await call.join();\n console.log('Joined call', call);\n this.notifyListeners('callEvent', { callId: call.id, state: CallingState.JOINED });\n this.setupParticipantListener();\n return { success: true };\n }\n async rejectCall() {\n if (!this.incomingCall || !this.client) {\n console.log('No incoming call to reject', this.incomingCall, this.client);\n throw new Error('No incoming call to reject');\n }\n console.log('Rejecting call', this.incomingCall);\n const call = this.client.call(this.incomingCall.type, this.incomingCall.id);\n console.log('Leaving call', call);\n await call.leave();\n this.incomingCall = undefined;\n console.log('Rejected call', call);\n this.notifyListeners('callEvent', { callId: call.id, state: CallingState.LEFT });\n this.cleanupCall();\n return { success: true };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin","CallingState","StreamVideoClient"],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
import StreamVideo
|
|
3
|
+
import StreamVideoSwiftUI
|
|
4
|
+
import Combine
|
|
5
|
+
|
|
6
|
+
class CallOverlayViewModel: ObservableObject {
|
|
7
|
+
@Published var streamVideo: StreamVideo?
|
|
8
|
+
@Published var call: Call?
|
|
9
|
+
@Published var callState: CallState?
|
|
10
|
+
@Published var viewModel: CallViewModel?
|
|
11
|
+
@Published var participants: [CallParticipant] = []
|
|
12
|
+
|
|
13
|
+
private var participantsSubscription: AnyCancellable?
|
|
14
|
+
|
|
15
|
+
init(streamVideo: StreamVideo?) {
|
|
16
|
+
self.streamVideo = streamVideo
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@MainActor
|
|
20
|
+
func updateCall(_ call: Call?) {
|
|
21
|
+
self.call = call
|
|
22
|
+
// Clean up previous subscription if any
|
|
23
|
+
participantsSubscription?.cancel()
|
|
24
|
+
|
|
25
|
+
if let call = call {
|
|
26
|
+
participantsSubscription = call.state.$participants.sink { [weak self] participants in
|
|
27
|
+
print("Participants update \(participants.map { $0.name })")
|
|
28
|
+
self?.participants = participants
|
|
29
|
+
}
|
|
30
|
+
self.callState = call.state
|
|
31
|
+
participantsSubscription = call.state.$callSettings.sink { [weak self] callSettings in
|
|
32
|
+
print("Call settings update")
|
|
33
|
+
self?.viewModel = CallViewModel(callSettings: callSettings)
|
|
34
|
+
self?.viewModel?.setActiveCall(call)
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
// Clear participants when call ends
|
|
38
|
+
self.participants = []
|
|
39
|
+
self.callState = nil
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@MainActor
|
|
44
|
+
func updateStreamVideo(_ streamVideo: StreamVideo?) {
|
|
45
|
+
self.streamVideo = streamVideo
|
|
46
|
+
if streamVideo == nil {
|
|
47
|
+
self.call = nil
|
|
48
|
+
self.callState = nil
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class CallOverlayViewFactory: ViewFactory {
|
|
54
|
+
// ... existing ViewFactory methods ...
|
|
55
|
+
func makeVideoParticipantView(
|
|
56
|
+
participant: CallParticipant,
|
|
57
|
+
id: String,
|
|
58
|
+
availableFrame: CGRect,
|
|
59
|
+
contentMode: UIView.ContentMode,
|
|
60
|
+
customData: [String: RawJSON],
|
|
61
|
+
call: Call?
|
|
62
|
+
) -> some View {
|
|
63
|
+
VideoCallParticipantView(
|
|
64
|
+
viewFactory: self,
|
|
65
|
+
participant: participant,
|
|
66
|
+
id: id,
|
|
67
|
+
availableFrame: availableFrame,
|
|
68
|
+
contentMode: .scaleAspectFit,
|
|
69
|
+
customData: customData,
|
|
70
|
+
call: call
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
struct CallOverlayView: View {
|
|
76
|
+
@ObservedObject var viewModel: CallOverlayViewModel
|
|
77
|
+
@State private var safeAreaInsets: EdgeInsets = .init()
|
|
78
|
+
private let viewFactory: CallOverlayViewFactory
|
|
79
|
+
|
|
80
|
+
init(viewModel: CallOverlayViewModel) {
|
|
81
|
+
self.viewModel = viewModel
|
|
82
|
+
self.viewFactory = CallOverlayViewFactory()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
var body: some View {
|
|
86
|
+
VStack(spacing: 0) {
|
|
87
|
+
if let viewModelStandard = viewModel.viewModel {
|
|
88
|
+
ZStack {
|
|
89
|
+
CustomCallView(viewFactory: viewFactory, viewModel: viewModelStandard)
|
|
90
|
+
}
|
|
91
|
+
.padding(.top, safeAreaInsets.top)
|
|
92
|
+
.padding(.bottom, safeAreaInsets.bottom)
|
|
93
|
+
} else {
|
|
94
|
+
Color.white
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
.edgesIgnoringSafeArea(.all)
|
|
98
|
+
.overlay(
|
|
99
|
+
GeometryReader { geometry in
|
|
100
|
+
Color.clear
|
|
101
|
+
.preference(key: SafeAreaInsetsKey.self, value: geometry.safeAreaInsets)
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
.onPreferenceChange(SafeAreaInsetsKey.self) { value in
|
|
105
|
+
safeAreaInsets = value
|
|
106
|
+
}
|
|
107
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private func changeTrackVisibility(_ participant: CallParticipant?, isVisible: Bool) {
|
|
111
|
+
print("changeTrackVisibility for \(participant?.userId), visible: \(isVisible)")
|
|
112
|
+
guard let participant = participant,
|
|
113
|
+
let call = viewModel.call else { return }
|
|
114
|
+
Task {
|
|
115
|
+
await call.changeTrackVisibility(for: participant, isVisible: isVisible)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
extension CallOverlayView {
|
|
121
|
+
static func create(streamVideo: StreamVideo?) -> (UIHostingController<CallOverlayView>, CallOverlayViewModel) {
|
|
122
|
+
let viewModel = CallOverlayViewModel(streamVideo: streamVideo)
|
|
123
|
+
let view = CallOverlayView(viewModel: viewModel)
|
|
124
|
+
let hostingController = UIHostingController(rootView: view)
|
|
125
|
+
hostingController.view.backgroundColor = .clear
|
|
126
|
+
|
|
127
|
+
// Make sure we respect safe areas
|
|
128
|
+
hostingController.view.insetsLayoutMarginsFromSafeArea = true
|
|
129
|
+
|
|
130
|
+
return (hostingController, viewModel)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
#if DEBUG
|
|
135
|
+
struct CallOverlayView_Previews: PreviewProvider {
|
|
136
|
+
static var previews: some View {
|
|
137
|
+
CallOverlayView(viewModel: CallOverlayViewModel(streamVideo: nil))
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
#endif
|
|
141
|
+
|
|
142
|
+
struct SafeAreaInsetsKey: PreferenceKey {
|
|
143
|
+
static var defaultValue: EdgeInsets = .init()
|
|
144
|
+
static func reduce(value: inout EdgeInsets, nextValue: () -> EdgeInsets) {
|
|
145
|
+
value = nextValue()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
import StreamVideo
|
|
3
|
+
import StreamVideoSwiftUI
|
|
4
|
+
|
|
5
|
+
struct CircledTitleView: View {
|
|
6
|
+
|
|
7
|
+
@Injected(\.colors) var colors
|
|
8
|
+
@Injected(\.fonts) var fonts
|
|
9
|
+
|
|
10
|
+
var title: String
|
|
11
|
+
var size: CGFloat = 172 // .expandedAvatarSize
|
|
12
|
+
|
|
13
|
+
var body: some View {
|
|
14
|
+
ZStack {
|
|
15
|
+
Circle()
|
|
16
|
+
.foregroundColor(colors.tintColor)
|
|
17
|
+
Text(title)
|
|
18
|
+
.foregroundColor(.white)
|
|
19
|
+
.font(fonts.title)
|
|
20
|
+
.minimumScaleFactor(0.4)
|
|
21
|
+
.padding()
|
|
22
|
+
}
|
|
23
|
+
.frame(maxWidth: size, maxHeight: size)
|
|
24
|
+
// .modifier(ShadowModifier())
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
struct CustomCallParticipantImageView<Factory: ViewFactory>: View {
|
|
29
|
+
var viewFactory: Factory
|
|
30
|
+
var id: String
|
|
31
|
+
var name: String
|
|
32
|
+
var imageURL: URL?
|
|
33
|
+
var size: CGFloat = 90
|
|
34
|
+
// var frame: CGSize?
|
|
35
|
+
|
|
36
|
+
@Injected(\.colors) var colors
|
|
37
|
+
|
|
38
|
+
var body: some View {
|
|
39
|
+
StreamLazyImage(imageURL: imageURL) {
|
|
40
|
+
Color(colors.participantBackground)
|
|
41
|
+
}
|
|
42
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
43
|
+
.blur(radius: 8)
|
|
44
|
+
.clipped()
|
|
45
|
+
.overlay(
|
|
46
|
+
viewFactory.makeUserAvatar(
|
|
47
|
+
.init(id: id, name: name, imageURL: imageURL),
|
|
48
|
+
with: .init(size: size) {
|
|
49
|
+
AnyView(
|
|
50
|
+
CircledTitleView(
|
|
51
|
+
title: name.isEmpty ? id : String(name.uppercased().first!),
|
|
52
|
+
size: size
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
.clipShape(Rectangle())
|
|
59
|
+
}
|
|
60
|
+
}
|