@product7/feedback-sdk 1.3.8 → 1.4.0
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 +3008 -2568
- 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 +5 -5
- package/src/core/APIService.js +37 -14
- package/src/index.js +1 -1
- package/src/styles/base.js +1 -1
- package/src/styles/changelog.js +58 -40
- package/src/styles/components.js +19 -2
- package/src/styles/design-tokens.js +4 -4
- package/src/styles/feedback.js +3 -8
- package/src/styles/messenger-components.js +473 -0
- package/src/styles/messenger-core.js +37 -268
- package/src/styles/messenger-features.js +89 -267
- package/src/styles/messenger-views.js +391 -325
- package/src/styles/messenger.js +17 -558
- package/src/styles/styles.js +21 -24
- package/src/styles/{surveys.js → survey.js} +56 -21
- package/src/widgets/BaseWidget.js +1 -1
- package/src/widgets/ButtonWidget.js +1 -1
- package/src/widgets/ChangelogWidget.js +1 -1
- package/src/widgets/InlineWidget.js +1 -1
- package/src/widgets/MessengerWidget.js +111 -122
- package/src/widgets/SurveyWidget.js +1 -1
- package/src/widgets/TabWidget.js +1 -1
- package/src/widgets/messenger/MessengerState.js +5 -2
- package/src/widgets/messenger/components/MessengerLauncher.js +22 -18
- package/src/widgets/messenger/components/MessengerPanel.js +1 -1
- package/src/widgets/messenger/components/NavigationTabs.js +36 -15
- package/src/widgets/messenger/views/ChangelogView.js +8 -32
- package/src/widgets/messenger/views/ChatView.js +96 -60
- package/src/widgets/messenger/views/ConversationsView.js +67 -45
- package/src/widgets/messenger/views/HelpView.js +22 -32
- package/src/widgets/messenger/views/HomeView.js +58 -40
- package/src/widgets/messenger/views/PreChatFormView.js +12 -5
- package/src/styles/messenger-help.js +0 -298
- package/src/styles/messenger-themes.js +0 -500
|
@@ -8,7 +8,7 @@ export class NavigationTabs {
|
|
|
8
8
|
|
|
9
9
|
render() {
|
|
10
10
|
this.element = document.createElement('div');
|
|
11
|
-
this.element.className = 'messenger-nav';
|
|
11
|
+
this.element.className = 'messenger-panel-nav';
|
|
12
12
|
|
|
13
13
|
this._updateContent();
|
|
14
14
|
this._attachEvents();
|
|
@@ -68,7 +68,22 @@ export class NavigationTabs {
|
|
|
68
68
|
})
|
|
69
69
|
.join('');
|
|
70
70
|
|
|
71
|
-
this.element.innerHTML =
|
|
71
|
+
this.element.innerHTML = `
|
|
72
|
+
<div class="messenger-nav-tabs">
|
|
73
|
+
${tabsHtml}
|
|
74
|
+
</div>
|
|
75
|
+
<div class="messenger-nav-footer">
|
|
76
|
+
<a href="https://product7.io" target="_blank" rel="noopener noreferrer" class="messenger-powered-by">
|
|
77
|
+
<svg width="12" height="14" viewBox="0 0 28 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
78
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.0615 5.28044C8.5161 4.42949 3.30825 11.1456 5.89967 17.6588C6.9321 20.2538 9.06268 22.2644 11.8777 23.1968C16.2682 24.6507 18.4038 22.3222 19.0483 23.9691C19.4055 24.8894 18.7282 25.3209 17.988 25.4938C10.9146 27.15 5.15304 22.7566 3.5869 17.5531C1.52205 10.6941 5.98684 4.6667 11.3483 3.41065C17.8801 1.88094 24.0325 6.19355 24.3926 12.7175C24.7448 19.0921 18.6217 24.5978 11.927 22.2036C10.8789 21.8285 8.8419 20.6682 8.46823 19.858C8.06026 18.9727 8.80261 18.1725 9.68285 18.3576C10.2223 18.4726 10.3116 18.8706 11.3161 19.5372C14.4549 21.6213 19.1276 20.6132 21.2046 17.0972C23.991 12.3817 21.0481 6.05351 15.06 5.27758L15.0615 5.28044Z" fill="#21244A"/>
|
|
79
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2492 2.19833C11.944 1.71463 8.88819 3.07214 6.91479 4.49682C2.27067 7.85488 0.76169 14.5038 3.49672 19.8731C4.08535 21.0096 4.84379 22.0497 5.7459 22.9576L7.16343 24.2515C7.67214 24.9131 7.27203 25.7176 6.64115 25.9269C5.13502 26.4271 2.0499 21.8172 1.42044 20.5383C0.0872204 17.8297 -0.312889 14.9047 0.242977 11.503C1.66908 2.77063 11.221 -2.51652 19.7197 1.21021C27.7548 4.73331 30.2733 15.4555 23.9351 22.0773C23.3107 22.7296 21.6352 24.4823 20.6278 23.8907C20.0076 23.5263 19.8933 22.6446 20.5192 22.1238C21.0301 21.6986 21.4759 21.435 21.9896 20.9734C23.6665 19.4688 25.2562 16.8752 25.3477 13.5636C25.4427 10.2055 24.1266 7.5848 22.3904 5.74859C20.6392 3.89665 18.6751 2.69919 15.2456 2.19691L15.2492 2.19833Z" fill="#F69F06"/>
|
|
80
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.48332 27.2217C7.93817 26.265 8.89987 25.3776 10.1352 25.8641C15.5653 27.9926 18.3081 25.5269 19.0255 27.0823C19.2655 27.6039 19.0448 28.1619 18.7354 28.3863C17.9895 28.9257 14.82 28.9343 13.9262 28.8714C12.9071 28.8053 11.897 28.6377 10.9111 28.3713C10.0888 28.1348 8.88057 27.9247 8.48189 27.2281L8.48332 27.2217Z" fill="#21244A"/>
|
|
81
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.8722 31.0607C15.7765 32.1381 14.579 32.0331 13.5766 31.9545C12.5742 31.8759 11.5203 31.8502 11.601 30.7013C11.6789 29.5882 12.8035 29.7532 13.8274 29.8332C14.4425 29.8811 15.9951 29.681 15.8722 31.0607Z" fill="#21244A"/>
|
|
82
|
+
</svg>
|
|
83
|
+
Powered by <strong>Product7</strong>
|
|
84
|
+
</a>
|
|
85
|
+
</div>
|
|
86
|
+
`;
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
_attachEvents() {
|
|
@@ -103,27 +118,33 @@ export class NavigationTabs {
|
|
|
103
118
|
}
|
|
104
119
|
|
|
105
120
|
_getHomeIcon() {
|
|
106
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
|
107
|
-
|
|
108
|
-
|
|
121
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 256 256">
|
|
122
|
+
<path d="M40,216H216V120a8,8,0,0,0-2.34-5.66l-80-80a8,8,0,0,0-11.32,0l-80,80A8,8,0,0,0,40,120Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
123
|
+
</svg>`;
|
|
109
124
|
}
|
|
110
125
|
|
|
111
126
|
_getMessagesIcon() {
|
|
112
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 256 256">
|
|
128
|
+
<circle cx="128" cy="128" r="12"/>
|
|
129
|
+
<circle cx="84" cy="128" r="12"/>
|
|
130
|
+
<circle cx="172" cy="128" r="12"/>
|
|
131
|
+
<path d="M79.93,211.11a96,96,0,1,0-35-35h0L32.42,213.46a8,8,0,0,0,10.12,10.12l37.39-12.47Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
132
|
+
</svg>`;
|
|
115
133
|
}
|
|
116
134
|
|
|
117
135
|
_getHelpIcon() {
|
|
118
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
|
119
|
-
|
|
120
|
-
|
|
136
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 256 256">
|
|
137
|
+
<circle cx="128" cy="128" r="96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
138
|
+
<circle cx="128" cy="180" r="12"/>
|
|
139
|
+
<path d="M128,144v-8a28,28,0,1,0-28-28" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
140
|
+
</svg>`;
|
|
121
141
|
}
|
|
122
142
|
|
|
123
143
|
_getChangelogIcon() {
|
|
124
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
|
125
|
-
|
|
126
|
-
|
|
144
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 256 256">
|
|
145
|
+
<path d="M160,80V200.67a8,8,0,0,0,3.56,6.65l11,7.33a8,8,0,0,0,12.2-4.72L200,160" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
146
|
+
<path d="M40,200a8,8,0,0,0,13.15,6.12C105.55,162.16,160,160,160,160h40a40,40,0,0,0,0-80H160S105.55,77.84,53.15,33.89A8,8,0,0,0,40,40Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
147
|
+
</svg>`;
|
|
127
148
|
}
|
|
128
149
|
|
|
129
150
|
destroy() {
|
|
@@ -134,4 +155,4 @@ export class NavigationTabs {
|
|
|
134
155
|
this.element.parentNode.removeChild(this.element);
|
|
135
156
|
}
|
|
136
157
|
}
|
|
137
|
-
}
|
|
158
|
+
}
|
|
@@ -22,30 +22,15 @@ export class ChangelogView {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
_updateContent() {
|
|
25
|
-
const avatarsHtml = this._renderTeamAvatars();
|
|
26
|
-
|
|
27
25
|
this.element.innerHTML = `
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 256 256">
|
|
32
|
-
<path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path>
|
|
33
|
-
</svg>
|
|
34
|
-
</button>
|
|
35
|
-
</div>
|
|
26
|
+
<div class="messenger-changelog-header">
|
|
27
|
+
<h2>Latest Updates</h2>
|
|
28
|
+
</div>
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
${avatarsHtml}
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
|
|
45
|
-
<div class="messenger-changelog-body">
|
|
46
|
-
<div class="messenger-changelog-list"></div>
|
|
47
|
-
</div>
|
|
48
|
-
`;
|
|
30
|
+
<div class="messenger-changelog-body">
|
|
31
|
+
<div class="messenger-changelog-list"></div>
|
|
32
|
+
</div>
|
|
33
|
+
`;
|
|
49
34
|
|
|
50
35
|
this._updateChangelogList();
|
|
51
36
|
this._attachEvents();
|
|
@@ -96,9 +81,6 @@ export class ChangelogView {
|
|
|
96
81
|
${item.description ? `<p class="messenger-changelog-description">${this._truncateText(item.description, 100)}</p>` : ''}
|
|
97
82
|
<div class="messenger-changelog-meta">
|
|
98
83
|
<span class="messenger-changelog-date">${dateStr}</span>
|
|
99
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 256 256" class="messenger-changelog-arrow">
|
|
100
|
-
<path d="M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z"></path>
|
|
101
|
-
</svg>
|
|
102
84
|
</div>
|
|
103
85
|
</div>
|
|
104
86
|
</div>
|
|
@@ -159,12 +141,6 @@ export class ChangelogView {
|
|
|
159
141
|
}
|
|
160
142
|
|
|
161
143
|
_attachEvents() {
|
|
162
|
-
this.element
|
|
163
|
-
.querySelector('.sdk-close-btn')
|
|
164
|
-
.addEventListener('click', () => {
|
|
165
|
-
this.state.setOpen(false);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
144
|
this._attachChangelogEvents();
|
|
169
145
|
}
|
|
170
146
|
|
|
@@ -194,4 +170,4 @@ export class ChangelogView {
|
|
|
194
170
|
this.element.parentNode.removeChild(this.element);
|
|
195
171
|
}
|
|
196
172
|
}
|
|
197
|
-
}
|
|
173
|
+
}
|
|
@@ -40,7 +40,10 @@ export class ChatView {
|
|
|
40
40
|
data.conversationId === this.state.activeConversationId
|
|
41
41
|
) {
|
|
42
42
|
this._updateContent();
|
|
43
|
-
} else if (
|
|
43
|
+
} else if (
|
|
44
|
+
type === 'messagesUpdate' &&
|
|
45
|
+
data.conversationId === this.state.activeConversationId
|
|
46
|
+
) {
|
|
44
47
|
this._updateContent();
|
|
45
48
|
}
|
|
46
49
|
});
|
|
@@ -60,7 +63,6 @@ export class ChatView {
|
|
|
60
63
|
? this._renderEmptyState(isNewConversation)
|
|
61
64
|
: messages.map((msg) => this._renderMessage(msg)).join('');
|
|
62
65
|
|
|
63
|
-
const avatarHtml = this._renderConversationAvatar(conversation);
|
|
64
66
|
const title = isNewConversation
|
|
65
67
|
? 'New conversation'
|
|
66
68
|
: conversation?.title || 'Chat with team';
|
|
@@ -78,7 +80,6 @@ export class ChatView {
|
|
|
78
80
|
</svg>
|
|
79
81
|
</button>
|
|
80
82
|
<div class="messenger-chat-header-info">
|
|
81
|
-
${avatarHtml}
|
|
82
83
|
<span class="messenger-chat-title">${title}</span>
|
|
83
84
|
</div>
|
|
84
85
|
<button class="sdk-close-btn" aria-label="Close">
|
|
@@ -90,12 +91,16 @@ export class ChatView {
|
|
|
90
91
|
|
|
91
92
|
<div class="messenger-chat-messages">
|
|
92
93
|
${messagesHtml}
|
|
93
|
-
${
|
|
94
|
+
${
|
|
95
|
+
isClosed
|
|
96
|
+
? `
|
|
94
97
|
<div class="messenger-closed-banner">
|
|
95
98
|
<i class="ph ph-check-circle"></i>
|
|
96
99
|
<span>This conversation has been resolved</span>
|
|
97
100
|
</div>
|
|
98
|
-
`
|
|
101
|
+
`
|
|
102
|
+
: ''
|
|
103
|
+
}
|
|
99
104
|
<div class="messenger-typing-indicator">
|
|
100
105
|
<div class="messenger-typing-dots">
|
|
101
106
|
<span></span><span></span><span></span>
|
|
@@ -104,7 +109,10 @@ export class ChatView {
|
|
|
104
109
|
</div>
|
|
105
110
|
</div>
|
|
106
111
|
|
|
107
|
-
${
|
|
112
|
+
${
|
|
113
|
+
isClosed
|
|
114
|
+
? ''
|
|
115
|
+
: `
|
|
108
116
|
<div class="messenger-compose-attachments-preview"></div>
|
|
109
117
|
|
|
110
118
|
<div class="messenger-chat-compose">
|
|
@@ -121,10 +129,13 @@ export class ChatView {
|
|
|
121
129
|
</button>
|
|
122
130
|
<input type="file" class="messenger-compose-file-input" multiple accept="image/*,.pdf,.doc,.docx,.xls,.xlsx,.txt,.zip" />
|
|
123
131
|
</div>
|
|
124
|
-
`
|
|
132
|
+
`
|
|
133
|
+
}
|
|
125
134
|
`;
|
|
126
135
|
|
|
127
|
-
this._typingIndicator = this.element.querySelector(
|
|
136
|
+
this._typingIndicator = this.element.querySelector(
|
|
137
|
+
'.messenger-typing-indicator'
|
|
138
|
+
);
|
|
128
139
|
this._attachEvents();
|
|
129
140
|
this._scrollToBottom();
|
|
130
141
|
this._renderAttachmentPreviews();
|
|
@@ -132,7 +143,8 @@ export class ChatView {
|
|
|
132
143
|
|
|
133
144
|
_renderEmptyState(isNewConversation = false) {
|
|
134
145
|
const avatarHtml = this._renderTeamAvatars();
|
|
135
|
-
const responseTime =
|
|
146
|
+
const responseTime =
|
|
147
|
+
this.state.responseTime || 'We typically reply within a few minutes';
|
|
136
148
|
const isOnline = this.state.agentsOnline;
|
|
137
149
|
|
|
138
150
|
return `
|
|
@@ -150,26 +162,34 @@ export class ChatView {
|
|
|
150
162
|
|
|
151
163
|
_renderMessageAttachments(attachments) {
|
|
152
164
|
if (!attachments || attachments.length === 0) return '';
|
|
153
|
-
return attachments
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
return attachments
|
|
166
|
+
.map((att) => {
|
|
167
|
+
if (att.type === 'image') {
|
|
168
|
+
return `<img class="messenger-message-image" src="${this._escapeHtml(att.url)}" alt="${this._escapeHtml(att.name || 'image')}" data-url="${this._escapeHtml(att.url)}" />`;
|
|
169
|
+
}
|
|
170
|
+
return `<a class="messenger-message-file" href="${this._escapeHtml(att.url)}" data-url="${this._escapeHtml(att.url)}" data-name="${this._escapeHtml(att.name || 'file')}">
|
|
158
171
|
<i class="ph ph-file"></i>
|
|
159
172
|
<span>${this._escapeHtml(att.name || 'file')}</span>
|
|
160
173
|
<i class="ph ph-download-simple messenger-file-download-icon"></i>
|
|
161
174
|
</a>`;
|
|
162
|
-
|
|
175
|
+
})
|
|
176
|
+
.join('');
|
|
163
177
|
}
|
|
164
178
|
|
|
165
179
|
_renderMessage(message) {
|
|
166
180
|
const isOwn = message.isOwn;
|
|
167
|
-
const messageClass = isOwn
|
|
181
|
+
const messageClass = isOwn
|
|
182
|
+
? 'messenger-message-own'
|
|
183
|
+
: 'messenger-message-received';
|
|
168
184
|
const timeStr = this._formatMessageTime(message.timestamp);
|
|
169
185
|
const attachmentsHtml = this._renderMessageAttachments(message.attachments);
|
|
170
186
|
|
|
171
|
-
const contentHtml = message.content
|
|
172
|
-
|
|
187
|
+
const contentHtml = message.content
|
|
188
|
+
? `<div class="messenger-message-content">${this._formatMessageContent(message.content)}</div>`
|
|
189
|
+
: '';
|
|
190
|
+
const bubbleHtml = contentHtml
|
|
191
|
+
? `<div class="messenger-message-bubble">${contentHtml}</div>`
|
|
192
|
+
: '';
|
|
173
193
|
|
|
174
194
|
if (isOwn) {
|
|
175
195
|
return `
|
|
@@ -184,13 +204,15 @@ export class ChatView {
|
|
|
184
204
|
const avatarHtml = this._renderSenderAvatar(message.sender);
|
|
185
205
|
return `
|
|
186
206
|
<div class="messenger-message ${messageClass}">
|
|
187
|
-
<div class="messenger-message-
|
|
188
|
-
<div class="messenger-message-
|
|
189
|
-
<div class="messenger-message-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
207
|
+
<div class="messenger-message-sender">${message.sender?.name || 'Support'}</div>
|
|
208
|
+
<div class="messenger-message-row">
|
|
209
|
+
<div class="messenger-message-avatar">${avatarHtml}</div>
|
|
210
|
+
<div class="messenger-message-wrapper">
|
|
211
|
+
${bubbleHtml}
|
|
212
|
+
${attachmentsHtml}
|
|
213
|
+
</div>
|
|
193
214
|
</div>
|
|
215
|
+
<div class="messenger-message-time">${timeStr}</div>
|
|
194
216
|
</div>
|
|
195
217
|
`;
|
|
196
218
|
}
|
|
@@ -203,17 +225,6 @@ export class ChatView {
|
|
|
203
225
|
return `<div class="sdk-avatar sdk-avatar-sm">${initial}</div>`;
|
|
204
226
|
}
|
|
205
227
|
|
|
206
|
-
_renderConversationAvatar(conversation) {
|
|
207
|
-
if (!conversation?.participants?.length) {
|
|
208
|
-
return `<div class="sdk-avatar sdk-avatar-sm">S</div>`;
|
|
209
|
-
}
|
|
210
|
-
const p = conversation.participants[0];
|
|
211
|
-
if (p.avatarUrl) {
|
|
212
|
-
return `<div class="sdk-avatar sdk-avatar-sm"><img src="${p.avatarUrl}" alt="${p.name}" /></div>`;
|
|
213
|
-
}
|
|
214
|
-
return `<div class="sdk-avatar sdk-avatar-sm">${(p.name || 'S').charAt(0).toUpperCase()}</div>`;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
228
|
_renderTeamAvatars() {
|
|
218
229
|
const avatars = this.state.teamAvatars;
|
|
219
230
|
if (!avatars || avatars.length === 0) {
|
|
@@ -258,7 +269,9 @@ export class ChatView {
|
|
|
258
269
|
}
|
|
259
270
|
|
|
260
271
|
_appendMessage(message) {
|
|
261
|
-
const messagesContainer = this.element.querySelector(
|
|
272
|
+
const messagesContainer = this.element.querySelector(
|
|
273
|
+
'.messenger-chat-messages'
|
|
274
|
+
);
|
|
262
275
|
const emptyState = messagesContainer.querySelector('.messenger-chat-empty');
|
|
263
276
|
if (emptyState) {
|
|
264
277
|
emptyState.remove();
|
|
@@ -271,7 +284,9 @@ export class ChatView {
|
|
|
271
284
|
}
|
|
272
285
|
|
|
273
286
|
_scrollToBottom() {
|
|
274
|
-
const messagesContainer = this.element.querySelector(
|
|
287
|
+
const messagesContainer = this.element.querySelector(
|
|
288
|
+
'.messenger-chat-messages'
|
|
289
|
+
);
|
|
275
290
|
if (messagesContainer) {
|
|
276
291
|
setTimeout(() => {
|
|
277
292
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
@@ -283,12 +298,15 @@ export class ChatView {
|
|
|
283
298
|
const input = this.element.querySelector('.messenger-compose-input');
|
|
284
299
|
const sendBtn = this.element.querySelector('.messenger-compose-send');
|
|
285
300
|
if (input && sendBtn) {
|
|
286
|
-
sendBtn.disabled =
|
|
301
|
+
sendBtn.disabled =
|
|
302
|
+
!input.value.trim() && this._pendingAttachments.length === 0;
|
|
287
303
|
}
|
|
288
304
|
}
|
|
289
305
|
|
|
290
306
|
_renderAttachmentPreviews() {
|
|
291
|
-
const container = this.element.querySelector(
|
|
307
|
+
const container = this.element.querySelector(
|
|
308
|
+
'.messenger-compose-attachments-preview'
|
|
309
|
+
);
|
|
292
310
|
if (!container) return;
|
|
293
311
|
|
|
294
312
|
if (this._pendingAttachments.length === 0) {
|
|
@@ -298,27 +316,31 @@ export class ChatView {
|
|
|
298
316
|
}
|
|
299
317
|
|
|
300
318
|
container.style.display = 'flex';
|
|
301
|
-
container.innerHTML = this._pendingAttachments
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
319
|
+
container.innerHTML = this._pendingAttachments
|
|
320
|
+
.map((att, i) => {
|
|
321
|
+
const isImage = att.type.startsWith('image');
|
|
322
|
+
const thumb = isImage
|
|
323
|
+
? `<img class="messenger-attachment-thumb" src="${att.preview}" alt="${this._escapeHtml(att.file.name)}" />`
|
|
324
|
+
: `<div class="messenger-attachment-thumb messenger-attachment-file-icon"><i class="ph ph-file"></i></div>`;
|
|
325
|
+
return `
|
|
307
326
|
<div class="messenger-attachment-preview" data-index="${i}">
|
|
308
327
|
${thumb}
|
|
309
328
|
<button class="messenger-attachment-remove" data-index="${i}" aria-label="Remove">×</button>
|
|
310
329
|
</div>
|
|
311
330
|
`;
|
|
312
|
-
|
|
331
|
+
})
|
|
332
|
+
.join('');
|
|
313
333
|
|
|
314
|
-
container
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
334
|
+
container
|
|
335
|
+
.querySelectorAll('.messenger-attachment-remove')
|
|
336
|
+
.forEach((btn) => {
|
|
337
|
+
btn.addEventListener('click', (e) => {
|
|
338
|
+
const idx = parseInt(e.currentTarget.dataset.index, 10);
|
|
339
|
+
this._pendingAttachments.splice(idx, 1);
|
|
340
|
+
this._renderAttachmentPreviews();
|
|
341
|
+
this._updateSendButtonState();
|
|
342
|
+
});
|
|
320
343
|
});
|
|
321
|
-
});
|
|
322
344
|
}
|
|
323
345
|
|
|
324
346
|
_attachEvents() {
|
|
@@ -361,7 +383,9 @@ export class ChatView {
|
|
|
361
383
|
}
|
|
362
384
|
|
|
363
385
|
const attachBtn = this.element.querySelector('.messenger-compose-attach');
|
|
364
|
-
const fileInput = this.element.querySelector(
|
|
386
|
+
const fileInput = this.element.querySelector(
|
|
387
|
+
'.messenger-compose-file-input'
|
|
388
|
+
);
|
|
365
389
|
|
|
366
390
|
if (attachBtn && fileInput) {
|
|
367
391
|
attachBtn.addEventListener('click', () => {
|
|
@@ -388,7 +412,9 @@ export class ChatView {
|
|
|
388
412
|
});
|
|
389
413
|
}
|
|
390
414
|
|
|
391
|
-
const messagesContainer = this.element.querySelector(
|
|
415
|
+
const messagesContainer = this.element.querySelector(
|
|
416
|
+
'.messenger-chat-messages'
|
|
417
|
+
);
|
|
392
418
|
if (messagesContainer) {
|
|
393
419
|
messagesContainer.addEventListener('click', (e) => {
|
|
394
420
|
const fileLink = e.target.closest('.messenger-message-file');
|
|
@@ -429,12 +455,16 @@ export class ChatView {
|
|
|
429
455
|
|
|
430
456
|
_escapeHtml(text) {
|
|
431
457
|
if (!text) return '';
|
|
432
|
-
return text
|
|
458
|
+
return text
|
|
459
|
+
.replace(/&/g, '&')
|
|
460
|
+
.replace(/</g, '<')
|
|
461
|
+
.replace(/>/g, '>')
|
|
462
|
+
.replace(/"/g, '"');
|
|
433
463
|
}
|
|
434
464
|
|
|
435
465
|
async _sendMessage() {
|
|
436
466
|
if (this._isConversationClosed) return;
|
|
437
|
-
|
|
467
|
+
|
|
438
468
|
const input = this.element.querySelector('.messenger-compose-input');
|
|
439
469
|
const content = input.value.trim();
|
|
440
470
|
const hasAttachments = this._pendingAttachments.length > 0;
|
|
@@ -466,7 +496,11 @@ export class ChatView {
|
|
|
466
496
|
this.state.addMessage(this.state.activeConversationId, message);
|
|
467
497
|
|
|
468
498
|
if (this.options.onSendMessage) {
|
|
469
|
-
this.options.onSendMessage(
|
|
499
|
+
this.options.onSendMessage(
|
|
500
|
+
this.state.activeConversationId,
|
|
501
|
+
message,
|
|
502
|
+
attachmentsToSend
|
|
503
|
+
);
|
|
470
504
|
}
|
|
471
505
|
}
|
|
472
506
|
|
|
@@ -510,7 +544,9 @@ export class ChatView {
|
|
|
510
544
|
_showTypingIndicator(userName) {
|
|
511
545
|
if (this._typingIndicator) {
|
|
512
546
|
this._typingIndicator.style.display = 'flex';
|
|
513
|
-
const textEl = this._typingIndicator.querySelector(
|
|
547
|
+
const textEl = this._typingIndicator.querySelector(
|
|
548
|
+
'.messenger-typing-text'
|
|
549
|
+
);
|
|
514
550
|
if (textEl) {
|
|
515
551
|
textEl.textContent = `${userName || 'Support'} is typing...`;
|
|
516
552
|
}
|
|
@@ -536,4 +572,4 @@ export class ChatView {
|
|
|
536
572
|
this.element.parentNode.removeChild(this.element);
|
|
537
573
|
}
|
|
538
574
|
}
|
|
539
|
-
}
|
|
575
|
+
}
|
|
@@ -4,6 +4,15 @@ export class ConversationsView {
|
|
|
4
4
|
this.options = options;
|
|
5
5
|
this.element = null;
|
|
6
6
|
this._unsubscribe = null;
|
|
7
|
+
this.avatarColors = [
|
|
8
|
+
'#155EEF',
|
|
9
|
+
'#8b5cf6',
|
|
10
|
+
'#10b981',
|
|
11
|
+
'#f59e0b',
|
|
12
|
+
'#ef4444',
|
|
13
|
+
'#ec4899',
|
|
14
|
+
'#06b6d4',
|
|
15
|
+
];
|
|
7
16
|
}
|
|
8
17
|
|
|
9
18
|
render() {
|
|
@@ -27,55 +36,61 @@ export class ConversationsView {
|
|
|
27
36
|
return this.element;
|
|
28
37
|
}
|
|
29
38
|
|
|
39
|
+
_getAvatarColor(name) {
|
|
40
|
+
// Generate consistent color based on name
|
|
41
|
+
const charCode = (name || 'S').charCodeAt(0);
|
|
42
|
+
return this.avatarColors[charCode % this.avatarColors.length];
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
_updateContent() {
|
|
31
46
|
const conversations = this.state.conversations;
|
|
32
|
-
const avatarsHtml = this._renderAvatarStack();
|
|
33
47
|
|
|
34
48
|
let conversationsHtml;
|
|
35
49
|
if (conversations.length === 0) {
|
|
36
50
|
conversationsHtml = `
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
<div class="messenger-empty-state">
|
|
52
|
+
<div class="messenger-empty-state-icon">
|
|
53
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="currentColor" viewBox="0 0 256 256">
|
|
54
|
+
<path d="M216,48H40A16,16,0,0,0,24,64V224a15.85,15.85,0,0,0,9.24,14.5A16.13,16.13,0,0,0,40,240a15.89,15.89,0,0,0,10.25-3.78.69.69,0,0,0,.13-.11L82.5,208H216a16,16,0,0,0,16-16V64A16,16,0,0,0,216,48ZM40,224h0ZM216,192H82.5a16,16,0,0,0-10.3,3.75l-.12.11L40,224V64H216Z"></path>
|
|
55
|
+
</svg>
|
|
56
|
+
</div>
|
|
57
|
+
<h3>No conversations yet</h3>
|
|
58
|
+
<p>Start a new conversation with our team</p>
|
|
59
|
+
</div>
|
|
60
|
+
`;
|
|
47
61
|
} else {
|
|
48
62
|
conversationsHtml = `
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
<div class="messenger-conversations-list">
|
|
64
|
+
${conversations.map((conv) => this._renderConversationItem(conv)).join('')}
|
|
65
|
+
</div>
|
|
66
|
+
`;
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
this.element.innerHTML = `
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
<div class="messenger-conversations-header">
|
|
71
|
+
<h2>Messages</h2>
|
|
72
|
+
<button class="sdk-close-btn" aria-label="Close">
|
|
73
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="#000000" viewBox="0 0 256 256">
|
|
74
|
+
<path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path>
|
|
75
|
+
</svg>
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="messenger-conversations-body">
|
|
80
|
+
${conversationsHtml}
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<div class="messenger-conversations-footer">
|
|
84
|
+
<button class="messenger-new-message-btn">
|
|
85
|
+
<span>Send us a message</span>
|
|
86
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 256 256" style="flex-shrink: 0;">
|
|
87
|
+
<rect width="256" height="256" fill="none"/>
|
|
88
|
+
<line x1="144" y1="128" x2="80" y2="128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
89
|
+
<path d="M48.49,221.28A8,8,0,0,0,59.93,231l168-96.09a8,8,0,0,0,0-14l-168-95.85a8,8,0,0,0-11.44,9.67L80,128Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
90
|
+
</svg>
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
`;
|
|
79
94
|
|
|
80
95
|
this._attachEvents();
|
|
81
96
|
}
|
|
@@ -108,34 +123,41 @@ export class ConversationsView {
|
|
|
108
123
|
|
|
109
124
|
_renderConversationAvatars(participants) {
|
|
110
125
|
if (!participants || participants.length === 0) {
|
|
111
|
-
|
|
126
|
+
const color = this._getAvatarColor('S');
|
|
127
|
+
return `<div class="sdk-avatar sdk-avatar-md" style="background-color: ${color};">S</div>`;
|
|
112
128
|
}
|
|
113
129
|
|
|
114
130
|
const p = participants[0];
|
|
115
131
|
if (p.avatarUrl) {
|
|
116
132
|
return `<div class="sdk-avatar sdk-avatar-md"><img src="${p.avatarUrl}" alt="${p.name}" /></div>`;
|
|
117
133
|
}
|
|
118
|
-
|
|
134
|
+
const initial = (p.name || 'S').charAt(0).toUpperCase();
|
|
135
|
+
const color = this._getAvatarColor(p.name);
|
|
136
|
+
return `<div class="sdk-avatar sdk-avatar-md" style="background-color: ${color};">${initial}</div>`;
|
|
119
137
|
}
|
|
120
138
|
|
|
121
139
|
_renderAvatarStack() {
|
|
122
140
|
const avatars = this.state.teamAvatars;
|
|
123
141
|
if (!avatars || avatars.length === 0) {
|
|
142
|
+
const color1 = this._getAvatarColor('S');
|
|
143
|
+
const color2 = this._getAvatarColor('T');
|
|
124
144
|
return `
|
|
125
145
|
<div class="messenger-avatar-stack">
|
|
126
|
-
<div class="sdk-avatar sdk-avatar-sm">S</div>
|
|
127
|
-
<div class="sdk-avatar sdk-avatar-sm">T</div>
|
|
146
|
+
<div class="sdk-avatar sdk-avatar-sm" style="background-color: ${color1};">S</div>
|
|
147
|
+
<div class="sdk-avatar sdk-avatar-sm" style="background-color: ${color2};">T</div>
|
|
128
148
|
</div>
|
|
129
149
|
`;
|
|
130
150
|
}
|
|
131
151
|
|
|
132
152
|
const avatarItems = avatars
|
|
133
153
|
.slice(0, 2)
|
|
134
|
-
.map((avatar) => {
|
|
154
|
+
.map((avatar, index) => {
|
|
135
155
|
if (typeof avatar === 'string' && avatar.startsWith('http')) {
|
|
136
156
|
return `<div class="sdk-avatar sdk-avatar-sm"><img src="${avatar}" alt="Team member" /></div>`;
|
|
137
157
|
}
|
|
138
|
-
|
|
158
|
+
const initial = avatar.charAt(0).toUpperCase();
|
|
159
|
+
const color = this._getAvatarColor(avatar);
|
|
160
|
+
return `<div class="sdk-avatar sdk-avatar-sm" style="background-color: ${color};">${initial}</div>`;
|
|
139
161
|
})
|
|
140
162
|
.join('');
|
|
141
163
|
|
|
@@ -227,4 +249,4 @@ export class ConversationsView {
|
|
|
227
249
|
this.element.parentNode.removeChild(this.element);
|
|
228
250
|
}
|
|
229
251
|
}
|
|
230
|
-
}
|
|
252
|
+
}
|