@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/dist/feedback-sdk.js
CHANGED
|
@@ -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({
|
|
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
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
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
|
-
|
|
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.
|
|
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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
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
|
-
|
|
4387
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
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
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
);
|
|
5593
|
+
const openConversation = this.messengerState.conversations.find(
|
|
5594
|
+
(c) => c.status === 'open'
|
|
5595
|
+
);
|
|
5831
5596
|
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
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 (
|
|
5887
|
-
|
|
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
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
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
|
-
|
|
5912
|
-
|
|
5913
|
-
console.
|
|
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
|
-
|
|
6478
|
-
|
|
6215
|
+
async onMount() {
|
|
6216
|
+
this.loadInitialData();
|
|
6479
6217
|
|
|
6480
|
-
|
|
6481
|
-
|
|
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
|
-
|
|
6489
|
-
|
|
6223
|
+
|
|
6224
|
+
this._availabilityInterval = setInterval(() => {
|
|
6225
|
+
this.checkAgentAvailability();
|
|
6226
|
+
}, 60000);
|
|
6227
|
+
}
|
|
6490
6228
|
|
|
6491
6229
|
onDestroy() {
|
|
6492
6230
|
if (this._stateUnsubscribe) {
|