@rehers/rehers-roleplay-sdk 2.4.0 → 2.4.1
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/README.md +223 -131
- package/index.d.ts +4 -4
- package/package.json +1 -1
- package/roleplay-sdk.js +6 -27
package/README.md
CHANGED
|
@@ -1,177 +1,269 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Roleplay SDK for the Seamless.AI Dashboard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This SDK is only for embedding Roleplay inside the Seamless.AI dashboard.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Use it in these two places:
|
|
6
|
+
|
|
7
|
+
- The dedicated Roleplay page or tab inside the Seamless dashboard
|
|
8
|
+
- The Contact Search screen, where clicking `Roleplay` opens a dialog for a single contact
|
|
9
|
+
|
|
10
|
+
The SDK must be initialized with the currently logged-in Seamless user before either embed flow is used.
|
|
11
|
+
|
|
12
|
+
## Load the SDK
|
|
13
|
+
|
|
14
|
+
If Seamless is using the downloaded SDK file directly:
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<script src="/path/to/roleplay-sdk.js"></script>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If Seamless is using the npm package:
|
|
6
21
|
|
|
7
22
|
```bash
|
|
8
23
|
npm install @rehers/rehers-roleplay-sdk
|
|
9
24
|
```
|
|
10
25
|
|
|
11
|
-
|
|
26
|
+
## Get the logged-in Seamless user
|
|
12
27
|
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
Use the existing Seamless dashboard session and call:
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
const meResponse = await fetch("https://api.seamless.ai/api/users/me", {
|
|
32
|
+
method: "GET",
|
|
33
|
+
credentials: "include",
|
|
34
|
+
headers: {
|
|
35
|
+
accept: "application/json, text/plain, */*",
|
|
36
|
+
},
|
|
37
|
+
}).then((res) => res.json());
|
|
15
38
|
```
|
|
16
39
|
|
|
17
|
-
|
|
40
|
+
Normalize the response before reading fields:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
const me = meResponse.data ?? meResponse;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Required mapping for `init()`
|
|
47
|
+
|
|
48
|
+
These are the values Seamless must pass into `SeamlessRoleplay.init(...)`:
|
|
49
|
+
|
|
50
|
+
| SDK field | Seamless `/api/users/me` field |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `userId` | `String(me.id)` |
|
|
53
|
+
| `userEmail` | `me.username` |
|
|
54
|
+
| `userRole` | Optional. Suggested mapping from `me.orgRole` / `me.isOrgAdmin` |
|
|
55
|
+
|
|
56
|
+
Example:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
const seamlessUserId = String(me.id);
|
|
60
|
+
const seamlessUserEmail = me.username;
|
|
61
|
+
const seamlessUserRole =
|
|
62
|
+
me.orgRole === "owner" ? "owner" :
|
|
63
|
+
me.isOrgAdmin ? "admin" :
|
|
64
|
+
"member";
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
For the response shape currently returned by Seamless, this means:
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
const seamlessUserId = String(me.id);
|
|
71
|
+
const seamlessUserEmail = me.username;
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Initialize the SDK once
|
|
75
|
+
|
|
76
|
+
Initialize the SDK once per page load using the logged-in Seamless user. Reuse that same initialized SDK for both the full-page embed and the Contact Search dialog.
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
let roleplayReadyPromise;
|
|
80
|
+
|
|
81
|
+
function ensureRoleplaySdkReady() {
|
|
82
|
+
if (roleplayReadyPromise) return roleplayReadyPromise;
|
|
83
|
+
|
|
84
|
+
roleplayReadyPromise = (async () => {
|
|
85
|
+
const meResponse = await fetch("https://api.seamless.ai/api/users/me", {
|
|
86
|
+
method: "GET",
|
|
87
|
+
credentials: "include",
|
|
88
|
+
headers: {
|
|
89
|
+
accept: "application/json, text/plain, */*",
|
|
90
|
+
},
|
|
91
|
+
}).then((res) => res.json());
|
|
92
|
+
|
|
93
|
+
const me = meResponse.data ?? meResponse;
|
|
94
|
+
|
|
95
|
+
const seamlessUserId = String(me.id);
|
|
96
|
+
const seamlessUserEmail = me.username;
|
|
97
|
+
const seamlessUserRole =
|
|
98
|
+
me.orgRole === "owner" ? "owner" :
|
|
99
|
+
me.isOrgAdmin ? "admin" :
|
|
100
|
+
"member";
|
|
101
|
+
|
|
102
|
+
await new Promise((resolve, reject) => {
|
|
103
|
+
SeamlessRoleplay.init({
|
|
104
|
+
publishableKey: ROLEPLAY_PUBLISHABLE_KEY,
|
|
105
|
+
userId: seamlessUserId,
|
|
106
|
+
userEmail: seamlessUserEmail,
|
|
107
|
+
userRole: seamlessUserRole,
|
|
108
|
+
onReady: resolve,
|
|
109
|
+
onError: reject,
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
})();
|
|
113
|
+
|
|
114
|
+
return roleplayReadyPromise;
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Embed in the full Roleplay page
|
|
119
|
+
|
|
120
|
+
Use `mount(container)` when Seamless has a dedicated Roleplay page or tab and the full content area should be the Roleplay app.
|
|
18
121
|
|
|
19
122
|
```html
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
123
|
+
<div id="roleplay-root" style="width: 100%; height: 100%;"></div>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
async function mountRoleplayPage() {
|
|
128
|
+
await ensureRoleplaySdkReady();
|
|
129
|
+
|
|
130
|
+
const container = document.getElementById("roleplay-root");
|
|
131
|
+
SeamlessRoleplay.mount(container);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If the Seamless page or tab is torn down, unmount the SDK:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
SeamlessRoleplay.unmount();
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Embed in Contact Search
|
|
142
|
+
|
|
143
|
+
Use `open(contactData)` when the user clicks a `Roleplay` button from a single contact row on the Contact Search screen.
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
async function openRoleplayForContact(contact) {
|
|
147
|
+
await ensureRoleplaySdkReady();
|
|
148
|
+
|
|
149
|
+
SeamlessRoleplay.open({
|
|
150
|
+
name: contact.name,
|
|
151
|
+
domain: contact.domain,
|
|
152
|
+
company: contact.company,
|
|
153
|
+
title: contact.title,
|
|
154
|
+
liUrl: contact.linkedinUrl,
|
|
155
|
+
companyDescription: contact.companyDescription,
|
|
156
|
+
onCallStarted(data) {
|
|
157
|
+
console.log("Roleplay call started", data.callId);
|
|
158
|
+
},
|
|
159
|
+
onCallEnded(data) {
|
|
160
|
+
console.log("Roleplay call ended", data.callId, data.duration);
|
|
30
161
|
},
|
|
31
|
-
|
|
32
|
-
console.
|
|
162
|
+
onClose() {
|
|
163
|
+
console.log("Roleplay dialog closed");
|
|
164
|
+
},
|
|
165
|
+
onError(err) {
|
|
166
|
+
console.error("Roleplay dialog error", err);
|
|
33
167
|
},
|
|
34
168
|
});
|
|
169
|
+
}
|
|
170
|
+
```
|
|
35
171
|
|
|
36
|
-
|
|
37
|
-
SeamlessRoleplay.open({
|
|
38
|
-
name: 'Jane Smith',
|
|
39
|
-
domain: 'acme.com',
|
|
40
|
-
company: 'Acme Corp',
|
|
41
|
-
title: 'VP of Sales',
|
|
42
|
-
companyDescription: 'Enterprise software company', // optional
|
|
43
|
-
liUrl: 'https://linkedin.com/in/jane-smith', // optional
|
|
44
|
-
onCallStarted: function(data) { console.log('Call started:', data.callId); },
|
|
45
|
-
onCallEnded: function(data) { console.log('Call ended:', data.callId, data.duration); },
|
|
46
|
-
onClose: function() { console.log('Dialog closed'); },
|
|
47
|
-
onError: function(data) { console.error('Error:', data.code, data.message); },
|
|
48
|
-
});
|
|
172
|
+
The contact object passed to `open(...)` should map to:
|
|
49
173
|
|
|
50
|
-
|
|
51
|
-
|
|
174
|
+
| SDK field | Contact Search value |
|
|
175
|
+
|---|---|
|
|
176
|
+
| `name` | Contact full name |
|
|
177
|
+
| `domain` | Company domain |
|
|
178
|
+
| `company` | Company name |
|
|
179
|
+
| `title` | Contact title |
|
|
180
|
+
| `liUrl` | LinkedIn profile URL, if available |
|
|
181
|
+
| `companyDescription` | Company description, if available |
|
|
52
182
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
183
|
+
## Optional: add contacts to a scenario
|
|
184
|
+
|
|
185
|
+
If Seamless wants to send multiple contacts into a scenario picker dialog, use `addToScenario(...)`.
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
async function addContactsToScenario(contacts) {
|
|
189
|
+
await ensureRoleplaySdkReady();
|
|
60
190
|
|
|
61
|
-
// 3. Add contacts to a scenario in bulk
|
|
62
191
|
SeamlessRoleplay.addToScenario({
|
|
63
|
-
contacts:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
192
|
+
contacts: contacts.map((contact) => ({
|
|
193
|
+
name: contact.name,
|
|
194
|
+
company: contact.company,
|
|
195
|
+
title: contact.title,
|
|
196
|
+
domain: contact.domain,
|
|
197
|
+
liUrl: contact.linkedinUrl,
|
|
198
|
+
companyDescription: contact.companyDescription,
|
|
199
|
+
})),
|
|
200
|
+
onComplete(data) {
|
|
201
|
+
console.log("Scenario import complete", data);
|
|
202
|
+
},
|
|
203
|
+
onClose() {
|
|
204
|
+
console.log("Add to scenario dialog closed");
|
|
205
|
+
},
|
|
206
|
+
onError(err) {
|
|
207
|
+
console.error("Add to scenario error", err);
|
|
69
208
|
},
|
|
70
|
-
onClose: function() { console.log('Dialog closed'); },
|
|
71
|
-
onError: function(err) { console.error('Error:', err.code, err.message); },
|
|
72
209
|
});
|
|
73
|
-
|
|
74
|
-
// Close and destroy
|
|
75
|
-
SeamlessRoleplay.close();
|
|
76
|
-
SeamlessRoleplay.destroy();
|
|
77
|
-
</script>
|
|
210
|
+
}
|
|
78
211
|
```
|
|
79
212
|
|
|
80
|
-
##
|
|
213
|
+
## Minimal API surface
|
|
214
|
+
|
|
215
|
+
### `SeamlessRoleplay.init(options)`
|
|
216
|
+
|
|
217
|
+
Initializes the SDK with the logged-in Seamless user.
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
SeamlessRoleplay.init({
|
|
221
|
+
publishableKey,
|
|
222
|
+
userId,
|
|
223
|
+
userEmail,
|
|
224
|
+
userRole,
|
|
225
|
+
onReady,
|
|
226
|
+
onError,
|
|
227
|
+
});
|
|
228
|
+
```
|
|
81
229
|
|
|
82
|
-
|
|
230
|
+
### `SeamlessRoleplay.mount(container)`
|
|
83
231
|
|
|
84
|
-
|
|
232
|
+
Mounts the full Roleplay app into a dashboard container.
|
|
85
233
|
|
|
86
|
-
### `SeamlessRoleplay.
|
|
234
|
+
### `SeamlessRoleplay.open(contactData)`
|
|
87
235
|
|
|
88
|
-
|
|
89
|
-
|---|---|---|---|
|
|
90
|
-
| `publishableKey` | `string` | Yes | Publishable API key |
|
|
91
|
-
| `userId` | `string` | Yes | Your user's unique identifier |
|
|
92
|
-
| `userEmail` | `string` | Yes | User email for secure account matching |
|
|
93
|
-
| `userRole` | `string` | No | User role — `"owner"`, `"admin"`, or `"member"` |
|
|
94
|
-
| `userToken` | `string` | No | Signed JWT for identity verification |
|
|
95
|
-
| `origin` | `string` | No | Override app origin (dev only) |
|
|
96
|
-
| `onReady` | `function` | No | Called when session is ready |
|
|
97
|
-
| `onError` | `function` | No | Called on init error `({ code, message })` |
|
|
98
|
-
|
|
99
|
-
### `SeamlessRoleplay.open(data)`
|
|
100
|
-
|
|
101
|
-
Opens the roleplay in a modal dialog overlay.
|
|
102
|
-
|
|
103
|
-
| Field | Type | Required | Description |
|
|
104
|
-
|---|---|---|---|
|
|
105
|
-
| `name` | `string` | Yes | Full name of the contact |
|
|
106
|
-
| `domain` | `string` | Yes | Company domain (e.g. "stripe.com") |
|
|
107
|
-
| `company` | `string` | Yes | Company name |
|
|
108
|
-
| `title` | `string` | Yes | Job title |
|
|
109
|
-
| `companyDescription` | `string` | No | Brief company description |
|
|
110
|
-
| `liUrl` | `string` | No | LinkedIn profile URL |
|
|
111
|
-
| `onCallStarted` | `function` | No | `({ callId })` |
|
|
112
|
-
| `onCallEnded` | `function` | No | `({ callId, duration? })` |
|
|
113
|
-
| `onClose` | `function` | No | Called when dialog closes |
|
|
114
|
-
| `onError` | `function` | No | `({ code, message })` |
|
|
115
|
-
|
|
116
|
-
### `SeamlessRoleplay.mount(container, data?)`
|
|
117
|
-
|
|
118
|
-
Mounts into a DOM element. Two modes:
|
|
119
|
-
|
|
120
|
-
- **`mount(container)`** — embeds the full Roleplay app (dashboard, scenarios, call logs)
|
|
121
|
-
- **`mount(container, data)`** — embeds a roleplay call for a specific contact (same `data` options as `open()`)
|
|
236
|
+
Opens the Roleplay dialog for a single contact.
|
|
122
237
|
|
|
123
238
|
### `SeamlessRoleplay.addToScenario(options)`
|
|
124
239
|
|
|
125
|
-
Opens
|
|
126
|
-
|
|
127
|
-
| Field | Type | Required | Description |
|
|
128
|
-
|---|---|---|---|
|
|
129
|
-
| `contacts` | `array` | Yes | 1–25 contacts to import |
|
|
130
|
-
| `contacts[].name` | `string` | Yes | Full name |
|
|
131
|
-
| `contacts[].company` | `string` | Yes | Company name |
|
|
132
|
-
| `contacts[].title` | `string` | Yes | Job title |
|
|
133
|
-
| `contacts[].domain` | `string` | Yes | Company domain |
|
|
134
|
-
| `contacts[].liUrl` | `string` | No | LinkedIn profile URL |
|
|
135
|
-
| `contacts[].companyDescription` | `string` | No | Brief company description |
|
|
136
|
-
| `onComplete` | `function` | No | `({ scenarioId, scenarioName, addedCount, skippedCount })` |
|
|
137
|
-
| `onClose` | `function` | No | Called when dialog closes |
|
|
138
|
-
| `onError` | `function` | No | `({ code, message })` |
|
|
240
|
+
Opens the bulk add-to-scenario dialog for 1 to 25 contacts.
|
|
139
241
|
|
|
140
242
|
### `SeamlessRoleplay.close()`
|
|
141
243
|
|
|
142
|
-
Closes the active dialog
|
|
244
|
+
Closes the active dialog.
|
|
143
245
|
|
|
144
|
-
### `SeamlessRoleplay.
|
|
246
|
+
### `SeamlessRoleplay.unmount()`
|
|
145
247
|
|
|
146
|
-
|
|
248
|
+
Unmounts the full-page Roleplay embed.
|
|
147
249
|
|
|
148
|
-
|
|
250
|
+
### `SeamlessRoleplay.destroy()`
|
|
149
251
|
|
|
150
|
-
|
|
151
|
-
SDK Backend
|
|
152
|
-
───────────────── ─────────────────
|
|
153
|
-
POST /api/sdk/session
|
|
154
|
-
X-Publishable-Key: pk_live_abc123
|
|
155
|
-
Body: { userId, userEmail, userRole? }
|
|
156
|
-
1. Validate key + origin
|
|
157
|
-
2. Validate userId + userEmail combo
|
|
158
|
-
3. Find/create user
|
|
159
|
-
4. Mint JWT (1hr TTL)
|
|
160
|
-
←──── { sessionToken, expiresIn }
|
|
161
|
-
OR { error: "USER_NOT_FOUND" }
|
|
162
|
-
|
|
163
|
-
Token stored in memory (not localStorage)
|
|
164
|
-
Auto-refreshes at 80% of TTL
|
|
165
|
-
```
|
|
252
|
+
Destroys the SDK state, timers, mount, and dialogs.
|
|
166
253
|
|
|
167
|
-
##
|
|
254
|
+
## Trial and upgrade handling
|
|
168
255
|
|
|
169
|
-
|
|
256
|
+
If the backend responds with `USER_NOT_FOUND` during SDK session creation, the SDK handles the upgrade or trial UI automatically. Seamless does not need separate client-side logic for that case.
|
|
170
257
|
|
|
171
258
|
## TypeScript
|
|
172
259
|
|
|
173
|
-
|
|
260
|
+
Type declarations are included with the SDK package.
|
|
174
261
|
|
|
175
262
|
```ts
|
|
176
|
-
import type {
|
|
263
|
+
import type {
|
|
264
|
+
SeamlessRoleplaySDK,
|
|
265
|
+
SeamlessRoleplayInitOptions,
|
|
266
|
+
SeamlessRoleplayOpenData,
|
|
267
|
+
AddToScenarioOptions,
|
|
268
|
+
} from "@rehers/rehers-roleplay-sdk";
|
|
177
269
|
```
|
package/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export interface SeamlessRoleplayInitOptions {
|
|
2
2
|
/** Publishable API key (starts with pk_live_ or pk_test_) */
|
|
3
3
|
publishableKey: string;
|
|
4
|
-
/**
|
|
4
|
+
/** Logged-in Seamless user ID. Pass String(me.id) from GET /api/users/me */
|
|
5
5
|
userId: string;
|
|
6
|
-
/**
|
|
6
|
+
/** Logged-in Seamless user email. Pass me.username from GET /api/users/me */
|
|
7
7
|
userEmail: string;
|
|
8
8
|
/** Optional user role for syncing permissions ("owner" | "admin" | "member") */
|
|
9
9
|
userRole?: "owner" | "admin" | "member";
|
|
@@ -78,8 +78,8 @@ export interface SeamlessRoleplaySDK {
|
|
|
78
78
|
init(options: SeamlessRoleplayInitOptions): void;
|
|
79
79
|
/** Open the roleplay modal for a contact (dialog mode). */
|
|
80
80
|
open(data: SeamlessRoleplayOpenData): void;
|
|
81
|
-
/** Mount the full Roleplay app into a container
|
|
82
|
-
mount(container: HTMLElement
|
|
81
|
+
/** Mount the full Roleplay app into a container. */
|
|
82
|
+
mount(container: HTMLElement): void;
|
|
83
83
|
/** Open the add-to-scenario dialog for bulk contact import. */
|
|
84
84
|
addToScenario(options: AddToScenarioOptions): void;
|
|
85
85
|
/** Close the active dialog. Does not affect mount. */
|
package/package.json
CHANGED
package/roleplay-sdk.js
CHANGED
|
@@ -35,7 +35,6 @@
|
|
|
35
35
|
// ── Mount state (persistent embed — survives dialog open/close) ───
|
|
36
36
|
var mountIframe = null;
|
|
37
37
|
var mountContainer = null;
|
|
38
|
-
var mountContactData = null;
|
|
39
38
|
var mountCallbacks = { onCallStarted: null, onCallEnded: null, onClose: null, onError: null };
|
|
40
39
|
var mountListener = null;
|
|
41
40
|
|
|
@@ -219,7 +218,6 @@
|
|
|
219
218
|
|
|
220
219
|
mountIframe = null;
|
|
221
220
|
mountContainer = null;
|
|
222
|
-
mountContactData = null;
|
|
223
221
|
mountCallbacks = { onCallStarted: null, onCallEnded: null, onClose: null, onError: null };
|
|
224
222
|
}
|
|
225
223
|
|
|
@@ -279,10 +277,10 @@
|
|
|
279
277
|
case "ROLEPLAY_READY":
|
|
280
278
|
getSessionToken()
|
|
281
279
|
.then(function (token) {
|
|
282
|
-
dispatchInitToTarget(mountIframe,
|
|
280
|
+
dispatchInitToTarget(mountIframe, null, null);
|
|
283
281
|
})
|
|
284
282
|
.catch(function () {
|
|
285
|
-
dispatchInitToTarget(mountIframe,
|
|
283
|
+
dispatchInitToTarget(mountIframe, null, null);
|
|
286
284
|
});
|
|
287
285
|
break;
|
|
288
286
|
|
|
@@ -571,13 +569,9 @@
|
|
|
571
569
|
},
|
|
572
570
|
|
|
573
571
|
/**
|
|
574
|
-
* Mount the
|
|
575
|
-
*
|
|
576
|
-
* Two modes:
|
|
577
|
-
* mount(container) — full app embed (dashboard, scenarios, call logs, etc.)
|
|
578
|
-
* mount(container, contactData) — roleplay call embed for a specific contact
|
|
572
|
+
* Mount the full Roleplay app into a container element.
|
|
579
573
|
*/
|
|
580
|
-
mount: function (container
|
|
574
|
+
mount: function (container) {
|
|
581
575
|
try {
|
|
582
576
|
if (!initCalled) {
|
|
583
577
|
logError("mount", "init() must be called first");
|
|
@@ -588,37 +582,22 @@
|
|
|
588
582
|
return;
|
|
589
583
|
}
|
|
590
584
|
|
|
591
|
-
// If contact data is provided, validate required fields
|
|
592
|
-
var hasContactData = data && data.name && data.domain && data.company && data.title;
|
|
593
|
-
if (data && !hasContactData && (data.name || data.domain || data.company || data.title)) {
|
|
594
|
-
logError("mount", "contact data requires { name, domain, company, title }");
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
585
|
// Tear down any existing mount (re-mount)
|
|
599
586
|
if (mountIframe) teardownMount();
|
|
600
587
|
|
|
601
|
-
|
|
602
|
-
mountCallbacks.onCallStarted = (data && data.onCallStarted) || null;
|
|
603
|
-
mountCallbacks.onCallEnded = (data && data.onCallEnded) || null;
|
|
604
|
-
mountCallbacks.onClose = (data && data.onClose) || null;
|
|
605
|
-
mountCallbacks.onError = (data && data.onError) || null;
|
|
588
|
+
mountCallbacks = { onCallStarted: null, onCallEnded: null, onClose: null, onError: null };
|
|
606
589
|
mountContainer = container;
|
|
607
590
|
|
|
608
591
|
// Listen for messages
|
|
609
592
|
mountListener = handleMountMessage;
|
|
610
593
|
window.addEventListener("message", mountListener);
|
|
611
594
|
|
|
612
|
-
|
|
613
|
-
var iframeEl = createIframe(hasContactData ? "/embed/roleplay-call" : "/");
|
|
595
|
+
var iframeEl = createIframe("/");
|
|
614
596
|
mountIframe = iframeEl;
|
|
615
597
|
container.appendChild(iframeEl);
|
|
616
598
|
} catch (e) {
|
|
617
599
|
logError("mount", e);
|
|
618
600
|
teardownMount();
|
|
619
|
-
if (data && typeof data.onError === "function") {
|
|
620
|
-
try { data.onError({ code: "SDK_ERROR", message: e.message || "Unexpected error during mount" }); } catch (_) {}
|
|
621
|
-
}
|
|
622
601
|
}
|
|
623
602
|
},
|
|
624
603
|
|