@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@product7/feedback-sdk",
3
- "version": "1.3.6",
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,47 +190,33 @@ 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
- if (!this.messengerState.userContext) {
208
- this.messengerState.userContext = {};
209
- }
210
- this.messengerState.userContext.name = contactData.name;
211
- this.messengerState.userContext.email = contactData.email;
212
- this.messengerState.isIdentified = true;
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
- return response;
233
- } catch (error) {
234
- console.error('[MessengerWidget] Failed to identify contact:', error);
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
- this.loadInitialData();
767
+ async onMount() {
768
+ this.loadInitialData();
800
769
 
801
- if (this.apiService?.sessionToken) {
802
- this._initWebSocket();
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
- }, 60000);
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
- 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
+ }