@product7/product7-js 0.3.2 → 0.3.5
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 +515 -141
- 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 +87 -1
- package/src/core/BaseAPIService.js +128 -11
- package/src/core/Product7.js +183 -19
- 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 +30 -30
- 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/dist/product7-js.js
CHANGED
|
@@ -737,36 +737,7 @@
|
|
|
737
737
|
}
|
|
738
738
|
|
|
739
739
|
async identifyContact(data) {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
if (this.api.mock) {
|
|
743
|
-
await delay$1(300);
|
|
744
|
-
return {
|
|
745
|
-
status: true,
|
|
746
|
-
data: {
|
|
747
|
-
contact_id: 'mock_contact_' + Date.now(),
|
|
748
|
-
email: data.email,
|
|
749
|
-
name: data.name || '',
|
|
750
|
-
is_new: true,
|
|
751
|
-
},
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
return this.api._makeRequest('/widget/messenger/identify', {
|
|
756
|
-
method: 'POST',
|
|
757
|
-
headers: {
|
|
758
|
-
'Content-Type': 'application/json',
|
|
759
|
-
Authorization: `Bearer ${this.api.sessionToken}`,
|
|
760
|
-
},
|
|
761
|
-
body: JSON.stringify({
|
|
762
|
-
email: data.email,
|
|
763
|
-
name: data.name || '',
|
|
764
|
-
phone: data.phone || '',
|
|
765
|
-
company: data.company || '',
|
|
766
|
-
avatar_url: data.avatar_url || '',
|
|
767
|
-
metadata: data.metadata || {},
|
|
768
|
-
}),
|
|
769
|
-
});
|
|
740
|
+
return this.api.identify(data);
|
|
770
741
|
}
|
|
771
742
|
|
|
772
743
|
async sendTypingIndicator(conversationId, isTyping) {
|
|
@@ -995,11 +966,16 @@
|
|
|
995
966
|
this.sessionToken = null;
|
|
996
967
|
this.sessionExpiry = null;
|
|
997
968
|
this.metadata = config.metadata || null;
|
|
969
|
+
this.identitySyncedToken = null;
|
|
998
970
|
this.mock = config.mock || false;
|
|
999
971
|
this.env = config.env || 'production';
|
|
1000
972
|
this.baseURL = this._getBaseURL(config);
|
|
1001
973
|
|
|
1002
974
|
this._loadStoredSession();
|
|
975
|
+
this._loadStoredMetadata();
|
|
976
|
+
if (this.isSessionValid() && this.metadata) {
|
|
977
|
+
this.identitySyncedToken = this.sessionToken;
|
|
978
|
+
}
|
|
1003
979
|
}
|
|
1004
980
|
|
|
1005
981
|
_getBaseURL(config) {
|
|
@@ -1039,20 +1015,17 @@
|
|
|
1039
1015
|
: envConfig.base;
|
|
1040
1016
|
}
|
|
1041
1017
|
|
|
1042
|
-
async init(metadata =
|
|
1043
|
-
if (metadata) {
|
|
1044
|
-
this.metadata
|
|
1018
|
+
async init(metadata = undefined) {
|
|
1019
|
+
if (metadata !== undefined) {
|
|
1020
|
+
this.setMetadata(metadata);
|
|
1045
1021
|
}
|
|
1046
1022
|
|
|
1047
1023
|
if (this.isSessionValid()) {
|
|
1048
1024
|
return { sessionToken: this.sessionToken };
|
|
1049
1025
|
}
|
|
1050
1026
|
|
|
1051
|
-
if (!this.workspace
|
|
1052
|
-
throw new APIError$1(
|
|
1053
|
-
400,
|
|
1054
|
-
`Missing ${!this.workspace ? 'workspace' : 'user context'} for initialization`
|
|
1055
|
-
);
|
|
1027
|
+
if (!this.workspace) {
|
|
1028
|
+
throw new APIError$1(400, 'Missing workspace for initialization');
|
|
1056
1029
|
}
|
|
1057
1030
|
|
|
1058
1031
|
if (this.mock) {
|
|
@@ -1065,6 +1038,7 @@
|
|
|
1065
1038
|
async _initMockSession() {
|
|
1066
1039
|
this.sessionToken = 'mock_session_' + Date.now();
|
|
1067
1040
|
this.sessionExpiry = new Date(Date.now() + 3600 * 1000);
|
|
1041
|
+
this.identitySyncedToken = null;
|
|
1068
1042
|
this._storeSession();
|
|
1069
1043
|
return {
|
|
1070
1044
|
sessionToken: this.sessionToken,
|
|
@@ -1083,7 +1057,6 @@
|
|
|
1083
1057
|
async _initRealSession() {
|
|
1084
1058
|
const payload = {
|
|
1085
1059
|
workspace: this.workspace,
|
|
1086
|
-
user: this.metadata,
|
|
1087
1060
|
};
|
|
1088
1061
|
|
|
1089
1062
|
try {
|
|
@@ -1096,6 +1069,7 @@
|
|
|
1096
1069
|
|
|
1097
1070
|
this.sessionToken = initData.sessionToken;
|
|
1098
1071
|
this.sessionExpiry = new Date(Date.now() + initData.expiresIn * 1000);
|
|
1072
|
+
this.identitySyncedToken = null;
|
|
1099
1073
|
this._storeSession();
|
|
1100
1074
|
|
|
1101
1075
|
return {
|
|
@@ -1162,12 +1136,52 @@
|
|
|
1162
1136
|
this.sessionToken = null;
|
|
1163
1137
|
this.sessionExpiry = null;
|
|
1164
1138
|
await this.init();
|
|
1139
|
+
await this._restoreIdentity();
|
|
1165
1140
|
return await method.apply(this, args);
|
|
1166
1141
|
}
|
|
1167
1142
|
throw error;
|
|
1168
1143
|
}
|
|
1169
1144
|
}
|
|
1170
1145
|
|
|
1146
|
+
async identify(metadata = this.metadata) {
|
|
1147
|
+
if (metadata !== undefined) {
|
|
1148
|
+
this.setMetadata(metadata);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
if (!this.metadata) {
|
|
1152
|
+
throw new APIError$1(400, 'Missing user context for identify');
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
await this._ensureSession();
|
|
1156
|
+
|
|
1157
|
+
const payload = this._buildIdentifyPayload(this.metadata);
|
|
1158
|
+
|
|
1159
|
+
if (this.mock) {
|
|
1160
|
+
this.identitySyncedToken = this.sessionToken;
|
|
1161
|
+
return {
|
|
1162
|
+
status: true,
|
|
1163
|
+
identified: true,
|
|
1164
|
+
data: {
|
|
1165
|
+
user_id: payload.user_id || null,
|
|
1166
|
+
email: payload.email || null,
|
|
1167
|
+
name: payload.name || null,
|
|
1168
|
+
},
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
const response = await this._makeRequest('/widget/identify', {
|
|
1173
|
+
method: 'POST',
|
|
1174
|
+
body: JSON.stringify(payload),
|
|
1175
|
+
headers: {
|
|
1176
|
+
'Content-Type': 'application/json',
|
|
1177
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
1178
|
+
},
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
this.identitySyncedToken = this.sessionToken;
|
|
1182
|
+
return response;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1171
1185
|
isSessionValid() {
|
|
1172
1186
|
return (
|
|
1173
1187
|
this.sessionToken && this.sessionExpiry && new Date() < this.sessionExpiry
|
|
@@ -1175,8 +1189,13 @@
|
|
|
1175
1189
|
}
|
|
1176
1190
|
|
|
1177
1191
|
setMetadata(metadata) {
|
|
1178
|
-
this.metadata = metadata;
|
|
1179
|
-
this.
|
|
1192
|
+
this.metadata = metadata || null;
|
|
1193
|
+
this.identitySyncedToken = null;
|
|
1194
|
+
if (this.metadata) {
|
|
1195
|
+
this._storeData('product7_metadata', this.metadata);
|
|
1196
|
+
} else {
|
|
1197
|
+
this._removeData('product7_metadata');
|
|
1198
|
+
}
|
|
1180
1199
|
}
|
|
1181
1200
|
|
|
1182
1201
|
getMetadata() {
|
|
@@ -1186,8 +1205,13 @@
|
|
|
1186
1205
|
clearSession() {
|
|
1187
1206
|
this.sessionToken = null;
|
|
1188
1207
|
this.sessionExpiry = null;
|
|
1208
|
+
this.identitySyncedToken = null;
|
|
1209
|
+
this.contactId = null;
|
|
1210
|
+
this.contactEmail = null;
|
|
1211
|
+
this.contactName = null;
|
|
1189
1212
|
this._removeData('product7_session');
|
|
1190
1213
|
this._removeData('product7_metadata');
|
|
1214
|
+
this._removeData('product7_contact');
|
|
1191
1215
|
}
|
|
1192
1216
|
|
|
1193
1217
|
_storeSession() {
|
|
@@ -1211,6 +1235,14 @@
|
|
|
1211
1235
|
if (!stored) return false;
|
|
1212
1236
|
|
|
1213
1237
|
const sessionData = JSON.parse(stored);
|
|
1238
|
+
if (
|
|
1239
|
+
this.workspace &&
|
|
1240
|
+
sessionData.workspace &&
|
|
1241
|
+
sessionData.workspace !== this.workspace
|
|
1242
|
+
) {
|
|
1243
|
+
localStorage.removeItem('product7_session');
|
|
1244
|
+
return false;
|
|
1245
|
+
}
|
|
1214
1246
|
this.sessionToken = sessionData.token;
|
|
1215
1247
|
this.sessionExpiry = new Date(sessionData.expiry);
|
|
1216
1248
|
|
|
@@ -1220,6 +1252,32 @@
|
|
|
1220
1252
|
}
|
|
1221
1253
|
}
|
|
1222
1254
|
|
|
1255
|
+
_loadStoredMetadata() {
|
|
1256
|
+
if (this.metadata || typeof localStorage === 'undefined') return false;
|
|
1257
|
+
|
|
1258
|
+
try {
|
|
1259
|
+
const session = localStorage.getItem('product7_session');
|
|
1260
|
+
if (session) {
|
|
1261
|
+
const sessionData = JSON.parse(session);
|
|
1262
|
+
if (
|
|
1263
|
+
this.workspace &&
|
|
1264
|
+
sessionData.workspace &&
|
|
1265
|
+
sessionData.workspace !== this.workspace
|
|
1266
|
+
) {
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
const stored = localStorage.getItem('product7_metadata');
|
|
1272
|
+
if (!stored) return false;
|
|
1273
|
+
|
|
1274
|
+
this.metadata = JSON.parse(stored);
|
|
1275
|
+
return true;
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1223
1281
|
_storeData(key, value) {
|
|
1224
1282
|
if (typeof localStorage !== 'undefined') {
|
|
1225
1283
|
localStorage.setItem(key, JSON.stringify(value));
|
|
@@ -1282,6 +1340,36 @@
|
|
|
1282
1340
|
const queryString = this._buildQueryParams(params);
|
|
1283
1341
|
return `${endpoint}${queryString ? '?' + queryString : ''}`;
|
|
1284
1342
|
}
|
|
1343
|
+
|
|
1344
|
+
_buildIdentifyPayload(metadata = {}) {
|
|
1345
|
+
const payload = {
|
|
1346
|
+
user_id: metadata.user_id,
|
|
1347
|
+
email: metadata.email,
|
|
1348
|
+
name: metadata.name,
|
|
1349
|
+
avatar:
|
|
1350
|
+
metadata.profile_picture || metadata.avatar_url || metadata.avatar,
|
|
1351
|
+
attributes: metadata.custom_fields || {},
|
|
1352
|
+
};
|
|
1353
|
+
|
|
1354
|
+
if (metadata.company) {
|
|
1355
|
+
payload.company = metadata.company;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
return payload;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
async _restoreIdentity() {
|
|
1362
|
+
if (
|
|
1363
|
+
!this.metadata ||
|
|
1364
|
+
!this.sessionToken ||
|
|
1365
|
+
!this.identitySyncedToken ||
|
|
1366
|
+
this.identitySyncedToken === this.sessionToken
|
|
1367
|
+
) {
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
await this.identify(this.metadata);
|
|
1372
|
+
}
|
|
1285
1373
|
}
|
|
1286
1374
|
|
|
1287
1375
|
class APIService extends BaseAPIService {
|
|
@@ -1503,8 +1591,94 @@
|
|
|
1503
1591
|
return this.messenger.submitRating(conversationId, data);
|
|
1504
1592
|
}
|
|
1505
1593
|
|
|
1594
|
+
async identify(metadata) {
|
|
1595
|
+
await this._ensureSession();
|
|
1596
|
+
|
|
1597
|
+
if (this.mock) {
|
|
1598
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
1599
|
+
const mockResponse = {
|
|
1600
|
+
status: true,
|
|
1601
|
+
data: {
|
|
1602
|
+
contact_id: 'mock_contact_' + Date.now(),
|
|
1603
|
+
email: metadata.email,
|
|
1604
|
+
name: metadata.name || '',
|
|
1605
|
+
is_new: true,
|
|
1606
|
+
},
|
|
1607
|
+
};
|
|
1608
|
+
this._storeContactIdentity(mockResponse.data, metadata);
|
|
1609
|
+
return mockResponse;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
const response = await this._makeRequest('/widget/messenger/identify', {
|
|
1613
|
+
method: 'POST',
|
|
1614
|
+
headers: {
|
|
1615
|
+
'Content-Type': 'application/json',
|
|
1616
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
1617
|
+
},
|
|
1618
|
+
body: JSON.stringify({
|
|
1619
|
+
user_id: metadata.user_id || null,
|
|
1620
|
+
email: metadata.email || '',
|
|
1621
|
+
name: metadata.name || '',
|
|
1622
|
+
phone: metadata.phone || '',
|
|
1623
|
+
company: metadata.company || '',
|
|
1624
|
+
avatar_url: metadata.avatar_url || '',
|
|
1625
|
+
metadata: metadata.custom_fields || {},
|
|
1626
|
+
}),
|
|
1627
|
+
});
|
|
1628
|
+
|
|
1629
|
+
if (response?.status && response?.data) {
|
|
1630
|
+
this._storeContactIdentity(response.data, metadata);
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
return response;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
_storeContactIdentity(data, metadata = {}) {
|
|
1637
|
+
this.contactId = data.contact_id || null;
|
|
1638
|
+
this.contactEmail = data.email || metadata.email || null;
|
|
1639
|
+
this.contactName = data.name || metadata.name || null;
|
|
1640
|
+
|
|
1641
|
+
try {
|
|
1642
|
+
localStorage.setItem(
|
|
1643
|
+
'product7_contact',
|
|
1644
|
+
JSON.stringify({
|
|
1645
|
+
contactId: this.contactId,
|
|
1646
|
+
contactEmail: this.contactEmail,
|
|
1647
|
+
contactName: this.contactName,
|
|
1648
|
+
})
|
|
1649
|
+
);
|
|
1650
|
+
} catch (e) {
|
|
1651
|
+
/* silent */
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
getContactIdentity() {
|
|
1656
|
+
if (this.contactId) {
|
|
1657
|
+
return {
|
|
1658
|
+
contactId: this.contactId,
|
|
1659
|
+
contactEmail: this.contactEmail,
|
|
1660
|
+
contactName: this.contactName,
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
try {
|
|
1665
|
+
const stored = localStorage.getItem('product7_contact');
|
|
1666
|
+
if (stored) {
|
|
1667
|
+
const parsed = JSON.parse(stored);
|
|
1668
|
+
this.contactId = parsed.contactId;
|
|
1669
|
+
this.contactEmail = parsed.contactEmail;
|
|
1670
|
+
this.contactName = parsed.contactName;
|
|
1671
|
+
return parsed;
|
|
1672
|
+
}
|
|
1673
|
+
} catch (e) {
|
|
1674
|
+
/* silent */
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
return null;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1506
1680
|
async identifyContact(data) {
|
|
1507
|
-
return this.
|
|
1681
|
+
return this.identify(data);
|
|
1508
1682
|
}
|
|
1509
1683
|
|
|
1510
1684
|
async getHelpCollections(options) {
|
|
@@ -9492,7 +9666,9 @@
|
|
|
9492
9666
|
|
|
9493
9667
|
this._unsubscribe = this.state.subscribe((type, data) => {
|
|
9494
9668
|
if (type === 'connectionChange') {
|
|
9495
|
-
const banner = this.element?.querySelector(
|
|
9669
|
+
const banner = this.element?.querySelector(
|
|
9670
|
+
'.messenger-connection-banner'
|
|
9671
|
+
);
|
|
9496
9672
|
if (banner) {
|
|
9497
9673
|
banner.style.display = data.connected ? 'none' : 'flex';
|
|
9498
9674
|
}
|
|
@@ -9542,9 +9718,10 @@
|
|
|
9542
9718
|
? this._renderEmptyState(isNewConversation)
|
|
9543
9719
|
: this._renderGroupedMessages(messages);
|
|
9544
9720
|
|
|
9545
|
-
const defaultPlaceholder =
|
|
9721
|
+
const defaultPlaceholder =
|
|
9722
|
+
this.options.composePlaceholder || 'Write a message...';
|
|
9546
9723
|
const placeholder = isNewConversation
|
|
9547
|
-
?
|
|
9724
|
+
? this.options.composePlaceholder || 'Start typing your message...'
|
|
9548
9725
|
: isClosed
|
|
9549
9726
|
? 'Conversation closed'
|
|
9550
9727
|
: defaultPlaceholder;
|
|
@@ -9565,7 +9742,7 @@
|
|
|
9565
9742
|
</div>
|
|
9566
9743
|
<div class="messenger-chat-header-info">
|
|
9567
9744
|
<span class="messenger-chat-title">${this._escapeHtml(teamName)}</span>
|
|
9568
|
-
<span class="messenger-chat-subtitle">${isClosed ? 'Conversation resolved' :
|
|
9745
|
+
<span class="messenger-chat-subtitle">${isClosed ? 'Conversation resolved' : this.state.responseTime || 'Typically replies within minutes'}</span>
|
|
9569
9746
|
</div>
|
|
9570
9747
|
<div class="messenger-chat-header-actions">
|
|
9571
9748
|
<button class="sdk-btn-icon sdk-close-btn messenger-mobile-close-btn" aria-label="Close">
|
|
@@ -9682,7 +9859,9 @@
|
|
|
9682
9859
|
const messageClass = isOwn
|
|
9683
9860
|
? 'messenger-message-own'
|
|
9684
9861
|
: 'messenger-message-received';
|
|
9685
|
-
const timeStr = isLastInGroup
|
|
9862
|
+
const timeStr = isLastInGroup
|
|
9863
|
+
? this._formatMessageTime(message.timestamp)
|
|
9864
|
+
: '';
|
|
9686
9865
|
const attachmentsHtml = this._renderMessageAttachments(message.attachments);
|
|
9687
9866
|
const isOptimistic = message.isOptimistic;
|
|
9688
9867
|
|
|
@@ -9696,9 +9875,10 @@
|
|
|
9696
9875
|
if (isOwn) {
|
|
9697
9876
|
const sentIndicator = isLastInGroup
|
|
9698
9877
|
? `<div class="messenger-message-meta messenger-message-meta-own">
|
|
9699
|
-
${
|
|
9700
|
-
|
|
9701
|
-
|
|
9878
|
+
${
|
|
9879
|
+
isOptimistic
|
|
9880
|
+
? `<span class="messenger-message-sent-status">Sending…</span>`
|
|
9881
|
+
: `<span class="messenger-message-sent-status">Sent</span>`
|
|
9702
9882
|
}
|
|
9703
9883
|
${timeStr ? `<span>·</span><span>${timeStr}</span>` : ''}
|
|
9704
9884
|
</div>`
|
|
@@ -10929,7 +11109,8 @@
|
|
|
10929
11109
|
const title = conversation.title || teamName;
|
|
10930
11110
|
const timeAgo = this._formatTimeAgo(conversation.lastMessageTime);
|
|
10931
11111
|
const preview = conversation.lastMessage
|
|
10932
|
-
? conversation.lastMessage.substring(0, 48) +
|
|
11112
|
+
? conversation.lastMessage.substring(0, 48) +
|
|
11113
|
+
(conversation.lastMessage.length > 48 ? '...' : '')
|
|
10933
11114
|
: '';
|
|
10934
11115
|
const hasUnread = conversation.unread > 0;
|
|
10935
11116
|
|
|
@@ -11045,7 +11226,9 @@
|
|
|
11045
11226
|
}
|
|
11046
11227
|
|
|
11047
11228
|
_attachEvents() {
|
|
11048
|
-
const recentCard = this.element.querySelector(
|
|
11229
|
+
const recentCard = this.element.querySelector(
|
|
11230
|
+
'.messenger-home-recent-card'
|
|
11231
|
+
);
|
|
11049
11232
|
if (recentCard) {
|
|
11050
11233
|
recentCard.addEventListener('click', () => {
|
|
11051
11234
|
const convId = recentCard.dataset.conversationId;
|
|
@@ -11284,6 +11467,7 @@
|
|
|
11284
11467
|
initialView: options.initialView || 'home',
|
|
11285
11468
|
previewData: options.previewData || null,
|
|
11286
11469
|
featuredContent: options.featuredContent || null,
|
|
11470
|
+
feedbackBoardName: options.feedbackBoardName || null,
|
|
11287
11471
|
feedbackUrl: options.feedbackUrl || null,
|
|
11288
11472
|
changelogUrl: options.changelogUrl || null,
|
|
11289
11473
|
helpUrl: options.helpUrl || null,
|
|
@@ -11326,12 +11510,17 @@
|
|
|
11326
11510
|
this._handleConversationClosed = this._handleConversationClosed.bind(this);
|
|
11327
11511
|
}
|
|
11328
11512
|
|
|
11513
|
+
_hasTrigger() {
|
|
11514
|
+
return this.options.trigger === true || this.options.trigger === undefined;
|
|
11515
|
+
}
|
|
11516
|
+
|
|
11329
11517
|
_createInternalFeedbackWidget() {
|
|
11330
11518
|
try {
|
|
11331
11519
|
const widget = this.sdk.createWidget('button', {
|
|
11332
11520
|
trigger: false,
|
|
11333
11521
|
displayMode: 'modal',
|
|
11334
|
-
boardName:
|
|
11522
|
+
boardName:
|
|
11523
|
+
this.messengerOptions.feedbackBoardName || this.sdk.config.boardName,
|
|
11335
11524
|
primaryColor: this.messengerOptions.primaryColor,
|
|
11336
11525
|
theme: this.messengerOptions.theme,
|
|
11337
11526
|
});
|
|
@@ -11361,11 +11550,13 @@
|
|
|
11361
11550
|
this._feedbackWidget = this._createInternalFeedbackWidget();
|
|
11362
11551
|
}
|
|
11363
11552
|
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
|
|
11553
|
+
if (this._hasTrigger()) {
|
|
11554
|
+
this.launcher = new MessengerLauncher(this.messengerState, {
|
|
11555
|
+
position: this.messengerOptions.position,
|
|
11556
|
+
primaryColor: this.messengerOptions.primaryColor,
|
|
11557
|
+
});
|
|
11558
|
+
container.appendChild(this.launcher.render());
|
|
11559
|
+
}
|
|
11369
11560
|
|
|
11370
11561
|
this.panel = new MessengerPanel(this.messengerState, {
|
|
11371
11562
|
position: this.messengerOptions.position,
|
|
@@ -11500,28 +11691,13 @@
|
|
|
11500
11691
|
|
|
11501
11692
|
async _handleIdentifyContact(contactData) {
|
|
11502
11693
|
try {
|
|
11503
|
-
|
|
11504
|
-
|
|
11694
|
+
// Route through sdk.identify() so the SDK-level identity state is updated
|
|
11695
|
+
// and applyIdentity() handles the messenger state + WebSocket as a side effect.
|
|
11696
|
+
const result = await this.sdk.identify({
|
|
11505
11697
|
email: contactData.email,
|
|
11698
|
+
name: contactData.name,
|
|
11506
11699
|
});
|
|
11507
|
-
|
|
11508
|
-
if (response.status) {
|
|
11509
|
-
console.log(
|
|
11510
|
-
'[MessengerWidget] Contact identified:',
|
|
11511
|
-
response.data.contact_id
|
|
11512
|
-
);
|
|
11513
|
-
this.messengerState.setIdentified(true, {
|
|
11514
|
-
name: contactData.name,
|
|
11515
|
-
email: contactData.email,
|
|
11516
|
-
});
|
|
11517
|
-
|
|
11518
|
-
// Start WebSocket now that session token is available
|
|
11519
|
-
if (this.apiService?.sessionToken && !this.wsService?.isConnected) {
|
|
11520
|
-
this._initWebSocket();
|
|
11521
|
-
}
|
|
11522
|
-
}
|
|
11523
|
-
|
|
11524
|
-
return response;
|
|
11700
|
+
return result;
|
|
11525
11701
|
} catch (error) {
|
|
11526
11702
|
console.error('[MessengerWidget] Failed to identify contact:', error);
|
|
11527
11703
|
throw error;
|
|
@@ -11529,10 +11705,14 @@
|
|
|
11529
11705
|
}
|
|
11530
11706
|
|
|
11531
11707
|
markAsIdentified(name, email) {
|
|
11532
|
-
|
|
11533
|
-
|
|
11708
|
+
// Called externally by the app when the user is already known.
|
|
11709
|
+
// No API call needed — identity was already established via sdk.identify().
|
|
11710
|
+
this.applyIdentity({ name, email });
|
|
11711
|
+
}
|
|
11712
|
+
|
|
11713
|
+
applyIdentity(metadata = {}) {
|
|
11714
|
+
this.messengerState.setIdentified(true, metadata);
|
|
11534
11715
|
|
|
11535
|
-
// Start WebSocket now that we have a session token
|
|
11536
11716
|
if (this.apiService?.sessionToken && !this.wsService?.isConnected) {
|
|
11537
11717
|
this._initWebSocket();
|
|
11538
11718
|
}
|
|
@@ -12076,7 +12256,10 @@
|
|
|
12076
12256
|
}
|
|
12077
12257
|
|
|
12078
12258
|
_hasExplicitOption(key) {
|
|
12079
|
-
return Object.prototype.hasOwnProperty.call(
|
|
12259
|
+
return Object.prototype.hasOwnProperty.call(
|
|
12260
|
+
this._explicitOptions || {},
|
|
12261
|
+
key
|
|
12262
|
+
);
|
|
12080
12263
|
}
|
|
12081
12264
|
|
|
12082
12265
|
async checkAgentAvailability() {
|
|
@@ -12371,6 +12554,7 @@
|
|
|
12371
12554
|
|
|
12372
12555
|
SurveyWidget.removeDanglingElements();
|
|
12373
12556
|
this._renderSurvey();
|
|
12557
|
+
this.state.isOpen = true;
|
|
12374
12558
|
this.surveyState.isVisible = true;
|
|
12375
12559
|
this.sdk.eventBus.emit('survey:shown', {
|
|
12376
12560
|
widget: this,
|
|
@@ -12380,6 +12564,7 @@
|
|
|
12380
12564
|
}
|
|
12381
12565
|
|
|
12382
12566
|
hide() {
|
|
12567
|
+
this.state.isOpen = false;
|
|
12383
12568
|
this._closeSurvey();
|
|
12384
12569
|
return this;
|
|
12385
12570
|
}
|
|
@@ -12454,6 +12639,7 @@
|
|
|
12454
12639
|
this._attachSurveyEvents();
|
|
12455
12640
|
|
|
12456
12641
|
requestAnimationFrame(() => {
|
|
12642
|
+
if (!this.surveyElement) return;
|
|
12457
12643
|
this.surveyElement.style.opacity = '1';
|
|
12458
12644
|
this.surveyElement.style.transform =
|
|
12459
12645
|
this.surveyOptions.position === 'center'
|
|
@@ -13591,6 +13777,7 @@
|
|
|
13591
13777
|
}
|
|
13592
13778
|
|
|
13593
13779
|
_closeSurvey(resetState = true, immediate = false) {
|
|
13780
|
+
this.state.isOpen = false;
|
|
13594
13781
|
if (this._escapeHandler) {
|
|
13595
13782
|
document.removeEventListener('keydown', this._escapeHandler);
|
|
13596
13783
|
this._escapeHandler = null;
|
|
@@ -13653,6 +13840,22 @@
|
|
|
13653
13840
|
this.sdk.eventBus.emit('survey:closed', { widget: this });
|
|
13654
13841
|
}
|
|
13655
13842
|
|
|
13843
|
+
open() {
|
|
13844
|
+
return this.show();
|
|
13845
|
+
}
|
|
13846
|
+
|
|
13847
|
+
close() {
|
|
13848
|
+
return this.hide();
|
|
13849
|
+
}
|
|
13850
|
+
|
|
13851
|
+
toggle() {
|
|
13852
|
+
if (this.surveyState.isVisible) {
|
|
13853
|
+
return this.hide();
|
|
13854
|
+
}
|
|
13855
|
+
|
|
13856
|
+
return this.show();
|
|
13857
|
+
}
|
|
13858
|
+
|
|
13656
13859
|
destroy() {
|
|
13657
13860
|
this._closeSurvey(true, true);
|
|
13658
13861
|
super.destroy();
|
|
@@ -13770,6 +13973,7 @@
|
|
|
13770
13973
|
constructor(config = {}) {
|
|
13771
13974
|
this.config = this._validateAndMergeConfig(config);
|
|
13772
13975
|
this.initialized = false;
|
|
13976
|
+
this.identified = false;
|
|
13773
13977
|
this.widgets = new Map();
|
|
13774
13978
|
this.eventBus = new EventBus();
|
|
13775
13979
|
|
|
@@ -13814,13 +14018,14 @@
|
|
|
13814
14018
|
this._injectStyles();
|
|
13815
14019
|
|
|
13816
14020
|
try {
|
|
13817
|
-
const initData = await this.apiService.init(
|
|
14021
|
+
const initData = await this.apiService.init();
|
|
13818
14022
|
|
|
13819
14023
|
if (initData.config) {
|
|
13820
14024
|
this.config = deepMerge(initData.config, this.config);
|
|
13821
14025
|
}
|
|
13822
14026
|
|
|
13823
14027
|
this.initialized = true;
|
|
14028
|
+
const identifyResult = await this._syncConfiguredMetadataAfterInit();
|
|
13824
14029
|
this.eventBus.emit('sdk:initialized', {
|
|
13825
14030
|
config: this.config,
|
|
13826
14031
|
sessionToken: initData.sessionToken,
|
|
@@ -13831,6 +14036,7 @@
|
|
|
13831
14036
|
config: initData.config || {},
|
|
13832
14037
|
sessionToken: initData.sessionToken,
|
|
13833
14038
|
expiresIn: initData.expiresIn,
|
|
14039
|
+
identified: Boolean(identifyResult?.identified),
|
|
13834
14040
|
};
|
|
13835
14041
|
} catch (error) {
|
|
13836
14042
|
this.eventBus.emit('sdk:error', { error });
|
|
@@ -13845,10 +14051,14 @@
|
|
|
13845
14051
|
);
|
|
13846
14052
|
}
|
|
13847
14053
|
|
|
14054
|
+
const requestedType = type || 'button';
|
|
14055
|
+
const normalizedType = this._normalizeWidgetType(requestedType);
|
|
13848
14056
|
const widgetId = generateId('widget');
|
|
13849
|
-
const widgetConfig = this._getWidgetTypeConfig(
|
|
13850
|
-
const explicitOptions = this._omitUndefined(
|
|
13851
|
-
|
|
14057
|
+
const widgetConfig = this._getWidgetTypeConfig(normalizedType);
|
|
14058
|
+
const explicitOptions = this._omitUndefined(
|
|
14059
|
+
this._normalizeWidgetOptions(normalizedType, options)
|
|
14060
|
+
);
|
|
14061
|
+
const widgetEnabled = this._isWidgetEnabled(normalizedType, {
|
|
13852
14062
|
...widgetConfig,
|
|
13853
14063
|
...explicitOptions,
|
|
13854
14064
|
});
|
|
@@ -13863,15 +14073,35 @@
|
|
|
13863
14073
|
};
|
|
13864
14074
|
|
|
13865
14075
|
try {
|
|
13866
|
-
const widget = WidgetFactory.create(
|
|
14076
|
+
const widget = WidgetFactory.create(normalizedType, widgetOptions);
|
|
13867
14077
|
this.widgets.set(widgetId, widget);
|
|
13868
|
-
this.eventBus.emit('widget:created', {
|
|
14078
|
+
this.eventBus.emit('widget:created', {
|
|
14079
|
+
widget,
|
|
14080
|
+
type: requestedType,
|
|
14081
|
+
internalType: normalizedType,
|
|
14082
|
+
});
|
|
13869
14083
|
return widget;
|
|
13870
14084
|
} catch (error) {
|
|
13871
14085
|
throw new SDKError(`Failed to create widget: ${error.message}`, error);
|
|
13872
14086
|
}
|
|
13873
14087
|
}
|
|
13874
14088
|
|
|
14089
|
+
createFeedbackWidget(options = {}) {
|
|
14090
|
+
return this.createWidget('feedback', options);
|
|
14091
|
+
}
|
|
14092
|
+
|
|
14093
|
+
createMessengerWidget(options = {}) {
|
|
14094
|
+
return this.createWidget('messenger', options);
|
|
14095
|
+
}
|
|
14096
|
+
|
|
14097
|
+
createChangelogWidget(options = {}) {
|
|
14098
|
+
return this.createWidget('changelog', options);
|
|
14099
|
+
}
|
|
14100
|
+
|
|
14101
|
+
createSurveyWidget(options = {}) {
|
|
14102
|
+
return this.createWidget('survey', options);
|
|
14103
|
+
}
|
|
14104
|
+
|
|
13875
14105
|
getWidget(id) {
|
|
13876
14106
|
return this.widgets.get(id);
|
|
13877
14107
|
}
|
|
@@ -13990,7 +14220,7 @@
|
|
|
13990
14220
|
return null;
|
|
13991
14221
|
}
|
|
13992
14222
|
|
|
13993
|
-
const surveyWidget = this.
|
|
14223
|
+
const surveyWidget = this.createSurveyWidget({
|
|
13994
14224
|
surveyId: normalizedOptions.surveyId,
|
|
13995
14225
|
surveyType:
|
|
13996
14226
|
normalizedOptions.surveyType || normalizedOptions.type || 'nps',
|
|
@@ -14224,18 +14454,34 @@
|
|
|
14224
14454
|
? this.config.widgets
|
|
14225
14455
|
: {};
|
|
14226
14456
|
|
|
14227
|
-
const
|
|
14228
|
-
|
|
14229
|
-
|
|
14457
|
+
const mergedTypeConfig = this._getWidgetTypeAliases(type).reduce(
|
|
14458
|
+
(config, alias) => {
|
|
14459
|
+
const legacyTypeConfig = this._isPlainObject(this.config?.[alias])
|
|
14460
|
+
? this.config[alias]
|
|
14461
|
+
: {};
|
|
14230
14462
|
|
|
14231
|
-
|
|
14232
|
-
|
|
14233
|
-
|
|
14463
|
+
const namespacedTypeConfig = this._isPlainObject(widgetsConfig?.[alias])
|
|
14464
|
+
? widgetsConfig[alias]
|
|
14465
|
+
: {};
|
|
14234
14466
|
|
|
14235
|
-
|
|
14467
|
+
return deepMerge(
|
|
14468
|
+
config,
|
|
14469
|
+
deepMerge(legacyTypeConfig, namespacedTypeConfig)
|
|
14470
|
+
);
|
|
14471
|
+
},
|
|
14472
|
+
{}
|
|
14473
|
+
);
|
|
14236
14474
|
return this._toCamelCaseObject(mergedTypeConfig);
|
|
14237
14475
|
}
|
|
14238
14476
|
|
|
14477
|
+
_getWidgetTypeAliases(type) {
|
|
14478
|
+
if (type === 'button') {
|
|
14479
|
+
return ['button', 'feedback'];
|
|
14480
|
+
}
|
|
14481
|
+
|
|
14482
|
+
return [type];
|
|
14483
|
+
}
|
|
14484
|
+
|
|
14239
14485
|
_isWidgetEnabled(type, options = {}) {
|
|
14240
14486
|
const typeConfig = this._getWidgetTypeConfig(type);
|
|
14241
14487
|
if (typeConfig.enabled === false) {
|
|
@@ -14278,6 +14524,31 @@
|
|
|
14278
14524
|
return normalized;
|
|
14279
14525
|
}
|
|
14280
14526
|
|
|
14527
|
+
_normalizeWidgetType(type) {
|
|
14528
|
+
if (type === 'feedback') {
|
|
14529
|
+
return 'button';
|
|
14530
|
+
}
|
|
14531
|
+
|
|
14532
|
+
return type;
|
|
14533
|
+
}
|
|
14534
|
+
|
|
14535
|
+
_normalizeWidgetOptions(type, options = {}) {
|
|
14536
|
+
if (!this._isPlainObject(options)) {
|
|
14537
|
+
return options;
|
|
14538
|
+
}
|
|
14539
|
+
|
|
14540
|
+
const normalizedOptions = { ...options };
|
|
14541
|
+
if (
|
|
14542
|
+
normalizedOptions.headless === true &&
|
|
14543
|
+
normalizedOptions.trigger === undefined &&
|
|
14544
|
+
type !== 'survey'
|
|
14545
|
+
) {
|
|
14546
|
+
normalizedOptions.trigger = false;
|
|
14547
|
+
}
|
|
14548
|
+
|
|
14549
|
+
return normalizedOptions;
|
|
14550
|
+
}
|
|
14551
|
+
|
|
14281
14552
|
_omitUndefined(value) {
|
|
14282
14553
|
if (!this._isPlainObject(value)) {
|
|
14283
14554
|
return value;
|
|
@@ -14322,7 +14593,7 @@
|
|
|
14322
14593
|
return null;
|
|
14323
14594
|
}
|
|
14324
14595
|
|
|
14325
|
-
const changelogWidget = this.
|
|
14596
|
+
const changelogWidget = this.createChangelogWidget({
|
|
14326
14597
|
...defaults,
|
|
14327
14598
|
...configDefaults,
|
|
14328
14599
|
...explicitOptions,
|
|
@@ -14389,10 +14660,15 @@
|
|
|
14389
14660
|
}
|
|
14390
14661
|
|
|
14391
14662
|
setMetadata(metadata) {
|
|
14663
|
+
if (metadata) {
|
|
14664
|
+
this._validateMetadata(metadata);
|
|
14665
|
+
}
|
|
14666
|
+
|
|
14392
14667
|
this.config.metadata = metadata;
|
|
14393
14668
|
if (this.apiService) {
|
|
14394
14669
|
this.apiService.setMetadata(metadata);
|
|
14395
14670
|
}
|
|
14671
|
+
this.identified = false;
|
|
14396
14672
|
this.eventBus.emit('metadata:updated', { metadata });
|
|
14397
14673
|
}
|
|
14398
14674
|
|
|
@@ -14403,11 +14679,53 @@
|
|
|
14403
14679
|
);
|
|
14404
14680
|
}
|
|
14405
14681
|
|
|
14682
|
+
async identify(metadata = this.config.metadata) {
|
|
14683
|
+
if (!this.initialized) {
|
|
14684
|
+
throw new SDKError(
|
|
14685
|
+
'SDK must be initialized before identifying users. Call init() first.'
|
|
14686
|
+
);
|
|
14687
|
+
}
|
|
14688
|
+
|
|
14689
|
+
if (!metadata) {
|
|
14690
|
+
throw new SDKError(
|
|
14691
|
+
'Identify requires metadata. Provide at least user_id or email.'
|
|
14692
|
+
);
|
|
14693
|
+
}
|
|
14694
|
+
|
|
14695
|
+
this._validateMetadata(metadata);
|
|
14696
|
+
this.setMetadata(metadata);
|
|
14697
|
+
|
|
14698
|
+
try {
|
|
14699
|
+
const response = await this.apiService.identify(metadata);
|
|
14700
|
+
const configPatch = this._extractIdentifyConfig(response);
|
|
14701
|
+
if (Object.keys(configPatch).length > 0) {
|
|
14702
|
+
this.updateConfig(configPatch);
|
|
14703
|
+
}
|
|
14704
|
+
|
|
14705
|
+
this.identified = true;
|
|
14706
|
+
this._applyIdentityToWidgets(metadata);
|
|
14707
|
+
|
|
14708
|
+
const result = {
|
|
14709
|
+
identified: true,
|
|
14710
|
+
metadata: this.getMetadata(),
|
|
14711
|
+
response,
|
|
14712
|
+
};
|
|
14713
|
+
|
|
14714
|
+
this.eventBus.emit('sdk:identified', result);
|
|
14715
|
+
return result;
|
|
14716
|
+
} catch (error) {
|
|
14717
|
+
this.identified = false;
|
|
14718
|
+
this.eventBus.emit('sdk:error', { error, phase: 'identify' });
|
|
14719
|
+
throw new SDKError(`Failed to identify user: ${error.message}`, error);
|
|
14720
|
+
}
|
|
14721
|
+
}
|
|
14722
|
+
|
|
14406
14723
|
async reinitialize(newMetadata = null) {
|
|
14407
14724
|
this.apiService.clearSession();
|
|
14408
14725
|
this.initialized = false;
|
|
14726
|
+
this.identified = false;
|
|
14409
14727
|
|
|
14410
|
-
if (newMetadata) {
|
|
14728
|
+
if (newMetadata !== null) {
|
|
14411
14729
|
this.setMetadata(newMetadata);
|
|
14412
14730
|
}
|
|
14413
14731
|
|
|
@@ -14436,10 +14754,11 @@
|
|
|
14436
14754
|
|
|
14437
14755
|
destroy() {
|
|
14438
14756
|
this.destroyAllWidgets();
|
|
14439
|
-
this.eventBus.
|
|
14757
|
+
this.eventBus.emit('sdk:destroyed');
|
|
14758
|
+
this.eventBus.clear();
|
|
14440
14759
|
this.apiService.clearSession();
|
|
14441
14760
|
this.initialized = false;
|
|
14442
|
-
this.
|
|
14761
|
+
this.identified = false;
|
|
14443
14762
|
}
|
|
14444
14763
|
|
|
14445
14764
|
hasFeedbackBeenSubmitted(cooldownDays = 30) {
|
|
@@ -14507,7 +14826,7 @@
|
|
|
14507
14826
|
metadata: null,
|
|
14508
14827
|
position: 'right',
|
|
14509
14828
|
theme: 'light',
|
|
14510
|
-
boardName: '
|
|
14829
|
+
boardName: 'feature-requests',
|
|
14511
14830
|
autoShow: true,
|
|
14512
14831
|
debug: false,
|
|
14513
14832
|
mock: false,
|
|
@@ -14558,7 +14877,12 @@
|
|
|
14558
14877
|
|
|
14559
14878
|
_bindMethods() {
|
|
14560
14879
|
this.createWidget = this.createWidget.bind(this);
|
|
14880
|
+
this.createFeedbackWidget = this.createFeedbackWidget.bind(this);
|
|
14881
|
+
this.createMessengerWidget = this.createMessengerWidget.bind(this);
|
|
14882
|
+
this.createChangelogWidget = this.createChangelogWidget.bind(this);
|
|
14883
|
+
this.createSurveyWidget = this.createSurveyWidget.bind(this);
|
|
14561
14884
|
this.destroyWidget = this.destroyWidget.bind(this);
|
|
14885
|
+
this.identify = this.identify.bind(this);
|
|
14562
14886
|
this.updateConfig = this.updateConfig.bind(this);
|
|
14563
14887
|
}
|
|
14564
14888
|
|
|
@@ -14594,6 +14918,49 @@
|
|
|
14594
14918
|
: undefined,
|
|
14595
14919
|
};
|
|
14596
14920
|
}
|
|
14921
|
+
|
|
14922
|
+
async _syncConfiguredMetadataAfterInit() {
|
|
14923
|
+
if (
|
|
14924
|
+
!this.config.metadata ||
|
|
14925
|
+
this.apiService.identitySyncedToken === this.apiService.sessionToken
|
|
14926
|
+
) {
|
|
14927
|
+
return null;
|
|
14928
|
+
}
|
|
14929
|
+
|
|
14930
|
+
try {
|
|
14931
|
+
return await this.identify(this.config.metadata);
|
|
14932
|
+
} catch (error) {
|
|
14933
|
+
if (this.config.debug) {
|
|
14934
|
+
console.warn('[Product7] Initial identify failed:', error);
|
|
14935
|
+
}
|
|
14936
|
+
return null;
|
|
14937
|
+
}
|
|
14938
|
+
}
|
|
14939
|
+
|
|
14940
|
+
_extractIdentifyConfig(response) {
|
|
14941
|
+
const payload = this._isPlainObject(response?.data)
|
|
14942
|
+
? response.data
|
|
14943
|
+
: response || {};
|
|
14944
|
+
const configPatch = this._isPlainObject(payload.config)
|
|
14945
|
+
? payload.config
|
|
14946
|
+
: {};
|
|
14947
|
+
|
|
14948
|
+
if (payload.last_feedback_at !== undefined) {
|
|
14949
|
+
configPatch.last_feedback_at = payload.last_feedback_at;
|
|
14950
|
+
} else if (payload.lastFeedbackAt !== undefined) {
|
|
14951
|
+
configPatch.last_feedback_at = payload.lastFeedbackAt;
|
|
14952
|
+
}
|
|
14953
|
+
|
|
14954
|
+
return this._omitUndefined(configPatch);
|
|
14955
|
+
}
|
|
14956
|
+
|
|
14957
|
+
_applyIdentityToWidgets(metadata) {
|
|
14958
|
+
for (const widget of this.widgets.values()) {
|
|
14959
|
+
if (typeof widget.applyIdentity === 'function') {
|
|
14960
|
+
widget.applyIdentity(metadata);
|
|
14961
|
+
}
|
|
14962
|
+
}
|
|
14963
|
+
}
|
|
14597
14964
|
};
|
|
14598
14965
|
|
|
14599
14966
|
// --- Identify: transform flat user data into internal format ---
|
|
@@ -14676,9 +15043,21 @@
|
|
|
14676
15043
|
return obj;
|
|
14677
15044
|
}
|
|
14678
15045
|
|
|
15046
|
+
function hasIdentifyMetadata(metadata = {}) {
|
|
15047
|
+
return Object.values(metadata).some((value) => value !== undefined);
|
|
15048
|
+
}
|
|
15049
|
+
|
|
14679
15050
|
// --- Ensure SDK is initialized (shared by widget inits) ---
|
|
14680
15051
|
|
|
14681
15052
|
async function ensureSDK(options) {
|
|
15053
|
+
if (
|
|
15054
|
+
Product7._sdk &&
|
|
15055
|
+
options.organization &&
|
|
15056
|
+
Product7._sdk.config.workspace !== options.organization
|
|
15057
|
+
) {
|
|
15058
|
+
Product7.destroy();
|
|
15059
|
+
}
|
|
15060
|
+
|
|
14682
15061
|
if (Product7._sdk) return Product7._sdk;
|
|
14683
15062
|
|
|
14684
15063
|
if (options.organization) {
|
|
@@ -14721,63 +15100,58 @@
|
|
|
14721
15100
|
async identify(data = {}, callback) {
|
|
14722
15101
|
try {
|
|
14723
15102
|
const transformed = transformIdentifyData(data);
|
|
14724
|
-
Product7._organization =
|
|
15103
|
+
Product7._organization =
|
|
15104
|
+
transformed.workspace || Product7._organization || null;
|
|
14725
15105
|
|
|
14726
15106
|
const config = cleanUndefined({
|
|
14727
|
-
workspace:
|
|
14728
|
-
metadata: transformed.metadata,
|
|
15107
|
+
workspace: Product7._organization,
|
|
14729
15108
|
debug: transformed.debug,
|
|
14730
15109
|
mock: transformed.mock,
|
|
14731
15110
|
env: transformed.env,
|
|
14732
15111
|
apiUrl: transformed.apiUrl,
|
|
14733
15112
|
});
|
|
14734
15113
|
|
|
14735
|
-
|
|
14736
|
-
const
|
|
14737
|
-
|
|
14738
|
-
Product7._sdk = sdk;
|
|
14739
|
-
Product7._identified = true;
|
|
15114
|
+
let sdk = Product7._sdk;
|
|
15115
|
+
const requiresNewSDK =
|
|
15116
|
+
!sdk || (config.workspace && sdk.config.workspace !== config.workspace);
|
|
14740
15117
|
|
|
14741
|
-
|
|
14742
|
-
|
|
14743
|
-
|
|
14744
|
-
|
|
14745
|
-
user_id: transformed.metadata.user_id,
|
|
14746
|
-
email: transformed.metadata.email,
|
|
14747
|
-
name: transformed.metadata.name,
|
|
14748
|
-
avatar: transformed.metadata.profile_picture,
|
|
14749
|
-
attributes: transformed.metadata.custom_fields || {},
|
|
14750
|
-
};
|
|
14751
|
-
if (transformed.metadata.company) {
|
|
14752
|
-
identifyPayload.company = transformed.metadata.company;
|
|
14753
|
-
}
|
|
14754
|
-
await sdk.apiService._makeRequest('/widget/identify', {
|
|
14755
|
-
method: 'POST',
|
|
14756
|
-
body: JSON.stringify(identifyPayload),
|
|
14757
|
-
headers: {
|
|
14758
|
-
'Content-Type': 'application/json',
|
|
14759
|
-
Authorization: `Bearer ${sdk.apiService.sessionToken}`,
|
|
14760
|
-
},
|
|
14761
|
-
});
|
|
14762
|
-
} catch (identifyErr) {
|
|
14763
|
-
if (config.debug) {
|
|
14764
|
-
console.warn('[Product7] Attribute sync failed:', identifyErr);
|
|
14765
|
-
}
|
|
15118
|
+
if (requiresNewSDK) {
|
|
15119
|
+
if (Product7._sdk) {
|
|
15120
|
+
Product7.destroy();
|
|
15121
|
+
Product7._organization = config.workspace || null;
|
|
14766
15122
|
}
|
|
15123
|
+
sdk = new Product7$1(config);
|
|
15124
|
+
Product7._sdk = sdk;
|
|
14767
15125
|
}
|
|
14768
15126
|
|
|
15127
|
+
const initData = sdk.initialized
|
|
15128
|
+
? {
|
|
15129
|
+
alreadyInitialized: true,
|
|
15130
|
+
sessionToken: sdk.apiService?.sessionToken,
|
|
15131
|
+
}
|
|
15132
|
+
: await sdk.init();
|
|
15133
|
+
|
|
15134
|
+
const identifyData = hasIdentifyMetadata(transformed.metadata)
|
|
15135
|
+
? await sdk.identify(transformed.metadata)
|
|
15136
|
+
: null;
|
|
15137
|
+
Product7._identified = Boolean(identifyData?.identified);
|
|
15138
|
+
|
|
14769
15139
|
Product7._flushQueue();
|
|
14770
15140
|
|
|
14771
15141
|
if (typeof window !== 'undefined' && typeof CustomEvent !== 'undefined') {
|
|
14772
15142
|
window.dispatchEvent(
|
|
14773
15143
|
new CustomEvent('Product7Ready', {
|
|
14774
|
-
detail: { sdk, config, initData },
|
|
15144
|
+
detail: { sdk, config: sdk.config, initData, identifyData },
|
|
14775
15145
|
})
|
|
14776
15146
|
);
|
|
14777
15147
|
}
|
|
14778
15148
|
|
|
14779
15149
|
if (typeof callback === 'function') callback(null);
|
|
14780
|
-
return
|
|
15150
|
+
return {
|
|
15151
|
+
...initData,
|
|
15152
|
+
identified: Product7._identified,
|
|
15153
|
+
identify: identifyData,
|
|
15154
|
+
};
|
|
14781
15155
|
} catch (error) {
|
|
14782
15156
|
console.error('[Product7] Identify failed:', error);
|
|
14783
15157
|
|
|
@@ -14836,7 +15210,7 @@
|
|
|
14836
15210
|
if (!options.placement) {
|
|
14837
15211
|
widgetOptions.autoShow = false;
|
|
14838
15212
|
widgetOptions.displayMode = 'modal';
|
|
14839
|
-
widgetOptions.
|
|
15213
|
+
widgetOptions.headless = true;
|
|
14840
15214
|
} else {
|
|
14841
15215
|
// Trigger button is always visible when placement is set
|
|
14842
15216
|
widgetOptions.suppressAfterSubmission = false;
|
|
@@ -14844,7 +15218,7 @@
|
|
|
14844
15218
|
}
|
|
14845
15219
|
|
|
14846
15220
|
try {
|
|
14847
|
-
const widget = sdk.
|
|
15221
|
+
const widget = sdk.createFeedbackWidget(widgetOptions);
|
|
14848
15222
|
widget.mount();
|
|
14849
15223
|
|
|
14850
15224
|
if (options.placement) {
|
|
@@ -14883,13 +15257,13 @@
|
|
|
14883
15257
|
if (options.setBoard) {
|
|
14884
15258
|
Product7._feedbackWidget.options.boardName = options.setBoard;
|
|
14885
15259
|
}
|
|
14886
|
-
Product7._feedbackWidget.
|
|
15260
|
+
Product7._feedbackWidget.open();
|
|
14887
15261
|
}
|
|
14888
15262
|
},
|
|
14889
15263
|
|
|
14890
15264
|
closeFeedback() {
|
|
14891
15265
|
if (Product7._feedbackWidget) {
|
|
14892
|
-
Product7._feedbackWidget.
|
|
15266
|
+
Product7._feedbackWidget.close();
|
|
14893
15267
|
}
|
|
14894
15268
|
},
|
|
14895
15269
|
|
|
@@ -14948,7 +15322,7 @@
|
|
|
14948
15322
|
widgetOptions.enabled = true;
|
|
14949
15323
|
|
|
14950
15324
|
try {
|
|
14951
|
-
const widget = sdk.
|
|
15325
|
+
const widget = sdk.createMessengerWidget(widgetOptions);
|
|
14952
15326
|
widget.mount();
|
|
14953
15327
|
widget.show();
|
|
14954
15328
|
|