@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
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @interface LoginOptions
|
|
3
|
+
* @description Configuration options for logging into the Stream Video service
|
|
4
|
+
* @property {string} token - Stream Video API token for authentication
|
|
5
|
+
* @property {string} userId - Unique identifier for the current user
|
|
6
|
+
* @property {string} name - Display name for the current user
|
|
7
|
+
* @property {string} [imageURL] - Avatar URL for the current user
|
|
8
|
+
* @property {string} apiKey - Stream Video API key for your application
|
|
9
|
+
* @property {string} [magicDivId] - DOM element ID where video will be rendered
|
|
10
|
+
* @property {Object} [refreshToken] - Token refresh configuration
|
|
11
|
+
* @property {string} refreshToken.url - Endpoint URL for token refresh
|
|
12
|
+
* @property {Record<string, string>} [refreshToken.headers] - Custom headers for refresh request
|
|
13
|
+
*/
|
|
14
|
+
export interface LoginOptions {
|
|
15
|
+
/** Stream Video API token */
|
|
16
|
+
token: string;
|
|
17
|
+
/** User ID for the current user */
|
|
18
|
+
userId: string;
|
|
19
|
+
/** Display name for the current user */
|
|
20
|
+
name: string;
|
|
21
|
+
/** Optional avatar URL for the current user */
|
|
22
|
+
imageURL?: string;
|
|
23
|
+
/** Stream Video API key */
|
|
24
|
+
apiKey: string;
|
|
25
|
+
/** ID of the HTML element where the video will be rendered */
|
|
26
|
+
magicDivId?: string;
|
|
27
|
+
/** Configuration for token refresh */
|
|
28
|
+
refreshToken?: {
|
|
29
|
+
/** URL to call for refreshing the token */
|
|
30
|
+
url: string;
|
|
31
|
+
/** Optional headers to include in the refresh request */
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* @interface CallOptions
|
|
37
|
+
* @description Options for initiating a video call
|
|
38
|
+
* @property {string} userId - ID of the user to call
|
|
39
|
+
* @property {string} [type=default] - Type of call
|
|
40
|
+
* @property {boolean} [ring=true] - Whether to send ring notification
|
|
41
|
+
*/
|
|
42
|
+
export interface CallOptions {
|
|
43
|
+
/** User ID of the person to call */
|
|
44
|
+
userId: string;
|
|
45
|
+
/** Type of call, defaults to 'default' */
|
|
46
|
+
type?: string;
|
|
47
|
+
/** Whether to ring the other user, defaults to true */
|
|
48
|
+
ring?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @interface SuccessResponse
|
|
52
|
+
* @description Standard response indicating operation success/failure
|
|
53
|
+
* @property {boolean} success - Whether the operation succeeded
|
|
54
|
+
*/
|
|
55
|
+
export interface SuccessResponse {
|
|
56
|
+
/** Whether the operation was successful */
|
|
57
|
+
success: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @interface CallEvent
|
|
61
|
+
* @description Event emitted when call state changes
|
|
62
|
+
* @property {string} callId - Unique identifier of the call
|
|
63
|
+
* @property {string} state - Current state of the call (joined, left, ringing, etc)
|
|
64
|
+
*/
|
|
65
|
+
export interface CallEvent {
|
|
66
|
+
/** ID of the call */
|
|
67
|
+
callId: string;
|
|
68
|
+
/** Current state of the call */
|
|
69
|
+
state: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @interface StreamCallPlugin
|
|
73
|
+
* @description Capacitor plugin for Stream Video calling functionality
|
|
74
|
+
*/
|
|
75
|
+
export interface StreamCallPlugin {
|
|
76
|
+
/**
|
|
77
|
+
* Login to Stream Video service
|
|
78
|
+
* @param {LoginOptions} options - Login configuration
|
|
79
|
+
* @returns {Promise<SuccessResponse>} Success status
|
|
80
|
+
* @example
|
|
81
|
+
* await StreamCall.login({
|
|
82
|
+
* token: 'your-token',
|
|
83
|
+
* userId: 'user-123',
|
|
84
|
+
* name: 'John Doe',
|
|
85
|
+
* apiKey: 'your-api-key'
|
|
86
|
+
* });
|
|
87
|
+
*/
|
|
88
|
+
login(options: LoginOptions): Promise<SuccessResponse>;
|
|
89
|
+
/**
|
|
90
|
+
* Logout from Stream Video service
|
|
91
|
+
* @returns {Promise<SuccessResponse>} Success status
|
|
92
|
+
* @example
|
|
93
|
+
* await StreamCall.logout();
|
|
94
|
+
*/
|
|
95
|
+
logout(): Promise<SuccessResponse>;
|
|
96
|
+
/**
|
|
97
|
+
* Initiate a call to another user
|
|
98
|
+
* @param {CallOptions} options - Call configuration
|
|
99
|
+
* @returns {Promise<SuccessResponse>} Success status
|
|
100
|
+
* @example
|
|
101
|
+
* await StreamCall.call({
|
|
102
|
+
* userId: 'user-456',
|
|
103
|
+
* type: 'video',
|
|
104
|
+
* ring: true
|
|
105
|
+
* });
|
|
106
|
+
*/
|
|
107
|
+
call(options: CallOptions): Promise<SuccessResponse>;
|
|
108
|
+
/**
|
|
109
|
+
* End the current call
|
|
110
|
+
* @returns {Promise<SuccessResponse>} Success status
|
|
111
|
+
* @example
|
|
112
|
+
* await StreamCall.endCall();
|
|
113
|
+
*/
|
|
114
|
+
endCall(): Promise<SuccessResponse>;
|
|
115
|
+
/**
|
|
116
|
+
* Enable or disable microphone
|
|
117
|
+
* @param {{ enabled: boolean }} options - Microphone state
|
|
118
|
+
* @returns {Promise<SuccessResponse>} Success status
|
|
119
|
+
* @example
|
|
120
|
+
* await StreamCall.setMicrophoneEnabled({ enabled: false });
|
|
121
|
+
*/
|
|
122
|
+
setMicrophoneEnabled(options: {
|
|
123
|
+
enabled: boolean;
|
|
124
|
+
}): Promise<SuccessResponse>;
|
|
125
|
+
/**
|
|
126
|
+
* Enable or disable camera
|
|
127
|
+
* @param {{ enabled: boolean }} options - Camera state
|
|
128
|
+
* @returns {Promise<SuccessResponse>} Success status
|
|
129
|
+
* @example
|
|
130
|
+
* await StreamCall.setCameraEnabled({ enabled: false });
|
|
131
|
+
*/
|
|
132
|
+
setCameraEnabled(options: {
|
|
133
|
+
enabled: boolean;
|
|
134
|
+
}): Promise<SuccessResponse>;
|
|
135
|
+
/**
|
|
136
|
+
* Add listener for call events
|
|
137
|
+
* @param {'callEvent'} eventName - Name of the event to listen for
|
|
138
|
+
* @param {(event: CallEvent) => void} listenerFunc - Callback function
|
|
139
|
+
* @returns {Promise<{ remove: () => Promise<void> }>} Function to remove listener
|
|
140
|
+
* @example
|
|
141
|
+
* const listener = await StreamCall.addListener('callEvent', (event) => {
|
|
142
|
+
* console.log(`Call ${event.callId} is now ${event.state}`);
|
|
143
|
+
* });
|
|
144
|
+
*/
|
|
145
|
+
addListener(eventName: 'callEvent', listenerFunc: (event: CallEvent) => void): Promise<{
|
|
146
|
+
remove: () => Promise<void>;
|
|
147
|
+
}>;
|
|
148
|
+
/**
|
|
149
|
+
* Remove all event listeners
|
|
150
|
+
* @returns {Promise<void>}
|
|
151
|
+
* @example
|
|
152
|
+
* await StreamCall.removeAllListeners();
|
|
153
|
+
*/
|
|
154
|
+
removeAllListeners(): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Accept an incoming call
|
|
157
|
+
* @returns {Promise<SuccessResponse>} Success status
|
|
158
|
+
* @example
|
|
159
|
+
* await StreamCall.acceptCall();
|
|
160
|
+
*/
|
|
161
|
+
acceptCall(): Promise<SuccessResponse>;
|
|
162
|
+
/**
|
|
163
|
+
* Reject an incoming call
|
|
164
|
+
* @returns {Promise<SuccessResponse>} Success status
|
|
165
|
+
* @example
|
|
166
|
+
* await StreamCall.rejectCall();
|
|
167
|
+
*/
|
|
168
|
+
rejectCall(): Promise<SuccessResponse>;
|
|
169
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * @interface LoginOptions\n * @description Configuration options for logging into the Stream Video service\n * @property {string} token - Stream Video API token for authentication\n * @property {string} userId - Unique identifier for the current user\n * @property {string} name - Display name for the current user\n * @property {string} [imageURL] - Avatar URL for the current user\n * @property {string} apiKey - Stream Video API key for your application\n * @property {string} [magicDivId] - DOM element ID where video will be rendered\n * @property {Object} [refreshToken] - Token refresh configuration\n * @property {string} refreshToken.url - Endpoint URL for token refresh\n * @property {Record<string, string>} [refreshToken.headers] - Custom headers for refresh request\n */\nexport interface LoginOptions {\n /** Stream Video API token */\n token: string;\n /** User ID for the current user */\n userId: string;\n /** Display name for the current user */\n name: string;\n /** Optional avatar URL for the current user */\n imageURL?: string;\n /** Stream Video API key */\n apiKey: string;\n /** ID of the HTML element where the video will be rendered */\n magicDivId?: string;\n /** Configuration for token refresh */\n refreshToken?: {\n /** URL to call for refreshing the token */\n url: string;\n /** Optional headers to include in the refresh request */\n headers?: Record<string, string>;\n };\n}\n\n/**\n * @interface CallOptions\n * @description Options for initiating a video call\n * @property {string} userId - ID of the user to call\n * @property {string} [type=default] - Type of call\n * @property {boolean} [ring=true] - Whether to send ring notification\n */\nexport interface CallOptions {\n /** User ID of the person to call */\n userId: string;\n /** Type of call, defaults to 'default' */\n type?: string;\n /** Whether to ring the other user, defaults to true */\n ring?: boolean;\n}\n\n/**\n * @interface SuccessResponse\n * @description Standard response indicating operation success/failure\n * @property {boolean} success - Whether the operation succeeded\n */\nexport interface SuccessResponse {\n /** Whether the operation was successful */\n success: boolean;\n}\n\n/**\n * @interface CallEvent\n * @description Event emitted when call state changes\n * @property {string} callId - Unique identifier of the call\n * @property {string} state - Current state of the call (joined, left, ringing, etc)\n */\nexport interface CallEvent {\n /** ID of the call */\n callId: string;\n /** Current state of the call */\n state: string;\n}\n\n/**\n * @interface StreamCallPlugin\n * @description Capacitor plugin for Stream Video calling functionality\n */\nexport interface StreamCallPlugin {\n /**\n * Login to Stream Video service\n * @param {LoginOptions} options - Login configuration\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.login({\n * token: 'your-token',\n * userId: 'user-123',\n * name: 'John Doe',\n * apiKey: 'your-api-key'\n * });\n */\n login(options: LoginOptions): Promise<SuccessResponse>;\n\n /**\n * Logout from Stream Video service\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.logout();\n */\n logout(): Promise<SuccessResponse>;\n\n /**\n * Initiate a call to another user\n * @param {CallOptions} options - Call configuration\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.call({\n * userId: 'user-456',\n * type: 'video',\n * ring: true\n * });\n */\n call(options: CallOptions): Promise<SuccessResponse>;\n\n /**\n * End the current call\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.endCall();\n */\n endCall(): Promise<SuccessResponse>;\n\n /**\n * Enable or disable microphone\n * @param {{ enabled: boolean }} options - Microphone state\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.setMicrophoneEnabled({ enabled: false });\n */\n setMicrophoneEnabled(options: { enabled: boolean }): Promise<SuccessResponse>;\n\n /**\n * Enable or disable camera\n * @param {{ enabled: boolean }} options - Camera state\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.setCameraEnabled({ enabled: false });\n */\n setCameraEnabled(options: { enabled: boolean }): Promise<SuccessResponse>;\n\n /**\n * Add listener for call events\n * @param {'callEvent'} eventName - Name of the event to listen for\n * @param {(event: CallEvent) => void} listenerFunc - Callback function\n * @returns {Promise<{ remove: () => Promise<void> }>} Function to remove listener\n * @example\n * const listener = await StreamCall.addListener('callEvent', (event) => {\n * console.log(`Call ${event.callId} is now ${event.state}`);\n * });\n */\n addListener(\n eventName: 'callEvent',\n listenerFunc: (event: CallEvent) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Remove all event listeners\n * @returns {Promise<void>}\n * @example\n * await StreamCall.removeAllListeners();\n */\n removeAllListeners(): Promise<void>;\n\n /**\n * Accept an incoming call\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.acceptCall();\n */\n acceptCall(): Promise<SuccessResponse>;\n\n /**\n * Reject an incoming call\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.rejectCall();\n */\n rejectCall(): Promise<SuccessResponse>;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,UAAU,GAAG,cAAc,CAAmB,YAAY,EAAE;IAChE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;CAC9D,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { StreamCallPlugin } from './definitions';\n\nconst StreamCall = registerPlugin<StreamCallPlugin>('StreamCall', {\n web: () => import('./web').then((m) => new m.StreamCallWeb()),\n});\n\nexport * from './definitions';\nexport { StreamCall };\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
import type { CallOptions, StreamCallPlugin, SuccessResponse, LoginOptions } from './definitions';
|
|
3
|
+
export declare class StreamCallWeb extends WebPlugin implements StreamCallPlugin {
|
|
4
|
+
private client?;
|
|
5
|
+
private currentCall?;
|
|
6
|
+
private callStateSubscription?;
|
|
7
|
+
private incomingCall?;
|
|
8
|
+
private outgoingCall?;
|
|
9
|
+
private magicDivId?;
|
|
10
|
+
private videoBindings;
|
|
11
|
+
private audioBindings;
|
|
12
|
+
private participantJoinedListener?;
|
|
13
|
+
private participantLeftListener?;
|
|
14
|
+
private setupCallRingListener;
|
|
15
|
+
private ringCallback;
|
|
16
|
+
private setupParticipantListener;
|
|
17
|
+
private setupParticipantVideo;
|
|
18
|
+
private setupParticipantAudio;
|
|
19
|
+
private cleanupCall;
|
|
20
|
+
login(options: LoginOptions): Promise<SuccessResponse>;
|
|
21
|
+
logout(): Promise<SuccessResponse>;
|
|
22
|
+
call(options: CallOptions): Promise<SuccessResponse>;
|
|
23
|
+
endCall(): Promise<SuccessResponse>;
|
|
24
|
+
setMicrophoneEnabled(options: {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
}): Promise<SuccessResponse>;
|
|
27
|
+
setCameraEnabled(options: {
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
}): Promise<SuccessResponse>;
|
|
30
|
+
acceptCall(): Promise<SuccessResponse>;
|
|
31
|
+
rejectCall(): Promise<SuccessResponse>;
|
|
32
|
+
}
|
package/dist/esm/web.js
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
import { CallingState, StreamVideoClient } from '@stream-io/video-client';
|
|
3
|
+
export class StreamCallWeb extends WebPlugin {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments);
|
|
6
|
+
this.videoBindings = new Map();
|
|
7
|
+
this.audioBindings = new Map();
|
|
8
|
+
this.ringCallback = (event) => {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
console.log('Call ringing', event, this.currentCall);
|
|
11
|
+
this.incomingCall = event.call;
|
|
12
|
+
if (!this.currentCall) {
|
|
13
|
+
console.log('Creating new call', event.call.id);
|
|
14
|
+
this.currentCall = (_a = this.client) === null || _a === void 0 ? void 0 : _a.call(event.call.type, event.call.id);
|
|
15
|
+
this.notifyListeners('callEvent', { callId: event.call.id, state: CallingState.RINGING });
|
|
16
|
+
}
|
|
17
|
+
if (this.currentCall) {
|
|
18
|
+
console.log('Call found', this.currentCall.id);
|
|
19
|
+
this.callStateSubscription = (_b = this.currentCall) === null || _b === void 0 ? void 0 : _b.state.callingState$.subscribe((s) => {
|
|
20
|
+
var _a;
|
|
21
|
+
console.log('Call state', s);
|
|
22
|
+
if (s === CallingState.JOINED) {
|
|
23
|
+
this.setupParticipantListener();
|
|
24
|
+
}
|
|
25
|
+
else if (s === CallingState.LEFT || s === CallingState.RECONNECTING_FAILED) {
|
|
26
|
+
this.cleanupCall();
|
|
27
|
+
}
|
|
28
|
+
if (this.outgoingCall && s === CallingState.RINGING) {
|
|
29
|
+
this.outgoingCall = undefined;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
this.notifyListeners('callEvent', { callId: (_a = this.currentCall) === null || _a === void 0 ? void 0 : _a.id, state: s });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
setupCallRingListener() {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
(_a = this.client) === null || _a === void 0 ? void 0 : _a.off('call.ring', this.ringCallback);
|
|
41
|
+
(_b = this.client) === null || _b === void 0 ? void 0 : _b.on('call.ring', this.ringCallback);
|
|
42
|
+
}
|
|
43
|
+
setupParticipantListener() {
|
|
44
|
+
// Subscribe to participant changes
|
|
45
|
+
this.incomingCall = undefined;
|
|
46
|
+
if (!this.currentCall)
|
|
47
|
+
return;
|
|
48
|
+
this.participantJoinedListener = (event) => {
|
|
49
|
+
if (this.magicDivId && event.participant) {
|
|
50
|
+
const magicDiv = document.getElementById(this.magicDivId);
|
|
51
|
+
if (magicDiv && this.currentCall) {
|
|
52
|
+
this.setupParticipantVideo(this.currentCall, event.participant, magicDiv);
|
|
53
|
+
this.setupParticipantAudio(this.currentCall, event.participant, magicDiv);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
this.participantLeftListener = (event) => {
|
|
58
|
+
if (this.magicDivId && event.participant) {
|
|
59
|
+
const videoId = `video-${event.participant.sessionId}`;
|
|
60
|
+
const audioId = `audio-${event.participant.sessionId}`;
|
|
61
|
+
// Remove video element
|
|
62
|
+
const videoEl = document.getElementById(videoId);
|
|
63
|
+
if (videoEl) {
|
|
64
|
+
const unbindVideo = this.videoBindings.get(videoId);
|
|
65
|
+
if (unbindVideo) {
|
|
66
|
+
unbindVideo();
|
|
67
|
+
this.videoBindings.delete(videoId);
|
|
68
|
+
}
|
|
69
|
+
const tracks = videoEl.srcObject;
|
|
70
|
+
if (tracks) {
|
|
71
|
+
tracks.getTracks().forEach((track) => {
|
|
72
|
+
track.stop();
|
|
73
|
+
track.enabled = false;
|
|
74
|
+
});
|
|
75
|
+
videoEl.srcObject = null;
|
|
76
|
+
}
|
|
77
|
+
videoEl.remove();
|
|
78
|
+
}
|
|
79
|
+
// Remove audio element
|
|
80
|
+
const audioEl = document.getElementById(audioId);
|
|
81
|
+
if (audioEl) {
|
|
82
|
+
const unbindAudio = this.audioBindings.get(audioId);
|
|
83
|
+
if (unbindAudio) {
|
|
84
|
+
unbindAudio();
|
|
85
|
+
this.audioBindings.delete(audioId);
|
|
86
|
+
}
|
|
87
|
+
const tracks = audioEl.srcObject;
|
|
88
|
+
if (tracks) {
|
|
89
|
+
tracks.getTracks().forEach((track) => {
|
|
90
|
+
track.stop();
|
|
91
|
+
track.enabled = false;
|
|
92
|
+
});
|
|
93
|
+
audioEl.srcObject = null;
|
|
94
|
+
}
|
|
95
|
+
audioEl.remove();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
this.currentCall.on('participantJoined', this.participantJoinedListener);
|
|
100
|
+
this.currentCall.on('participantLeft', this.participantLeftListener);
|
|
101
|
+
// Setup initial participants
|
|
102
|
+
const participants = this.currentCall.state.participants;
|
|
103
|
+
if (this.magicDivId) {
|
|
104
|
+
const magicDiv = document.getElementById(this.magicDivId);
|
|
105
|
+
if (magicDiv) {
|
|
106
|
+
participants.forEach((participant) => {
|
|
107
|
+
if (this.currentCall) {
|
|
108
|
+
this.setupParticipantVideo(this.currentCall, participant, magicDiv);
|
|
109
|
+
this.setupParticipantAudio(this.currentCall, participant, magicDiv);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
setupParticipantVideo(call, participant, container) {
|
|
116
|
+
const id = `video-${participant.sessionId}`;
|
|
117
|
+
if (!document.getElementById(id)) {
|
|
118
|
+
const videoEl = document.createElement('video');
|
|
119
|
+
videoEl.id = id;
|
|
120
|
+
videoEl.style.width = '100%';
|
|
121
|
+
videoEl.style.maxWidth = '300px';
|
|
122
|
+
videoEl.style.aspectRatio = '16/9';
|
|
123
|
+
container.appendChild(videoEl);
|
|
124
|
+
const unbind = call.bindVideoElement(videoEl, participant.sessionId, 'videoTrack');
|
|
125
|
+
if (unbind)
|
|
126
|
+
this.videoBindings.set(id, unbind);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
setupParticipantAudio(call, participant, container) {
|
|
130
|
+
if (participant.isLocalParticipant)
|
|
131
|
+
return;
|
|
132
|
+
const id = `audio-${participant.sessionId}`;
|
|
133
|
+
if (!document.getElementById(id)) {
|
|
134
|
+
const audioEl = document.createElement('audio');
|
|
135
|
+
audioEl.id = id;
|
|
136
|
+
container.appendChild(audioEl);
|
|
137
|
+
const unbind = call.bindAudioElement(audioEl, participant.sessionId);
|
|
138
|
+
if (unbind)
|
|
139
|
+
this.audioBindings.set(id, unbind);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
cleanupCall() {
|
|
143
|
+
var _a;
|
|
144
|
+
// First cleanup the call listeners
|
|
145
|
+
if (this.currentCall) {
|
|
146
|
+
if (this.participantJoinedListener) {
|
|
147
|
+
this.currentCall.off('participantJoined', this.participantJoinedListener);
|
|
148
|
+
this.participantJoinedListener = undefined;
|
|
149
|
+
}
|
|
150
|
+
if (this.participantLeftListener) {
|
|
151
|
+
this.currentCall.off('participantLeft', this.participantLeftListener);
|
|
152
|
+
this.participantLeftListener = undefined;
|
|
153
|
+
}
|
|
154
|
+
(_a = this.callStateSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
|
|
155
|
+
}
|
|
156
|
+
if (this.magicDivId) {
|
|
157
|
+
const magicDiv = document.getElementById(this.magicDivId);
|
|
158
|
+
if (magicDiv) {
|
|
159
|
+
// Remove all video elements
|
|
160
|
+
const videoElements = magicDiv.querySelectorAll('video');
|
|
161
|
+
videoElements.forEach((video) => {
|
|
162
|
+
const id = video.id;
|
|
163
|
+
const unbind = this.videoBindings.get(id);
|
|
164
|
+
if (unbind) {
|
|
165
|
+
unbind();
|
|
166
|
+
this.videoBindings.delete(id);
|
|
167
|
+
}
|
|
168
|
+
// Stop all tracks
|
|
169
|
+
const tracks = video.srcObject;
|
|
170
|
+
if (tracks) {
|
|
171
|
+
tracks.getTracks().forEach((track) => {
|
|
172
|
+
track.stop();
|
|
173
|
+
track.enabled = false;
|
|
174
|
+
});
|
|
175
|
+
video.srcObject = null;
|
|
176
|
+
}
|
|
177
|
+
video.remove();
|
|
178
|
+
});
|
|
179
|
+
// Remove all audio elements
|
|
180
|
+
const audioElements = magicDiv.querySelectorAll('audio');
|
|
181
|
+
audioElements.forEach((audio) => {
|
|
182
|
+
const id = audio.id;
|
|
183
|
+
const unbind = this.audioBindings.get(id);
|
|
184
|
+
if (unbind) {
|
|
185
|
+
unbind();
|
|
186
|
+
this.audioBindings.delete(id);
|
|
187
|
+
}
|
|
188
|
+
// Stop all tracks
|
|
189
|
+
const tracks = audio.srcObject;
|
|
190
|
+
if (tracks) {
|
|
191
|
+
tracks.getTracks().forEach((track) => {
|
|
192
|
+
track.stop();
|
|
193
|
+
track.enabled = false;
|
|
194
|
+
});
|
|
195
|
+
audio.srcObject = null;
|
|
196
|
+
}
|
|
197
|
+
audio.remove();
|
|
198
|
+
});
|
|
199
|
+
// Clear the container
|
|
200
|
+
while (magicDiv.firstChild) {
|
|
201
|
+
magicDiv.removeChild(magicDiv.firstChild);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Clear all bindings
|
|
206
|
+
this.videoBindings.clear();
|
|
207
|
+
this.audioBindings.clear();
|
|
208
|
+
// Clear call references
|
|
209
|
+
this.currentCall = undefined;
|
|
210
|
+
this.incomingCall = undefined;
|
|
211
|
+
}
|
|
212
|
+
async login(options) {
|
|
213
|
+
this.client = StreamVideoClient.getOrCreateInstance({
|
|
214
|
+
apiKey: options.apiKey,
|
|
215
|
+
user: { id: options.userId, name: options.name, image: options.imageURL },
|
|
216
|
+
token: options.token,
|
|
217
|
+
});
|
|
218
|
+
this.magicDivId = options.magicDivId;
|
|
219
|
+
this.setupCallRingListener();
|
|
220
|
+
return { success: true };
|
|
221
|
+
}
|
|
222
|
+
async logout() {
|
|
223
|
+
var _a;
|
|
224
|
+
if (!this.client) {
|
|
225
|
+
console.log('No client', this.client);
|
|
226
|
+
throw new Error('Client not initialized');
|
|
227
|
+
}
|
|
228
|
+
// Cleanup subscription
|
|
229
|
+
(_a = this.callStateSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
|
|
230
|
+
this.callStateSubscription = undefined;
|
|
231
|
+
await this.client.disconnectUser();
|
|
232
|
+
this.client = undefined;
|
|
233
|
+
this.currentCall = undefined;
|
|
234
|
+
return { success: true };
|
|
235
|
+
}
|
|
236
|
+
async call(options) {
|
|
237
|
+
if (!this.client) {
|
|
238
|
+
console.log('No client', this.client);
|
|
239
|
+
throw new Error('Client not initialized - Please login first');
|
|
240
|
+
}
|
|
241
|
+
const call = this.client.call(options.type || 'default', crypto.randomUUID());
|
|
242
|
+
const members = [{ user_id: options.userId }];
|
|
243
|
+
if (this.client.streamClient.userID && options.userId !== this.client.streamClient.userID) {
|
|
244
|
+
members.push({ user_id: this.client.streamClient.userID });
|
|
245
|
+
}
|
|
246
|
+
await call.getOrCreate({ data: { members } });
|
|
247
|
+
this.currentCall = call;
|
|
248
|
+
if (options.ring) {
|
|
249
|
+
this.outgoingCall = call.cid;
|
|
250
|
+
await call.ring();
|
|
251
|
+
}
|
|
252
|
+
await call.join();
|
|
253
|
+
return { success: true };
|
|
254
|
+
}
|
|
255
|
+
async endCall() {
|
|
256
|
+
if (!this.currentCall) {
|
|
257
|
+
console.log('No active call', this.currentCall);
|
|
258
|
+
throw new Error('No active call');
|
|
259
|
+
}
|
|
260
|
+
await this.currentCall.leave();
|
|
261
|
+
this.currentCall = undefined;
|
|
262
|
+
this.cleanupCall();
|
|
263
|
+
return { success: true };
|
|
264
|
+
}
|
|
265
|
+
async setMicrophoneEnabled(options) {
|
|
266
|
+
if (!this.currentCall) {
|
|
267
|
+
console.log('No active call', this.currentCall);
|
|
268
|
+
throw new Error('No active call');
|
|
269
|
+
}
|
|
270
|
+
if (options.enabled) {
|
|
271
|
+
await this.currentCall.microphone.enable();
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
await this.currentCall.microphone.disable();
|
|
275
|
+
}
|
|
276
|
+
return { success: true };
|
|
277
|
+
}
|
|
278
|
+
async setCameraEnabled(options) {
|
|
279
|
+
if (!this.currentCall) {
|
|
280
|
+
console.log('No active call', this.currentCall);
|
|
281
|
+
throw new Error('No active call');
|
|
282
|
+
}
|
|
283
|
+
if (options.enabled) {
|
|
284
|
+
await this.currentCall.camera.enable();
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
await this.currentCall.camera.disable();
|
|
288
|
+
}
|
|
289
|
+
return { success: true };
|
|
290
|
+
}
|
|
291
|
+
async acceptCall() {
|
|
292
|
+
if (!this.incomingCall || !this.client) {
|
|
293
|
+
console.log('No incoming call to accept', this.incomingCall, this.client);
|
|
294
|
+
throw new Error('No incoming call to accept');
|
|
295
|
+
}
|
|
296
|
+
console.log('Accepting call', this.incomingCall);
|
|
297
|
+
const call = this.client.call(this.incomingCall.type, this.incomingCall.id);
|
|
298
|
+
this.currentCall = call;
|
|
299
|
+
console.log('Joining call', call);
|
|
300
|
+
await call.accept();
|
|
301
|
+
await call.join();
|
|
302
|
+
console.log('Joined call', call);
|
|
303
|
+
this.notifyListeners('callEvent', { callId: call.id, state: CallingState.JOINED });
|
|
304
|
+
this.setupParticipantListener();
|
|
305
|
+
return { success: true };
|
|
306
|
+
}
|
|
307
|
+
async rejectCall() {
|
|
308
|
+
if (!this.incomingCall || !this.client) {
|
|
309
|
+
console.log('No incoming call to reject', this.incomingCall, this.client);
|
|
310
|
+
throw new Error('No incoming call to reject');
|
|
311
|
+
}
|
|
312
|
+
console.log('Rejecting call', this.incomingCall);
|
|
313
|
+
const call = this.client.call(this.incomingCall.type, this.incomingCall.id);
|
|
314
|
+
console.log('Leaving call', call);
|
|
315
|
+
await call.leave();
|
|
316
|
+
this.incomingCall = undefined;
|
|
317
|
+
console.log('Rejected call', call);
|
|
318
|
+
this.notifyListeners('callEvent', { callId: call.id, state: CallingState.LEFT });
|
|
319
|
+
this.cleanupCall();
|
|
320
|
+
return { success: true };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport type { AllClientEvents, Call, CallResponse, StreamVideoParticipant } from '@stream-io/video-client';\nimport { CallingState, StreamVideoClient } from '@stream-io/video-client';\n\nimport type { CallOptions, StreamCallPlugin, SuccessResponse, LoginOptions } from './definitions';\n\nexport class StreamCallWeb extends WebPlugin implements StreamCallPlugin {\n private client?: StreamVideoClient;\n private currentCall?: Call;\n private callStateSubscription?: { unsubscribe: () => void };\n private incomingCall?: CallResponse;\n private outgoingCall?: string;\n private magicDivId?: string;\n private videoBindings: Map<string, () => void> = new Map();\n private audioBindings: Map<string, () => void> = new Map();\n private participantJoinedListener?: (event: { participant?: { sessionId: string } }) => void;\n private participantLeftListener?: (event: { participant?: { sessionId: string } }) => void;\n\n private setupCallRingListener() {\n this.client?.off('call.ring', this.ringCallback);\n this.client?.on('call.ring', this.ringCallback);\n }\n\n private ringCallback = (event: AllClientEvents['call.ring']) => {\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 = this.client?.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 = this.currentCall?.state.callingState$.subscribe((s) => {\n console.log('Call state', s);\n if (s === CallingState.JOINED) {\n this.setupParticipantListener();\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 } else {\n this.notifyListeners('callEvent', { callId: this.currentCall?.id, state: s });\n }\n });\n }\n };\n\n private setupParticipantListener() {\n // Subscribe to participant changes\n this.incomingCall = undefined;\n if (!this.currentCall) return;\n\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 as StreamVideoParticipant, magicDiv);\n this.setupParticipantAudio(this.currentCall, event.participant as StreamVideoParticipant, magicDiv);\n }\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\n // Remove video element\n const videoEl = document.getElementById(videoId) as HTMLVideoElement;\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 as MediaStream;\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\n // Remove audio element\n const audioEl = document.getElementById(audioId) as HTMLAudioElement;\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 as MediaStream;\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\n this.currentCall.on('participantJoined', this.participantJoinedListener);\n this.currentCall.on('participantLeft', this.participantLeftListener);\n\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: StreamVideoParticipant) => {\n if (this.currentCall) {\n this.setupParticipantVideo(this.currentCall, participant, magicDiv);\n this.setupParticipantAudio(this.currentCall, participant, magicDiv);\n }\n });\n }\n }\n }\n\n private setupParticipantVideo(call: Call, participant: StreamVideoParticipant, container: HTMLElement) {\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\n const unbind = call.bindVideoElement(videoEl, participant.sessionId, 'videoTrack');\n if (unbind) this.videoBindings.set(id, unbind);\n }\n }\n\n private setupParticipantAudio(call: Call, participant: StreamVideoParticipant, container: HTMLElement) {\n if (participant.isLocalParticipant) return;\n\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\n const unbind = call.bindAudioElement(audioEl, participant.sessionId);\n if (unbind) this.audioBindings.set(id, unbind);\n }\n }\n\n private cleanupCall() {\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 this.callStateSubscription?.unsubscribe();\n }\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 as HTMLVideoElement).srcObject as MediaStream;\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\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 as HTMLAudioElement).srcObject as MediaStream;\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\n // Clear the container\n while (magicDiv.firstChild) {\n magicDiv.removeChild(magicDiv.firstChild);\n }\n }\n }\n\n // Clear all bindings\n this.videoBindings.clear();\n this.audioBindings.clear();\n\n // Clear call references\n this.currentCall = undefined;\n this.incomingCall = undefined;\n }\n\n async login(options: LoginOptions): Promise<SuccessResponse> {\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\n this.magicDivId = options.magicDivId;\n this.setupCallRingListener();\n\n return { success: true };\n }\n\n async logout(): Promise<SuccessResponse> {\n if (!this.client) {\n console.log('No client', this.client);\n throw new Error('Client not initialized');\n }\n\n // Cleanup subscription\n this.callStateSubscription?.unsubscribe();\n this.callStateSubscription = undefined;\n\n await this.client.disconnectUser();\n this.client = undefined;\n this.currentCall = undefined;\n return { success: true };\n }\n\n async call(options: CallOptions): Promise<SuccessResponse> {\n if (!this.client) {\n console.log('No client', this.client);\n throw new Error('Client not initialized - Please login first');\n }\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\n await call.join();\n return { success: true };\n }\n\n async endCall(): Promise<SuccessResponse> {\n if (!this.currentCall) {\n console.log('No active call', this.currentCall);\n throw new Error('No active call');\n }\n\n await this.currentCall.leave();\n this.currentCall = undefined;\n this.cleanupCall();\n\n return { success: true };\n }\n\n async setMicrophoneEnabled(options: { enabled: boolean }): Promise<SuccessResponse> {\n if (!this.currentCall) {\n console.log('No active call', this.currentCall);\n throw new Error('No active call');\n }\n\n if (options.enabled) {\n await this.currentCall.microphone.enable();\n } else {\n await this.currentCall.microphone.disable();\n }\n\n return { success: true };\n }\n\n async setCameraEnabled(options: { enabled: boolean }): Promise<SuccessResponse> {\n if (!this.currentCall) {\n console.log('No active call', this.currentCall);\n throw new Error('No active call');\n }\n\n if (options.enabled) {\n await this.currentCall.camera.enable();\n } else {\n await this.currentCall.camera.disable();\n }\n\n return { success: true };\n }\n\n async acceptCall(): Promise<SuccessResponse> {\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\n async rejectCall(): Promise<SuccessResponse> {\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: 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"]}
|