@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.
@@ -461,6 +461,7 @@
461
461
  content: data.message,
462
462
  sender_type: 'customer',
463
463
  created_at: new Date().toISOString(),
464
+ attachments: data.attachments || [],
464
465
  },
465
466
  ],
466
467
  };
@@ -478,6 +479,7 @@
478
479
  body: JSON.stringify({
479
480
  message: data.message,
480
481
  subject: data.subject || '',
482
+ attachments: data.attachments || []
481
483
  }),
482
484
  });
483
485
  }
@@ -492,6 +494,7 @@
492
494
  content: data.content,
493
495
  sender_type: 'customer',
494
496
  created_at: new Date().toISOString(),
497
+ attachments: data.attachments || [],
495
498
  };
496
499
  if (!MOCK_MESSAGES$1[conversationId]) {
497
500
  MOCK_MESSAGES$1[conversationId] = [];
@@ -508,7 +511,10 @@
508
511
  'Content-Type': 'application/json',
509
512
  Authorization: `Bearer ${this.api.sessionToken}`,
510
513
  },
511
- body: JSON.stringify({ content: data.content }),
514
+ body: JSON.stringify({
515
+ content: data.content,
516
+ attachments: data.attachments || []
517
+ }),
512
518
  }
513
519
  );
514
520
  }
@@ -2951,136 +2957,102 @@
2951
2957
  }
2952
2958
  }
2953
2959
 
2954
- /**
2955
- * MessengerState - State management for the Messenger widget
2956
- */
2957
2960
  class MessengerState {
2958
- constructor(options = {}) {
2959
- this.currentView = 'home';
2960
- this.isOpen = false;
2961
- this.unreadCount = 0;
2962
- this.activeConversationId = null;
2963
-
2964
- // Conversations
2965
- this.conversations = [];
2966
- this.messages = {};
2967
-
2968
- // Help articles
2969
- this.helpArticles = [];
2970
- this.helpSearchQuery = '';
2971
-
2972
- // Changelog
2973
- this.homeChangelogItems = [];
2974
- this.changelogItems = [];
2975
-
2976
- // Team info
2977
- this.teamName = options.teamName || 'Support';
2978
- this.teamAvatars = options.teamAvatars || [];
2979
- this.welcomeMessage = options.welcomeMessage || 'How can we help?';
2980
-
2981
- // User info
2982
- this.userContext = options.userContext || null;
2983
-
2984
- // ADD THESE TWO PROPERTIES
2985
- this.isIdentified = false; // Track if user has been identified
2986
- this.pendingMessage = null; // { content, attachments } - for pre-chat flow
2987
-
2988
- // Feature flags
2989
- this.enableHelp = options.enableHelp !== false;
2990
- this.enableChangelog = options.enableChangelog !== false;
2991
-
2992
- // Agent availability
2993
- this.agentsOnline = false;
2994
- this.onlineCount = 0;
2995
- this.responseTime = 'Usually replies within a few minutes';
2996
-
2997
- // Typing indicators
2998
- this.typingUsers = {};
2999
-
3000
- // Loading states
3001
- this.isLoading = false;
3002
- this.isLoadingMessages = false;
3003
-
3004
- // Listeners
3005
- this._listeners = new Set();
3006
- }
3007
- /**
3008
- * Subscribe to state changes
3009
- */
2961
+ constructor(options = {}) {
2962
+ this.currentView = 'home';
2963
+ this.isOpen = false;
2964
+ this.unreadCount = 0;
2965
+ this.activeConversationId = null;
2966
+
2967
+ this.conversations = [];
2968
+ this.messages = {};
2969
+
2970
+ this.helpArticles = [];
2971
+ this.helpSearchQuery = '';
2972
+
2973
+ this.homeChangelogItems = [];
2974
+ this.changelogItems = [];
2975
+
2976
+ this.teamName = options.teamName || 'Support';
2977
+ this.teamAvatars = options.teamAvatars || [];
2978
+ this.welcomeMessage = options.welcomeMessage || 'How can we help?';
2979
+
2980
+ this.userContext = options.userContext || null;
2981
+ this.isIdentified = false;
2982
+ this.pendingMessage = null;
2983
+
2984
+ this.enableHelp = options.enableHelp !== false;
2985
+ this.enableChangelog = options.enableChangelog !== false;
2986
+
2987
+ this.agentsOnline = false;
2988
+ this.onlineCount = 0;
2989
+ this.responseTime = 'Usually replies within a few minutes';
2990
+
2991
+ this.typingUsers = {};
2992
+
2993
+ this.isLoading = false;
2994
+ this.isLoadingMessages = false;
2995
+
2996
+ this._listeners = new Set();
2997
+ }
2998
+
3010
2999
  subscribe(callback) {
3011
3000
  this._listeners.add(callback);
3012
3001
  return () => this._listeners.delete(callback);
3013
3002
  }
3014
3003
 
3015
- /**
3016
- * Notify all listeners of state change
3017
- */
3018
3004
  _notify(changeType, data) {
3019
3005
  this._listeners.forEach((cb) => cb(changeType, data, this));
3020
3006
  }
3021
3007
 
3022
- /**
3023
- * Set current view
3024
- */
3025
3008
  setView(view) {
3026
3009
  const previousView = this.currentView;
3027
3010
  this.currentView = view;
3028
3011
  this._notify('viewChange', { previousView, currentView: view });
3029
3012
  }
3030
3013
 
3031
- /**
3032
- * Toggle panel open/closed
3033
- */
3034
3014
  setOpen(isOpen) {
3035
3015
  this.isOpen = isOpen;
3036
3016
  this._notify('openChange', { isOpen });
3037
3017
  }
3038
3018
 
3039
- /**
3040
- * Set active conversation for chat view
3041
- */
3042
3019
  setActiveConversation(conversationId) {
3043
3020
  const previousConversationId = this.activeConversationId;
3044
3021
  this.activeConversationId = conversationId;
3045
3022
  this._notify('conversationChange', { conversationId, previousConversationId });
3046
3023
  }
3047
3024
 
3048
- /**
3049
- * Update conversations list
3050
- */
3025
+ setIdentified(isIdentified, userContext = null) {
3026
+ this.isIdentified = isIdentified;
3027
+ if (userContext) {
3028
+ this.userContext = { ...this.userContext, ...userContext };
3029
+ }
3030
+ this._notify('identificationChange', { isIdentified, userContext });
3031
+ }
3032
+
3051
3033
  setConversations(conversations) {
3052
3034
  this.conversations = conversations;
3053
3035
  this._updateUnreadCount();
3054
3036
  this._notify('conversationsUpdate', { conversations });
3055
3037
  }
3056
3038
 
3057
- /**
3058
- * Add a new conversation
3059
- */
3060
3039
  addConversation(conversation) {
3061
3040
  this.conversations.unshift(conversation);
3062
3041
  this._updateUnreadCount();
3063
3042
  this._notify('conversationAdded', { conversation });
3064
3043
  }
3065
3044
 
3066
- /**
3067
- * Update messages for a conversation
3068
- */
3069
3045
  setMessages(conversationId, messages) {
3070
3046
  this.messages[conversationId] = messages;
3071
3047
  this._notify('messagesUpdate', { conversationId, messages });
3072
3048
  }
3073
3049
 
3074
- /**
3075
- * Add a message to a conversation
3076
- */
3077
3050
  addMessage(conversationId, message) {
3078
3051
  if (!this.messages[conversationId]) {
3079
3052
  this.messages[conversationId] = [];
3080
3053
  }
3081
3054
  this.messages[conversationId].push(message);
3082
3055
 
3083
- // Update conversation preview
3084
3056
  const conv = this.conversations.find((c) => c.id === conversationId);
3085
3057
  if (conv) {
3086
3058
  conv.lastMessage = message.content;
@@ -3094,9 +3066,6 @@
3094
3066
  this._notify('messageAdded', { conversationId, message });
3095
3067
  }
3096
3068
 
3097
- /**
3098
- * Update a conversation by id
3099
- */
3100
3069
  updateConversation(conversationId, updates) {
3101
3070
  const conv = this.conversations.find((c) => c.id === conversationId);
3102
3071
  if (!conv) {
@@ -3108,9 +3077,6 @@
3108
3077
  return conv;
3109
3078
  }
3110
3079
 
3111
- /**
3112
- * Mark conversation as read
3113
- */
3114
3080
  markAsRead(conversationId) {
3115
3081
  const conv = this.conversations.find((c) => c.id === conversationId);
3116
3082
  if (conv && conv.unread > 0) {
@@ -3120,9 +3086,6 @@
3120
3086
  }
3121
3087
  }
3122
3088
 
3123
- /**
3124
- * Update unread count
3125
- */
3126
3089
  _updateUnreadCount() {
3127
3090
  this.unreadCount = this.conversations.reduce(
3128
3091
  (sum, c) => sum + (c.unread || 0),
@@ -3131,57 +3094,34 @@
3131
3094
  this._notify('unreadCountChange', { count: this.unreadCount });
3132
3095
  }
3133
3096
 
3134
- /**
3135
- * Set help articles
3136
- */
3137
3097
  setHelpArticles(articles) {
3138
3098
  this.helpArticles = articles;
3139
3099
  this._notify('helpArticlesUpdate', { articles });
3140
3100
  }
3141
3101
 
3142
- /**
3143
- * Set help search query
3144
- */
3145
3102
  setHelpSearchQuery(query) {
3146
3103
  this.helpSearchQuery = query;
3147
3104
  this._notify('helpSearchChange', { query });
3148
3105
  }
3149
3106
 
3150
- /**
3151
- * Set home changelog items
3152
- */
3153
3107
  setHomeChangelogItems(items) {
3154
3108
  this.homeChangelogItems = items;
3155
3109
  this._notify('homeChangelogUpdate', { items });
3156
3110
  }
3157
3111
 
3158
- /**
3159
- * Set changelog items
3160
- */
3161
3112
  setChangelogItems(items) {
3162
3113
  this.changelogItems = items;
3163
3114
  this._notify('changelogUpdate', { items });
3164
3115
  }
3165
3116
 
3166
- /**
3167
- * Get current conversation
3168
- */
3169
3117
  getActiveConversation() {
3170
3118
  return this.conversations.find((c) => c.id === this.activeConversationId);
3171
3119
  }
3172
3120
 
3173
- /**
3174
- * Get messages for active conversation
3175
- */
3176
3121
  getActiveMessages() {
3177
3122
  return this.messages[this.activeConversationId] || [];
3178
3123
  }
3179
3124
 
3180
- /**
3181
- * Update team avatars from backend agent data.
3182
- * Converts available_agents ({full_name, picture}) into avatar strings
3183
- * the views already support (URL strings or initial strings).
3184
- */
3185
3125
  setTeamAvatarsFromAgents(agents) {
3186
3126
  if (!agents || agents.length === 0) return;
3187
3127
 
@@ -3192,9 +3132,6 @@
3192
3132
  this._notify('teamAvatarsUpdate', { teamAvatars: this.teamAvatars });
3193
3133
  }
3194
3134
 
3195
- /**
3196
- * Get filtered help articles
3197
- */
3198
3135
  getFilteredHelpArticles() {
3199
3136
  if (!this.helpSearchQuery) {
3200
3137
  return this.helpArticles;
@@ -3208,9 +3145,6 @@
3208
3145
  );
3209
3146
  }
3210
3147
 
3211
- /**
3212
- * Reset state
3213
- */
3214
3148
  reset() {
3215
3149
  this.currentView = 'home';
3216
3150
  this.activeConversationId = null;
@@ -3797,8 +3731,7 @@
3797
3731
  this._isTyping = false;
3798
3732
  this._typingIndicator = null;
3799
3733
  this._isConversationClosed = false;
3800
- this._showEmailOverlayFlag = false;
3801
- this._pendingAttachments = [];
3734
+ this._pendingAttachments = [];
3802
3735
  }
3803
3736
 
3804
3737
  render() {
@@ -3860,8 +3793,6 @@
3860
3793
  ? 'Conversation closed'
3861
3794
  : 'Write a message...';
3862
3795
 
3863
- const existingName = this.state.userContext?.name || '';
3864
-
3865
3796
  this.element.innerHTML = `
3866
3797
  <div class="messenger-chat-header">
3867
3798
  <button class="sdk-btn-icon messenger-back-btn" aria-label="Back">
@@ -3914,46 +3845,17 @@
3914
3845
  <input type="file" class="messenger-compose-file-input" multiple accept="image/*,.pdf,.doc,.docx,.xls,.xlsx,.txt,.zip" />
3915
3846
  </div>
3916
3847
  `}
3917
-
3918
- <div class="messenger-email-overlay">
3919
- <div class="sdk-card messenger-email-card">
3920
- <div class="sdk-card-header">
3921
- <h4>What is your email address?</h4>
3922
- <p>Enter your email to know when we reply:</p>
3923
- </div>
3924
- <div class="sdk-card-body">
3925
- <div class="sdk-form-group">
3926
- <input type="text" class="sdk-input messenger-email-name" placeholder="Name (optional)" value="${this._escapeHtml(existingName)}" autocomplete="name" />
3927
- </div>
3928
- <div class="sdk-form-group">
3929
- <input type="email" class="sdk-input messenger-email-input" placeholder="Enter your email address..." autocomplete="email" />
3930
- </div>
3931
- </div>
3932
- <div class="sdk-card-footer messenger-email-actions">
3933
- <button class="sdk-btn sdk-btn-primary sdk-btn-block messenger-email-submit" disabled>Set my email</button>
3934
- <button class="sdk-btn sdk-btn-secondary sdk-btn-block messenger-email-skip">Skip</button>
3935
- </div>
3936
- </div>
3937
- </div>
3938
3848
  `;
3939
3849
 
3940
- this._typingIndicator = this.element.querySelector(
3941
- '.messenger-typing-indicator'
3942
- );
3850
+ this._typingIndicator = this.element.querySelector('.messenger-typing-indicator');
3943
3851
  this._attachEvents();
3944
3852
  this._scrollToBottom();
3945
3853
  this._renderAttachmentPreviews();
3946
-
3947
- // Show email overlay after first message sent without email
3948
- if (this._showEmailOverlayFlag) {
3949
- this._showEmailOverlay();
3950
- }
3951
3854
  }
3952
3855
 
3953
3856
  _renderEmptyState(isNewConversation = false) {
3954
3857
  const avatarHtml = this._renderTeamAvatars();
3955
- const responseTime =
3956
- this.state.responseTime || 'We typically reply within a few minutes';
3858
+ const responseTime = this.state.responseTime || 'We typically reply within a few minutes';
3957
3859
  const isOnline = this.state.agentsOnline;
3958
3860
 
3959
3861
  return `
@@ -3985,14 +3887,11 @@
3985
3887
 
3986
3888
  _renderMessage(message) {
3987
3889
  const isOwn = message.isOwn;
3988
- const messageClass = isOwn
3989
- ? 'messenger-message-own'
3990
- : 'messenger-message-received';
3890
+ const messageClass = isOwn ? 'messenger-message-own' : 'messenger-message-received';
3991
3891
  const timeStr = this._formatMessageTime(message.timestamp);
3992
3892
  const attachmentsHtml = this._renderMessageAttachments(message.attachments);
3993
3893
 
3994
3894
  const contentHtml = message.content ? `<div class="messenger-message-content">${this._formatMessageContent(message.content)}</div>` : '';
3995
-
3996
3895
  const bubbleHtml = contentHtml ? `<div class="messenger-message-bubble">${contentHtml}</div>` : '';
3997
3896
 
3998
3897
  if (isOwn) {
@@ -4082,9 +3981,7 @@
4082
3981
  }
4083
3982
 
4084
3983
  _appendMessage(message) {
4085
- const messagesContainer = this.element.querySelector(
4086
- '.messenger-chat-messages'
4087
- );
3984
+ const messagesContainer = this.element.querySelector('.messenger-chat-messages');
4088
3985
  const emptyState = messagesContainer.querySelector('.messenger-chat-empty');
4089
3986
  if (emptyState) {
4090
3987
  emptyState.remove();
@@ -4097,9 +3994,7 @@
4097
3994
  }
4098
3995
 
4099
3996
  _scrollToBottom() {
4100
- const messagesContainer = this.element.querySelector(
4101
- '.messenger-chat-messages'
4102
- );
3997
+ const messagesContainer = this.element.querySelector('.messenger-chat-messages');
4103
3998
  if (messagesContainer) {
4104
3999
  setTimeout(() => {
4105
4000
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
@@ -4139,7 +4034,6 @@
4139
4034
  `;
4140
4035
  }).join('');
4141
4036
 
4142
- // Attach remove button events
4143
4037
  container.querySelectorAll('.messenger-attachment-remove').forEach((btn) => {
4144
4038
  btn.addEventListener('click', (e) => {
4145
4039
  const idx = parseInt(e.currentTarget.dataset.index, 10);
@@ -4163,20 +4057,15 @@
4163
4057
  this.state.setOpen(false);
4164
4058
  });
4165
4059
 
4166
- // Compose input (not rendered when conversation is closed)
4167
4060
  const input = this.element.querySelector('.messenger-compose-input');
4168
4061
  const sendBtn = this.element.querySelector('.messenger-compose-send');
4169
4062
 
4170
4063
  if (input && sendBtn) {
4171
4064
  input.addEventListener('input', () => {
4172
- // Auto-resize textarea
4173
4065
  input.style.height = 'auto';
4174
4066
  input.style.height = Math.min(input.scrollHeight, 120) + 'px';
4175
-
4176
- // Enable/disable send button
4177
4067
  this._updateSendButtonState();
4178
4068
 
4179
- // Send typing indicator
4180
4069
  if (input.value.trim()) {
4181
4070
  this._startTyping();
4182
4071
  }
@@ -4194,7 +4083,6 @@
4194
4083
  });
4195
4084
  }
4196
4085
 
4197
- // Attach button + file input
4198
4086
  const attachBtn = this.element.querySelector('.messenger-compose-attach');
4199
4087
  const fileInput = this.element.querySelector('.messenger-compose-file-input');
4200
4088
 
@@ -4223,42 +4111,9 @@
4223
4111
  });
4224
4112
  }
4225
4113
 
4226
- // Email overlay events
4227
- const emailInput = this.element.querySelector('.messenger-email-input');
4228
- const emailSubmit = this.element.querySelector('.messenger-email-submit');
4229
- const emailSkip = this.element.querySelector('.messenger-email-skip');
4230
-
4231
- if (emailInput) {
4232
- emailInput.addEventListener('input', () => {
4233
- const isValid = this._isValidEmail(emailInput.value.trim());
4234
- emailSubmit.disabled = !isValid;
4235
- });
4236
-
4237
- emailInput.addEventListener('keydown', (e) => {
4238
- if (e.key === 'Enter' && !emailSubmit.disabled) {
4239
- e.preventDefault();
4240
- this._handleEmailSubmit();
4241
- }
4242
- });
4243
- }
4244
-
4245
- if (emailSubmit) {
4246
- emailSubmit.addEventListener('click', () => {
4247
- this._handleEmailSubmit();
4248
- });
4249
- }
4250
-
4251
- if (emailSkip) {
4252
- emailSkip.addEventListener('click', () => {
4253
- this._hideEmailOverlay();
4254
- });
4255
- }
4256
-
4257
- // Delegated events for attachment clicks
4258
4114
  const messagesContainer = this.element.querySelector('.messenger-chat-messages');
4259
4115
  if (messagesContainer) {
4260
4116
  messagesContainer.addEventListener('click', (e) => {
4261
- // File click -> download
4262
4117
  const fileLink = e.target.closest('.messenger-message-file');
4263
4118
  if (fileLink) {
4264
4119
  e.preventDefault();
@@ -4268,7 +4123,6 @@
4268
4123
  return;
4269
4124
  }
4270
4125
 
4271
- // Image click -> open in new tab
4272
4126
  const img = e.target.closest('.messenger-message-image');
4273
4127
  if (img) {
4274
4128
  const url = img.dataset.url || img.src;
@@ -4292,7 +4146,6 @@
4292
4146
  document.body.removeChild(a);
4293
4147
  URL.revokeObjectURL(blobUrl);
4294
4148
  } catch {
4295
- // Fallback: open in new tab
4296
4149
  window.open(url, '_blank');
4297
4150
  }
4298
4151
  }
@@ -4302,72 +4155,9 @@
4302
4155
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
4303
4156
  }
4304
4157
 
4305
- _isValidEmail(email) {
4306
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
4307
- }
4308
-
4309
- _showEmailOverlay() {
4310
- const overlay = this.element.querySelector('.messenger-email-overlay');
4311
- if (overlay) {
4312
- overlay.style.display = 'flex';
4313
- const emailInput = overlay.querySelector('.messenger-email-input');
4314
- if (emailInput) {
4315
- setTimeout(() => emailInput.focus(), 100);
4316
- }
4317
- }
4318
- }
4319
-
4320
- _startPendingConversation() {
4321
- if (this._pendingMessage && this.options.onStartConversation) {
4322
- this.options.onStartConversation(this._pendingMessage, this._pendingAttachmentsForSend || []);
4323
- this._pendingMessage = null;
4324
- this._pendingAttachmentsForSend = null;
4325
- }
4326
- }
4327
-
4328
- _hideEmailOverlay() {
4329
- this._showEmailOverlayFlag = false;
4330
- const overlay = this.element.querySelector('.messenger-email-overlay');
4331
- if (overlay) {
4332
- overlay.style.display = 'none';
4333
- }
4334
- }
4335
-
4336
- async _handleEmailSubmit() {
4337
- const nameInput = this.element.querySelector('.messenger-email-name');
4338
- const emailInput = this.element.querySelector('.messenger-email-input');
4339
- const submitBtn = this.element.querySelector('.messenger-email-submit');
4340
-
4341
- const name = nameInput?.value.trim() || '';
4342
- const email = emailInput?.value.trim();
4343
-
4344
- if (!email || !this._isValidEmail(email)) return;
4345
-
4346
- submitBtn.disabled = true;
4347
- submitBtn.textContent = 'Saving...';
4348
-
4349
- try {
4350
- if (this.options.onIdentifyContact) {
4351
- await this.options.onIdentifyContact({ name, email });
4352
- }
4353
-
4354
- if (!this.state.userContext) {
4355
- this.state.userContext = {};
4356
- }
4357
- this.state.userContext.name = name;
4358
- this.state.userContext.email = email;
4359
-
4360
- this._hideEmailOverlay();
4361
- this._startPendingConversation();
4362
- } catch (error) {
4363
- console.error('[ChatView] Failed to save email:', error);
4364
- submitBtn.disabled = false;
4365
- submitBtn.textContent = 'Set my email';
4366
- }
4367
- }
4368
-
4369
4158
  async _sendMessage() {
4370
4159
  if (this._isConversationClosed) return;
4160
+
4371
4161
  const input = this.element.querySelector('.messenger-compose-input');
4372
4162
  const content = input.value.trim();
4373
4163
  const hasAttachments = this._pendingAttachments.length > 0;
@@ -4376,37 +4166,12 @@
4376
4166
 
4377
4167
  this._stopTyping();
4378
4168
 
4379
- // Collect attachments to upload
4380
4169
  const attachmentsToSend = [...this._pendingAttachments];
4381
-
4382
4170
  const isNewConversation = !this.state.activeConversationId;
4383
- const needsContactInfo = !this.state.userContext?.email;
4384
4171
 
4385
4172
  if (isNewConversation) {
4386
- // Show user's message in chat immediately
4387
- const localMessage = {
4388
- id: 'msg_' + Date.now(),
4389
- content: content,
4390
- isOwn: true,
4391
- timestamp: new Date().toISOString(),
4392
- attachments: attachmentsToSend.map((a) => ({
4393
- url: a.preview,
4394
- type: a.type.startsWith('image') ? 'image' : 'file',
4395
- name: a.file.name,
4396
- })),
4397
- };
4398
- this._appendMessage(localMessage);
4399
- this._scrollToBottom();
4400
-
4401
- if (needsContactInfo) {
4402
- this._pendingMessage = content;
4403
- this._pendingAttachmentsForSend = attachmentsToSend;
4404
- this._showEmailOverlayFlag = true;
4405
- setTimeout(() => this._showEmailOverlay(), 300);
4406
- } else {
4407
- if (this.options.onStartConversation) {
4408
- this.options.onStartConversation(content, attachmentsToSend);
4409
- }
4173
+ if (this.options.onStartConversation) {
4174
+ this.options.onStartConversation(content, attachmentsToSend);
4410
4175
  }
4411
4176
  } else {
4412
4177
  const message = {
@@ -4428,7 +4193,6 @@
4428
4193
  }
4429
4194
  }
4430
4195
 
4431
- // Clear input and attachments
4432
4196
  input.value = '';
4433
4197
  input.style.height = 'auto';
4434
4198
  this._pendingAttachments = [];
@@ -4469,9 +4233,7 @@
4469
4233
  _showTypingIndicator(userName) {
4470
4234
  if (this._typingIndicator) {
4471
4235
  this._typingIndicator.style.display = 'flex';
4472
- const textEl = this._typingIndicator.querySelector(
4473
- '.messenger-typing-text'
4474
- );
4236
+ const textEl = this._typingIndicator.querySelector('.messenger-typing-text');
4475
4237
  if (textEl) {
4476
4238
  textEl.textContent = `${userName || 'Support'} is typing...`;
4477
4239
  }
@@ -5302,7 +5064,6 @@
5302
5064
  }
5303
5065
 
5304
5066
  _attachEvents() {
5305
- // Form validation
5306
5067
  const form = this.element.querySelector('form');
5307
5068
  const emailInput = this.element.querySelector('#messenger-prechat-email');
5308
5069
  const submitBtn = this.element.querySelector('.messenger-prechat-submit');
@@ -5387,15 +5148,22 @@
5387
5148
  await this.options.onIdentifyContact({ name, email });
5388
5149
  }
5389
5150
 
5390
- if (!this.state.userContext) {
5391
- this.state.userContext = {};
5392
- }
5393
- this.state.userContext.name = name;
5394
- this.state.userContext.email = email;
5151
+ this.state.setIdentified(true, { name, email });
5395
5152
 
5396
5153
  this._isSubmitting = false;
5397
5154
 
5398
- this.state.setView('chat');
5155
+ const pendingMessage = this.state.pendingMessage;
5156
+ if (pendingMessage && this.options.onStartConversation) {
5157
+ this.state.pendingMessage = null;
5158
+ this.state.setView('chat');
5159
+
5160
+ await this.options.onStartConversation(
5161
+ pendingMessage.content,
5162
+ pendingMessage.attachments
5163
+ );
5164
+ } else {
5165
+ this.state.setView('chat');
5166
+ }
5399
5167
  } catch (error) {
5400
5168
  console.error('[PreChatFormView] Error submitting form:', error);
5401
5169
  this._showError('messenger-email-error', 'Something went wrong. Please try again.');
@@ -5811,44 +5579,41 @@
5811
5579
  }
5812
5580
  }
5813
5581
 
5814
- async _handleStartConversation(messageContent, pendingAttachments) {
5815
- try {
5816
- // Check if already identified
5817
- if (!this.messengerState.isIdentified) {
5818
- // Not identified - show pre-chat form
5819
- this.messengerState.pendingMessage = {
5820
- content: messageContent,
5821
- attachments: pendingAttachments,
5822
- };
5823
- this.messengerState.setView('prechat');
5824
- return null;
5825
- }
5582
+ async _handleStartConversation(messageContent, pendingAttachments) {
5583
+ try {
5584
+ if (!this.messengerState.isIdentified) {
5585
+ this.messengerState.pendingMessage = {
5586
+ content: messageContent,
5587
+ attachments: pendingAttachments,
5588
+ };
5589
+ this.messengerState.setView('prechat');
5590
+ return null;
5591
+ }
5826
5592
 
5827
- // Already identified - proceed with conversation
5828
- const openConversation = this.messengerState.conversations.find(
5829
- (c) => c.status === 'open'
5830
- );
5593
+ const openConversation = this.messengerState.conversations.find(
5594
+ (c) => c.status === 'open'
5595
+ );
5831
5596
 
5832
- if (openConversation) {
5833
- this.messengerState.setActiveConversation(openConversation.id);
5834
- await this._handleSendMessage(
5835
- openConversation.id,
5836
- { content: messageContent },
5597
+ if (openConversation) {
5598
+ this.messengerState.setActiveConversation(openConversation.id);
5599
+ await this._handleSendMessage(
5600
+ openConversation.id,
5601
+ { content: messageContent },
5602
+ pendingAttachments
5603
+ );
5604
+ return openConversation;
5605
+ }
5606
+
5607
+ return await this.startNewConversation(
5608
+ messageContent,
5609
+ '',
5837
5610
  pendingAttachments
5838
5611
  );
5839
- return openConversation;
5612
+ } catch (error) {
5613
+ console.error('[MessengerWidget] Failed to start conversation:', error);
5614
+ return null;
5840
5615
  }
5841
-
5842
- return await this.startNewConversation(
5843
- messageContent,
5844
- '',
5845
- pendingAttachments
5846
- );
5847
- } catch (error) {
5848
- console.error('[MessengerWidget] Failed to start conversation:', error);
5849
- return null;
5850
5616
  }
5851
- }
5852
5617
 
5853
5618
  async _handleSelectConversation(conversationId) {
5854
5619
  try {
@@ -5873,47 +5638,33 @@
5873
5638
  }
5874
5639
  }
5875
5640
 
5876
- async _handleIdentifyContact(contactData) {
5877
- try {
5878
- const response = await this.apiService.identifyContact({
5879
- name: contactData.name,
5880
- email: contactData.email,
5881
- });
5882
-
5883
- if (response.status) {
5884
- console.log('[MessengerWidget] Contact identified:', response.data.contact_id);
5641
+ async _handleIdentifyContact(contactData) {
5642
+ try {
5643
+ const response = await this.apiService.identifyContact({
5644
+ name: contactData.name,
5645
+ email: contactData.email,
5646
+ });
5885
5647
 
5886
- if (!this.messengerState.userContext) {
5887
- this.messengerState.userContext = {};
5648
+ if (response.status) {
5649
+ console.log('[MessengerWidget] Contact identified:', response.data.contact_id);
5650
+ this.messengerState.setIdentified(true, {
5651
+ name: contactData.name,
5652
+ email: contactData.email,
5653
+ });
5888
5654
  }
5889
- this.messengerState.userContext.name = contactData.name;
5890
- this.messengerState.userContext.email = contactData.email;
5891
- this.messengerState.isIdentified = true;
5892
5655
 
5893
- const pendingMessage = this.messengerState.pendingMessage;
5894
- if (pendingMessage) {
5895
- this.messengerState.pendingMessage = null;
5896
-
5897
- // SET VIEW FIRST
5898
- this.messengerState.setView('chat');
5899
-
5900
- // THEN start conversation
5901
- await this.startNewConversation(
5902
- pendingMessage.content,
5903
- '',
5904
- pendingMessage.attachments
5905
- );
5906
- } else {
5907
- this.messengerState.setView('chat');
5908
- }
5656
+ return response;
5657
+ } catch (error) {
5658
+ console.error('[MessengerWidget] Failed to identify contact:', error);
5659
+ throw error;
5909
5660
  }
5661
+ }
5910
5662
 
5911
- return response;
5912
- } catch (error) {
5913
- console.error('[MessengerWidget] Failed to identify contact:', error);
5914
- throw error;
5663
+ markAsIdentified(name, email) {
5664
+ this.messengerState.setIdentified(true, { name, email });
5665
+ console.log('[MessengerWidget] Marked as identified:', email);
5915
5666
  }
5916
- }
5667
+
5917
5668
  async _handleUploadFile(base64Data, filename) {
5918
5669
  try {
5919
5670
  const response = await this.apiService.uploadFile(base64Data, filename);
@@ -6289,25 +6040,12 @@
6289
6040
  const uploadedAttachments =
6290
6041
  await this._uploadPendingAttachments(pendingAttachments);
6291
6042
 
6292
- console.log('[MessengerWidget] Starting conversation...', {
6293
- message,
6294
- attachmentCount: uploadedAttachments.length,
6295
- hasSession: this.apiService.isSessionValid(),
6296
- sessionToken: this.apiService.sessionToken
6297
- ? this.apiService.sessionToken.substring(0, 10) + '...'
6298
- : null,
6299
- baseURL: this.apiService.baseURL,
6300
- mock: this.apiService.mock,
6301
- });
6302
-
6303
6043
  const response = await this.apiService.startConversation({
6304
6044
  message,
6305
6045
  subject,
6306
6046
  attachments: uploadedAttachments,
6307
6047
  });
6308
6048
 
6309
- console.log('[MessengerWidget] Conversation response:', response);
6310
-
6311
6049
  if (response.status && response.data) {
6312
6050
  const conv = response.data;
6313
6051
  const newConversation = {
@@ -6474,19 +6212,19 @@
6474
6212
  };
6475
6213
  }
6476
6214
 
6477
- async onMount() {
6478
- this.loadInitialData();
6215
+ async onMount() {
6216
+ this.loadInitialData();
6479
6217
 
6480
- if (this.apiService?.sessionToken) {
6481
- this._initWebSocket();
6482
- }
6483
-
6484
- this.checkAgentAvailability();
6218
+ if (this.apiService?.sessionToken) {
6219
+ this._initWebSocket();
6220
+ }
6485
6221
 
6486
- this._availabilityInterval = setInterval(() => {
6487
6222
  this.checkAgentAvailability();
6488
- }, 60000);
6489
- }
6223
+
6224
+ this._availabilityInterval = setInterval(() => {
6225
+ this.checkAgentAvailability();
6226
+ }, 60000);
6227
+ }
6490
6228
 
6491
6229
  onDestroy() {
6492
6230
  if (this._stateUnsubscribe) {