@pocketping/widget 1.1.0 → 1.3.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/index.cjs +1493 -142
- package/dist/index.d.cts +85 -4
- package/dist/index.d.ts +85 -4
- package/dist/index.js +1490 -141
- package/dist/pocketping.min.global.js +593 -6
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -35,7 +35,9 @@ __export(index_exports, {
|
|
|
35
35
|
sendMessage: () => sendMessage,
|
|
36
36
|
setupTrackedElements: () => setupTrackedElements,
|
|
37
37
|
toggle: () => toggle,
|
|
38
|
-
trigger: () => trigger
|
|
38
|
+
trigger: () => trigger,
|
|
39
|
+
uploadFile: () => uploadFile,
|
|
40
|
+
uploadFiles: () => uploadFiles
|
|
39
41
|
});
|
|
40
42
|
module.exports = __toCommonJS(index_exports);
|
|
41
43
|
var import_preact2 = require("preact");
|
|
@@ -229,6 +231,12 @@ function styles(primaryColor, theme) {
|
|
|
229
231
|
border-radius: 4px;
|
|
230
232
|
opacity: 0.8;
|
|
231
233
|
transition: opacity 0.2s;
|
|
234
|
+
flex-shrink: 0;
|
|
235
|
+
width: 28px;
|
|
236
|
+
height: 28px;
|
|
237
|
+
display: flex;
|
|
238
|
+
align-items: center;
|
|
239
|
+
justify-content: center;
|
|
232
240
|
}
|
|
233
241
|
|
|
234
242
|
.pp-close-btn:hover {
|
|
@@ -236,8 +244,8 @@ function styles(primaryColor, theme) {
|
|
|
236
244
|
}
|
|
237
245
|
|
|
238
246
|
.pp-close-btn svg {
|
|
239
|
-
width:
|
|
240
|
-
height:
|
|
247
|
+
width: 16px;
|
|
248
|
+
height: 16px;
|
|
241
249
|
}
|
|
242
250
|
|
|
243
251
|
.pp-messages {
|
|
@@ -261,6 +269,94 @@ function styles(primaryColor, theme) {
|
|
|
261
269
|
padding: 10px 14px;
|
|
262
270
|
border-radius: 16px;
|
|
263
271
|
word-wrap: break-word;
|
|
272
|
+
position: relative;
|
|
273
|
+
user-select: text;
|
|
274
|
+
-webkit-user-select: text;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* Hover actions container - positioned above message (Slack style) */
|
|
278
|
+
.pp-message-actions {
|
|
279
|
+
position: absolute;
|
|
280
|
+
top: -28px;
|
|
281
|
+
display: flex;
|
|
282
|
+
gap: 2px;
|
|
283
|
+
background: ${colors.bg};
|
|
284
|
+
border: 1px solid ${colors.border};
|
|
285
|
+
border-radius: 6px;
|
|
286
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
287
|
+
padding: 2px;
|
|
288
|
+
opacity: 0;
|
|
289
|
+
animation: pp-actions-fade-in 0.12s ease forwards;
|
|
290
|
+
z-index: 10;
|
|
291
|
+
/* Reset color inheritance from message */
|
|
292
|
+
color: ${colors.textSecondary};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@keyframes pp-actions-fade-in {
|
|
296
|
+
from { opacity: 0; transform: translateY(4px); }
|
|
297
|
+
to { opacity: 1; transform: translateY(0); }
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* Visitor messages: actions aligned right */
|
|
301
|
+
.pp-actions-left {
|
|
302
|
+
right: 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* Operator messages: actions aligned left */
|
|
306
|
+
.pp-actions-right {
|
|
307
|
+
left: 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.pp-message-actions .pp-action-btn {
|
|
311
|
+
width: 24px;
|
|
312
|
+
height: 24px;
|
|
313
|
+
border: none;
|
|
314
|
+
background: transparent;
|
|
315
|
+
border-radius: 4px;
|
|
316
|
+
cursor: pointer;
|
|
317
|
+
display: flex;
|
|
318
|
+
align-items: center;
|
|
319
|
+
justify-content: center;
|
|
320
|
+
color: ${colors.textSecondary} !important;
|
|
321
|
+
transition: background 0.1s, color 0.1s;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.pp-message-actions .pp-action-btn:hover {
|
|
325
|
+
background: ${colors.bgSecondary};
|
|
326
|
+
color: ${colors.text} !important;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.pp-message-actions .pp-action-btn svg {
|
|
330
|
+
width: 14px;
|
|
331
|
+
height: 14px;
|
|
332
|
+
stroke: ${colors.textSecondary};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.pp-message-actions .pp-action-btn:hover svg {
|
|
336
|
+
stroke: ${colors.text};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.pp-message-actions .pp-action-delete:hover {
|
|
340
|
+
background: #fef2f2;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.pp-message-actions .pp-action-delete:hover svg {
|
|
344
|
+
stroke: #ef4444;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.pp-theme-dark .pp-message-actions .pp-action-delete:hover {
|
|
348
|
+
background: #7f1d1d;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.pp-theme-dark .pp-message-actions .pp-action-delete:hover svg {
|
|
352
|
+
stroke: #fca5a5;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* Hide hover actions on mobile */
|
|
356
|
+
@media (hover: none) and (pointer: coarse) {
|
|
357
|
+
.pp-message-actions {
|
|
358
|
+
display: none;
|
|
359
|
+
}
|
|
264
360
|
}
|
|
265
361
|
|
|
266
362
|
.pp-message-visitor {
|
|
@@ -344,72 +440,565 @@ function styles(primaryColor, theme) {
|
|
|
344
440
|
40% { transform: scale(1); }
|
|
345
441
|
}
|
|
346
442
|
|
|
347
|
-
.pp-input-form {
|
|
348
|
-
display: flex;
|
|
349
|
-
padding: 12px;
|
|
350
|
-
gap: 8px;
|
|
351
|
-
border-top: 1px solid ${colors.border};
|
|
443
|
+
.pp-input-form {
|
|
444
|
+
display: flex;
|
|
445
|
+
padding: 12px;
|
|
446
|
+
gap: 8px;
|
|
447
|
+
border-top: 1px solid ${colors.border};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.pp-input {
|
|
451
|
+
flex: 1;
|
|
452
|
+
padding: 10px 14px;
|
|
453
|
+
border: 1px solid ${colors.border};
|
|
454
|
+
border-radius: 20px;
|
|
455
|
+
background: ${colors.bg};
|
|
456
|
+
color: ${colors.text};
|
|
457
|
+
font-size: 14px;
|
|
458
|
+
outline: none;
|
|
459
|
+
transition: border-color 0.2s;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.pp-input:focus {
|
|
463
|
+
border-color: ${primaryColor};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.pp-input::placeholder {
|
|
467
|
+
color: ${colors.textSecondary};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.pp-send-btn {
|
|
471
|
+
width: 40px;
|
|
472
|
+
height: 40px;
|
|
473
|
+
border-radius: 50%;
|
|
474
|
+
background: ${primaryColor};
|
|
475
|
+
color: white;
|
|
476
|
+
border: none;
|
|
477
|
+
cursor: pointer;
|
|
478
|
+
display: flex;
|
|
479
|
+
align-items: center;
|
|
480
|
+
justify-content: center;
|
|
481
|
+
transition: opacity 0.2s;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.pp-send-btn:disabled {
|
|
485
|
+
opacity: 0.5;
|
|
486
|
+
cursor: not-allowed;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.pp-send-btn svg {
|
|
490
|
+
width: 18px;
|
|
491
|
+
height: 18px;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.pp-footer {
|
|
495
|
+
text-align: center;
|
|
496
|
+
padding: 8px;
|
|
497
|
+
font-size: 11px;
|
|
498
|
+
color: ${colors.textSecondary};
|
|
499
|
+
border-top: 1px solid ${colors.border};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.pp-footer a {
|
|
503
|
+
color: ${primaryColor};
|
|
504
|
+
text-decoration: none;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.pp-footer a:hover {
|
|
508
|
+
text-decoration: underline;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/* Attachment Styles */
|
|
512
|
+
.pp-file-input {
|
|
513
|
+
/* Use offscreen positioning instead of display:none for better browser compatibility */
|
|
514
|
+
position: absolute;
|
|
515
|
+
width: 1px;
|
|
516
|
+
height: 1px;
|
|
517
|
+
padding: 0;
|
|
518
|
+
margin: -1px;
|
|
519
|
+
overflow: hidden;
|
|
520
|
+
clip: rect(0, 0, 0, 0);
|
|
521
|
+
white-space: nowrap;
|
|
522
|
+
border: 0;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.pp-attach-btn {
|
|
526
|
+
width: 40px;
|
|
527
|
+
height: 40px;
|
|
528
|
+
border-radius: 50%;
|
|
529
|
+
background: transparent;
|
|
530
|
+
color: ${colors.textSecondary};
|
|
531
|
+
border: 1px solid ${colors.border};
|
|
532
|
+
cursor: pointer;
|
|
533
|
+
display: flex;
|
|
534
|
+
align-items: center;
|
|
535
|
+
justify-content: center;
|
|
536
|
+
transition: color 0.2s, border-color 0.2s;
|
|
537
|
+
flex-shrink: 0;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.pp-attach-btn:hover:not(:disabled) {
|
|
541
|
+
color: ${primaryColor};
|
|
542
|
+
border-color: ${primaryColor};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.pp-attach-btn:disabled {
|
|
546
|
+
opacity: 0.5;
|
|
547
|
+
cursor: not-allowed;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.pp-attach-btn svg {
|
|
551
|
+
width: 18px;
|
|
552
|
+
height: 18px;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.pp-attachments-preview {
|
|
556
|
+
display: flex;
|
|
557
|
+
gap: 8px;
|
|
558
|
+
padding: 8px 12px;
|
|
559
|
+
border-top: 1px solid ${colors.border};
|
|
560
|
+
overflow-x: auto;
|
|
561
|
+
background: ${colors.bgSecondary};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.pp-attachment-preview {
|
|
565
|
+
position: relative;
|
|
566
|
+
width: 60px;
|
|
567
|
+
height: 60px;
|
|
568
|
+
border-radius: 8px;
|
|
569
|
+
overflow: hidden;
|
|
570
|
+
flex-shrink: 0;
|
|
571
|
+
background: ${colors.bg};
|
|
572
|
+
border: 1px solid ${colors.border};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.pp-preview-img {
|
|
576
|
+
width: 100%;
|
|
577
|
+
height: 100%;
|
|
578
|
+
object-fit: cover;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.pp-preview-file {
|
|
582
|
+
width: 100%;
|
|
583
|
+
height: 100%;
|
|
584
|
+
display: flex;
|
|
585
|
+
align-items: center;
|
|
586
|
+
justify-content: center;
|
|
587
|
+
color: ${colors.textSecondary};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.pp-preview-file svg {
|
|
591
|
+
width: 24px;
|
|
592
|
+
height: 24px;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.pp-remove-attachment {
|
|
596
|
+
position: absolute;
|
|
597
|
+
top: 2px;
|
|
598
|
+
right: 2px;
|
|
599
|
+
width: 18px;
|
|
600
|
+
height: 18px;
|
|
601
|
+
border-radius: 50%;
|
|
602
|
+
background: rgba(0, 0, 0, 0.6);
|
|
603
|
+
color: white;
|
|
604
|
+
border: none;
|
|
605
|
+
cursor: pointer;
|
|
606
|
+
display: flex;
|
|
607
|
+
align-items: center;
|
|
608
|
+
justify-content: center;
|
|
609
|
+
padding: 0;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.pp-remove-attachment svg {
|
|
613
|
+
width: 10px;
|
|
614
|
+
height: 10px;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.pp-upload-progress {
|
|
618
|
+
position: absolute;
|
|
619
|
+
bottom: 0;
|
|
620
|
+
left: 0;
|
|
621
|
+
height: 3px;
|
|
622
|
+
background: ${primaryColor};
|
|
623
|
+
transition: width 0.1s;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.pp-upload-error {
|
|
627
|
+
position: absolute;
|
|
628
|
+
top: 50%;
|
|
629
|
+
left: 50%;
|
|
630
|
+
transform: translate(-50%, -50%);
|
|
631
|
+
width: 24px;
|
|
632
|
+
height: 24px;
|
|
633
|
+
border-radius: 50%;
|
|
634
|
+
background: #ef4444;
|
|
635
|
+
color: white;
|
|
636
|
+
font-weight: bold;
|
|
637
|
+
display: flex;
|
|
638
|
+
align-items: center;
|
|
639
|
+
justify-content: center;
|
|
640
|
+
font-size: 14px;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.pp-attachment-uploading {
|
|
644
|
+
opacity: 0.7;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.pp-attachment-error {
|
|
648
|
+
border-color: #ef4444;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/* Message Attachments */
|
|
652
|
+
.pp-message-attachments {
|
|
653
|
+
display: flex;
|
|
654
|
+
flex-direction: column;
|
|
655
|
+
gap: 8px;
|
|
656
|
+
margin-top: 4px;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.pp-attachment {
|
|
660
|
+
display: block;
|
|
661
|
+
text-decoration: none;
|
|
662
|
+
color: inherit;
|
|
663
|
+
border-radius: 8px;
|
|
664
|
+
overflow: hidden;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.pp-attachment-image img {
|
|
668
|
+
max-width: 200px;
|
|
669
|
+
max-height: 200px;
|
|
670
|
+
border-radius: 8px;
|
|
671
|
+
display: block;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.pp-attachment-audio {
|
|
675
|
+
display: flex;
|
|
676
|
+
flex-direction: column;
|
|
677
|
+
gap: 4px;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.pp-attachment-audio audio {
|
|
681
|
+
width: 200px;
|
|
682
|
+
height: 36px;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.pp-attachment-audio .pp-attachment-name {
|
|
686
|
+
font-size: 11px;
|
|
687
|
+
opacity: 0.7;
|
|
688
|
+
white-space: nowrap;
|
|
689
|
+
overflow: hidden;
|
|
690
|
+
text-overflow: ellipsis;
|
|
691
|
+
max-width: 200px;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
.pp-attachment-video video {
|
|
695
|
+
max-width: 200px;
|
|
696
|
+
max-height: 200px;
|
|
697
|
+
border-radius: 8px;
|
|
698
|
+
display: block;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
.pp-attachment-file {
|
|
702
|
+
display: flex;
|
|
703
|
+
align-items: center;
|
|
704
|
+
gap: 8px;
|
|
705
|
+
padding: 8px 12px;
|
|
706
|
+
background: ${isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
|
|
707
|
+
border-radius: 8px;
|
|
708
|
+
transition: background 0.2s;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
.pp-attachment-file:hover {
|
|
712
|
+
background: ${isDark ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.08)"};
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
.pp-attachment-file svg {
|
|
716
|
+
width: 24px;
|
|
717
|
+
height: 24px;
|
|
718
|
+
flex-shrink: 0;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
.pp-attachment-info {
|
|
722
|
+
display: flex;
|
|
723
|
+
flex-direction: column;
|
|
724
|
+
min-width: 0;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
.pp-attachment-name {
|
|
728
|
+
font-size: 13px;
|
|
729
|
+
font-weight: 500;
|
|
730
|
+
white-space: nowrap;
|
|
731
|
+
overflow: hidden;
|
|
732
|
+
text-overflow: ellipsis;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
.pp-attachment-size {
|
|
736
|
+
font-size: 11px;
|
|
737
|
+
opacity: 0.7;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/* Drag & Drop */
|
|
741
|
+
.pp-dragging {
|
|
742
|
+
position: relative;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.pp-drop-overlay {
|
|
746
|
+
position: absolute;
|
|
747
|
+
inset: 0;
|
|
748
|
+
background: ${isDark ? "rgba(0,0,0,0.9)" : "rgba(255,255,255,0.95)"};
|
|
749
|
+
display: flex;
|
|
750
|
+
flex-direction: column;
|
|
751
|
+
align-items: center;
|
|
752
|
+
justify-content: center;
|
|
753
|
+
gap: 12px;
|
|
754
|
+
z-index: 100;
|
|
755
|
+
border: 3px dashed ${primaryColor};
|
|
756
|
+
border-radius: 16px;
|
|
757
|
+
margin: 4px;
|
|
758
|
+
pointer-events: none;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.pp-drop-icon svg {
|
|
762
|
+
width: 48px;
|
|
763
|
+
height: 48px;
|
|
764
|
+
color: ${primaryColor};
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
.pp-drop-text {
|
|
768
|
+
font-size: 16px;
|
|
769
|
+
font-weight: 500;
|
|
770
|
+
color: ${colors.text};
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/* Message Context Menu */
|
|
774
|
+
.pp-message-menu {
|
|
775
|
+
position: fixed;
|
|
776
|
+
background: ${colors.bg};
|
|
777
|
+
border: 1px solid ${colors.border};
|
|
778
|
+
border-radius: 8px;
|
|
779
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
780
|
+
padding: 4px;
|
|
781
|
+
z-index: 200;
|
|
782
|
+
min-width: 120px;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
.pp-message-menu button {
|
|
786
|
+
display: flex;
|
|
787
|
+
align-items: center;
|
|
788
|
+
gap: 8px;
|
|
789
|
+
width: 100%;
|
|
790
|
+
padding: 8px 12px;
|
|
791
|
+
border: none;
|
|
792
|
+
background: transparent;
|
|
793
|
+
color: ${colors.text};
|
|
794
|
+
font-size: 13px;
|
|
795
|
+
cursor: pointer;
|
|
796
|
+
border-radius: 4px;
|
|
797
|
+
text-align: left;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
.pp-message-menu button:hover {
|
|
801
|
+
background: ${isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.pp-message-menu button svg {
|
|
805
|
+
width: 16px;
|
|
806
|
+
height: 16px;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.pp-menu-delete {
|
|
810
|
+
color: #ef4444 !important;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/* Edit Modal */
|
|
814
|
+
.pp-edit-modal {
|
|
815
|
+
position: absolute;
|
|
816
|
+
bottom: 80px;
|
|
817
|
+
left: 12px;
|
|
818
|
+
right: 12px;
|
|
819
|
+
background: ${colors.bg};
|
|
820
|
+
border: 1px solid ${colors.border};
|
|
821
|
+
border-radius: 12px;
|
|
822
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
|
823
|
+
z-index: 150;
|
|
824
|
+
overflow: hidden;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
.pp-edit-header {
|
|
828
|
+
display: flex;
|
|
829
|
+
justify-content: space-between;
|
|
830
|
+
align-items: center;
|
|
831
|
+
padding: 12px 16px;
|
|
832
|
+
border-bottom: 1px solid ${colors.border};
|
|
833
|
+
font-weight: 500;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
.pp-edit-header button {
|
|
837
|
+
background: transparent;
|
|
838
|
+
border: none;
|
|
839
|
+
color: ${colors.textSecondary};
|
|
840
|
+
cursor: pointer;
|
|
841
|
+
padding: 4px;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.pp-edit-header button svg {
|
|
845
|
+
width: 18px;
|
|
846
|
+
height: 18px;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.pp-edit-input {
|
|
850
|
+
width: 100%;
|
|
851
|
+
padding: 12px 16px;
|
|
852
|
+
border: none;
|
|
853
|
+
background: transparent;
|
|
854
|
+
color: ${colors.text};
|
|
855
|
+
font-size: 14px;
|
|
856
|
+
resize: none;
|
|
857
|
+
min-height: 80px;
|
|
858
|
+
outline: none;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.pp-edit-actions {
|
|
862
|
+
display: flex;
|
|
863
|
+
justify-content: flex-end;
|
|
864
|
+
gap: 8px;
|
|
865
|
+
padding: 12px 16px;
|
|
866
|
+
border-top: 1px solid ${colors.border};
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
.pp-edit-cancel {
|
|
870
|
+
padding: 8px 16px;
|
|
871
|
+
border: 1px solid ${colors.border};
|
|
872
|
+
border-radius: 6px;
|
|
873
|
+
background: transparent;
|
|
874
|
+
color: ${colors.text};
|
|
875
|
+
font-size: 13px;
|
|
876
|
+
cursor: pointer;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.pp-edit-save {
|
|
880
|
+
padding: 8px 16px;
|
|
881
|
+
border: none;
|
|
882
|
+
border-radius: 6px;
|
|
883
|
+
background: ${primaryColor};
|
|
884
|
+
color: white;
|
|
885
|
+
font-size: 13px;
|
|
886
|
+
cursor: pointer;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
.pp-edit-save:disabled {
|
|
890
|
+
opacity: 0.5;
|
|
891
|
+
cursor: not-allowed;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/* Reply Preview */
|
|
895
|
+
.pp-reply-preview {
|
|
896
|
+
display: flex;
|
|
897
|
+
align-items: center;
|
|
898
|
+
gap: 8px;
|
|
899
|
+
padding: 8px 12px;
|
|
900
|
+
background: ${isDark ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
|
|
901
|
+
border-top: 1px solid ${colors.border};
|
|
902
|
+
border-left: 3px solid ${primaryColor};
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.pp-reply-preview-content {
|
|
906
|
+
flex: 1;
|
|
907
|
+
min-width: 0;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
.pp-reply-label {
|
|
911
|
+
display: block;
|
|
912
|
+
font-size: 11px;
|
|
913
|
+
color: ${primaryColor};
|
|
914
|
+
font-weight: 500;
|
|
915
|
+
margin-bottom: 2px;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
.pp-reply-text {
|
|
919
|
+
display: block;
|
|
920
|
+
font-size: 12px;
|
|
921
|
+
color: ${colors.textSecondary};
|
|
922
|
+
white-space: nowrap;
|
|
923
|
+
overflow: hidden;
|
|
924
|
+
text-overflow: ellipsis;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
.pp-reply-cancel {
|
|
928
|
+
background: transparent;
|
|
929
|
+
border: none;
|
|
930
|
+
color: ${colors.textSecondary};
|
|
931
|
+
cursor: pointer;
|
|
932
|
+
padding: 4px;
|
|
933
|
+
flex-shrink: 0;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
.pp-reply-cancel svg {
|
|
937
|
+
width: 16px;
|
|
938
|
+
height: 16px;
|
|
352
939
|
}
|
|
353
940
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
border:
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
font-size:
|
|
362
|
-
|
|
363
|
-
|
|
941
|
+
/* Reply Quote in Message */
|
|
942
|
+
.pp-reply-quote {
|
|
943
|
+
background: ${isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
|
|
944
|
+
border-left: 2px solid ${primaryColor};
|
|
945
|
+
padding: 4px 8px;
|
|
946
|
+
margin-bottom: 6px;
|
|
947
|
+
border-radius: 0 4px 4px 0;
|
|
948
|
+
font-size: 12px;
|
|
949
|
+
position: relative;
|
|
950
|
+
z-index: 1;
|
|
364
951
|
}
|
|
365
952
|
|
|
366
|
-
.pp-
|
|
367
|
-
|
|
953
|
+
.pp-reply-sender {
|
|
954
|
+
display: block;
|
|
955
|
+
font-weight: 500;
|
|
956
|
+
color: ${primaryColor};
|
|
957
|
+
margin-bottom: 2px;
|
|
368
958
|
}
|
|
369
959
|
|
|
370
|
-
.pp-
|
|
960
|
+
.pp-reply-content {
|
|
961
|
+
display: block;
|
|
371
962
|
color: ${colors.textSecondary};
|
|
963
|
+
white-space: nowrap;
|
|
964
|
+
overflow: hidden;
|
|
965
|
+
text-overflow: ellipsis;
|
|
372
966
|
}
|
|
373
967
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
border-
|
|
378
|
-
background: ${primaryColor};
|
|
379
|
-
color: white;
|
|
380
|
-
border: none;
|
|
381
|
-
cursor: pointer;
|
|
382
|
-
display: flex;
|
|
383
|
-
align-items: center;
|
|
384
|
-
justify-content: center;
|
|
385
|
-
transition: opacity 0.2s;
|
|
968
|
+
/* Reply quote in visitor message bubble needs higher contrast */
|
|
969
|
+
.pp-message-visitor .pp-reply-quote {
|
|
970
|
+
background: rgba(255, 255, 255, 0.18);
|
|
971
|
+
border-left-color: rgba(255, 255, 255, 0.7);
|
|
386
972
|
}
|
|
387
973
|
|
|
388
|
-
.pp-
|
|
389
|
-
|
|
390
|
-
|
|
974
|
+
.pp-message-visitor .pp-reply-sender,
|
|
975
|
+
.pp-message-visitor .pp-reply-content {
|
|
976
|
+
color: rgba(255, 255, 255, 0.9);
|
|
391
977
|
}
|
|
392
978
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
979
|
+
/* Deleted Message */
|
|
980
|
+
.pp-message-deleted {
|
|
981
|
+
opacity: 0.6;
|
|
396
982
|
}
|
|
397
983
|
|
|
398
|
-
.pp-
|
|
399
|
-
|
|
400
|
-
padding: 8px;
|
|
401
|
-
font-size: 11px;
|
|
984
|
+
.pp-deleted-content {
|
|
985
|
+
font-style: italic;
|
|
402
986
|
color: ${colors.textSecondary};
|
|
403
|
-
|
|
987
|
+
display: flex;
|
|
988
|
+
align-items: center;
|
|
989
|
+
gap: 4px;
|
|
404
990
|
}
|
|
405
991
|
|
|
406
|
-
.pp-
|
|
407
|
-
|
|
408
|
-
text-decoration: none;
|
|
992
|
+
.pp-deleted-icon {
|
|
993
|
+
font-size: 12px;
|
|
409
994
|
}
|
|
410
995
|
|
|
411
|
-
|
|
412
|
-
|
|
996
|
+
/* Edited Badge */
|
|
997
|
+
.pp-edited-badge {
|
|
998
|
+
font-size: 10px;
|
|
999
|
+
color: ${colors.textSecondary};
|
|
1000
|
+
margin-left: 4px;
|
|
1001
|
+
font-style: italic;
|
|
413
1002
|
}
|
|
414
1003
|
`;
|
|
415
1004
|
}
|
|
@@ -424,9 +1013,20 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
424
1013
|
const [operatorOnline, setOperatorOnline] = (0, import_hooks.useState)(false);
|
|
425
1014
|
const [isConnected, setIsConnected] = (0, import_hooks.useState)(false);
|
|
426
1015
|
const [unreadCount, setUnreadCount] = (0, import_hooks.useState)(0);
|
|
1016
|
+
const [pendingAttachments, setPendingAttachments] = (0, import_hooks.useState)([]);
|
|
1017
|
+
const [isUploading, setIsUploading] = (0, import_hooks.useState)(false);
|
|
1018
|
+
const [replyingTo, setReplyingTo] = (0, import_hooks.useState)(null);
|
|
1019
|
+
const [editingMessage, setEditingMessage] = (0, import_hooks.useState)(null);
|
|
1020
|
+
const [editContent, setEditContent] = (0, import_hooks.useState)("");
|
|
1021
|
+
const [messageMenu, setMessageMenu] = (0, import_hooks.useState)(null);
|
|
1022
|
+
const [isDragging, setIsDragging] = (0, import_hooks.useState)(false);
|
|
1023
|
+
const [hoveredMessageId, setHoveredMessageId] = (0, import_hooks.useState)(null);
|
|
1024
|
+
const [longPressTimer, setLongPressTimer] = (0, import_hooks.useState)(null);
|
|
427
1025
|
const [config, setConfig] = (0, import_hooks.useState)(initialConfig);
|
|
428
1026
|
const messagesEndRef = (0, import_hooks.useRef)(null);
|
|
429
1027
|
const inputRef = (0, import_hooks.useRef)(null);
|
|
1028
|
+
const fileInputRef = (0, import_hooks.useRef)(null);
|
|
1029
|
+
const messagesContainerRef = (0, import_hooks.useRef)(null);
|
|
430
1030
|
(0, import_hooks.useEffect)(() => {
|
|
431
1031
|
const unsubOpen = client2.on("openChange", setIsOpen);
|
|
432
1032
|
const unsubMessage = client2.on("message", () => {
|
|
@@ -523,11 +1123,17 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
523
1123
|
if (!shouldShow) return null;
|
|
524
1124
|
const handleSubmit = async (e) => {
|
|
525
1125
|
e.preventDefault();
|
|
526
|
-
|
|
1126
|
+
const hasContent = inputValue.trim().length > 0;
|
|
1127
|
+
const readyAttachments = pendingAttachments.filter((a) => a.status === "ready" && a.attachment);
|
|
1128
|
+
if (!hasContent && readyAttachments.length === 0) return;
|
|
527
1129
|
const content = inputValue;
|
|
1130
|
+
const attachmentIds = readyAttachments.map((a) => a.attachment.id);
|
|
1131
|
+
const replyToId = replyingTo?.id;
|
|
528
1132
|
setInputValue("");
|
|
1133
|
+
setPendingAttachments([]);
|
|
1134
|
+
setReplyingTo(null);
|
|
529
1135
|
try {
|
|
530
|
-
await client2.sendMessage(content);
|
|
1136
|
+
await client2.sendMessage(content, attachmentIds, replyToId);
|
|
531
1137
|
} catch (err) {
|
|
532
1138
|
console.error("[PocketPing] Failed to send message:", err);
|
|
533
1139
|
}
|
|
@@ -537,9 +1143,213 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
537
1143
|
setInputValue(target.value);
|
|
538
1144
|
client2.sendTyping(true);
|
|
539
1145
|
};
|
|
1146
|
+
const handleFileSelect = async (e) => {
|
|
1147
|
+
const target = e.target;
|
|
1148
|
+
const files = target.files;
|
|
1149
|
+
if (!files || files.length === 0) return;
|
|
1150
|
+
const newPending = [];
|
|
1151
|
+
for (let i = 0; i < files.length; i++) {
|
|
1152
|
+
const file = files[i];
|
|
1153
|
+
const id = `pending-${Date.now()}-${i}`;
|
|
1154
|
+
let preview;
|
|
1155
|
+
if (file.type.startsWith("image/")) {
|
|
1156
|
+
preview = URL.createObjectURL(file);
|
|
1157
|
+
}
|
|
1158
|
+
newPending.push({
|
|
1159
|
+
id,
|
|
1160
|
+
file,
|
|
1161
|
+
preview,
|
|
1162
|
+
progress: 0,
|
|
1163
|
+
status: "pending"
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
setPendingAttachments((prev) => [...prev, ...newPending]);
|
|
1167
|
+
target.value = "";
|
|
1168
|
+
setIsUploading(true);
|
|
1169
|
+
for (const pending of newPending) {
|
|
1170
|
+
try {
|
|
1171
|
+
setPendingAttachments(
|
|
1172
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, status: "uploading" } : a)
|
|
1173
|
+
);
|
|
1174
|
+
const attachment = await client2.uploadFile(pending.file, (progress) => {
|
|
1175
|
+
setPendingAttachments(
|
|
1176
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, progress } : a)
|
|
1177
|
+
);
|
|
1178
|
+
});
|
|
1179
|
+
setPendingAttachments(
|
|
1180
|
+
(prev) => prev.map(
|
|
1181
|
+
(a) => a.id === pending.id ? { ...a, status: "ready", progress: 100, attachment } : a
|
|
1182
|
+
)
|
|
1183
|
+
);
|
|
1184
|
+
} catch (err) {
|
|
1185
|
+
console.error("[PocketPing] Failed to upload file:", err);
|
|
1186
|
+
setPendingAttachments(
|
|
1187
|
+
(prev) => prev.map(
|
|
1188
|
+
(a) => a.id === pending.id ? { ...a, status: "error", error: "Upload failed" } : a
|
|
1189
|
+
)
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
setIsUploading(false);
|
|
1194
|
+
};
|
|
1195
|
+
const handleRemoveAttachment = (id) => {
|
|
1196
|
+
setPendingAttachments((prev) => {
|
|
1197
|
+
const removed = prev.find((a) => a.id === id);
|
|
1198
|
+
if (removed?.preview) {
|
|
1199
|
+
URL.revokeObjectURL(removed.preview);
|
|
1200
|
+
}
|
|
1201
|
+
return prev.filter((a) => a.id !== id);
|
|
1202
|
+
});
|
|
1203
|
+
};
|
|
1204
|
+
const handleReply = (message) => {
|
|
1205
|
+
setReplyingTo(message);
|
|
1206
|
+
setMessageMenu(null);
|
|
1207
|
+
inputRef.current?.focus();
|
|
1208
|
+
};
|
|
1209
|
+
const handleCancelReply = () => {
|
|
1210
|
+
setReplyingTo(null);
|
|
1211
|
+
};
|
|
1212
|
+
const handleStartEdit = (message) => {
|
|
1213
|
+
if (message.sender !== "visitor") return;
|
|
1214
|
+
setEditingMessage(message);
|
|
1215
|
+
setEditContent(message.content);
|
|
1216
|
+
setMessageMenu(null);
|
|
1217
|
+
};
|
|
1218
|
+
const handleCancelEdit = () => {
|
|
1219
|
+
setEditingMessage(null);
|
|
1220
|
+
setEditContent("");
|
|
1221
|
+
};
|
|
1222
|
+
const handleSaveEdit = async () => {
|
|
1223
|
+
if (!editingMessage || !editContent.trim()) return;
|
|
1224
|
+
try {
|
|
1225
|
+
await client2.editMessage(editingMessage.id, editContent.trim());
|
|
1226
|
+
setEditingMessage(null);
|
|
1227
|
+
setEditContent("");
|
|
1228
|
+
} catch (err) {
|
|
1229
|
+
console.error("[PocketPing] Failed to edit message:", err);
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
const handleDelete = async (message) => {
|
|
1233
|
+
if (message.sender !== "visitor") return;
|
|
1234
|
+
setMessageMenu(null);
|
|
1235
|
+
if (confirm("Delete this message?")) {
|
|
1236
|
+
try {
|
|
1237
|
+
await client2.deleteMessage(message.id);
|
|
1238
|
+
} catch (err) {
|
|
1239
|
+
console.error("[PocketPing] Failed to delete message:", err);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
const handleMessageContextMenu = (e, message) => {
|
|
1244
|
+
e.preventDefault();
|
|
1245
|
+
const mouseEvent = e;
|
|
1246
|
+
setMessageMenu({
|
|
1247
|
+
message,
|
|
1248
|
+
x: mouseEvent.clientX,
|
|
1249
|
+
y: mouseEvent.clientY
|
|
1250
|
+
});
|
|
1251
|
+
};
|
|
1252
|
+
const handleTouchStart = (message) => {
|
|
1253
|
+
const timer = setTimeout(() => {
|
|
1254
|
+
if (navigator.vibrate) navigator.vibrate(50);
|
|
1255
|
+
setMessageMenu({
|
|
1256
|
+
message,
|
|
1257
|
+
x: window.innerWidth / 2 - 60,
|
|
1258
|
+
// Center horizontally
|
|
1259
|
+
y: window.innerHeight / 2 - 50
|
|
1260
|
+
// Center vertically
|
|
1261
|
+
});
|
|
1262
|
+
}, 500);
|
|
1263
|
+
setLongPressTimer(timer);
|
|
1264
|
+
};
|
|
1265
|
+
const handleTouchEnd = () => {
|
|
1266
|
+
if (longPressTimer) {
|
|
1267
|
+
clearTimeout(longPressTimer);
|
|
1268
|
+
setLongPressTimer(null);
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
(0, import_hooks.useEffect)(() => {
|
|
1272
|
+
if (!messageMenu) return;
|
|
1273
|
+
const handleClickOutside = () => setMessageMenu(null);
|
|
1274
|
+
document.addEventListener("click", handleClickOutside);
|
|
1275
|
+
return () => document.removeEventListener("click", handleClickOutside);
|
|
1276
|
+
}, [messageMenu]);
|
|
1277
|
+
const dragCounterRef = (0, import_hooks.useRef)(0);
|
|
1278
|
+
const handleDragEnter = (e) => {
|
|
1279
|
+
e.preventDefault();
|
|
1280
|
+
e.stopPropagation();
|
|
1281
|
+
dragCounterRef.current++;
|
|
1282
|
+
if (dragCounterRef.current === 1) {
|
|
1283
|
+
setIsDragging(true);
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
const handleDragOver = (e) => {
|
|
1287
|
+
e.preventDefault();
|
|
1288
|
+
e.stopPropagation();
|
|
1289
|
+
};
|
|
1290
|
+
const handleDragLeave = (e) => {
|
|
1291
|
+
e.preventDefault();
|
|
1292
|
+
e.stopPropagation();
|
|
1293
|
+
dragCounterRef.current--;
|
|
1294
|
+
if (dragCounterRef.current === 0) {
|
|
1295
|
+
setIsDragging(false);
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
const handleDrop = async (e) => {
|
|
1299
|
+
e.preventDefault();
|
|
1300
|
+
e.stopPropagation();
|
|
1301
|
+
dragCounterRef.current = 0;
|
|
1302
|
+
setIsDragging(false);
|
|
1303
|
+
const files = e.dataTransfer?.files;
|
|
1304
|
+
if (!files || files.length === 0) return;
|
|
1305
|
+
const newPending = [];
|
|
1306
|
+
for (let i = 0; i < files.length; i++) {
|
|
1307
|
+
const file = files[i];
|
|
1308
|
+
const id = `pending-${Date.now()}-${i}`;
|
|
1309
|
+
let preview;
|
|
1310
|
+
if (file.type.startsWith("image/")) {
|
|
1311
|
+
preview = URL.createObjectURL(file);
|
|
1312
|
+
}
|
|
1313
|
+
newPending.push({
|
|
1314
|
+
id,
|
|
1315
|
+
file,
|
|
1316
|
+
preview,
|
|
1317
|
+
progress: 0,
|
|
1318
|
+
status: "pending"
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
setPendingAttachments((prev) => [...prev, ...newPending]);
|
|
1322
|
+
setIsUploading(true);
|
|
1323
|
+
for (const pending of newPending) {
|
|
1324
|
+
try {
|
|
1325
|
+
setPendingAttachments(
|
|
1326
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, status: "uploading" } : a)
|
|
1327
|
+
);
|
|
1328
|
+
const attachment = await client2.uploadFile(pending.file, (progress) => {
|
|
1329
|
+
setPendingAttachments(
|
|
1330
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, progress } : a)
|
|
1331
|
+
);
|
|
1332
|
+
});
|
|
1333
|
+
setPendingAttachments(
|
|
1334
|
+
(prev) => prev.map(
|
|
1335
|
+
(a) => a.id === pending.id ? { ...a, status: "ready", progress: 100, attachment } : a
|
|
1336
|
+
)
|
|
1337
|
+
);
|
|
1338
|
+
} catch (err) {
|
|
1339
|
+
console.error("[PocketPing] Failed to upload dropped file:", err);
|
|
1340
|
+
setPendingAttachments(
|
|
1341
|
+
(prev) => prev.map(
|
|
1342
|
+
(a) => a.id === pending.id ? { ...a, status: "error", error: "Upload failed" } : a
|
|
1343
|
+
)
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
setIsUploading(false);
|
|
1348
|
+
};
|
|
540
1349
|
const position = config.position ?? "bottom-right";
|
|
541
1350
|
const theme = getTheme(config.theme ?? "auto");
|
|
542
1351
|
const primaryColor = config.primaryColor ?? "#6366f1";
|
|
1352
|
+
const actionIconColor = theme === "dark" ? "#9ca3af" : "#6b7280";
|
|
543
1353
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_preact.Fragment, { children: [
|
|
544
1354
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: styles(primaryColor, theme) }),
|
|
545
1355
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
@@ -555,84 +1365,263 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
555
1365
|
]
|
|
556
1366
|
}
|
|
557
1367
|
),
|
|
558
|
-
isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-messages", children: [
|
|
584
|
-
config.welcomeMessage && messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-welcome", children: config.welcomeMessage }),
|
|
585
|
-
messages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
586
|
-
"div",
|
|
587
|
-
{
|
|
588
|
-
class: `pp-message pp-message-${msg.sender}`,
|
|
589
|
-
children: [
|
|
590
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-message-content", children: msg.content }),
|
|
591
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message-time", children: [
|
|
592
|
-
formatTime(msg.timestamp),
|
|
593
|
-
msg.sender === "ai" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-ai-badge", children: "AI" }),
|
|
594
|
-
msg.sender === "visitor" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: `pp-status pp-status-${msg.status ?? "sent"}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusIcon, { status: msg.status }) })
|
|
1368
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1369
|
+
"div",
|
|
1370
|
+
{
|
|
1371
|
+
class: `pp-window pp-${position} pp-theme-${theme} ${isDragging ? "pp-dragging" : ""}`,
|
|
1372
|
+
onDragEnter: handleDragEnter,
|
|
1373
|
+
onDragOver: handleDragOver,
|
|
1374
|
+
onDragLeave: handleDragLeave,
|
|
1375
|
+
onDrop: handleDrop,
|
|
1376
|
+
children: [
|
|
1377
|
+
isDragging && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-drop-overlay", children: [
|
|
1378
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-drop-icon", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AttachIcon, {}) }),
|
|
1379
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-drop-text", children: "Drop files to upload" })
|
|
1380
|
+
] }),
|
|
1381
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-header", children: [
|
|
1382
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-header-info", children: [
|
|
1383
|
+
config.operatorAvatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: config.operatorAvatar, alt: "", class: "pp-avatar" }),
|
|
1384
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1385
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-header-title", children: config.operatorName ?? "Support" }),
|
|
1386
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-header-status", children: operatorOnline ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1387
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-status-dot pp-online" }),
|
|
1388
|
+
" Online"
|
|
1389
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-status-dot" }),
|
|
1391
|
+
" Away"
|
|
1392
|
+
] }) })
|
|
595
1393
|
] })
|
|
596
|
-
]
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
1394
|
+
] }),
|
|
1395
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1396
|
+
"button",
|
|
1397
|
+
{
|
|
1398
|
+
class: "pp-close-btn",
|
|
1399
|
+
onClick: () => client2.setOpen(false),
|
|
1400
|
+
"aria-label": "Close chat",
|
|
1401
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {})
|
|
1402
|
+
}
|
|
1403
|
+
)
|
|
1404
|
+
] }),
|
|
1405
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-messages", ref: messagesContainerRef, children: [
|
|
1406
|
+
config.welcomeMessage && messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-welcome", children: config.welcomeMessage }),
|
|
1407
|
+
messages.map((msg) => {
|
|
1408
|
+
const isDeleted = !!msg.deletedAt;
|
|
1409
|
+
const isEdited = !!msg.editedAt;
|
|
1410
|
+
let replyData = null;
|
|
1411
|
+
if (msg.replyTo) {
|
|
1412
|
+
if (typeof msg.replyTo === "object") {
|
|
1413
|
+
replyData = msg.replyTo;
|
|
1414
|
+
} else {
|
|
1415
|
+
const replyToMsg = messages.find((m) => m.id === msg.replyTo);
|
|
1416
|
+
if (replyToMsg) {
|
|
1417
|
+
replyData = {
|
|
1418
|
+
sender: replyToMsg.sender,
|
|
1419
|
+
content: replyToMsg.content,
|
|
1420
|
+
deleted: !!replyToMsg.deletedAt
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
const isHovered = hoveredMessageId === msg.id;
|
|
1426
|
+
const showActions = isHovered && !isDeleted;
|
|
1427
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1428
|
+
"div",
|
|
1429
|
+
{
|
|
1430
|
+
class: `pp-message pp-message-${msg.sender} ${isDeleted ? "pp-message-deleted" : ""}`,
|
|
1431
|
+
onContextMenu: (e) => handleMessageContextMenu(e, msg),
|
|
1432
|
+
onMouseEnter: () => setHoveredMessageId(msg.id),
|
|
1433
|
+
onMouseLeave: () => setHoveredMessageId(null),
|
|
1434
|
+
onTouchStart: () => handleTouchStart(msg),
|
|
1435
|
+
onTouchEnd: handleTouchEnd,
|
|
1436
|
+
onTouchCancel: handleTouchEnd,
|
|
1437
|
+
children: [
|
|
1438
|
+
showActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: `pp-message-actions ${msg.sender === "visitor" ? "pp-actions-left" : "pp-actions-right"}`, children: [
|
|
1439
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1440
|
+
"button",
|
|
1441
|
+
{
|
|
1442
|
+
class: "pp-action-btn",
|
|
1443
|
+
onClick: () => handleReply(msg),
|
|
1444
|
+
title: "Reply",
|
|
1445
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplyIcon, { color: actionIconColor })
|
|
1446
|
+
}
|
|
1447
|
+
),
|
|
1448
|
+
msg.sender === "visitor" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1450
|
+
"button",
|
|
1451
|
+
{
|
|
1452
|
+
class: "pp-action-btn",
|
|
1453
|
+
onClick: () => handleStartEdit(msg),
|
|
1454
|
+
title: "Edit",
|
|
1455
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditIcon, { color: actionIconColor })
|
|
1456
|
+
}
|
|
1457
|
+
),
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1459
|
+
"button",
|
|
1460
|
+
{
|
|
1461
|
+
class: "pp-action-btn pp-action-delete",
|
|
1462
|
+
onClick: () => handleDelete(msg),
|
|
1463
|
+
title: "Delete",
|
|
1464
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteIcon, { color: actionIconColor })
|
|
1465
|
+
}
|
|
1466
|
+
)
|
|
1467
|
+
] })
|
|
1468
|
+
] }),
|
|
1469
|
+
replyData && replyData.content && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-reply-quote", children: [
|
|
1470
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-reply-sender", children: replyData.sender === "visitor" ? "You" : "Support" }),
|
|
1471
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { class: "pp-reply-content", children: [
|
|
1472
|
+
replyData.deleted ? "Message deleted" : (replyData.content || "").slice(0, 50),
|
|
1473
|
+
(replyData.content || "").length > 50 ? "..." : ""
|
|
1474
|
+
] })
|
|
1475
|
+
] }),
|
|
1476
|
+
isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message-content pp-deleted-content", children: [
|
|
1477
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-deleted-icon", children: "\u{1F5D1}\uFE0F" }),
|
|
1478
|
+
" Message deleted"
|
|
1479
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1480
|
+
msg.content && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-message-content", children: msg.content }),
|
|
1481
|
+
msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-message-attachments", children: msg.attachments.map((att) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AttachmentDisplay, { attachment: att }, att.id)) })
|
|
1482
|
+
] }),
|
|
1483
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message-time", children: [
|
|
1484
|
+
formatTime(msg.timestamp),
|
|
1485
|
+
isEdited && !isDeleted && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-edited-badge", children: "edited" }),
|
|
1486
|
+
msg.sender === "ai" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-ai-badge", children: "AI" }),
|
|
1487
|
+
msg.sender === "visitor" && !isDeleted && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: `pp-status pp-status-${msg.status ?? "sent"}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusIcon, { status: msg.status }) })
|
|
1488
|
+
] })
|
|
1489
|
+
]
|
|
1490
|
+
},
|
|
1491
|
+
msg.id
|
|
1492
|
+
);
|
|
1493
|
+
}),
|
|
1494
|
+
isTyping && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message pp-message-operator pp-typing", children: [
|
|
1495
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
|
|
1496
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
|
|
1497
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {})
|
|
1498
|
+
] }),
|
|
1499
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: messagesEndRef })
|
|
1500
|
+
] }),
|
|
1501
|
+
messageMenu && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1502
|
+
"div",
|
|
1503
|
+
{
|
|
1504
|
+
class: "pp-message-menu",
|
|
1505
|
+
style: { top: `${messageMenu.y}px`, left: `${messageMenu.x}px` },
|
|
1506
|
+
children: [
|
|
1507
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => handleReply(messageMenu.message), children: [
|
|
1508
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplyIcon, { color: actionIconColor }),
|
|
1509
|
+
" Reply"
|
|
1510
|
+
] }),
|
|
1511
|
+
messageMenu.message.sender === "visitor" && !messageMenu.message.deletedAt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1512
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => handleStartEdit(messageMenu.message), children: [
|
|
1513
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditIcon, { color: actionIconColor }),
|
|
1514
|
+
" Edit"
|
|
1515
|
+
] }),
|
|
1516
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { class: "pp-menu-delete", onClick: () => handleDelete(messageMenu.message), children: [
|
|
1517
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteIcon, { color: "#ef4444" }),
|
|
1518
|
+
" Delete"
|
|
1519
|
+
] })
|
|
1520
|
+
] })
|
|
1521
|
+
]
|
|
1522
|
+
}
|
|
1523
|
+
),
|
|
1524
|
+
editingMessage && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-edit-modal", children: [
|
|
1525
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-edit-header", children: [
|
|
1526
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Edit message" }),
|
|
1527
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: handleCancelEdit, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) })
|
|
1528
|
+
] }),
|
|
1529
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1530
|
+
"textarea",
|
|
1531
|
+
{
|
|
1532
|
+
class: "pp-edit-input",
|
|
1533
|
+
value: editContent,
|
|
1534
|
+
onInput: (e) => setEditContent(e.target.value),
|
|
1535
|
+
autoFocus: true
|
|
1536
|
+
}
|
|
1537
|
+
),
|
|
1538
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-edit-actions", children: [
|
|
1539
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { class: "pp-edit-cancel", onClick: handleCancelEdit, children: "Cancel" }),
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { class: "pp-edit-save", onClick: handleSaveEdit, disabled: !editContent.trim(), children: "Save" })
|
|
1541
|
+
] })
|
|
1542
|
+
] }),
|
|
1543
|
+
replyingTo && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-reply-preview", children: [
|
|
1544
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-reply-preview-content", children: [
|
|
1545
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-reply-label", children: "Replying to" }),
|
|
1546
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { class: "pp-reply-text", children: [
|
|
1547
|
+
replyingTo.content.slice(0, 50),
|
|
1548
|
+
replyingTo.content.length > 50 ? "..." : ""
|
|
1549
|
+
] })
|
|
1550
|
+
] }),
|
|
1551
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { class: "pp-reply-cancel", onClick: handleCancelReply, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) })
|
|
1552
|
+
] }),
|
|
1553
|
+
pendingAttachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-attachments-preview", children: pendingAttachments.map((pending) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: `pp-attachment-preview pp-attachment-${pending.status}`, children: [
|
|
1554
|
+
pending.preview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: pending.preview, alt: pending.file.name, class: "pp-preview-img" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-preview-file", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileIcon, { mimeType: pending.file.type }) }),
|
|
1555
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1556
|
+
"button",
|
|
1557
|
+
{
|
|
1558
|
+
class: "pp-remove-attachment",
|
|
1559
|
+
onClick: () => handleRemoveAttachment(pending.id),
|
|
1560
|
+
"aria-label": "Remove attachment",
|
|
1561
|
+
type: "button",
|
|
1562
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {})
|
|
1563
|
+
}
|
|
1564
|
+
),
|
|
1565
|
+
pending.status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-upload-progress", style: { width: `${pending.progress}%` } }),
|
|
1566
|
+
pending.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-upload-error", title: pending.error, children: "!" })
|
|
1567
|
+
] }, pending.id)) }),
|
|
1568
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { class: "pp-input-form", onSubmit: handleSubmit, children: [
|
|
1569
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1570
|
+
"input",
|
|
1571
|
+
{
|
|
1572
|
+
ref: (el) => {
|
|
1573
|
+
fileInputRef.current = el;
|
|
1574
|
+
if (el) {
|
|
1575
|
+
el.onchange = handleFileSelect;
|
|
1576
|
+
}
|
|
1577
|
+
},
|
|
1578
|
+
type: "file",
|
|
1579
|
+
class: "pp-file-input",
|
|
1580
|
+
accept: "image/*,audio/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.txt",
|
|
1581
|
+
multiple: true
|
|
1582
|
+
}
|
|
1583
|
+
),
|
|
1584
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1585
|
+
"button",
|
|
1586
|
+
{
|
|
1587
|
+
type: "button",
|
|
1588
|
+
class: "pp-attach-btn",
|
|
1589
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1590
|
+
disabled: !isConnected || isUploading,
|
|
1591
|
+
"aria-label": "Attach file",
|
|
1592
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AttachIcon, {})
|
|
1593
|
+
}
|
|
1594
|
+
),
|
|
1595
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1596
|
+
"input",
|
|
1597
|
+
{
|
|
1598
|
+
ref: inputRef,
|
|
1599
|
+
type: "text",
|
|
1600
|
+
class: "pp-input",
|
|
1601
|
+
placeholder: config.placeholder ?? "Type a message...",
|
|
1602
|
+
value: inputValue,
|
|
1603
|
+
onInput: handleInputChange,
|
|
1604
|
+
disabled: !isConnected
|
|
1605
|
+
}
|
|
1606
|
+
),
|
|
1607
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1608
|
+
"button",
|
|
1609
|
+
{
|
|
1610
|
+
type: "submit",
|
|
1611
|
+
class: "pp-send-btn",
|
|
1612
|
+
disabled: !inputValue.trim() && pendingAttachments.filter((a) => a.status === "ready").length === 0 || !isConnected || isUploading,
|
|
1613
|
+
"aria-label": "Send message",
|
|
1614
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendIcon, {})
|
|
1615
|
+
}
|
|
1616
|
+
)
|
|
1617
|
+
] }),
|
|
1618
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-footer", children: [
|
|
1619
|
+
"Powered by ",
|
|
1620
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: "https://pocketping.io", target: "_blank", rel: "noopener", children: "PocketPing" })
|
|
1621
|
+
] })
|
|
1622
|
+
]
|
|
1623
|
+
}
|
|
1624
|
+
)
|
|
636
1625
|
] });
|
|
637
1626
|
}
|
|
638
1627
|
function checkPageVisibility(config) {
|
|
@@ -688,6 +1677,89 @@ function StatusIcon({ status }) {
|
|
|
688
1677
|
}
|
|
689
1678
|
return null;
|
|
690
1679
|
}
|
|
1680
|
+
function AttachIcon() {
|
|
1681
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
|
|
1682
|
+
}
|
|
1683
|
+
function ReplyIcon({ color, size = 16 }) {
|
|
1684
|
+
const strokeColor = color || "currentColor";
|
|
1685
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", "stroke-width": "2", style: { stroke: strokeColor, width: `${size}px`, minWidth: `${size}px`, height: `${size}px`, display: "block", flexShrink: 0 }, children: [
|
|
1686
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "9 17 4 12 9 7" }),
|
|
1687
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
1688
|
+
] });
|
|
1689
|
+
}
|
|
1690
|
+
function EditIcon({ color, size = 16 }) {
|
|
1691
|
+
const strokeColor = color || "currentColor";
|
|
1692
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", "stroke-width": "2", style: { stroke: strokeColor, width: `${size}px`, minWidth: `${size}px`, height: `${size}px`, display: "block", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" }) });
|
|
1693
|
+
}
|
|
1694
|
+
function DeleteIcon({ color, size = 16 }) {
|
|
1695
|
+
const strokeColor = color || "currentColor";
|
|
1696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", "stroke-width": "2", style: { stroke: strokeColor, width: `${size}px`, minWidth: `${size}px`, height: `${size}px`, display: "block", flexShrink: 0 }, children: [
|
|
1697
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3 6 5 6 21 6" }),
|
|
1698
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
1699
|
+
] });
|
|
1700
|
+
}
|
|
1701
|
+
function FileIcon({ mimeType }) {
|
|
1702
|
+
if (mimeType === "application/pdf") {
|
|
1703
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1704
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
1705
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "14 2 14 8 20 8" }),
|
|
1706
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 15h6" }),
|
|
1707
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 11h6" })
|
|
1708
|
+
] });
|
|
1709
|
+
}
|
|
1710
|
+
if (mimeType.startsWith("audio/")) {
|
|
1711
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1712
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 18V5l12-2v13" }),
|
|
1713
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "18", r: "3" }),
|
|
1714
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "18", cy: "16", r: "3" })
|
|
1715
|
+
] });
|
|
1716
|
+
}
|
|
1717
|
+
if (mimeType.startsWith("video/")) {
|
|
1718
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1719
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "2", width: "20", height: "20", rx: "2.18", ry: "2.18" }),
|
|
1720
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "7", y1: "2", x2: "7", y2: "22" }),
|
|
1721
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "17", y1: "2", x2: "17", y2: "22" }),
|
|
1722
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2", y1: "12", x2: "22", y2: "12" }),
|
|
1723
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2", y1: "7", x2: "7", y2: "7" }),
|
|
1724
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2", y1: "17", x2: "7", y2: "17" }),
|
|
1725
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "17", y1: "17", x2: "22", y2: "17" }),
|
|
1726
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "17", y1: "7", x2: "22", y2: "7" })
|
|
1727
|
+
] });
|
|
1728
|
+
}
|
|
1729
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1730
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
1731
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "14 2 14 8 20 8" })
|
|
1732
|
+
] });
|
|
1733
|
+
}
|
|
1734
|
+
function AttachmentDisplay({ attachment }) {
|
|
1735
|
+
const isImage = attachment.mimeType.startsWith("image/");
|
|
1736
|
+
const isAudio = attachment.mimeType.startsWith("audio/");
|
|
1737
|
+
const isVideo = attachment.mimeType.startsWith("video/");
|
|
1738
|
+
const formatSize = (bytes) => {
|
|
1739
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1740
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1741
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1742
|
+
};
|
|
1743
|
+
if (isImage) {
|
|
1744
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: attachment.url, target: "_blank", rel: "noopener", class: "pp-attachment pp-attachment-image", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: attachment.thumbnailUrl || attachment.url, alt: attachment.filename }) });
|
|
1745
|
+
}
|
|
1746
|
+
if (isAudio) {
|
|
1747
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-attachment pp-attachment-audio", children: [
|
|
1748
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("audio", { controls: true, preload: "metadata", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("source", { src: attachment.url, type: attachment.mimeType }) }),
|
|
1749
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-attachment-name", children: attachment.filename })
|
|
1750
|
+
] });
|
|
1751
|
+
}
|
|
1752
|
+
if (isVideo) {
|
|
1753
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-attachment pp-attachment-video", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("video", { controls: true, preload: "metadata", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("source", { src: attachment.url, type: attachment.mimeType }) }) });
|
|
1754
|
+
}
|
|
1755
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("a", { href: attachment.url, target: "_blank", rel: "noopener", class: "pp-attachment pp-attachment-file", children: [
|
|
1756
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileIcon, { mimeType: attachment.mimeType }),
|
|
1757
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-attachment-info", children: [
|
|
1758
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-attachment-name", children: attachment.filename }),
|
|
1759
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-attachment-size", children: formatSize(attachment.size) })
|
|
1760
|
+
] })
|
|
1761
|
+
] });
|
|
1762
|
+
}
|
|
691
1763
|
|
|
692
1764
|
// src/version.ts
|
|
693
1765
|
var VERSION = "0.3.6";
|
|
@@ -803,7 +1875,7 @@ var PocketPingClient = class {
|
|
|
803
1875
|
this.cleanupTrackedElements();
|
|
804
1876
|
this.disableInspectorMode();
|
|
805
1877
|
}
|
|
806
|
-
async sendMessage(content) {
|
|
1878
|
+
async sendMessage(content, attachmentIds, replyTo) {
|
|
807
1879
|
if (!this.session) {
|
|
808
1880
|
throw new Error("Not connected");
|
|
809
1881
|
}
|
|
@@ -814,7 +1886,8 @@ var PocketPingClient = class {
|
|
|
814
1886
|
content,
|
|
815
1887
|
sender: "visitor",
|
|
816
1888
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
817
|
-
status: "sending"
|
|
1889
|
+
status: "sending",
|
|
1890
|
+
replyTo
|
|
818
1891
|
};
|
|
819
1892
|
this.session.messages.push(tempMessage);
|
|
820
1893
|
this.emit("message", tempMessage);
|
|
@@ -824,7 +1897,9 @@ var PocketPingClient = class {
|
|
|
824
1897
|
body: JSON.stringify({
|
|
825
1898
|
sessionId: this.session.sessionId,
|
|
826
1899
|
content,
|
|
827
|
-
sender: "visitor"
|
|
1900
|
+
sender: "visitor",
|
|
1901
|
+
attachmentIds: attachmentIds || [],
|
|
1902
|
+
replyTo
|
|
828
1903
|
})
|
|
829
1904
|
});
|
|
830
1905
|
const messageIndex = this.session.messages.findIndex((m) => m.id === tempId);
|
|
@@ -832,6 +1907,9 @@ var PocketPingClient = class {
|
|
|
832
1907
|
this.session.messages[messageIndex].id = response.messageId;
|
|
833
1908
|
this.session.messages[messageIndex].timestamp = response.timestamp;
|
|
834
1909
|
this.session.messages[messageIndex].status = "sent";
|
|
1910
|
+
if (response.attachments && response.attachments.length > 0) {
|
|
1911
|
+
this.session.messages[messageIndex].attachments = response.attachments;
|
|
1912
|
+
}
|
|
835
1913
|
this.emit("message", this.session.messages[messageIndex]);
|
|
836
1914
|
}
|
|
837
1915
|
const message = this.session.messages[messageIndex] || {
|
|
@@ -840,7 +1918,8 @@ var PocketPingClient = class {
|
|
|
840
1918
|
content,
|
|
841
1919
|
sender: "visitor",
|
|
842
1920
|
timestamp: response.timestamp,
|
|
843
|
-
status: "sent"
|
|
1921
|
+
status: "sent",
|
|
1922
|
+
attachments: response.attachments
|
|
844
1923
|
};
|
|
845
1924
|
this.config.onMessage?.(message);
|
|
846
1925
|
return message;
|
|
@@ -853,6 +1932,110 @@ var PocketPingClient = class {
|
|
|
853
1932
|
throw error;
|
|
854
1933
|
}
|
|
855
1934
|
}
|
|
1935
|
+
/**
|
|
1936
|
+
* Upload a file attachment
|
|
1937
|
+
* Returns the attachment data after successful upload
|
|
1938
|
+
* @param file - File object to upload
|
|
1939
|
+
* @param onProgress - Optional callback for upload progress (0-100)
|
|
1940
|
+
* @example
|
|
1941
|
+
* const attachment = await PocketPing.uploadFile(file, (progress) => {
|
|
1942
|
+
* console.log(`Upload ${progress}% complete`)
|
|
1943
|
+
* })
|
|
1944
|
+
* await PocketPing.sendMessage('Check this file', [attachment.id])
|
|
1945
|
+
*/
|
|
1946
|
+
async uploadFile(file, onProgress) {
|
|
1947
|
+
if (!this.session) {
|
|
1948
|
+
throw new Error("Not connected");
|
|
1949
|
+
}
|
|
1950
|
+
this.emit("uploadStart", { filename: file.name, size: file.size });
|
|
1951
|
+
try {
|
|
1952
|
+
const initResponse = await this.fetch("/upload", {
|
|
1953
|
+
method: "POST",
|
|
1954
|
+
body: JSON.stringify({
|
|
1955
|
+
sessionId: this.session.sessionId,
|
|
1956
|
+
filename: file.name,
|
|
1957
|
+
mimeType: file.type || "application/octet-stream",
|
|
1958
|
+
size: file.size
|
|
1959
|
+
})
|
|
1960
|
+
});
|
|
1961
|
+
onProgress?.(10);
|
|
1962
|
+
this.emit("uploadProgress", { filename: file.name, progress: 10 });
|
|
1963
|
+
await this.uploadToPresignedUrl(initResponse.uploadUrl, file, (progress) => {
|
|
1964
|
+
const mappedProgress = 10 + progress * 0.8;
|
|
1965
|
+
onProgress?.(mappedProgress);
|
|
1966
|
+
this.emit("uploadProgress", { filename: file.name, progress: mappedProgress });
|
|
1967
|
+
});
|
|
1968
|
+
const completeResponse = await this.fetch("/upload/complete", {
|
|
1969
|
+
method: "POST",
|
|
1970
|
+
body: JSON.stringify({
|
|
1971
|
+
sessionId: this.session.sessionId,
|
|
1972
|
+
attachmentId: initResponse.attachmentId
|
|
1973
|
+
})
|
|
1974
|
+
});
|
|
1975
|
+
onProgress?.(100);
|
|
1976
|
+
this.emit("uploadComplete", completeResponse);
|
|
1977
|
+
return {
|
|
1978
|
+
id: completeResponse.id,
|
|
1979
|
+
filename: completeResponse.filename,
|
|
1980
|
+
mimeType: completeResponse.mimeType,
|
|
1981
|
+
size: completeResponse.size,
|
|
1982
|
+
url: completeResponse.url,
|
|
1983
|
+
thumbnailUrl: completeResponse.thumbnailUrl,
|
|
1984
|
+
status: completeResponse.status
|
|
1985
|
+
};
|
|
1986
|
+
} catch (error) {
|
|
1987
|
+
this.emit("uploadError", { filename: file.name, error });
|
|
1988
|
+
throw error;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Upload multiple files at once
|
|
1993
|
+
* @param files - Array of File objects to upload
|
|
1994
|
+
* @param onProgress - Optional callback for overall progress (0-100)
|
|
1995
|
+
* @returns Array of uploaded attachments
|
|
1996
|
+
*/
|
|
1997
|
+
async uploadFiles(files, onProgress) {
|
|
1998
|
+
const attachments = [];
|
|
1999
|
+
const totalFiles = files.length;
|
|
2000
|
+
for (let i = 0; i < totalFiles; i++) {
|
|
2001
|
+
const file = files[i];
|
|
2002
|
+
const baseProgress = i / totalFiles * 100;
|
|
2003
|
+
const fileProgress = 100 / totalFiles;
|
|
2004
|
+
const attachment = await this.uploadFile(file, (progress) => {
|
|
2005
|
+
const totalProgress = baseProgress + progress / 100 * fileProgress;
|
|
2006
|
+
onProgress?.(totalProgress);
|
|
2007
|
+
});
|
|
2008
|
+
attachments.push(attachment);
|
|
2009
|
+
}
|
|
2010
|
+
return attachments;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Upload file to presigned URL with progress tracking
|
|
2014
|
+
*/
|
|
2015
|
+
uploadToPresignedUrl(url, file, onProgress) {
|
|
2016
|
+
return new Promise((resolve, reject) => {
|
|
2017
|
+
const xhr = new XMLHttpRequest();
|
|
2018
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
2019
|
+
if (event.lengthComputable) {
|
|
2020
|
+
const progress = event.loaded / event.total * 100;
|
|
2021
|
+
onProgress?.(progress);
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
xhr.addEventListener("load", () => {
|
|
2025
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
2026
|
+
resolve();
|
|
2027
|
+
} else {
|
|
2028
|
+
reject(new Error(`Upload failed with status ${xhr.status}`));
|
|
2029
|
+
}
|
|
2030
|
+
});
|
|
2031
|
+
xhr.addEventListener("error", () => {
|
|
2032
|
+
reject(new Error("Upload failed"));
|
|
2033
|
+
});
|
|
2034
|
+
xhr.open("PUT", url);
|
|
2035
|
+
xhr.setRequestHeader("Content-Type", file.type || "application/octet-stream");
|
|
2036
|
+
xhr.send(file);
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
856
2039
|
async fetchMessages(after) {
|
|
857
2040
|
if (!this.session) {
|
|
858
2041
|
throw new Error("Not connected");
|
|
@@ -906,6 +2089,54 @@ var PocketPingClient = class {
|
|
|
906
2089
|
console.error("[PocketPing] Failed to send read status:", err);
|
|
907
2090
|
}
|
|
908
2091
|
}
|
|
2092
|
+
/**
|
|
2093
|
+
* Edit a message (visitor can only edit their own messages)
|
|
2094
|
+
* @param messageId - ID of the message to edit
|
|
2095
|
+
* @param content - New content for the message
|
|
2096
|
+
*/
|
|
2097
|
+
async editMessage(messageId, content) {
|
|
2098
|
+
if (!this.session) {
|
|
2099
|
+
throw new Error("Not connected");
|
|
2100
|
+
}
|
|
2101
|
+
const response = await this.fetch(`/message/${messageId}`, {
|
|
2102
|
+
method: "PATCH",
|
|
2103
|
+
body: JSON.stringify({
|
|
2104
|
+
sessionId: this.session.sessionId,
|
|
2105
|
+
content
|
|
2106
|
+
})
|
|
2107
|
+
});
|
|
2108
|
+
const messageIndex = this.session.messages.findIndex((m) => m.id === messageId);
|
|
2109
|
+
if (messageIndex >= 0) {
|
|
2110
|
+
this.session.messages[messageIndex].content = response.message.content;
|
|
2111
|
+
this.session.messages[messageIndex].editedAt = response.message.editedAt;
|
|
2112
|
+
this.emit("messageEdited", this.session.messages[messageIndex]);
|
|
2113
|
+
}
|
|
2114
|
+
return this.session.messages[messageIndex];
|
|
2115
|
+
}
|
|
2116
|
+
/**
|
|
2117
|
+
* Delete a message (soft delete - visitor can only delete their own messages)
|
|
2118
|
+
* @param messageId - ID of the message to delete
|
|
2119
|
+
*/
|
|
2120
|
+
async deleteMessage(messageId) {
|
|
2121
|
+
if (!this.session) {
|
|
2122
|
+
throw new Error("Not connected");
|
|
2123
|
+
}
|
|
2124
|
+
const response = await this.fetch(`/message/${messageId}`, {
|
|
2125
|
+
method: "DELETE",
|
|
2126
|
+
body: JSON.stringify({
|
|
2127
|
+
sessionId: this.session.sessionId
|
|
2128
|
+
})
|
|
2129
|
+
});
|
|
2130
|
+
if (response.deleted) {
|
|
2131
|
+
const messageIndex = this.session.messages.findIndex((m) => m.id === messageId);
|
|
2132
|
+
if (messageIndex >= 0) {
|
|
2133
|
+
this.session.messages[messageIndex].deletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2134
|
+
this.session.messages[messageIndex].content = "";
|
|
2135
|
+
this.emit("messageDeleted", this.session.messages[messageIndex]);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
return response.deleted;
|
|
2139
|
+
}
|
|
909
2140
|
async getPresence() {
|
|
910
2141
|
return this.fetch("/presence", { method: "GET" });
|
|
911
2142
|
}
|
|
@@ -1460,7 +2691,14 @@ var PocketPingClient = class {
|
|
|
1460
2691
|
}
|
|
1461
2692
|
connectSSE() {
|
|
1462
2693
|
if (!this.session) return;
|
|
1463
|
-
const
|
|
2694
|
+
const params = new URLSearchParams({
|
|
2695
|
+
sessionId: this.session.sessionId
|
|
2696
|
+
});
|
|
2697
|
+
const lastEventTimestamp = this.getLastEventTimestamp();
|
|
2698
|
+
if (lastEventTimestamp) {
|
|
2699
|
+
params.set("after", lastEventTimestamp);
|
|
2700
|
+
}
|
|
2701
|
+
const sseUrl = this.config.endpoint.replace(/\/$/, "") + `/stream?${params.toString()}`;
|
|
1464
2702
|
try {
|
|
1465
2703
|
this.sse = new EventSource(sseUrl);
|
|
1466
2704
|
const connectionTimeout = setTimeout(() => {
|
|
@@ -1515,6 +2753,19 @@ var PocketPingClient = class {
|
|
|
1515
2753
|
handleRealtimeEvent(event) {
|
|
1516
2754
|
this.handleWebSocketEvent(event);
|
|
1517
2755
|
}
|
|
2756
|
+
getLastEventTimestamp() {
|
|
2757
|
+
if (!this.session) return null;
|
|
2758
|
+
let latest = null;
|
|
2759
|
+
for (const msg of this.session.messages) {
|
|
2760
|
+
const candidates = [msg.timestamp, msg.editedAt, msg.deletedAt, msg.deliveredAt, msg.readAt].filter(Boolean).map((value) => new Date(value)).filter((date) => !isNaN(date.getTime()));
|
|
2761
|
+
for (const date of candidates) {
|
|
2762
|
+
if (!latest || date > latest) {
|
|
2763
|
+
latest = date;
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
return latest ? latest.toISOString() : null;
|
|
2768
|
+
}
|
|
1518
2769
|
handleWebSocketEvent(event) {
|
|
1519
2770
|
switch (event.type) {
|
|
1520
2771
|
case "message":
|
|
@@ -1537,12 +2788,41 @@ var PocketPingClient = class {
|
|
|
1537
2788
|
}
|
|
1538
2789
|
if (existingIndex >= 0) {
|
|
1539
2790
|
const existing = this.session.messages[existingIndex];
|
|
2791
|
+
let updated = false;
|
|
1540
2792
|
if (message.status && message.status !== existing.status) {
|
|
1541
2793
|
existing.status = message.status;
|
|
1542
|
-
|
|
1543
|
-
if (message.
|
|
2794
|
+
updated = true;
|
|
2795
|
+
if (message.deliveredAt) {
|
|
2796
|
+
existing.deliveredAt = message.deliveredAt;
|
|
2797
|
+
}
|
|
2798
|
+
if (message.readAt) {
|
|
2799
|
+
existing.readAt = message.readAt;
|
|
2800
|
+
}
|
|
1544
2801
|
this.emit("read", { messageIds: [message.id], status: message.status });
|
|
1545
2802
|
}
|
|
2803
|
+
if (message.content !== void 0 && message.content !== existing.content) {
|
|
2804
|
+
existing.content = message.content;
|
|
2805
|
+
updated = true;
|
|
2806
|
+
}
|
|
2807
|
+
if (message.editedAt !== void 0 && message.editedAt !== existing.editedAt) {
|
|
2808
|
+
existing.editedAt = message.editedAt;
|
|
2809
|
+
updated = true;
|
|
2810
|
+
}
|
|
2811
|
+
if (message.deletedAt !== void 0 && message.deletedAt !== existing.deletedAt) {
|
|
2812
|
+
existing.deletedAt = message.deletedAt;
|
|
2813
|
+
updated = true;
|
|
2814
|
+
}
|
|
2815
|
+
if (message.replyTo !== void 0) {
|
|
2816
|
+
existing.replyTo = message.replyTo;
|
|
2817
|
+
updated = true;
|
|
2818
|
+
}
|
|
2819
|
+
if (message.attachments !== void 0) {
|
|
2820
|
+
existing.attachments = message.attachments;
|
|
2821
|
+
updated = true;
|
|
2822
|
+
}
|
|
2823
|
+
if (updated) {
|
|
2824
|
+
this.emit("message", existing);
|
|
2825
|
+
}
|
|
1546
2826
|
} else {
|
|
1547
2827
|
this.session.messages.push(message);
|
|
1548
2828
|
this.emit("message", message);
|
|
@@ -1581,6 +2861,29 @@ var PocketPingClient = class {
|
|
|
1581
2861
|
}
|
|
1582
2862
|
this.emit("read", readData);
|
|
1583
2863
|
break;
|
|
2864
|
+
case "message_edited":
|
|
2865
|
+
if (this.session) {
|
|
2866
|
+
const editData = event.data;
|
|
2867
|
+
const msgIndex = this.session.messages.findIndex((m) => m.id === editData.messageId);
|
|
2868
|
+
if (msgIndex >= 0) {
|
|
2869
|
+
const existing = this.session.messages[msgIndex];
|
|
2870
|
+
existing.content = editData.content;
|
|
2871
|
+
existing.editedAt = editData.editedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2872
|
+
this.emit("message", existing);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
break;
|
|
2876
|
+
case "message_deleted":
|
|
2877
|
+
if (this.session) {
|
|
2878
|
+
const deleteData = event.data;
|
|
2879
|
+
const msgIndex = this.session.messages.findIndex((m) => m.id === deleteData.messageId);
|
|
2880
|
+
if (msgIndex >= 0) {
|
|
2881
|
+
const existing = this.session.messages[msgIndex];
|
|
2882
|
+
existing.deletedAt = deleteData.deletedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2883
|
+
this.emit("message", existing);
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
break;
|
|
1584
2887
|
case "event":
|
|
1585
2888
|
const customEvent = event.data;
|
|
1586
2889
|
this.emitCustomEvent(customEvent);
|
|
@@ -1640,11 +2943,45 @@ var PocketPingClient = class {
|
|
|
1640
2943
|
const poll = async () => {
|
|
1641
2944
|
if (!this.session) return;
|
|
1642
2945
|
try {
|
|
1643
|
-
const
|
|
1644
|
-
const newMessages = await this.fetchMessages(
|
|
2946
|
+
const lastEventTimestamp = this.getLastEventTimestamp();
|
|
2947
|
+
const newMessages = await this.fetchMessages(lastEventTimestamp ?? void 0);
|
|
1645
2948
|
this.pollingFailures = 0;
|
|
1646
2949
|
for (const message of newMessages) {
|
|
1647
|
-
|
|
2950
|
+
const existingIndex = this.session.messages.findIndex((m) => m.id === message.id);
|
|
2951
|
+
if (existingIndex >= 0) {
|
|
2952
|
+
const existing = this.session.messages[existingIndex];
|
|
2953
|
+
let updated = false;
|
|
2954
|
+
if (message.status && message.status !== existing.status) {
|
|
2955
|
+
existing.status = message.status;
|
|
2956
|
+
updated = true;
|
|
2957
|
+
if (message.deliveredAt) existing.deliveredAt = message.deliveredAt;
|
|
2958
|
+
if (message.readAt) existing.readAt = message.readAt;
|
|
2959
|
+
this.emit("read", { messageIds: [message.id], status: message.status });
|
|
2960
|
+
}
|
|
2961
|
+
if (message.content !== void 0 && message.content !== existing.content) {
|
|
2962
|
+
existing.content = message.content;
|
|
2963
|
+
updated = true;
|
|
2964
|
+
}
|
|
2965
|
+
if (message.editedAt !== void 0 && message.editedAt !== existing.editedAt) {
|
|
2966
|
+
existing.editedAt = message.editedAt;
|
|
2967
|
+
updated = true;
|
|
2968
|
+
}
|
|
2969
|
+
if (message.deletedAt !== void 0 && message.deletedAt !== existing.deletedAt) {
|
|
2970
|
+
existing.deletedAt = message.deletedAt;
|
|
2971
|
+
updated = true;
|
|
2972
|
+
}
|
|
2973
|
+
if (message.replyTo !== void 0) {
|
|
2974
|
+
existing.replyTo = message.replyTo;
|
|
2975
|
+
updated = true;
|
|
2976
|
+
}
|
|
2977
|
+
if (message.attachments !== void 0) {
|
|
2978
|
+
existing.attachments = message.attachments;
|
|
2979
|
+
updated = true;
|
|
2980
|
+
}
|
|
2981
|
+
if (updated) {
|
|
2982
|
+
this.emit("message", existing);
|
|
2983
|
+
}
|
|
2984
|
+
} else {
|
|
1648
2985
|
this.session.messages.push(message);
|
|
1649
2986
|
this.emit("message", message);
|
|
1650
2987
|
this.config.onMessage?.(message);
|
|
@@ -1809,11 +3146,23 @@ function close() {
|
|
|
1809
3146
|
function toggle() {
|
|
1810
3147
|
client?.toggleOpen();
|
|
1811
3148
|
}
|
|
1812
|
-
function sendMessage(content) {
|
|
3149
|
+
function sendMessage(content, attachmentIds) {
|
|
3150
|
+
if (!client) {
|
|
3151
|
+
throw new Error("[PocketPing] Not initialized");
|
|
3152
|
+
}
|
|
3153
|
+
return client.sendMessage(content, attachmentIds);
|
|
3154
|
+
}
|
|
3155
|
+
async function uploadFile(file, onProgress) {
|
|
3156
|
+
if (!client) {
|
|
3157
|
+
throw new Error("[PocketPing] Not initialized");
|
|
3158
|
+
}
|
|
3159
|
+
return client.uploadFile(file, onProgress);
|
|
3160
|
+
}
|
|
3161
|
+
async function uploadFiles(files, onProgress) {
|
|
1813
3162
|
if (!client) {
|
|
1814
3163
|
throw new Error("[PocketPing] Not initialized");
|
|
1815
3164
|
}
|
|
1816
|
-
return client.
|
|
3165
|
+
return client.uploadFiles(files, onProgress);
|
|
1817
3166
|
}
|
|
1818
3167
|
function trigger(eventName, data, options) {
|
|
1819
3168
|
if (!client) {
|
|
@@ -1882,7 +3231,7 @@ if (typeof document !== "undefined") {
|
|
|
1882
3231
|
}
|
|
1883
3232
|
}
|
|
1884
3233
|
}
|
|
1885
|
-
var index_default = { init, destroy, open, close, toggle, sendMessage, trigger, onEvent, offEvent, on, identify, reset, getIdentity, setupTrackedElements, getTrackedElements };
|
|
3234
|
+
var index_default = { init, destroy, open, close, toggle, sendMessage, uploadFile, uploadFiles, trigger, onEvent, offEvent, on, identify, reset, getIdentity, setupTrackedElements, getTrackedElements };
|
|
1886
3235
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1887
3236
|
0 && (module.exports = {
|
|
1888
3237
|
close,
|
|
@@ -1899,5 +3248,7 @@ var index_default = { init, destroy, open, close, toggle, sendMessage, trigger,
|
|
|
1899
3248
|
sendMessage,
|
|
1900
3249
|
setupTrackedElements,
|
|
1901
3250
|
toggle,
|
|
1902
|
-
trigger
|
|
3251
|
+
trigger,
|
|
3252
|
+
uploadFile,
|
|
3253
|
+
uploadFiles
|
|
1903
3254
|
});
|