@product7/feedback-sdk 1.3.6 → 1.3.8
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/dist/feedback-sdk.js +136 -398
- package/dist/feedback-sdk.js.map +1 -1
- package/dist/feedback-sdk.min.js +1 -1
- package/dist/feedback-sdk.min.js.map +1 -1
- package/package.json +1 -1
- package/src/api/services/MessengerService.js +8 -2
- package/src/widgets/MessengerWidget.js +61 -92
- package/src/widgets/messenger/MessengerState.js +47 -119
- package/src/widgets/messenger/views/ChatView.js +10 -182
- package/src/widgets/messenger/views/PreChatFormView.js +14 -8
package/package.json
CHANGED
|
@@ -107,6 +107,7 @@ export class MessengerService {
|
|
|
107
107
|
content: data.message,
|
|
108
108
|
sender_type: 'customer',
|
|
109
109
|
created_at: new Date().toISOString(),
|
|
110
|
+
attachments: data.attachments || [],
|
|
110
111
|
},
|
|
111
112
|
],
|
|
112
113
|
};
|
|
@@ -124,6 +125,7 @@ export class MessengerService {
|
|
|
124
125
|
body: JSON.stringify({
|
|
125
126
|
message: data.message,
|
|
126
127
|
subject: data.subject || '',
|
|
128
|
+
attachments: data.attachments || []
|
|
127
129
|
}),
|
|
128
130
|
});
|
|
129
131
|
}
|
|
@@ -138,6 +140,7 @@ export class MessengerService {
|
|
|
138
140
|
content: data.content,
|
|
139
141
|
sender_type: 'customer',
|
|
140
142
|
created_at: new Date().toISOString(),
|
|
143
|
+
attachments: data.attachments || [],
|
|
141
144
|
};
|
|
142
145
|
if (!MOCK_MESSAGES[conversationId]) {
|
|
143
146
|
MOCK_MESSAGES[conversationId] = [];
|
|
@@ -154,7 +157,10 @@ export class MessengerService {
|
|
|
154
157
|
'Content-Type': 'application/json',
|
|
155
158
|
Authorization: `Bearer ${this.api.sessionToken}`,
|
|
156
159
|
},
|
|
157
|
-
body: JSON.stringify({
|
|
160
|
+
body: JSON.stringify({
|
|
161
|
+
content: data.content,
|
|
162
|
+
attachments: data.attachments || []
|
|
163
|
+
}),
|
|
158
164
|
}
|
|
159
165
|
);
|
|
160
166
|
}
|
|
@@ -270,4 +276,4 @@ export class MessengerService {
|
|
|
270
276
|
}
|
|
271
277
|
);
|
|
272
278
|
}
|
|
273
|
-
}
|
|
279
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import { BaseWidget } from './BaseWidget.js';
|
|
3
2
|
import { MessengerState } from './messenger/MessengerState.js';
|
|
4
3
|
import { MessengerLauncher } from './messenger/components/MessengerLauncher.js';
|
|
@@ -132,44 +131,41 @@ export class MessengerWidget extends BaseWidget {
|
|
|
132
131
|
}
|
|
133
132
|
}
|
|
134
133
|
|
|
135
|
-
async _handleStartConversation(messageContent, pendingAttachments) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
134
|
+
async _handleStartConversation(messageContent, pendingAttachments) {
|
|
135
|
+
try {
|
|
136
|
+
if (!this.messengerState.isIdentified) {
|
|
137
|
+
this.messengerState.pendingMessage = {
|
|
138
|
+
content: messageContent,
|
|
139
|
+
attachments: pendingAttachments,
|
|
140
|
+
};
|
|
141
|
+
this.messengerState.setView('prechat');
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
147
144
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
);
|
|
145
|
+
const openConversation = this.messengerState.conversations.find(
|
|
146
|
+
(c) => c.status === 'open'
|
|
147
|
+
);
|
|
152
148
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
149
|
+
if (openConversation) {
|
|
150
|
+
this.messengerState.setActiveConversation(openConversation.id);
|
|
151
|
+
await this._handleSendMessage(
|
|
152
|
+
openConversation.id,
|
|
153
|
+
{ content: messageContent },
|
|
154
|
+
pendingAttachments
|
|
155
|
+
);
|
|
156
|
+
return openConversation;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return await this.startNewConversation(
|
|
160
|
+
messageContent,
|
|
161
|
+
'',
|
|
158
162
|
pendingAttachments
|
|
159
163
|
);
|
|
160
|
-
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error('[MessengerWidget] Failed to start conversation:', error);
|
|
166
|
+
return null;
|
|
161
167
|
}
|
|
162
|
-
|
|
163
|
-
return await this.startNewConversation(
|
|
164
|
-
messageContent,
|
|
165
|
-
'',
|
|
166
|
-
pendingAttachments
|
|
167
|
-
);
|
|
168
|
-
} catch (error) {
|
|
169
|
-
console.error('[MessengerWidget] Failed to start conversation:', error);
|
|
170
|
-
return null;
|
|
171
168
|
}
|
|
172
|
-
}
|
|
173
169
|
|
|
174
170
|
async _handleSelectConversation(conversationId) {
|
|
175
171
|
try {
|
|
@@ -194,47 +190,33 @@ async _handleStartConversation(messageContent, pendingAttachments) {
|
|
|
194
190
|
}
|
|
195
191
|
}
|
|
196
192
|
|
|
197
|
-
async _handleIdentifyContact(contactData) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (response.status) {
|
|
205
|
-
console.log('[MessengerWidget] Contact identified:', response.data.contact_id);
|
|
193
|
+
async _handleIdentifyContact(contactData) {
|
|
194
|
+
try {
|
|
195
|
+
const response = await this.apiService.identifyContact({
|
|
196
|
+
name: contactData.name,
|
|
197
|
+
email: contactData.email,
|
|
198
|
+
});
|
|
206
199
|
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const pendingMessage = this.messengerState.pendingMessage;
|
|
215
|
-
if (pendingMessage) {
|
|
216
|
-
this.messengerState.pendingMessage = null;
|
|
217
|
-
|
|
218
|
-
// SET VIEW FIRST
|
|
219
|
-
this.messengerState.setView('chat');
|
|
220
|
-
|
|
221
|
-
// THEN start conversation
|
|
222
|
-
await this.startNewConversation(
|
|
223
|
-
pendingMessage.content,
|
|
224
|
-
'',
|
|
225
|
-
pendingMessage.attachments
|
|
226
|
-
);
|
|
227
|
-
} else {
|
|
228
|
-
this.messengerState.setView('chat');
|
|
200
|
+
if (response.status) {
|
|
201
|
+
console.log('[MessengerWidget] Contact identified:', response.data.contact_id);
|
|
202
|
+
this.messengerState.setIdentified(true, {
|
|
203
|
+
name: contactData.name,
|
|
204
|
+
email: contactData.email,
|
|
205
|
+
});
|
|
229
206
|
}
|
|
207
|
+
|
|
208
|
+
return response;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error('[MessengerWidget] Failed to identify contact:', error);
|
|
211
|
+
throw error;
|
|
230
212
|
}
|
|
213
|
+
}
|
|
231
214
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
console.
|
|
235
|
-
throw error;
|
|
215
|
+
markAsIdentified(name, email) {
|
|
216
|
+
this.messengerState.setIdentified(true, { name, email });
|
|
217
|
+
console.log('[MessengerWidget] Marked as identified:', email);
|
|
236
218
|
}
|
|
237
|
-
|
|
219
|
+
|
|
238
220
|
async _handleUploadFile(base64Data, filename) {
|
|
239
221
|
try {
|
|
240
222
|
const response = await this.apiService.uploadFile(base64Data, filename);
|
|
@@ -610,25 +592,12 @@ async _handleIdentifyContact(contactData) {
|
|
|
610
592
|
const uploadedAttachments =
|
|
611
593
|
await this._uploadPendingAttachments(pendingAttachments);
|
|
612
594
|
|
|
613
|
-
console.log('[MessengerWidget] Starting conversation...', {
|
|
614
|
-
message,
|
|
615
|
-
attachmentCount: uploadedAttachments.length,
|
|
616
|
-
hasSession: this.apiService.isSessionValid(),
|
|
617
|
-
sessionToken: this.apiService.sessionToken
|
|
618
|
-
? this.apiService.sessionToken.substring(0, 10) + '...'
|
|
619
|
-
: null,
|
|
620
|
-
baseURL: this.apiService.baseURL,
|
|
621
|
-
mock: this.apiService.mock,
|
|
622
|
-
});
|
|
623
|
-
|
|
624
595
|
const response = await this.apiService.startConversation({
|
|
625
596
|
message,
|
|
626
597
|
subject,
|
|
627
598
|
attachments: uploadedAttachments,
|
|
628
599
|
});
|
|
629
600
|
|
|
630
|
-
console.log('[MessengerWidget] Conversation response:', response);
|
|
631
|
-
|
|
632
601
|
if (response.status && response.data) {
|
|
633
602
|
const conv = response.data;
|
|
634
603
|
const newConversation = {
|
|
@@ -795,19 +764,19 @@ async _handleIdentifyContact(contactData) {
|
|
|
795
764
|
};
|
|
796
765
|
}
|
|
797
766
|
|
|
798
|
-
async onMount() {
|
|
799
|
-
|
|
767
|
+
async onMount() {
|
|
768
|
+
this.loadInitialData();
|
|
800
769
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
this.checkAgentAvailability();
|
|
770
|
+
if (this.apiService?.sessionToken) {
|
|
771
|
+
this._initWebSocket();
|
|
772
|
+
}
|
|
806
773
|
|
|
807
|
-
this._availabilityInterval = setInterval(() => {
|
|
808
774
|
this.checkAgentAvailability();
|
|
809
|
-
|
|
810
|
-
|
|
775
|
+
|
|
776
|
+
this._availabilityInterval = setInterval(() => {
|
|
777
|
+
this.checkAgentAvailability();
|
|
778
|
+
}, 60000);
|
|
779
|
+
}
|
|
811
780
|
|
|
812
781
|
onDestroy() {
|
|
813
782
|
if (this._stateUnsubscribe) {
|
|
@@ -1,133 +1,99 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MessengerState - State management for the Messenger widget
|
|
3
|
-
*/
|
|
4
1
|
export class MessengerState {
|
|
5
|
-
constructor(options = {}) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Typing indicators
|
|
45
|
-
this.typingUsers = {};
|
|
46
|
-
|
|
47
|
-
// Loading states
|
|
48
|
-
this.isLoading = false;
|
|
49
|
-
this.isLoadingMessages = false;
|
|
50
|
-
|
|
51
|
-
// Listeners
|
|
52
|
-
this._listeners = new Set();
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Subscribe to state changes
|
|
56
|
-
*/
|
|
2
|
+
constructor(options = {}) {
|
|
3
|
+
this.currentView = 'home';
|
|
4
|
+
this.isOpen = false;
|
|
5
|
+
this.unreadCount = 0;
|
|
6
|
+
this.activeConversationId = null;
|
|
7
|
+
|
|
8
|
+
this.conversations = [];
|
|
9
|
+
this.messages = {};
|
|
10
|
+
|
|
11
|
+
this.helpArticles = [];
|
|
12
|
+
this.helpSearchQuery = '';
|
|
13
|
+
|
|
14
|
+
this.homeChangelogItems = [];
|
|
15
|
+
this.changelogItems = [];
|
|
16
|
+
|
|
17
|
+
this.teamName = options.teamName || 'Support';
|
|
18
|
+
this.teamAvatars = options.teamAvatars || [];
|
|
19
|
+
this.welcomeMessage = options.welcomeMessage || 'How can we help?';
|
|
20
|
+
|
|
21
|
+
this.userContext = options.userContext || null;
|
|
22
|
+
this.isIdentified = false;
|
|
23
|
+
this.pendingMessage = null;
|
|
24
|
+
|
|
25
|
+
this.enableHelp = options.enableHelp !== false;
|
|
26
|
+
this.enableChangelog = options.enableChangelog !== false;
|
|
27
|
+
|
|
28
|
+
this.agentsOnline = false;
|
|
29
|
+
this.onlineCount = 0;
|
|
30
|
+
this.responseTime = 'Usually replies within a few minutes';
|
|
31
|
+
|
|
32
|
+
this.typingUsers = {};
|
|
33
|
+
|
|
34
|
+
this.isLoading = false;
|
|
35
|
+
this.isLoadingMessages = false;
|
|
36
|
+
|
|
37
|
+
this._listeners = new Set();
|
|
38
|
+
}
|
|
39
|
+
|
|
57
40
|
subscribe(callback) {
|
|
58
41
|
this._listeners.add(callback);
|
|
59
42
|
return () => this._listeners.delete(callback);
|
|
60
43
|
}
|
|
61
44
|
|
|
62
|
-
/**
|
|
63
|
-
* Notify all listeners of state change
|
|
64
|
-
*/
|
|
65
45
|
_notify(changeType, data) {
|
|
66
46
|
this._listeners.forEach((cb) => cb(changeType, data, this));
|
|
67
47
|
}
|
|
68
48
|
|
|
69
|
-
/**
|
|
70
|
-
* Set current view
|
|
71
|
-
*/
|
|
72
49
|
setView(view) {
|
|
73
50
|
const previousView = this.currentView;
|
|
74
51
|
this.currentView = view;
|
|
75
52
|
this._notify('viewChange', { previousView, currentView: view });
|
|
76
53
|
}
|
|
77
54
|
|
|
78
|
-
/**
|
|
79
|
-
* Toggle panel open/closed
|
|
80
|
-
*/
|
|
81
55
|
setOpen(isOpen) {
|
|
82
56
|
this.isOpen = isOpen;
|
|
83
57
|
this._notify('openChange', { isOpen });
|
|
84
58
|
}
|
|
85
59
|
|
|
86
|
-
/**
|
|
87
|
-
* Set active conversation for chat view
|
|
88
|
-
*/
|
|
89
60
|
setActiveConversation(conversationId) {
|
|
90
61
|
const previousConversationId = this.activeConversationId;
|
|
91
62
|
this.activeConversationId = conversationId;
|
|
92
63
|
this._notify('conversationChange', { conversationId, previousConversationId });
|
|
93
64
|
}
|
|
94
65
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
66
|
+
setIdentified(isIdentified, userContext = null) {
|
|
67
|
+
this.isIdentified = isIdentified;
|
|
68
|
+
if (userContext) {
|
|
69
|
+
this.userContext = { ...this.userContext, ...userContext };
|
|
70
|
+
}
|
|
71
|
+
this._notify('identificationChange', { isIdentified, userContext });
|
|
72
|
+
}
|
|
73
|
+
|
|
98
74
|
setConversations(conversations) {
|
|
99
75
|
this.conversations = conversations;
|
|
100
76
|
this._updateUnreadCount();
|
|
101
77
|
this._notify('conversationsUpdate', { conversations });
|
|
102
78
|
}
|
|
103
79
|
|
|
104
|
-
/**
|
|
105
|
-
* Add a new conversation
|
|
106
|
-
*/
|
|
107
80
|
addConversation(conversation) {
|
|
108
81
|
this.conversations.unshift(conversation);
|
|
109
82
|
this._updateUnreadCount();
|
|
110
83
|
this._notify('conversationAdded', { conversation });
|
|
111
84
|
}
|
|
112
85
|
|
|
113
|
-
/**
|
|
114
|
-
* Update messages for a conversation
|
|
115
|
-
*/
|
|
116
86
|
setMessages(conversationId, messages) {
|
|
117
87
|
this.messages[conversationId] = messages;
|
|
118
88
|
this._notify('messagesUpdate', { conversationId, messages });
|
|
119
89
|
}
|
|
120
90
|
|
|
121
|
-
/**
|
|
122
|
-
* Add a message to a conversation
|
|
123
|
-
*/
|
|
124
91
|
addMessage(conversationId, message) {
|
|
125
92
|
if (!this.messages[conversationId]) {
|
|
126
93
|
this.messages[conversationId] = [];
|
|
127
94
|
}
|
|
128
95
|
this.messages[conversationId].push(message);
|
|
129
96
|
|
|
130
|
-
// Update conversation preview
|
|
131
97
|
const conv = this.conversations.find((c) => c.id === conversationId);
|
|
132
98
|
if (conv) {
|
|
133
99
|
conv.lastMessage = message.content;
|
|
@@ -141,9 +107,6 @@ constructor(options = {}) {
|
|
|
141
107
|
this._notify('messageAdded', { conversationId, message });
|
|
142
108
|
}
|
|
143
109
|
|
|
144
|
-
/**
|
|
145
|
-
* Update a conversation by id
|
|
146
|
-
*/
|
|
147
110
|
updateConversation(conversationId, updates) {
|
|
148
111
|
const conv = this.conversations.find((c) => c.id === conversationId);
|
|
149
112
|
if (!conv) {
|
|
@@ -155,9 +118,6 @@ constructor(options = {}) {
|
|
|
155
118
|
return conv;
|
|
156
119
|
}
|
|
157
120
|
|
|
158
|
-
/**
|
|
159
|
-
* Mark conversation as read
|
|
160
|
-
*/
|
|
161
121
|
markAsRead(conversationId) {
|
|
162
122
|
const conv = this.conversations.find((c) => c.id === conversationId);
|
|
163
123
|
if (conv && conv.unread > 0) {
|
|
@@ -167,9 +127,6 @@ constructor(options = {}) {
|
|
|
167
127
|
}
|
|
168
128
|
}
|
|
169
129
|
|
|
170
|
-
/**
|
|
171
|
-
* Update unread count
|
|
172
|
-
*/
|
|
173
130
|
_updateUnreadCount() {
|
|
174
131
|
this.unreadCount = this.conversations.reduce(
|
|
175
132
|
(sum, c) => sum + (c.unread || 0),
|
|
@@ -178,57 +135,34 @@ constructor(options = {}) {
|
|
|
178
135
|
this._notify('unreadCountChange', { count: this.unreadCount });
|
|
179
136
|
}
|
|
180
137
|
|
|
181
|
-
/**
|
|
182
|
-
* Set help articles
|
|
183
|
-
*/
|
|
184
138
|
setHelpArticles(articles) {
|
|
185
139
|
this.helpArticles = articles;
|
|
186
140
|
this._notify('helpArticlesUpdate', { articles });
|
|
187
141
|
}
|
|
188
142
|
|
|
189
|
-
/**
|
|
190
|
-
* Set help search query
|
|
191
|
-
*/
|
|
192
143
|
setHelpSearchQuery(query) {
|
|
193
144
|
this.helpSearchQuery = query;
|
|
194
145
|
this._notify('helpSearchChange', { query });
|
|
195
146
|
}
|
|
196
147
|
|
|
197
|
-
/**
|
|
198
|
-
* Set home changelog items
|
|
199
|
-
*/
|
|
200
148
|
setHomeChangelogItems(items) {
|
|
201
149
|
this.homeChangelogItems = items;
|
|
202
150
|
this._notify('homeChangelogUpdate', { items });
|
|
203
151
|
}
|
|
204
152
|
|
|
205
|
-
/**
|
|
206
|
-
* Set changelog items
|
|
207
|
-
*/
|
|
208
153
|
setChangelogItems(items) {
|
|
209
154
|
this.changelogItems = items;
|
|
210
155
|
this._notify('changelogUpdate', { items });
|
|
211
156
|
}
|
|
212
157
|
|
|
213
|
-
/**
|
|
214
|
-
* Get current conversation
|
|
215
|
-
*/
|
|
216
158
|
getActiveConversation() {
|
|
217
159
|
return this.conversations.find((c) => c.id === this.activeConversationId);
|
|
218
160
|
}
|
|
219
161
|
|
|
220
|
-
/**
|
|
221
|
-
* Get messages for active conversation
|
|
222
|
-
*/
|
|
223
162
|
getActiveMessages() {
|
|
224
163
|
return this.messages[this.activeConversationId] || [];
|
|
225
164
|
}
|
|
226
165
|
|
|
227
|
-
/**
|
|
228
|
-
* Update team avatars from backend agent data.
|
|
229
|
-
* Converts available_agents ({full_name, picture}) into avatar strings
|
|
230
|
-
* the views already support (URL strings or initial strings).
|
|
231
|
-
*/
|
|
232
166
|
setTeamAvatarsFromAgents(agents) {
|
|
233
167
|
if (!agents || agents.length === 0) return;
|
|
234
168
|
|
|
@@ -239,9 +173,6 @@ constructor(options = {}) {
|
|
|
239
173
|
this._notify('teamAvatarsUpdate', { teamAvatars: this.teamAvatars });
|
|
240
174
|
}
|
|
241
175
|
|
|
242
|
-
/**
|
|
243
|
-
* Get filtered help articles
|
|
244
|
-
*/
|
|
245
176
|
getFilteredHelpArticles() {
|
|
246
177
|
if (!this.helpSearchQuery) {
|
|
247
178
|
return this.helpArticles;
|
|
@@ -255,13 +186,10 @@ constructor(options = {}) {
|
|
|
255
186
|
);
|
|
256
187
|
}
|
|
257
188
|
|
|
258
|
-
/**
|
|
259
|
-
* Reset state
|
|
260
|
-
*/
|
|
261
189
|
reset() {
|
|
262
190
|
this.currentView = 'home';
|
|
263
191
|
this.activeConversationId = null;
|
|
264
192
|
this.helpSearchQuery = '';
|
|
265
193
|
this._notify('reset', {});
|
|
266
194
|
}
|
|
267
|
-
}
|
|
195
|
+
}
|