@product7/product7-js 0.3.2 → 0.3.4
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 +53 -29
- package/dist/README.md +53 -29
- package/dist/product7-js.js +455 -139
- package/dist/product7-js.js.map +1 -1
- package/dist/product7-js.min.js +1 -1
- package/dist/product7-js.min.js.map +1 -1
- package/package.json +1 -1
- package/src/api/services/MessengerService.js +1 -30
- package/src/core/APIService.js +35 -1
- package/src/core/BaseAPIService.js +124 -11
- package/src/core/Product7.js +182 -18
- package/src/docs/api.md +253 -89
- package/src/docs/example.md +203 -153
- package/src/docs/framework-integrations.md +236 -358
- package/src/docs/installation.md +171 -143
- package/src/index.js +48 -41
- package/src/widgets/MessengerWidget.js +27 -29
- package/src/widgets/SurveyWidget.js +20 -0
- package/src/widgets/messenger/views/ChatView.js +14 -8
- package/src/widgets/messenger/views/HomeView.js +5 -2
- package/types/index.d.ts +34 -0
package/src/docs/installation.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Package Installation
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### npm
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @product7/product7-js
|
|
@@ -17,34 +17,50 @@ yarn add @product7/product7-js
|
|
|
17
17
|
### CDN
|
|
18
18
|
|
|
19
19
|
```html
|
|
20
|
-
<script src="https://cdn.
|
|
20
|
+
<script src="https://cdn.jsdelivr.net/npm/@product7/product7-js@latest/dist/product7-js.min.js"></script>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
25
|
## Quick Setup
|
|
26
26
|
|
|
27
|
-
### 1.
|
|
27
|
+
### 1. Create the SDK
|
|
28
28
|
|
|
29
29
|
```javascript
|
|
30
|
-
import Product7 from '@product7/product7-js';
|
|
30
|
+
import { Product7 } from '@product7/product7-js';
|
|
31
31
|
|
|
32
32
|
const sdk = new Product7({
|
|
33
33
|
workspace: 'your-workspace',
|
|
34
|
-
|
|
35
|
-
user_id: 'user_123',
|
|
36
|
-
email: 'user@example.com',
|
|
37
|
-
name: 'John Doe',
|
|
38
|
-
},
|
|
34
|
+
boardName: 'feature-requests',
|
|
39
35
|
});
|
|
36
|
+
```
|
|
40
37
|
|
|
38
|
+
### 2. Initialize the session
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
41
|
await sdk.init();
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
###
|
|
44
|
+
### 3. Identify the current user
|
|
45
45
|
|
|
46
46
|
```javascript
|
|
47
|
-
|
|
47
|
+
await sdk.identify({
|
|
48
|
+
user_id: 'user_123',
|
|
49
|
+
email: 'user@example.com',
|
|
50
|
+
name: 'John Doe',
|
|
51
|
+
custom_fields: {
|
|
52
|
+
plan: 'pro',
|
|
53
|
+
role: 'admin',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 4. Create and mount a widget
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
const widget = sdk.createFeedbackWidget({
|
|
62
|
+
position: 'bottom-right',
|
|
63
|
+
});
|
|
48
64
|
widget.mount();
|
|
49
65
|
```
|
|
50
66
|
|
|
@@ -52,91 +68,117 @@ widget.mount();
|
|
|
52
68
|
|
|
53
69
|
## Installation Methods
|
|
54
70
|
|
|
55
|
-
### Method 1:
|
|
71
|
+
### Method 1: npm / ESM
|
|
56
72
|
|
|
57
73
|
```javascript
|
|
58
|
-
import Product7 from '@product7/product7-js';
|
|
74
|
+
import { Product7 } from '@product7/product7-js';
|
|
59
75
|
|
|
60
76
|
const sdk = new Product7({
|
|
61
|
-
|
|
77
|
+
workspace: 'your-workspace',
|
|
78
|
+
boardName: 'feature-requests',
|
|
62
79
|
});
|
|
80
|
+
|
|
63
81
|
await sdk.init();
|
|
82
|
+
await sdk.identify({
|
|
83
|
+
user_id: 'user_123',
|
|
84
|
+
email: 'user@example.com',
|
|
85
|
+
});
|
|
64
86
|
|
|
65
|
-
const widget = sdk.
|
|
87
|
+
const widget = sdk.createFeedbackWidget();
|
|
66
88
|
widget.mount();
|
|
67
89
|
```
|
|
68
90
|
|
|
69
|
-
### Method 2: CDN
|
|
91
|
+
### Method 2: CDN
|
|
70
92
|
|
|
71
93
|
```html
|
|
94
|
+
<script src="https://cdn.jsdelivr.net/npm/@product7/product7-js@latest/dist/product7-js.min.js"></script>
|
|
72
95
|
<script>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
96
|
+
async function bootProduct7() {
|
|
97
|
+
const sdk = new window.Product7.Product7SDK({
|
|
98
|
+
workspace: 'your-workspace',
|
|
99
|
+
boardName: 'feature-requests',
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await sdk.init();
|
|
103
|
+
await sdk.identify({
|
|
76
104
|
user_id: 'user_123',
|
|
77
105
|
email: 'user@example.com',
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// SDK auto-initializes and is available at window.Product7
|
|
84
|
-
window.Product7.onReady((sdk) => {
|
|
85
|
-
const widget = sdk.createWidget('button');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const widget = sdk.createFeedbackWidget({
|
|
109
|
+
position: 'bottom-right',
|
|
110
|
+
});
|
|
86
111
|
widget.mount();
|
|
87
|
-
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
bootProduct7();
|
|
88
115
|
</script>
|
|
89
116
|
```
|
|
90
117
|
|
|
91
|
-
### Method 3:
|
|
118
|
+
### Method 3: Headless Feedback
|
|
92
119
|
|
|
93
|
-
|
|
94
|
-
<script src="https://cdn.product7.io/product7-js/latest/product7-js.js"></script>
|
|
95
|
-
<script>
|
|
96
|
-
const sdk = new window.Product7({
|
|
97
|
-
workspace: 'your-workspace',
|
|
98
|
-
metadata: {
|
|
99
|
-
user_id: 'user_123',
|
|
100
|
-
email: 'user@example.com',
|
|
101
|
-
},
|
|
102
|
-
});
|
|
120
|
+
Use `headless: true` when you want to control opening from your own UI instead of rendering the default floating trigger.
|
|
103
121
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
122
|
+
```javascript
|
|
123
|
+
const widget = sdk.createFeedbackWidget({
|
|
124
|
+
headless: true,
|
|
125
|
+
boardName: 'feature-requests',
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
widget.mount();
|
|
129
|
+
|
|
130
|
+
document
|
|
131
|
+
.querySelector('#leave-feedback')
|
|
132
|
+
?.addEventListener('click', () => widget.open());
|
|
109
133
|
```
|
|
110
134
|
|
|
111
135
|
---
|
|
112
136
|
|
|
113
|
-
## Framework
|
|
137
|
+
## Framework Examples
|
|
114
138
|
|
|
115
139
|
### React
|
|
116
140
|
|
|
117
141
|
```jsx
|
|
118
|
-
import { useEffect,
|
|
119
|
-
import Product7 from '@product7/product7-js';
|
|
142
|
+
import { useEffect, useRef } from 'react';
|
|
143
|
+
import { Product7 } from '@product7/product7-js';
|
|
120
144
|
|
|
121
145
|
function App() {
|
|
122
|
-
const
|
|
146
|
+
const sdkRef = useRef(null);
|
|
147
|
+
const widgetRef = useRef(null);
|
|
123
148
|
|
|
124
149
|
useEffect(() => {
|
|
125
|
-
|
|
126
|
-
workspace: 'your-workspace',
|
|
127
|
-
metadata: {
|
|
128
|
-
user_id: 'user_123',
|
|
129
|
-
email: 'user@example.com',
|
|
130
|
-
},
|
|
131
|
-
});
|
|
150
|
+
let disposed = false;
|
|
132
151
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
152
|
+
(async () => {
|
|
153
|
+
const sdk = new Product7({
|
|
154
|
+
workspace: 'your-workspace',
|
|
155
|
+
boardName: 'feature-requests',
|
|
156
|
+
});
|
|
138
157
|
|
|
139
|
-
|
|
158
|
+
await sdk.init();
|
|
159
|
+
await sdk.identify({
|
|
160
|
+
user_id: 'user_123',
|
|
161
|
+
email: 'user@example.com',
|
|
162
|
+
name: 'John Doe',
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (disposed) {
|
|
166
|
+
sdk.destroy();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
sdkRef.current = sdk;
|
|
171
|
+
widgetRef.current = sdk.createFeedbackWidget({
|
|
172
|
+
position: 'bottom-right',
|
|
173
|
+
});
|
|
174
|
+
widgetRef.current.mount();
|
|
175
|
+
})();
|
|
176
|
+
|
|
177
|
+
return () => {
|
|
178
|
+
disposed = true;
|
|
179
|
+
widgetRef.current?.destroy();
|
|
180
|
+
sdkRef.current?.destroy();
|
|
181
|
+
};
|
|
140
182
|
}, []);
|
|
141
183
|
|
|
142
184
|
return <div>Your App</div>;
|
|
@@ -147,93 +189,64 @@ function App() {
|
|
|
147
189
|
|
|
148
190
|
```vue
|
|
149
191
|
<script setup>
|
|
150
|
-
import { onMounted, onUnmounted } from 'vue';
|
|
151
|
-
import Product7 from '@product7/product7-js';
|
|
192
|
+
import { onMounted, onUnmounted, ref } from 'vue';
|
|
193
|
+
import { Product7 } from '@product7/product7-js';
|
|
152
194
|
|
|
153
|
-
|
|
195
|
+
const sdk = ref(null);
|
|
196
|
+
const widget = ref(null);
|
|
154
197
|
|
|
155
198
|
onMounted(async () => {
|
|
156
|
-
sdk = new Product7({
|
|
199
|
+
sdk.value = new Product7({
|
|
157
200
|
workspace: 'your-workspace',
|
|
158
|
-
|
|
159
|
-
user_id: 'user_123',
|
|
160
|
-
email: 'user@example.com',
|
|
161
|
-
},
|
|
201
|
+
boardName: 'feature-requests',
|
|
162
202
|
});
|
|
163
203
|
|
|
164
|
-
await sdk.init();
|
|
165
|
-
|
|
166
|
-
|
|
204
|
+
await sdk.value.init();
|
|
205
|
+
await sdk.value.identify({
|
|
206
|
+
user_id: 'user_123',
|
|
207
|
+
email: 'user@example.com',
|
|
208
|
+
name: 'John Doe',
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
widget.value = sdk.value.createFeedbackWidget({
|
|
212
|
+
position: 'bottom-right',
|
|
213
|
+
});
|
|
214
|
+
widget.value.mount();
|
|
167
215
|
});
|
|
168
216
|
|
|
169
217
|
onUnmounted(() => {
|
|
170
|
-
|
|
218
|
+
widget.value?.destroy();
|
|
219
|
+
sdk.value?.destroy();
|
|
171
220
|
});
|
|
172
221
|
</script>
|
|
173
|
-
|
|
174
|
-
<template>
|
|
175
|
-
<div>Your App</div>
|
|
176
|
-
</template>
|
|
177
222
|
```
|
|
178
223
|
|
|
179
|
-
###
|
|
180
|
-
|
|
181
|
-
```javascript
|
|
182
|
-
// app/layout.js or pages/_app.js
|
|
183
|
-
'use client';
|
|
184
|
-
|
|
185
|
-
import { useEffect } from 'react';
|
|
186
|
-
import Product7 from '@product7/product7-js';
|
|
187
|
-
|
|
188
|
-
export default function RootLayout({ children }) {
|
|
189
|
-
useEffect(() => {
|
|
190
|
-
const sdk = new Product7({
|
|
191
|
-
workspace: 'your-workspace',
|
|
192
|
-
metadata: {
|
|
193
|
-
user_id: 'user_123',
|
|
194
|
-
email: 'user@example.com',
|
|
195
|
-
},
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
sdk.init().then(() => {
|
|
199
|
-
const widget = sdk.createWidget('button');
|
|
200
|
-
widget.mount();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
return () => sdk.destroy();
|
|
204
|
-
}, []);
|
|
205
|
-
|
|
206
|
-
return (
|
|
207
|
-
<html>
|
|
208
|
-
<body>{children}</body>
|
|
209
|
-
</html>
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
---
|
|
215
|
-
|
|
216
|
-
## TypeScript Setup
|
|
217
|
-
|
|
218
|
-
The SDK includes TypeScript definitions out of the box.
|
|
224
|
+
### TypeScript
|
|
219
225
|
|
|
220
226
|
```typescript
|
|
221
|
-
import
|
|
227
|
+
import {
|
|
228
|
+
Product7,
|
|
229
|
+
type FeedbackConfig,
|
|
230
|
+
type FeedbackWidget,
|
|
231
|
+
} from '@product7/product7-js';
|
|
222
232
|
|
|
223
|
-
const config:
|
|
233
|
+
const config: FeedbackConfig = {
|
|
224
234
|
workspace: 'your-workspace',
|
|
225
|
-
|
|
226
|
-
user_id: 'user_123',
|
|
227
|
-
email: 'user@example.com',
|
|
228
|
-
name: 'John Doe',
|
|
229
|
-
},
|
|
235
|
+
boardName: 'feature-requests',
|
|
230
236
|
theme: 'light',
|
|
231
237
|
};
|
|
232
238
|
|
|
233
239
|
const sdk = new Product7(config);
|
|
234
240
|
await sdk.init();
|
|
241
|
+
await sdk.identify({
|
|
242
|
+
user_id: 'user_123',
|
|
243
|
+
email: 'user@example.com',
|
|
244
|
+
name: 'John Doe',
|
|
245
|
+
});
|
|
235
246
|
|
|
236
|
-
const widget:
|
|
247
|
+
const widget: FeedbackWidget = sdk.createFeedbackWidget({
|
|
248
|
+
headless: true,
|
|
249
|
+
});
|
|
237
250
|
widget.mount();
|
|
238
251
|
```
|
|
239
252
|
|
|
@@ -243,39 +256,54 @@ widget.mount();
|
|
|
243
256
|
|
|
244
257
|
### SDK not initializing
|
|
245
258
|
|
|
246
|
-
Check
|
|
259
|
+
Check that `workspace` is set:
|
|
247
260
|
|
|
248
261
|
```javascript
|
|
249
262
|
const sdk = new Product7({
|
|
250
|
-
workspace: 'your-workspace',
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
263
|
+
workspace: 'your-workspace',
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Identify not working
|
|
268
|
+
|
|
269
|
+
Call `identify()` after `init()` and pass at least `user_id` or `email`:
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
await sdk.init();
|
|
273
|
+
await sdk.identify({
|
|
274
|
+
user_id: 'user_123',
|
|
275
|
+
email: 'user@example.com',
|
|
256
276
|
});
|
|
257
277
|
```
|
|
258
278
|
|
|
259
279
|
### Widget not showing
|
|
260
280
|
|
|
261
|
-
|
|
281
|
+
Make sure you initialize first, then mount the widget:
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
await sdk.init();
|
|
285
|
+
const widget = sdk.createFeedbackWidget();
|
|
286
|
+
widget.mount();
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Custom trigger does nothing
|
|
290
|
+
|
|
291
|
+
If you use `headless: true`, you still need to call `mount()` before `open()`:
|
|
262
292
|
|
|
263
293
|
```javascript
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
widget.
|
|
294
|
+
const widget = sdk.createFeedbackWidget({ headless: true });
|
|
295
|
+
widget.mount();
|
|
296
|
+
widget.open();
|
|
267
297
|
```
|
|
268
298
|
|
|
269
|
-
### CDN
|
|
299
|
+
### CDN usage fails
|
|
270
300
|
|
|
271
|
-
|
|
301
|
+
Use `window.Product7.Product7SDK` for the class-based API:
|
|
272
302
|
|
|
273
303
|
```html
|
|
274
|
-
<!-- Config MUST come before script tag -->
|
|
275
304
|
<script>
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
};
|
|
305
|
+
const sdk = new window.Product7.Product7SDK({
|
|
306
|
+
workspace: 'your-workspace',
|
|
307
|
+
});
|
|
279
308
|
</script>
|
|
280
|
-
<script src="https://cdn.product7.io/product7-js/latest/product7-js.js"></script>
|
|
281
309
|
```
|
package/src/index.js
CHANGED
|
@@ -98,9 +98,21 @@ function cleanUndefined(obj) {
|
|
|
98
98
|
return obj;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
function hasIdentifyMetadata(metadata = {}) {
|
|
102
|
+
return Object.values(metadata).some((value) => value !== undefined);
|
|
103
|
+
}
|
|
104
|
+
|
|
101
105
|
// --- Ensure SDK is initialized (shared by widget inits) ---
|
|
102
106
|
|
|
103
107
|
async function ensureSDK(options) {
|
|
108
|
+
if (
|
|
109
|
+
Product7._sdk &&
|
|
110
|
+
options.organization &&
|
|
111
|
+
Product7._sdk.config.workspace !== options.organization
|
|
112
|
+
) {
|
|
113
|
+
Product7.destroy();
|
|
114
|
+
}
|
|
115
|
+
|
|
104
116
|
if (Product7._sdk) return Product7._sdk;
|
|
105
117
|
|
|
106
118
|
if (options.organization) {
|
|
@@ -143,63 +155,58 @@ const Product7 = {
|
|
|
143
155
|
async identify(data = {}, callback) {
|
|
144
156
|
try {
|
|
145
157
|
const transformed = transformIdentifyData(data);
|
|
146
|
-
Product7._organization =
|
|
158
|
+
Product7._organization =
|
|
159
|
+
transformed.workspace || Product7._organization || null;
|
|
147
160
|
|
|
148
161
|
const config = cleanUndefined({
|
|
149
|
-
workspace:
|
|
150
|
-
metadata: transformed.metadata,
|
|
162
|
+
workspace: Product7._organization,
|
|
151
163
|
debug: transformed.debug,
|
|
152
164
|
mock: transformed.mock,
|
|
153
165
|
env: transformed.env,
|
|
154
166
|
apiUrl: transformed.apiUrl,
|
|
155
167
|
});
|
|
156
168
|
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const identifyPayload = {
|
|
167
|
-
user_id: transformed.metadata.user_id,
|
|
168
|
-
email: transformed.metadata.email,
|
|
169
|
-
name: transformed.metadata.name,
|
|
170
|
-
avatar: transformed.metadata.profile_picture,
|
|
171
|
-
attributes: transformed.metadata.custom_fields || {},
|
|
172
|
-
};
|
|
173
|
-
if (transformed.metadata.company) {
|
|
174
|
-
identifyPayload.company = transformed.metadata.company;
|
|
175
|
-
}
|
|
176
|
-
await sdk.apiService._makeRequest('/widget/identify', {
|
|
177
|
-
method: 'POST',
|
|
178
|
-
body: JSON.stringify(identifyPayload),
|
|
179
|
-
headers: {
|
|
180
|
-
'Content-Type': 'application/json',
|
|
181
|
-
Authorization: `Bearer ${sdk.apiService.sessionToken}`,
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
} catch (identifyErr) {
|
|
185
|
-
if (config.debug) {
|
|
186
|
-
console.warn('[Product7] Attribute sync failed:', identifyErr);
|
|
187
|
-
}
|
|
169
|
+
let sdk = Product7._sdk;
|
|
170
|
+
const requiresNewSDK =
|
|
171
|
+
!sdk || (config.workspace && sdk.config.workspace !== config.workspace);
|
|
172
|
+
|
|
173
|
+
if (requiresNewSDK) {
|
|
174
|
+
if (Product7._sdk) {
|
|
175
|
+
Product7.destroy();
|
|
176
|
+
Product7._organization = config.workspace || null;
|
|
188
177
|
}
|
|
178
|
+
sdk = new Product7SDK(config);
|
|
179
|
+
Product7._sdk = sdk;
|
|
189
180
|
}
|
|
190
181
|
|
|
182
|
+
const initData = sdk.initialized
|
|
183
|
+
? {
|
|
184
|
+
alreadyInitialized: true,
|
|
185
|
+
sessionToken: sdk.apiService?.sessionToken,
|
|
186
|
+
}
|
|
187
|
+
: await sdk.init();
|
|
188
|
+
|
|
189
|
+
const identifyData = hasIdentifyMetadata(transformed.metadata)
|
|
190
|
+
? await sdk.identify(transformed.metadata)
|
|
191
|
+
: null;
|
|
192
|
+
Product7._identified = Boolean(identifyData?.identified);
|
|
193
|
+
|
|
191
194
|
Product7._flushQueue();
|
|
192
195
|
|
|
193
196
|
if (typeof window !== 'undefined' && typeof CustomEvent !== 'undefined') {
|
|
194
197
|
window.dispatchEvent(
|
|
195
198
|
new CustomEvent('Product7Ready', {
|
|
196
|
-
detail: { sdk, config, initData },
|
|
199
|
+
detail: { sdk, config: sdk.config, initData, identifyData },
|
|
197
200
|
})
|
|
198
201
|
);
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
if (typeof callback === 'function') callback(null);
|
|
202
|
-
return
|
|
205
|
+
return {
|
|
206
|
+
...initData,
|
|
207
|
+
identified: Product7._identified,
|
|
208
|
+
identify: identifyData,
|
|
209
|
+
};
|
|
203
210
|
} catch (error) {
|
|
204
211
|
console.error('[Product7] Identify failed:', error);
|
|
205
212
|
|
|
@@ -258,7 +265,7 @@ const Product7 = {
|
|
|
258
265
|
if (!options.placement) {
|
|
259
266
|
widgetOptions.autoShow = false;
|
|
260
267
|
widgetOptions.displayMode = 'modal';
|
|
261
|
-
widgetOptions.
|
|
268
|
+
widgetOptions.headless = true;
|
|
262
269
|
} else {
|
|
263
270
|
// Trigger button is always visible when placement is set
|
|
264
271
|
widgetOptions.suppressAfterSubmission = false;
|
|
@@ -266,7 +273,7 @@ const Product7 = {
|
|
|
266
273
|
}
|
|
267
274
|
|
|
268
275
|
try {
|
|
269
|
-
const widget = sdk.
|
|
276
|
+
const widget = sdk.createFeedbackWidget(widgetOptions);
|
|
270
277
|
widget.mount();
|
|
271
278
|
|
|
272
279
|
if (options.placement) {
|
|
@@ -305,13 +312,13 @@ const Product7 = {
|
|
|
305
312
|
if (options.setBoard) {
|
|
306
313
|
Product7._feedbackWidget.options.boardName = options.setBoard;
|
|
307
314
|
}
|
|
308
|
-
Product7._feedbackWidget.
|
|
315
|
+
Product7._feedbackWidget.open();
|
|
309
316
|
}
|
|
310
317
|
},
|
|
311
318
|
|
|
312
319
|
closeFeedback() {
|
|
313
320
|
if (Product7._feedbackWidget) {
|
|
314
|
-
Product7._feedbackWidget.
|
|
321
|
+
Product7._feedbackWidget.close();
|
|
315
322
|
}
|
|
316
323
|
},
|
|
317
324
|
|
|
@@ -370,7 +377,7 @@ const Product7 = {
|
|
|
370
377
|
widgetOptions.enabled = true;
|
|
371
378
|
|
|
372
379
|
try {
|
|
373
|
-
const widget = sdk.
|
|
380
|
+
const widget = sdk.createMessengerWidget(widgetOptions);
|
|
374
381
|
widget.mount();
|
|
375
382
|
widget.show();
|
|
376
383
|
|
|
@@ -100,6 +100,10 @@ export class MessengerWidget extends BaseWidget {
|
|
|
100
100
|
this._handleConversationClosed = this._handleConversationClosed.bind(this);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
_hasTrigger() {
|
|
104
|
+
return this.options.trigger === true || this.options.trigger === undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
103
107
|
_createInternalFeedbackWidget() {
|
|
104
108
|
try {
|
|
105
109
|
const widget = this.sdk.createWidget('button', {
|
|
@@ -135,11 +139,13 @@ export class MessengerWidget extends BaseWidget {
|
|
|
135
139
|
this._feedbackWidget = this._createInternalFeedbackWidget();
|
|
136
140
|
}
|
|
137
141
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
if (this._hasTrigger()) {
|
|
143
|
+
this.launcher = new MessengerLauncher(this.messengerState, {
|
|
144
|
+
position: this.messengerOptions.position,
|
|
145
|
+
primaryColor: this.messengerOptions.primaryColor,
|
|
146
|
+
});
|
|
147
|
+
container.appendChild(this.launcher.render());
|
|
148
|
+
}
|
|
143
149
|
|
|
144
150
|
this.panel = new MessengerPanel(this.messengerState, {
|
|
145
151
|
position: this.messengerOptions.position,
|
|
@@ -274,28 +280,13 @@ export class MessengerWidget extends BaseWidget {
|
|
|
274
280
|
|
|
275
281
|
async _handleIdentifyContact(contactData) {
|
|
276
282
|
try {
|
|
277
|
-
|
|
278
|
-
|
|
283
|
+
// Route through sdk.identify() so the SDK-level identity state is updated
|
|
284
|
+
// and applyIdentity() handles the messenger state + WebSocket as a side effect.
|
|
285
|
+
const result = await this.sdk.identify({
|
|
279
286
|
email: contactData.email,
|
|
287
|
+
name: contactData.name,
|
|
280
288
|
});
|
|
281
|
-
|
|
282
|
-
if (response.status) {
|
|
283
|
-
console.log(
|
|
284
|
-
'[MessengerWidget] Contact identified:',
|
|
285
|
-
response.data.contact_id
|
|
286
|
-
);
|
|
287
|
-
this.messengerState.setIdentified(true, {
|
|
288
|
-
name: contactData.name,
|
|
289
|
-
email: contactData.email,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// Start WebSocket now that session token is available
|
|
293
|
-
if (this.apiService?.sessionToken && !this.wsService?.isConnected) {
|
|
294
|
-
this._initWebSocket();
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return response;
|
|
289
|
+
return result;
|
|
299
290
|
} catch (error) {
|
|
300
291
|
console.error('[MessengerWidget] Failed to identify contact:', error);
|
|
301
292
|
throw error;
|
|
@@ -303,10 +294,14 @@ export class MessengerWidget extends BaseWidget {
|
|
|
303
294
|
}
|
|
304
295
|
|
|
305
296
|
markAsIdentified(name, email) {
|
|
306
|
-
|
|
307
|
-
|
|
297
|
+
// Called externally by the app when the user is already known.
|
|
298
|
+
// No API call needed — identity was already established via sdk.identify().
|
|
299
|
+
this.applyIdentity({ name, email });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
applyIdentity(metadata = {}) {
|
|
303
|
+
this.messengerState.setIdentified(true, metadata);
|
|
308
304
|
|
|
309
|
-
// Start WebSocket now that we have a session token
|
|
310
305
|
if (this.apiService?.sessionToken && !this.wsService?.isConnected) {
|
|
311
306
|
this._initWebSocket();
|
|
312
307
|
}
|
|
@@ -850,7 +845,10 @@ export class MessengerWidget extends BaseWidget {
|
|
|
850
845
|
}
|
|
851
846
|
|
|
852
847
|
_hasExplicitOption(key) {
|
|
853
|
-
return Object.prototype.hasOwnProperty.call(
|
|
848
|
+
return Object.prototype.hasOwnProperty.call(
|
|
849
|
+
this._explicitOptions || {},
|
|
850
|
+
key
|
|
851
|
+
);
|
|
854
852
|
}
|
|
855
853
|
|
|
856
854
|
async checkAgentAvailability() {
|