@product7/feedback-sdk 1.3.7 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@product7/feedback-sdk",
3
- "version": "1.3.7",
3
+ "version": "1.3.8",
4
4
  "description": "JavaScript SDK for integrating Product7 feedback widgets into any website",
5
5
  "main": "dist/feedback-sdk.js",
6
6
  "module": "src/index.js",
@@ -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({ content: data.content }),
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
- try {
137
- // Check if already identified
138
- if (!this.messengerState.isIdentified) {
139
- // Not identified - show pre-chat form
140
- this.messengerState.pendingMessage = {
141
- content: messageContent,
142
- attachments: pendingAttachments,
143
- };
144
- this.messengerState.setView('prechat');
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
- // Already identified - proceed with conversation
149
- const openConversation = this.messengerState.conversations.find(
150
- (c) => c.status === 'open'
151
- );
145
+ const openConversation = this.messengerState.conversations.find(
146
+ (c) => c.status === 'open'
147
+ );
152
148
 
153
- if (openConversation) {
154
- this.messengerState.setActiveConversation(openConversation.id);
155
- await this._handleSendMessage(
156
- openConversation.id,
157
- { content: messageContent },
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
- return openConversation;
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,31 +190,32 @@ async _handleStartConversation(messageContent, pendingAttachments) {
194
190
  }
195
191
  }
196
192
 
197
- async _handleIdentifyContact(contactData) {
198
- try {
199
- const response = await this.apiService.identifyContact({
200
- name: contactData.name,
201
- email: contactData.email,
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
- // Update state
208
- if (!this.messengerState.userContext) {
209
- this.messengerState.userContext = {};
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
+ });
210
206
  }
211
- this.messengerState.userContext.name = contactData.name;
212
- this.messengerState.userContext.email = contactData.email;
213
- this.messengerState.isIdentified = true;
207
+
208
+ return response;
209
+ } catch (error) {
210
+ console.error('[MessengerWidget] Failed to identify contact:', error);
211
+ throw error;
214
212
  }
213
+ }
215
214
 
216
- return response;
217
- } catch (error) {
218
- console.error('[MessengerWidget] Failed to identify contact:', error);
219
- throw error;
215
+ markAsIdentified(name, email) {
216
+ this.messengerState.setIdentified(true, { name, email });
217
+ console.log('[MessengerWidget] Marked as identified:', email);
220
218
  }
221
- }
222
219
 
223
220
  async _handleUploadFile(base64Data, filename) {
224
221
  try {
@@ -595,25 +592,12 @@ async _handleIdentifyContact(contactData) {
595
592
  const uploadedAttachments =
596
593
  await this._uploadPendingAttachments(pendingAttachments);
597
594
 
598
- console.log('[MessengerWidget] Starting conversation...', {
599
- message,
600
- attachmentCount: uploadedAttachments.length,
601
- hasSession: this.apiService.isSessionValid(),
602
- sessionToken: this.apiService.sessionToken
603
- ? this.apiService.sessionToken.substring(0, 10) + '...'
604
- : null,
605
- baseURL: this.apiService.baseURL,
606
- mock: this.apiService.mock,
607
- });
608
-
609
595
  const response = await this.apiService.startConversation({
610
596
  message,
611
597
  subject,
612
598
  attachments: uploadedAttachments,
613
599
  });
614
600
 
615
- console.log('[MessengerWidget] Conversation response:', response);
616
-
617
601
  if (response.status && response.data) {
618
602
  const conv = response.data;
619
603
  const newConversation = {
@@ -780,19 +764,19 @@ async _handleIdentifyContact(contactData) {
780
764
  };
781
765
  }
782
766
 
783
- async onMount() {
784
- this.loadInitialData();
767
+ async onMount() {
768
+ this.loadInitialData();
785
769
 
786
- if (this.apiService?.sessionToken) {
787
- this._initWebSocket();
788
- }
789
-
790
- this.checkAgentAvailability();
770
+ if (this.apiService?.sessionToken) {
771
+ this._initWebSocket();
772
+ }
791
773
 
792
- this._availabilityInterval = setInterval(() => {
793
774
  this.checkAgentAvailability();
794
- }, 60000);
795
- }
775
+
776
+ this._availabilityInterval = setInterval(() => {
777
+ this.checkAgentAvailability();
778
+ }, 60000);
779
+ }
796
780
 
797
781
  onDestroy() {
798
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
- this.currentView = 'home';
7
- this.isOpen = false;
8
- this.unreadCount = 0;
9
- this.activeConversationId = null;
10
-
11
- // Conversations
12
- this.conversations = [];
13
- this.messages = {};
14
-
15
- // Help articles
16
- this.helpArticles = [];
17
- this.helpSearchQuery = '';
18
-
19
- // Changelog
20
- this.homeChangelogItems = [];
21
- this.changelogItems = [];
22
-
23
- // Team info
24
- this.teamName = options.teamName || 'Support';
25
- this.teamAvatars = options.teamAvatars || [];
26
- this.welcomeMessage = options.welcomeMessage || 'How can we help?';
27
-
28
- // User info
29
- this.userContext = options.userContext || null;
30
-
31
- // ADD THESE TWO PROPERTIES
32
- this.isIdentified = false; // Track if user has been identified
33
- this.pendingMessage = null; // { content, attachments } - for pre-chat flow
34
-
35
- // Feature flags
36
- this.enableHelp = options.enableHelp !== false;
37
- this.enableChangelog = options.enableChangelog !== false;
38
-
39
- // Agent availability
40
- this.agentsOnline = false;
41
- this.onlineCount = 0;
42
- this.responseTime = 'Usually replies within a few minutes';
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
- * Update conversations list
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
+ }