@product7/feedback-sdk 1.2.8 → 1.3.1
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 +3646 -2406
- 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 +2 -1
- package/src/api/mock-data/index.js +202 -0
- package/src/api/services/ChangelogService.js +28 -0
- package/src/api/services/FeedbackService.js +44 -0
- package/src/api/services/HelpService.js +50 -0
- package/src/api/services/MessengerService.js +197 -0
- package/src/api/services/SurveyService.js +99 -0
- package/src/api/utils/helpers.js +30 -0
- package/src/core/APIService.js +86 -1002
- package/src/core/BaseAPIService.js +245 -0
- package/src/core/FeedbackSDK.js +3 -68
- package/src/styles/base.js +75 -0
- package/src/styles/changelog.js +698 -0
- package/src/styles/feedback.js +574 -0
- package/src/styles/messengerStyles.js +580 -9
- package/src/styles/styles.js +5 -1379
- package/src/widgets/MessengerWidget.js +204 -21
- package/src/widgets/messenger/MessengerState.js +31 -1
- package/src/widgets/messenger/components/MessengerLauncher.js +7 -7
- package/src/widgets/messenger/components/NavigationTabs.js +16 -4
- package/src/widgets/messenger/views/ChangelogView.js +9 -9
- package/src/widgets/messenger/views/ChatView.js +356 -49
- package/src/widgets/messenger/views/ConversationsView.js +29 -18
- package/src/widgets/messenger/views/HelpView.js +15 -14
- package/src/widgets/messenger/views/HomeView.js +53 -22
- package/src/widgets/messenger/views/PreChatFormView.js +224 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MessengerWidget - Full-featured Messenger/Chat widget
|
|
3
3
|
*/
|
|
4
|
-
import { WebSocketService } from '../core/WebSocketService.js';
|
|
5
4
|
import { BaseWidget } from './BaseWidget.js';
|
|
6
5
|
import { MessengerState } from './messenger/MessengerState.js';
|
|
7
6
|
import { MessengerLauncher } from './messenger/components/MessengerLauncher.js';
|
|
@@ -11,6 +10,8 @@ import { ChatView } from './messenger/views/ChatView.js';
|
|
|
11
10
|
import { ConversationsView } from './messenger/views/ConversationsView.js';
|
|
12
11
|
import { HelpView } from './messenger/views/HelpView.js';
|
|
13
12
|
import { HomeView } from './messenger/views/HomeView.js';
|
|
13
|
+
import { PreChatFormView } from './messenger/views/PreChatFormView.js';
|
|
14
|
+
import { WebSocketService } from '../core/WebSocketService.js';
|
|
14
15
|
|
|
15
16
|
export class MessengerWidget extends BaseWidget {
|
|
16
17
|
constructor(options) {
|
|
@@ -24,7 +25,7 @@ export class MessengerWidget extends BaseWidget {
|
|
|
24
25
|
welcomeMessage: options.welcomeMessage || 'How can we help?',
|
|
25
26
|
enableHelp: options.enableHelp !== false,
|
|
26
27
|
enableChangelog: options.enableChangelog !== false,
|
|
27
|
-
logoUrl: options.logoUrl || 'https://
|
|
28
|
+
logoUrl: options.logoUrl || 'https://product7.io/p7logo.svg',
|
|
28
29
|
featuredContent: options.featuredContent || null,
|
|
29
30
|
primaryColor: options.primaryColor || '#1c1c1e',
|
|
30
31
|
// Callbacks
|
|
@@ -53,6 +54,7 @@ export class MessengerWidget extends BaseWidget {
|
|
|
53
54
|
this._handleWebSocketMessage = this._handleWebSocketMessage.bind(this);
|
|
54
55
|
this._handleTypingStarted = this._handleTypingStarted.bind(this);
|
|
55
56
|
this._handleTypingStopped = this._handleTypingStopped.bind(this);
|
|
57
|
+
this._handleConversationClosed = this._handleConversationClosed.bind(this);
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
_render() {
|
|
@@ -84,6 +86,8 @@ export class MessengerWidget extends BaseWidget {
|
|
|
84
86
|
// Conversation list callbacks
|
|
85
87
|
onSelectConversation: this._handleSelectConversation.bind(this),
|
|
86
88
|
onStartNewConversation: this._handleNewConversationClick.bind(this),
|
|
89
|
+
// Pre-chat form callbacks
|
|
90
|
+
onIdentifyContact: this._handleIdentifyContact.bind(this),
|
|
87
91
|
// Article/changelog callbacks
|
|
88
92
|
onArticleClick: this.messengerOptions.onArticleClick,
|
|
89
93
|
onChangelogClick: this.messengerOptions.onChangelogClick,
|
|
@@ -93,6 +97,7 @@ export class MessengerWidget extends BaseWidget {
|
|
|
93
97
|
this.panel.registerView('home', HomeView);
|
|
94
98
|
this.panel.registerView('messages', ConversationsView);
|
|
95
99
|
this.panel.registerView('chat', ChatView);
|
|
100
|
+
this.panel.registerView('prechat', PreChatFormView);
|
|
96
101
|
this.panel.registerView('help', HelpView);
|
|
97
102
|
this.panel.registerView('changelog', ChangelogView);
|
|
98
103
|
|
|
@@ -108,6 +113,9 @@ export class MessengerWidget extends BaseWidget {
|
|
|
108
113
|
if (type === 'openChange') {
|
|
109
114
|
this._handleOpenChange(data.isOpen);
|
|
110
115
|
}
|
|
116
|
+
if (type === 'conversationChange') {
|
|
117
|
+
this._handleActiveConversationChange(data.conversationId, data.previousConversationId);
|
|
118
|
+
}
|
|
111
119
|
});
|
|
112
120
|
}
|
|
113
121
|
|
|
@@ -122,14 +130,44 @@ export class MessengerWidget extends BaseWidget {
|
|
|
122
130
|
}
|
|
123
131
|
}
|
|
124
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Subscribe/unsubscribe to conversation WebSocket channel
|
|
135
|
+
*/
|
|
136
|
+
_handleActiveConversationChange(conversationId, previousConversationId) {
|
|
137
|
+
if (previousConversationId && this.wsService) {
|
|
138
|
+
this.wsService.send('conversation:unsubscribe', { conversation_id: previousConversationId });
|
|
139
|
+
}
|
|
140
|
+
if (conversationId && this.wsService) {
|
|
141
|
+
this.wsService.send('conversation:subscribe', { conversation_id: conversationId });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
125
145
|
/**
|
|
126
146
|
* Handle starting a new conversation
|
|
147
|
+
* If there's an existing open conversation, send the message there instead
|
|
127
148
|
*/
|
|
128
|
-
async _handleStartConversation(messageContent) {
|
|
149
|
+
async _handleStartConversation(messageContent, pendingAttachments) {
|
|
129
150
|
try {
|
|
130
|
-
|
|
151
|
+
// Check for existing open conversation first
|
|
152
|
+
const openConversation = this.messengerState.conversations.find(
|
|
153
|
+
(c) => c.status === 'open'
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (openConversation) {
|
|
157
|
+
// Route message to existing open conversation
|
|
158
|
+
this.messengerState.setActiveConversation(openConversation.id);
|
|
159
|
+
await this._handleSendMessage(
|
|
160
|
+
openConversation.id,
|
|
161
|
+
{ content: messageContent },
|
|
162
|
+
pendingAttachments
|
|
163
|
+
);
|
|
164
|
+
return openConversation;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return await this.startNewConversation(messageContent, '', pendingAttachments);
|
|
131
168
|
} catch (error) {
|
|
132
169
|
console.error('[MessengerWidget] Failed to start conversation:', error);
|
|
170
|
+
return null;
|
|
133
171
|
}
|
|
134
172
|
}
|
|
135
173
|
|
|
@@ -146,13 +184,88 @@ export class MessengerWidget extends BaseWidget {
|
|
|
146
184
|
|
|
147
185
|
/**
|
|
148
186
|
* Handle clicking "new conversation" button
|
|
187
|
+
* Reuses the most recent open conversation if one exists
|
|
149
188
|
*/
|
|
150
189
|
_handleNewConversationClick() {
|
|
151
|
-
//
|
|
152
|
-
|
|
190
|
+
// Check for an existing open conversation to reuse
|
|
191
|
+
const openConversation = this.messengerState.conversations.find(
|
|
192
|
+
(c) => c.status === 'open'
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (openConversation) {
|
|
196
|
+
// Reuse existing open conversation
|
|
197
|
+
this.messengerState.setActiveConversation(openConversation.id);
|
|
198
|
+
this.messengerState.setView('chat');
|
|
199
|
+
this._handleSelectConversation(openConversation.id);
|
|
200
|
+
} else {
|
|
201
|
+
// No open conversation — start a new one
|
|
202
|
+
this.messengerState.setActiveConversation(null);
|
|
203
|
+
this.messengerState.setView('chat');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Handle identifying contact from pre-chat form
|
|
209
|
+
*/
|
|
210
|
+
async _handleIdentifyContact(contactData) {
|
|
211
|
+
try {
|
|
212
|
+
// Call API to identify/update contact
|
|
213
|
+
const response = await this.apiService.identifyContact({
|
|
214
|
+
name: contactData.name,
|
|
215
|
+
email: contactData.email,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (response.status) {
|
|
219
|
+
console.log('[MessengerWidget] Contact identified:', contactData.email);
|
|
220
|
+
|
|
221
|
+
// Update local user context
|
|
222
|
+
if (!this.messengerState.userContext) {
|
|
223
|
+
this.messengerState.userContext = {};
|
|
224
|
+
}
|
|
225
|
+
this.messengerState.userContext.name = contactData.name;
|
|
226
|
+
this.messengerState.userContext.email = contactData.email;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return response;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error('[MessengerWidget] Failed to identify contact:', error);
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async _handleUploadFile(base64Data, filename) {
|
|
237
|
+
try {
|
|
238
|
+
const response = await this.apiService.uploadFile(base64Data, filename);
|
|
239
|
+
if (response.status && response.url) {
|
|
240
|
+
return response.url;
|
|
241
|
+
}
|
|
242
|
+
throw new Error('Upload failed');
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error('[MessengerWidget] Failed to upload file:', error);
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
153
247
|
}
|
|
154
248
|
|
|
155
|
-
async
|
|
249
|
+
async _uploadPendingAttachments(pendingAttachments) {
|
|
250
|
+
if (!pendingAttachments || pendingAttachments.length === 0) return [];
|
|
251
|
+
|
|
252
|
+
const uploaded = [];
|
|
253
|
+
for (const att of pendingAttachments) {
|
|
254
|
+
try {
|
|
255
|
+
const cdnUrl = await this._handleUploadFile(att.preview, att.file.name);
|
|
256
|
+
uploaded.push({
|
|
257
|
+
url: cdnUrl,
|
|
258
|
+
type: att.type.startsWith('image') ? 'image' : 'file',
|
|
259
|
+
name: att.file.name,
|
|
260
|
+
});
|
|
261
|
+
} catch (err) {
|
|
262
|
+
console.error('[MessengerWidget] Skipping failed attachment upload:', att.file.name, err);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return uploaded;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async _handleSendMessage(conversationId, message, pendingAttachments) {
|
|
156
269
|
// Emit event for external listeners
|
|
157
270
|
this.sdk.eventBus.emit('messenger:messageSent', {
|
|
158
271
|
widget: this,
|
|
@@ -161,9 +274,13 @@ export class MessengerWidget extends BaseWidget {
|
|
|
161
274
|
});
|
|
162
275
|
|
|
163
276
|
try {
|
|
277
|
+
// Upload attachments to CDN first
|
|
278
|
+
const uploadedAttachments = await this._uploadPendingAttachments(pendingAttachments);
|
|
279
|
+
|
|
164
280
|
// Send message through API
|
|
165
281
|
const response = await this.apiService.sendMessage(conversationId, {
|
|
166
282
|
content: message.content,
|
|
283
|
+
attachments: uploadedAttachments,
|
|
167
284
|
});
|
|
168
285
|
|
|
169
286
|
if (response.status && response.data) {
|
|
@@ -200,12 +317,25 @@ export class MessengerWidget extends BaseWidget {
|
|
|
200
317
|
_handleWebSocketMessage(data) {
|
|
201
318
|
const { conversation_id, message } = data;
|
|
202
319
|
|
|
320
|
+
// Parse attachments from server message
|
|
321
|
+
let attachments = [];
|
|
322
|
+
if (message.attachments) {
|
|
323
|
+
try {
|
|
324
|
+
attachments = typeof message.attachments === 'string'
|
|
325
|
+
? JSON.parse(message.attachments)
|
|
326
|
+
: message.attachments;
|
|
327
|
+
} catch (e) {
|
|
328
|
+
// ignore parse errors
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
203
332
|
// Transform message to local format
|
|
204
333
|
const localMessage = {
|
|
205
334
|
id: message.id,
|
|
206
335
|
content: message.content,
|
|
207
336
|
isOwn: message.sender_type === 'customer',
|
|
208
337
|
timestamp: message.created_at,
|
|
338
|
+
attachments: attachments.length > 0 ? attachments : undefined,
|
|
209
339
|
sender: {
|
|
210
340
|
name: message.sender_name || 'Support',
|
|
211
341
|
avatarUrl: message.sender_avatar || null,
|
|
@@ -245,6 +375,19 @@ export class MessengerWidget extends BaseWidget {
|
|
|
245
375
|
});
|
|
246
376
|
}
|
|
247
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Handle conversation closed event
|
|
380
|
+
*/
|
|
381
|
+
_handleConversationClosed(data) {
|
|
382
|
+
const conversationId =
|
|
383
|
+
data?.conversation_id ||
|
|
384
|
+
data?.id ||
|
|
385
|
+
data?.conversation?.id;
|
|
386
|
+
if (!conversationId) return;
|
|
387
|
+
|
|
388
|
+
this.messengerState.updateConversation(conversationId, { status: 'closed' });
|
|
389
|
+
}
|
|
390
|
+
|
|
248
391
|
/**
|
|
249
392
|
* Update unread count from API
|
|
250
393
|
*/
|
|
@@ -287,9 +430,18 @@ export class MessengerWidget extends BaseWidget {
|
|
|
287
430
|
this._wsUnsubscribers.push(
|
|
288
431
|
this.wsService.on('typing_stopped', this._handleTypingStopped)
|
|
289
432
|
);
|
|
433
|
+
this._wsUnsubscribers.push(
|
|
434
|
+
this.wsService.on('conversation_closed', this._handleConversationClosed)
|
|
435
|
+
);
|
|
290
436
|
this._wsUnsubscribers.push(
|
|
291
437
|
this.wsService.on('connected', () => {
|
|
292
438
|
console.log('[MessengerWidget] WebSocket connected');
|
|
439
|
+
// Re-subscribe to active conversation on reconnect
|
|
440
|
+
if (this.messengerState.activeConversationId) {
|
|
441
|
+
this.wsService.send('conversation:subscribe', {
|
|
442
|
+
conversation_id: this.messengerState.activeConversationId,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
293
445
|
})
|
|
294
446
|
);
|
|
295
447
|
this._wsUnsubscribers.push(
|
|
@@ -482,18 +634,29 @@ export class MessengerWidget extends BaseWidget {
|
|
|
482
634
|
try {
|
|
483
635
|
const response = await this.apiService.getConversation(conversationId);
|
|
484
636
|
if (response.status && response.data) {
|
|
485
|
-
const messages = (response.data.messages || []).map((msg) =>
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
|
|
637
|
+
const messages = (response.data.messages || []).map((msg) => {
|
|
638
|
+
let attachments;
|
|
639
|
+
if (msg.attachments) {
|
|
640
|
+
try {
|
|
641
|
+
attachments = typeof msg.attachments === 'string'
|
|
642
|
+
? JSON.parse(msg.attachments)
|
|
643
|
+
: msg.attachments;
|
|
644
|
+
} catch (e) {
|
|
645
|
+
// ignore parse errors
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
id: msg.id,
|
|
650
|
+
content: msg.content,
|
|
651
|
+
isOwn: msg.sender_type === 'customer',
|
|
652
|
+
timestamp: msg.created_at,
|
|
653
|
+
attachments: attachments && attachments.length > 0 ? attachments : undefined,
|
|
654
|
+
sender: {
|
|
655
|
+
name: msg.sender_name || (msg.sender_type === 'customer' ? 'You' : 'Support'),
|
|
656
|
+
avatarUrl: msg.sender_avatar || null,
|
|
657
|
+
},
|
|
658
|
+
};
|
|
659
|
+
});
|
|
497
660
|
this.messengerState.setMessages(conversationId, messages);
|
|
498
661
|
|
|
499
662
|
// Mark as read
|
|
@@ -512,13 +675,28 @@ export class MessengerWidget extends BaseWidget {
|
|
|
512
675
|
/**
|
|
513
676
|
* Start a new conversation
|
|
514
677
|
*/
|
|
515
|
-
async startNewConversation(message, subject = '') {
|
|
678
|
+
async startNewConversation(message, subject = '', pendingAttachments = []) {
|
|
516
679
|
try {
|
|
680
|
+
// Upload attachments to CDN first
|
|
681
|
+
const uploadedAttachments = await this._uploadPendingAttachments(pendingAttachments);
|
|
682
|
+
|
|
683
|
+
console.log('[MessengerWidget] Starting conversation...', {
|
|
684
|
+
message,
|
|
685
|
+
attachmentCount: uploadedAttachments.length,
|
|
686
|
+
hasSession: this.apiService.isSessionValid(),
|
|
687
|
+
sessionToken: this.apiService.sessionToken ? this.apiService.sessionToken.substring(0, 10) + '...' : null,
|
|
688
|
+
baseURL: this.apiService.baseURL,
|
|
689
|
+
mock: this.apiService.mock,
|
|
690
|
+
});
|
|
691
|
+
|
|
517
692
|
const response = await this.apiService.startConversation({
|
|
518
693
|
message,
|
|
519
694
|
subject,
|
|
695
|
+
attachments: uploadedAttachments,
|
|
520
696
|
});
|
|
521
697
|
|
|
698
|
+
console.log('[MessengerWidget] Conversation response:', response);
|
|
699
|
+
|
|
522
700
|
if (response.status && response.data) {
|
|
523
701
|
const conv = response.data;
|
|
524
702
|
const newConversation = {
|
|
@@ -578,6 +756,12 @@ export class MessengerWidget extends BaseWidget {
|
|
|
578
756
|
this.messengerState.agentsOnline = response.data.agents_online;
|
|
579
757
|
this.messengerState.onlineCount = response.data.online_count || 0;
|
|
580
758
|
this.messengerState.responseTime = response.data.response_time || '';
|
|
759
|
+
|
|
760
|
+
// Update team avatars from online agents
|
|
761
|
+
if (response.data.available_agents) {
|
|
762
|
+
this.messengerState.setTeamAvatarsFromAgents(response.data.available_agents);
|
|
763
|
+
}
|
|
764
|
+
|
|
581
765
|
this.messengerState._notify('availabilityUpdate', response.data);
|
|
582
766
|
return response.data;
|
|
583
767
|
}
|
|
@@ -589,7 +773,6 @@ export class MessengerWidget extends BaseWidget {
|
|
|
589
773
|
}
|
|
590
774
|
|
|
591
775
|
async _fetchChangelog() {
|
|
592
|
-
// Mock data for now - simulating changelog API response
|
|
593
776
|
if (this.apiService?.mock) {
|
|
594
777
|
return {
|
|
595
778
|
homeItems: [
|
|
@@ -84,8 +84,9 @@ export class MessengerState {
|
|
|
84
84
|
* Set active conversation for chat view
|
|
85
85
|
*/
|
|
86
86
|
setActiveConversation(conversationId) {
|
|
87
|
+
const previousConversationId = this.activeConversationId;
|
|
87
88
|
this.activeConversationId = conversationId;
|
|
88
|
-
this._notify('conversationChange', { conversationId });
|
|
89
|
+
this._notify('conversationChange', { conversationId, previousConversationId });
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
/**
|
|
@@ -137,6 +138,20 @@ export class MessengerState {
|
|
|
137
138
|
this._notify('messageAdded', { conversationId, message });
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Update a conversation by id
|
|
143
|
+
*/
|
|
144
|
+
updateConversation(conversationId, updates) {
|
|
145
|
+
const conv = this.conversations.find((c) => c.id === conversationId);
|
|
146
|
+
if (!conv) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
Object.assign(conv, updates);
|
|
151
|
+
this._notify('conversationUpdated', { conversationId, conversation: conv });
|
|
152
|
+
return conv;
|
|
153
|
+
}
|
|
154
|
+
|
|
140
155
|
/**
|
|
141
156
|
* Mark conversation as read
|
|
142
157
|
*/
|
|
@@ -206,6 +221,21 @@ export class MessengerState {
|
|
|
206
221
|
return this.messages[this.activeConversationId] || [];
|
|
207
222
|
}
|
|
208
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Update team avatars from backend agent data.
|
|
226
|
+
* Converts available_agents ({full_name, picture}) into avatar strings
|
|
227
|
+
* the views already support (URL strings or initial strings).
|
|
228
|
+
*/
|
|
229
|
+
setTeamAvatarsFromAgents(agents) {
|
|
230
|
+
if (!agents || agents.length === 0) return;
|
|
231
|
+
|
|
232
|
+
this.teamAvatars = agents.map((agent) => {
|
|
233
|
+
if (agent.picture) return agent.picture;
|
|
234
|
+
return agent.full_name || '?';
|
|
235
|
+
});
|
|
236
|
+
this._notify('teamAvatarsUpdate', { teamAvatars: this.teamAvatars });
|
|
237
|
+
}
|
|
238
|
+
|
|
209
239
|
/**
|
|
210
240
|
* Get filtered help articles
|
|
211
241
|
*/
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MessengerLauncher - Floating trigger button for messenger
|
|
3
|
-
*/
|
|
4
1
|
export class MessengerLauncher {
|
|
5
2
|
constructor(state, options = {}) {
|
|
6
3
|
this.state = state;
|
|
7
4
|
this.options = {
|
|
8
5
|
position: options.position || 'bottom-right',
|
|
9
|
-
primaryColor: options.primaryColor || '#
|
|
6
|
+
primaryColor: options.primaryColor || '#155eff',
|
|
10
7
|
...options,
|
|
11
8
|
};
|
|
12
9
|
this.element = null;
|
|
@@ -20,7 +17,6 @@ export class MessengerLauncher {
|
|
|
20
17
|
this._updateContent();
|
|
21
18
|
this._attachEvents();
|
|
22
19
|
|
|
23
|
-
// Subscribe to state changes
|
|
24
20
|
this._unsubscribe = this.state.subscribe((type, data) => {
|
|
25
21
|
if (type === 'openChange') {
|
|
26
22
|
this._updateIcon();
|
|
@@ -42,10 +38,14 @@ export class MessengerLauncher {
|
|
|
42
38
|
this.element.innerHTML = `
|
|
43
39
|
<button class="messenger-launcher-btn" aria-label="Open messenger" style="background: ${this.options.primaryColor};">
|
|
44
40
|
<span class="messenger-launcher-icon messenger-launcher-icon-chat">
|
|
45
|
-
<
|
|
41
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#ffffff" viewBox="0 0 256 256">
|
|
42
|
+
<path d="M144,140a12,12,0,1,1-12-12A12,12,0,0,1,144,140Zm44-12a12,12,0,1,0,12,12A12,12,0,0,0,188,128Zm51.34,83.47a16,16,0,0,1-19.87,19.87l-24.71-7.27A80,80,0,0,1,86.43,183.42a79,79,0,0,1-25.19-7.35l-24.71,7.27a16,16,0,0,1-19.87-19.87l7.27-24.71A80,80,0,1,1,169.58,72.59a80,80,0,0,1,62.49,114.17ZM81.3,166.3a79.94,79.94,0,0,1,70.38-93.87A64,64,0,0,0,39.55,134.19a8,8,0,0,1,.63,6L32,168l27.76-8.17a8,8,0,0,1,6,.63A63.45,63.45,0,0,0,81.3,166.3Zm135.15,15.89a64,64,0,1,0-26.26,26.26,8,8,0,0,1,6-.63L224,216l-8.17-27.76A8,8,0,0,1,216.45,182.19Z"></path>
|
|
43
|
+
</svg>
|
|
46
44
|
</span>
|
|
47
45
|
<span class="messenger-launcher-icon messenger-launcher-icon-close" style="display: none;">
|
|
48
|
-
<
|
|
46
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#ffffff" viewBox="0 0 256 256">
|
|
47
|
+
<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>
|
|
48
|
+
</svg>
|
|
49
49
|
</span>
|
|
50
50
|
${badgeHtml}
|
|
51
51
|
</button>
|
|
@@ -108,19 +108,31 @@ export class NavigationTabs {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
_getHomeIcon() {
|
|
111
|
-
return `<
|
|
111
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" viewBox="0 0 256 256">
|
|
112
|
+
<path d="M216,115.54V208a8,8,0,0,1-8,8H160a8,8,0,0,1-8-8V160a8,8,0,0,0-8-8H112a8,8,0,0,0-8,8v48a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V115.54a8,8,0,0,1,2.62-5.92l80-75.54a8,8,0,0,1,10.77,0l80,75.54A8,8,0,0,1,216,115.54Z" opacity="0.2"></path>
|
|
113
|
+
<path d="M218.83,103.77l-80-75.48a1.14,1.14,0,0,1-.11-.11,16,16,0,0,0-21.53,0l-.11.11L37.17,103.77A16,16,0,0,0,32,115.55V208a16,16,0,0,0,16,16H96a16,16,0,0,0,16-16V160h32v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V115.55A16,16,0,0,0,218.83,103.77ZM208,208H160V160a16,16,0,0,0-16-16H112a16,16,0,0,0-16,16v48H48V115.55l.11-.1L128,40l79.9,75.43.11.1Z"></path>
|
|
114
|
+
</svg>`;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
_getMessagesIcon() {
|
|
115
|
-
return `<
|
|
118
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" viewBox="0 0 256 256">
|
|
119
|
+
<path d="M224,64V192a8,8,0,0,1-8,8H82.5a8,8,0,0,0-5.15,1.88l-32.2,28.23A8,8,0,0,1,32,224V64a8,8,0,0,1,8-8H216A8,8,0,0,1,224,64Z" opacity="0.2"></path>
|
|
120
|
+
<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>
|
|
121
|
+
</svg>`;
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
_getHelpIcon() {
|
|
119
|
-
return `<
|
|
125
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" viewBox="0 0 256 256">
|
|
126
|
+
<path d="M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z" opacity="0.2"></path>
|
|
127
|
+
<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"></path>
|
|
128
|
+
</svg>`;
|
|
120
129
|
}
|
|
121
130
|
|
|
122
131
|
_getChangelogIcon() {
|
|
123
|
-
return `<
|
|
132
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#000000" viewBox="0 0 256 256">
|
|
133
|
+
<path d="M200,144a32,32,0,0,1-63.5,4.5L85.83,121.25a32.07,32.07,0,0,1-41.54-34l-24.15-21a8,8,0,0,1,10.25-12.29L54.55,75a32,32,0,0,1,59.16,2.5l50.63,27.25a31.88,31.88,0,0,1,35.66,7l26.46-23A8,8,0,1,1,236.71,101l-26.46,23A32,32,0,0,1,200,144Z" opacity="0.2"></path>
|
|
134
|
+
<path d="M228.54,86.66l-26.46,23.07A40,40,0,0,0,168,72.13L120.89,46.5a40,40,0,0,0-75.44-4l-22-19.2a8,8,0,0,0-10.5,12L35.44,54.77a40,40,0,0,0,50,61.07l47.1,25.64a40,40,0,0,0,75.41,4.07l26.46-23.07a8,8,0,0,0-10.5-12ZM56,96A24,24,0,1,1,77.25,82.75,24,24,0,0,1,56,96Zm144,64a24,24,0,1,1,24-24A24,24,0,0,1,200,160Z"></path>
|
|
135
|
+
</svg>`;
|
|
124
136
|
}
|
|
125
137
|
|
|
126
138
|
destroy() {
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ChangelogView - Changelog and announcements
|
|
3
|
-
*/
|
|
4
1
|
export class ChangelogView {
|
|
5
2
|
constructor(state, options = {}) {
|
|
6
3
|
this.state = state;
|
|
@@ -15,7 +12,6 @@ export class ChangelogView {
|
|
|
15
12
|
|
|
16
13
|
this._updateContent();
|
|
17
14
|
|
|
18
|
-
// Subscribe to state changes
|
|
19
15
|
this._unsubscribe = this.state.subscribe((type) => {
|
|
20
16
|
if (type === 'changelogUpdate') {
|
|
21
17
|
this._updateChangelogList();
|
|
@@ -32,7 +28,9 @@ export class ChangelogView {
|
|
|
32
28
|
<div class="messenger-changelog-header">
|
|
33
29
|
<h2>Changelog</h2>
|
|
34
30
|
<button class="messenger-close-btn" aria-label="Close">
|
|
35
|
-
<
|
|
31
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="#000000" 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>
|
|
36
34
|
</button>
|
|
37
35
|
</div>
|
|
38
36
|
|
|
@@ -68,7 +66,6 @@ export class ChangelogView {
|
|
|
68
66
|
.map((item) => this._renderChangelogCard(item))
|
|
69
67
|
.join('');
|
|
70
68
|
|
|
71
|
-
// Attach click events
|
|
72
69
|
this._attachChangelogEvents();
|
|
73
70
|
}
|
|
74
71
|
|
|
@@ -99,7 +96,9 @@ export class ChangelogView {
|
|
|
99
96
|
${item.description ? `<p class="messenger-changelog-description">${this._truncateText(item.description, 100)}</p>` : ''}
|
|
100
97
|
<div class="messenger-changelog-meta">
|
|
101
98
|
<span class="messenger-changelog-date">${dateStr}</span>
|
|
102
|
-
<
|
|
99
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#000000" 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>
|
|
103
102
|
</div>
|
|
104
103
|
</div>
|
|
105
104
|
</div>
|
|
@@ -134,7 +133,9 @@ export class ChangelogView {
|
|
|
134
133
|
return `
|
|
135
134
|
<div class="messenger-changelog-empty">
|
|
136
135
|
<div class="messenger-changelog-empty-icon">
|
|
137
|
-
<
|
|
136
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="#000000" viewBox="0 0 256 256">
|
|
137
|
+
<path d="M228.54,86.66l-26.46,23.07A40,40,0,0,0,168,72.13L120.89,46.5a40,40,0,0,0-75.44-4l-22-19.2a8,8,0,0,0-10.5,12L35.44,54.77a40,40,0,0,0,50,61.07l47.1,25.64a40,40,0,0,0,75.41,4.07l26.46-23.07a8,8,0,0,0-10.5-12ZM56,96A24,24,0,1,1,77.25,82.75,24,24,0,0,1,56,96Zm144,64a24,24,0,1,1,24-24A24,24,0,0,1,200,160Z"></path>
|
|
138
|
+
</svg>
|
|
138
139
|
</div>
|
|
139
140
|
<h3>No changelog yet</h3>
|
|
140
141
|
<p>Check back later for updates</p>
|
|
@@ -159,7 +160,6 @@ export class ChangelogView {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
_attachEvents() {
|
|
162
|
-
// Close button
|
|
163
163
|
this.element
|
|
164
164
|
.querySelector('.messenger-close-btn')
|
|
165
165
|
.addEventListener('click', () => {
|