@pocketping/widget 1.1.0 → 1.2.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 +1157 -90
- package/dist/index.d.cts +75 -3
- package/dist/index.d.ts +75 -3
- package/dist/index.js +1154 -89
- package/dist/pocketping.min.global.js +493 -7
- 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 {
|
|
@@ -411,6 +419,486 @@ function styles(primaryColor, theme) {
|
|
|
411
419
|
.pp-footer a:hover {
|
|
412
420
|
text-decoration: underline;
|
|
413
421
|
}
|
|
422
|
+
|
|
423
|
+
/* Attachment Styles */
|
|
424
|
+
.pp-file-input {
|
|
425
|
+
/* Use offscreen positioning instead of display:none for better browser compatibility */
|
|
426
|
+
position: absolute;
|
|
427
|
+
width: 1px;
|
|
428
|
+
height: 1px;
|
|
429
|
+
padding: 0;
|
|
430
|
+
margin: -1px;
|
|
431
|
+
overflow: hidden;
|
|
432
|
+
clip: rect(0, 0, 0, 0);
|
|
433
|
+
white-space: nowrap;
|
|
434
|
+
border: 0;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.pp-attach-btn {
|
|
438
|
+
width: 40px;
|
|
439
|
+
height: 40px;
|
|
440
|
+
border-radius: 50%;
|
|
441
|
+
background: transparent;
|
|
442
|
+
color: ${colors.textSecondary};
|
|
443
|
+
border: 1px solid ${colors.border};
|
|
444
|
+
cursor: pointer;
|
|
445
|
+
display: flex;
|
|
446
|
+
align-items: center;
|
|
447
|
+
justify-content: center;
|
|
448
|
+
transition: color 0.2s, border-color 0.2s;
|
|
449
|
+
flex-shrink: 0;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.pp-attach-btn:hover:not(:disabled) {
|
|
453
|
+
color: ${primaryColor};
|
|
454
|
+
border-color: ${primaryColor};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.pp-attach-btn:disabled {
|
|
458
|
+
opacity: 0.5;
|
|
459
|
+
cursor: not-allowed;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.pp-attach-btn svg {
|
|
463
|
+
width: 18px;
|
|
464
|
+
height: 18px;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.pp-attachments-preview {
|
|
468
|
+
display: flex;
|
|
469
|
+
gap: 8px;
|
|
470
|
+
padding: 8px 12px;
|
|
471
|
+
border-top: 1px solid ${colors.border};
|
|
472
|
+
overflow-x: auto;
|
|
473
|
+
background: ${colors.bgSecondary};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.pp-attachment-preview {
|
|
477
|
+
position: relative;
|
|
478
|
+
width: 60px;
|
|
479
|
+
height: 60px;
|
|
480
|
+
border-radius: 8px;
|
|
481
|
+
overflow: hidden;
|
|
482
|
+
flex-shrink: 0;
|
|
483
|
+
background: ${colors.bg};
|
|
484
|
+
border: 1px solid ${colors.border};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.pp-preview-img {
|
|
488
|
+
width: 100%;
|
|
489
|
+
height: 100%;
|
|
490
|
+
object-fit: cover;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.pp-preview-file {
|
|
494
|
+
width: 100%;
|
|
495
|
+
height: 100%;
|
|
496
|
+
display: flex;
|
|
497
|
+
align-items: center;
|
|
498
|
+
justify-content: center;
|
|
499
|
+
color: ${colors.textSecondary};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.pp-preview-file svg {
|
|
503
|
+
width: 24px;
|
|
504
|
+
height: 24px;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.pp-remove-attachment {
|
|
508
|
+
position: absolute;
|
|
509
|
+
top: 2px;
|
|
510
|
+
right: 2px;
|
|
511
|
+
width: 18px;
|
|
512
|
+
height: 18px;
|
|
513
|
+
border-radius: 50%;
|
|
514
|
+
background: rgba(0, 0, 0, 0.6);
|
|
515
|
+
color: white;
|
|
516
|
+
border: none;
|
|
517
|
+
cursor: pointer;
|
|
518
|
+
display: flex;
|
|
519
|
+
align-items: center;
|
|
520
|
+
justify-content: center;
|
|
521
|
+
padding: 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
.pp-remove-attachment svg {
|
|
525
|
+
width: 10px;
|
|
526
|
+
height: 10px;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.pp-upload-progress {
|
|
530
|
+
position: absolute;
|
|
531
|
+
bottom: 0;
|
|
532
|
+
left: 0;
|
|
533
|
+
height: 3px;
|
|
534
|
+
background: ${primaryColor};
|
|
535
|
+
transition: width 0.1s;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.pp-upload-error {
|
|
539
|
+
position: absolute;
|
|
540
|
+
top: 50%;
|
|
541
|
+
left: 50%;
|
|
542
|
+
transform: translate(-50%, -50%);
|
|
543
|
+
width: 24px;
|
|
544
|
+
height: 24px;
|
|
545
|
+
border-radius: 50%;
|
|
546
|
+
background: #ef4444;
|
|
547
|
+
color: white;
|
|
548
|
+
font-weight: bold;
|
|
549
|
+
display: flex;
|
|
550
|
+
align-items: center;
|
|
551
|
+
justify-content: center;
|
|
552
|
+
font-size: 14px;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.pp-attachment-uploading {
|
|
556
|
+
opacity: 0.7;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.pp-attachment-error {
|
|
560
|
+
border-color: #ef4444;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/* Message Attachments */
|
|
564
|
+
.pp-message-attachments {
|
|
565
|
+
display: flex;
|
|
566
|
+
flex-direction: column;
|
|
567
|
+
gap: 8px;
|
|
568
|
+
margin-top: 4px;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.pp-attachment {
|
|
572
|
+
display: block;
|
|
573
|
+
text-decoration: none;
|
|
574
|
+
color: inherit;
|
|
575
|
+
border-radius: 8px;
|
|
576
|
+
overflow: hidden;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.pp-attachment-image img {
|
|
580
|
+
max-width: 200px;
|
|
581
|
+
max-height: 200px;
|
|
582
|
+
border-radius: 8px;
|
|
583
|
+
display: block;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.pp-attachment-audio {
|
|
587
|
+
display: flex;
|
|
588
|
+
flex-direction: column;
|
|
589
|
+
gap: 4px;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.pp-attachment-audio audio {
|
|
593
|
+
width: 200px;
|
|
594
|
+
height: 36px;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.pp-attachment-audio .pp-attachment-name {
|
|
598
|
+
font-size: 11px;
|
|
599
|
+
opacity: 0.7;
|
|
600
|
+
white-space: nowrap;
|
|
601
|
+
overflow: hidden;
|
|
602
|
+
text-overflow: ellipsis;
|
|
603
|
+
max-width: 200px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.pp-attachment-video video {
|
|
607
|
+
max-width: 200px;
|
|
608
|
+
max-height: 200px;
|
|
609
|
+
border-radius: 8px;
|
|
610
|
+
display: block;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.pp-attachment-file {
|
|
614
|
+
display: flex;
|
|
615
|
+
align-items: center;
|
|
616
|
+
gap: 8px;
|
|
617
|
+
padding: 8px 12px;
|
|
618
|
+
background: ${isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
|
|
619
|
+
border-radius: 8px;
|
|
620
|
+
transition: background 0.2s;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.pp-attachment-file:hover {
|
|
624
|
+
background: ${isDark ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.08)"};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.pp-attachment-file svg {
|
|
628
|
+
width: 24px;
|
|
629
|
+
height: 24px;
|
|
630
|
+
flex-shrink: 0;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.pp-attachment-info {
|
|
634
|
+
display: flex;
|
|
635
|
+
flex-direction: column;
|
|
636
|
+
min-width: 0;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.pp-attachment-name {
|
|
640
|
+
font-size: 13px;
|
|
641
|
+
font-weight: 500;
|
|
642
|
+
white-space: nowrap;
|
|
643
|
+
overflow: hidden;
|
|
644
|
+
text-overflow: ellipsis;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.pp-attachment-size {
|
|
648
|
+
font-size: 11px;
|
|
649
|
+
opacity: 0.7;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/* Drag & Drop */
|
|
653
|
+
.pp-dragging {
|
|
654
|
+
position: relative;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.pp-drop-overlay {
|
|
658
|
+
position: absolute;
|
|
659
|
+
inset: 0;
|
|
660
|
+
background: ${isDark ? "rgba(0,0,0,0.9)" : "rgba(255,255,255,0.95)"};
|
|
661
|
+
display: flex;
|
|
662
|
+
flex-direction: column;
|
|
663
|
+
align-items: center;
|
|
664
|
+
justify-content: center;
|
|
665
|
+
gap: 12px;
|
|
666
|
+
z-index: 100;
|
|
667
|
+
border: 3px dashed ${primaryColor};
|
|
668
|
+
border-radius: 16px;
|
|
669
|
+
margin: 4px;
|
|
670
|
+
pointer-events: none;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.pp-drop-icon svg {
|
|
674
|
+
width: 48px;
|
|
675
|
+
height: 48px;
|
|
676
|
+
color: ${primaryColor};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.pp-drop-text {
|
|
680
|
+
font-size: 16px;
|
|
681
|
+
font-weight: 500;
|
|
682
|
+
color: ${colors.text};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/* Message Context Menu */
|
|
686
|
+
.pp-message-menu {
|
|
687
|
+
position: fixed;
|
|
688
|
+
background: ${colors.bg};
|
|
689
|
+
border: 1px solid ${colors.border};
|
|
690
|
+
border-radius: 8px;
|
|
691
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
692
|
+
padding: 4px;
|
|
693
|
+
z-index: 200;
|
|
694
|
+
min-width: 120px;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.pp-message-menu button {
|
|
698
|
+
display: flex;
|
|
699
|
+
align-items: center;
|
|
700
|
+
gap: 8px;
|
|
701
|
+
width: 100%;
|
|
702
|
+
padding: 8px 12px;
|
|
703
|
+
border: none;
|
|
704
|
+
background: transparent;
|
|
705
|
+
color: ${colors.text};
|
|
706
|
+
font-size: 13px;
|
|
707
|
+
cursor: pointer;
|
|
708
|
+
border-radius: 4px;
|
|
709
|
+
text-align: left;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
.pp-message-menu button:hover {
|
|
713
|
+
background: ${isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.pp-message-menu button svg {
|
|
717
|
+
width: 16px;
|
|
718
|
+
height: 16px;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
.pp-menu-delete {
|
|
722
|
+
color: #ef4444 !important;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/* Edit Modal */
|
|
726
|
+
.pp-edit-modal {
|
|
727
|
+
position: absolute;
|
|
728
|
+
bottom: 80px;
|
|
729
|
+
left: 12px;
|
|
730
|
+
right: 12px;
|
|
731
|
+
background: ${colors.bg};
|
|
732
|
+
border: 1px solid ${colors.border};
|
|
733
|
+
border-radius: 12px;
|
|
734
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
|
735
|
+
z-index: 150;
|
|
736
|
+
overflow: hidden;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
.pp-edit-header {
|
|
740
|
+
display: flex;
|
|
741
|
+
justify-content: space-between;
|
|
742
|
+
align-items: center;
|
|
743
|
+
padding: 12px 16px;
|
|
744
|
+
border-bottom: 1px solid ${colors.border};
|
|
745
|
+
font-weight: 500;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.pp-edit-header button {
|
|
749
|
+
background: transparent;
|
|
750
|
+
border: none;
|
|
751
|
+
color: ${colors.textSecondary};
|
|
752
|
+
cursor: pointer;
|
|
753
|
+
padding: 4px;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.pp-edit-header button svg {
|
|
757
|
+
width: 18px;
|
|
758
|
+
height: 18px;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.pp-edit-input {
|
|
762
|
+
width: 100%;
|
|
763
|
+
padding: 12px 16px;
|
|
764
|
+
border: none;
|
|
765
|
+
background: transparent;
|
|
766
|
+
color: ${colors.text};
|
|
767
|
+
font-size: 14px;
|
|
768
|
+
resize: none;
|
|
769
|
+
min-height: 80px;
|
|
770
|
+
outline: none;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.pp-edit-actions {
|
|
774
|
+
display: flex;
|
|
775
|
+
justify-content: flex-end;
|
|
776
|
+
gap: 8px;
|
|
777
|
+
padding: 12px 16px;
|
|
778
|
+
border-top: 1px solid ${colors.border};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.pp-edit-cancel {
|
|
782
|
+
padding: 8px 16px;
|
|
783
|
+
border: 1px solid ${colors.border};
|
|
784
|
+
border-radius: 6px;
|
|
785
|
+
background: transparent;
|
|
786
|
+
color: ${colors.text};
|
|
787
|
+
font-size: 13px;
|
|
788
|
+
cursor: pointer;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
.pp-edit-save {
|
|
792
|
+
padding: 8px 16px;
|
|
793
|
+
border: none;
|
|
794
|
+
border-radius: 6px;
|
|
795
|
+
background: ${primaryColor};
|
|
796
|
+
color: white;
|
|
797
|
+
font-size: 13px;
|
|
798
|
+
cursor: pointer;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.pp-edit-save:disabled {
|
|
802
|
+
opacity: 0.5;
|
|
803
|
+
cursor: not-allowed;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/* Reply Preview */
|
|
807
|
+
.pp-reply-preview {
|
|
808
|
+
display: flex;
|
|
809
|
+
align-items: center;
|
|
810
|
+
gap: 8px;
|
|
811
|
+
padding: 8px 12px;
|
|
812
|
+
background: ${isDark ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
|
|
813
|
+
border-top: 1px solid ${colors.border};
|
|
814
|
+
border-left: 3px solid ${primaryColor};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.pp-reply-preview-content {
|
|
818
|
+
flex: 1;
|
|
819
|
+
min-width: 0;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.pp-reply-label {
|
|
823
|
+
display: block;
|
|
824
|
+
font-size: 11px;
|
|
825
|
+
color: ${primaryColor};
|
|
826
|
+
font-weight: 500;
|
|
827
|
+
margin-bottom: 2px;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
.pp-reply-text {
|
|
831
|
+
display: block;
|
|
832
|
+
font-size: 12px;
|
|
833
|
+
color: ${colors.textSecondary};
|
|
834
|
+
white-space: nowrap;
|
|
835
|
+
overflow: hidden;
|
|
836
|
+
text-overflow: ellipsis;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
.pp-reply-cancel {
|
|
840
|
+
background: transparent;
|
|
841
|
+
border: none;
|
|
842
|
+
color: ${colors.textSecondary};
|
|
843
|
+
cursor: pointer;
|
|
844
|
+
padding: 4px;
|
|
845
|
+
flex-shrink: 0;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
.pp-reply-cancel svg {
|
|
849
|
+
width: 16px;
|
|
850
|
+
height: 16px;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/* Reply Quote in Message */
|
|
854
|
+
.pp-reply-quote {
|
|
855
|
+
background: ${isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
|
|
856
|
+
border-left: 2px solid ${primaryColor};
|
|
857
|
+
padding: 4px 8px;
|
|
858
|
+
margin-bottom: 6px;
|
|
859
|
+
border-radius: 0 4px 4px 0;
|
|
860
|
+
font-size: 12px;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
.pp-reply-sender {
|
|
864
|
+
display: block;
|
|
865
|
+
font-weight: 500;
|
|
866
|
+
color: ${primaryColor};
|
|
867
|
+
margin-bottom: 2px;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
.pp-reply-content {
|
|
871
|
+
display: block;
|
|
872
|
+
color: ${colors.textSecondary};
|
|
873
|
+
white-space: nowrap;
|
|
874
|
+
overflow: hidden;
|
|
875
|
+
text-overflow: ellipsis;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/* Deleted Message */
|
|
879
|
+
.pp-message-deleted {
|
|
880
|
+
opacity: 0.6;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
.pp-deleted-content {
|
|
884
|
+
font-style: italic;
|
|
885
|
+
color: ${colors.textSecondary};
|
|
886
|
+
display: flex;
|
|
887
|
+
align-items: center;
|
|
888
|
+
gap: 4px;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
.pp-deleted-icon {
|
|
892
|
+
font-size: 12px;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/* Edited Badge */
|
|
896
|
+
.pp-edited-badge {
|
|
897
|
+
font-size: 10px;
|
|
898
|
+
color: ${colors.textSecondary};
|
|
899
|
+
margin-left: 4px;
|
|
900
|
+
font-style: italic;
|
|
901
|
+
}
|
|
414
902
|
`;
|
|
415
903
|
}
|
|
416
904
|
|
|
@@ -424,9 +912,18 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
424
912
|
const [operatorOnline, setOperatorOnline] = (0, import_hooks.useState)(false);
|
|
425
913
|
const [isConnected, setIsConnected] = (0, import_hooks.useState)(false);
|
|
426
914
|
const [unreadCount, setUnreadCount] = (0, import_hooks.useState)(0);
|
|
915
|
+
const [pendingAttachments, setPendingAttachments] = (0, import_hooks.useState)([]);
|
|
916
|
+
const [isUploading, setIsUploading] = (0, import_hooks.useState)(false);
|
|
917
|
+
const [replyingTo, setReplyingTo] = (0, import_hooks.useState)(null);
|
|
918
|
+
const [editingMessage, setEditingMessage] = (0, import_hooks.useState)(null);
|
|
919
|
+
const [editContent, setEditContent] = (0, import_hooks.useState)("");
|
|
920
|
+
const [messageMenu, setMessageMenu] = (0, import_hooks.useState)(null);
|
|
921
|
+
const [isDragging, setIsDragging] = (0, import_hooks.useState)(false);
|
|
427
922
|
const [config, setConfig] = (0, import_hooks.useState)(initialConfig);
|
|
428
923
|
const messagesEndRef = (0, import_hooks.useRef)(null);
|
|
429
924
|
const inputRef = (0, import_hooks.useRef)(null);
|
|
925
|
+
const fileInputRef = (0, import_hooks.useRef)(null);
|
|
926
|
+
const messagesContainerRef = (0, import_hooks.useRef)(null);
|
|
430
927
|
(0, import_hooks.useEffect)(() => {
|
|
431
928
|
const unsubOpen = client2.on("openChange", setIsOpen);
|
|
432
929
|
const unsubMessage = client2.on("message", () => {
|
|
@@ -523,11 +1020,17 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
523
1020
|
if (!shouldShow) return null;
|
|
524
1021
|
const handleSubmit = async (e) => {
|
|
525
1022
|
e.preventDefault();
|
|
526
|
-
|
|
1023
|
+
const hasContent = inputValue.trim().length > 0;
|
|
1024
|
+
const readyAttachments = pendingAttachments.filter((a) => a.status === "ready" && a.attachment);
|
|
1025
|
+
if (!hasContent && readyAttachments.length === 0) return;
|
|
527
1026
|
const content = inputValue;
|
|
1027
|
+
const attachmentIds = readyAttachments.map((a) => a.attachment.id);
|
|
1028
|
+
const replyToId = replyingTo?.id;
|
|
528
1029
|
setInputValue("");
|
|
1030
|
+
setPendingAttachments([]);
|
|
1031
|
+
setReplyingTo(null);
|
|
529
1032
|
try {
|
|
530
|
-
await client2.sendMessage(content);
|
|
1033
|
+
await client2.sendMessage(content, attachmentIds, replyToId);
|
|
531
1034
|
} catch (err) {
|
|
532
1035
|
console.error("[PocketPing] Failed to send message:", err);
|
|
533
1036
|
}
|
|
@@ -537,6 +1040,190 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
537
1040
|
setInputValue(target.value);
|
|
538
1041
|
client2.sendTyping(true);
|
|
539
1042
|
};
|
|
1043
|
+
const handleFileSelect = async (e) => {
|
|
1044
|
+
const target = e.target;
|
|
1045
|
+
const files = target.files;
|
|
1046
|
+
if (!files || files.length === 0) return;
|
|
1047
|
+
const newPending = [];
|
|
1048
|
+
for (let i = 0; i < files.length; i++) {
|
|
1049
|
+
const file = files[i];
|
|
1050
|
+
const id = `pending-${Date.now()}-${i}`;
|
|
1051
|
+
let preview;
|
|
1052
|
+
if (file.type.startsWith("image/")) {
|
|
1053
|
+
preview = URL.createObjectURL(file);
|
|
1054
|
+
}
|
|
1055
|
+
newPending.push({
|
|
1056
|
+
id,
|
|
1057
|
+
file,
|
|
1058
|
+
preview,
|
|
1059
|
+
progress: 0,
|
|
1060
|
+
status: "pending"
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
setPendingAttachments((prev) => [...prev, ...newPending]);
|
|
1064
|
+
target.value = "";
|
|
1065
|
+
setIsUploading(true);
|
|
1066
|
+
for (const pending of newPending) {
|
|
1067
|
+
try {
|
|
1068
|
+
setPendingAttachments(
|
|
1069
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, status: "uploading" } : a)
|
|
1070
|
+
);
|
|
1071
|
+
const attachment = await client2.uploadFile(pending.file, (progress) => {
|
|
1072
|
+
setPendingAttachments(
|
|
1073
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, progress } : a)
|
|
1074
|
+
);
|
|
1075
|
+
});
|
|
1076
|
+
setPendingAttachments(
|
|
1077
|
+
(prev) => prev.map(
|
|
1078
|
+
(a) => a.id === pending.id ? { ...a, status: "ready", progress: 100, attachment } : a
|
|
1079
|
+
)
|
|
1080
|
+
);
|
|
1081
|
+
} catch (err) {
|
|
1082
|
+
console.error("[PocketPing] Failed to upload file:", err);
|
|
1083
|
+
setPendingAttachments(
|
|
1084
|
+
(prev) => prev.map(
|
|
1085
|
+
(a) => a.id === pending.id ? { ...a, status: "error", error: "Upload failed" } : a
|
|
1086
|
+
)
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
setIsUploading(false);
|
|
1091
|
+
};
|
|
1092
|
+
const handleRemoveAttachment = (id) => {
|
|
1093
|
+
setPendingAttachments((prev) => {
|
|
1094
|
+
const removed = prev.find((a) => a.id === id);
|
|
1095
|
+
if (removed?.preview) {
|
|
1096
|
+
URL.revokeObjectURL(removed.preview);
|
|
1097
|
+
}
|
|
1098
|
+
return prev.filter((a) => a.id !== id);
|
|
1099
|
+
});
|
|
1100
|
+
};
|
|
1101
|
+
const handleReply = (message) => {
|
|
1102
|
+
setReplyingTo(message);
|
|
1103
|
+
setMessageMenu(null);
|
|
1104
|
+
inputRef.current?.focus();
|
|
1105
|
+
};
|
|
1106
|
+
const handleCancelReply = () => {
|
|
1107
|
+
setReplyingTo(null);
|
|
1108
|
+
};
|
|
1109
|
+
const handleStartEdit = (message) => {
|
|
1110
|
+
if (message.sender !== "visitor") return;
|
|
1111
|
+
setEditingMessage(message);
|
|
1112
|
+
setEditContent(message.content);
|
|
1113
|
+
setMessageMenu(null);
|
|
1114
|
+
};
|
|
1115
|
+
const handleCancelEdit = () => {
|
|
1116
|
+
setEditingMessage(null);
|
|
1117
|
+
setEditContent("");
|
|
1118
|
+
};
|
|
1119
|
+
const handleSaveEdit = async () => {
|
|
1120
|
+
if (!editingMessage || !editContent.trim()) return;
|
|
1121
|
+
try {
|
|
1122
|
+
await client2.editMessage(editingMessage.id, editContent.trim());
|
|
1123
|
+
setEditingMessage(null);
|
|
1124
|
+
setEditContent("");
|
|
1125
|
+
} catch (err) {
|
|
1126
|
+
console.error("[PocketPing] Failed to edit message:", err);
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
const handleDelete = async (message) => {
|
|
1130
|
+
if (message.sender !== "visitor") return;
|
|
1131
|
+
setMessageMenu(null);
|
|
1132
|
+
if (confirm("Delete this message?")) {
|
|
1133
|
+
try {
|
|
1134
|
+
await client2.deleteMessage(message.id);
|
|
1135
|
+
} catch (err) {
|
|
1136
|
+
console.error("[PocketPing] Failed to delete message:", err);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
const handleMessageContextMenu = (e, message) => {
|
|
1141
|
+
e.preventDefault();
|
|
1142
|
+
const mouseEvent = e;
|
|
1143
|
+
setMessageMenu({
|
|
1144
|
+
message,
|
|
1145
|
+
x: mouseEvent.clientX,
|
|
1146
|
+
y: mouseEvent.clientY
|
|
1147
|
+
});
|
|
1148
|
+
};
|
|
1149
|
+
(0, import_hooks.useEffect)(() => {
|
|
1150
|
+
if (!messageMenu) return;
|
|
1151
|
+
const handleClickOutside = () => setMessageMenu(null);
|
|
1152
|
+
document.addEventListener("click", handleClickOutside);
|
|
1153
|
+
return () => document.removeEventListener("click", handleClickOutside);
|
|
1154
|
+
}, [messageMenu]);
|
|
1155
|
+
const dragCounterRef = (0, import_hooks.useRef)(0);
|
|
1156
|
+
const handleDragEnter = (e) => {
|
|
1157
|
+
e.preventDefault();
|
|
1158
|
+
e.stopPropagation();
|
|
1159
|
+
dragCounterRef.current++;
|
|
1160
|
+
if (dragCounterRef.current === 1) {
|
|
1161
|
+
setIsDragging(true);
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
const handleDragOver = (e) => {
|
|
1165
|
+
e.preventDefault();
|
|
1166
|
+
e.stopPropagation();
|
|
1167
|
+
};
|
|
1168
|
+
const handleDragLeave = (e) => {
|
|
1169
|
+
e.preventDefault();
|
|
1170
|
+
e.stopPropagation();
|
|
1171
|
+
dragCounterRef.current--;
|
|
1172
|
+
if (dragCounterRef.current === 0) {
|
|
1173
|
+
setIsDragging(false);
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
const handleDrop = async (e) => {
|
|
1177
|
+
e.preventDefault();
|
|
1178
|
+
e.stopPropagation();
|
|
1179
|
+
dragCounterRef.current = 0;
|
|
1180
|
+
setIsDragging(false);
|
|
1181
|
+
const files = e.dataTransfer?.files;
|
|
1182
|
+
if (!files || files.length === 0) return;
|
|
1183
|
+
const newPending = [];
|
|
1184
|
+
for (let i = 0; i < files.length; i++) {
|
|
1185
|
+
const file = files[i];
|
|
1186
|
+
const id = `pending-${Date.now()}-${i}`;
|
|
1187
|
+
let preview;
|
|
1188
|
+
if (file.type.startsWith("image/")) {
|
|
1189
|
+
preview = URL.createObjectURL(file);
|
|
1190
|
+
}
|
|
1191
|
+
newPending.push({
|
|
1192
|
+
id,
|
|
1193
|
+
file,
|
|
1194
|
+
preview,
|
|
1195
|
+
progress: 0,
|
|
1196
|
+
status: "pending"
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
setPendingAttachments((prev) => [...prev, ...newPending]);
|
|
1200
|
+
setIsUploading(true);
|
|
1201
|
+
for (const pending of newPending) {
|
|
1202
|
+
try {
|
|
1203
|
+
setPendingAttachments(
|
|
1204
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, status: "uploading" } : a)
|
|
1205
|
+
);
|
|
1206
|
+
const attachment = await client2.uploadFile(pending.file, (progress) => {
|
|
1207
|
+
setPendingAttachments(
|
|
1208
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, progress } : a)
|
|
1209
|
+
);
|
|
1210
|
+
});
|
|
1211
|
+
setPendingAttachments(
|
|
1212
|
+
(prev) => prev.map(
|
|
1213
|
+
(a) => a.id === pending.id ? { ...a, status: "ready", progress: 100, attachment } : a
|
|
1214
|
+
)
|
|
1215
|
+
);
|
|
1216
|
+
} catch (err) {
|
|
1217
|
+
console.error("[PocketPing] Failed to upload dropped file:", err);
|
|
1218
|
+
setPendingAttachments(
|
|
1219
|
+
(prev) => prev.map(
|
|
1220
|
+
(a) => a.id === pending.id ? { ...a, status: "error", error: "Upload failed" } : a
|
|
1221
|
+
)
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
setIsUploading(false);
|
|
1226
|
+
};
|
|
540
1227
|
const position = config.position ?? "bottom-right";
|
|
541
1228
|
const theme = getTheme(config.theme ?? "auto");
|
|
542
1229
|
const primaryColor = config.primaryColor ?? "#6366f1";
|
|
@@ -555,84 +1242,211 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
555
1242
|
]
|
|
556
1243
|
}
|
|
557
1244
|
),
|
|
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 }) })
|
|
1245
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1246
|
+
"div",
|
|
1247
|
+
{
|
|
1248
|
+
class: `pp-window pp-${position} pp-theme-${theme} ${isDragging ? "pp-dragging" : ""}`,
|
|
1249
|
+
onDragEnter: handleDragEnter,
|
|
1250
|
+
onDragOver: handleDragOver,
|
|
1251
|
+
onDragLeave: handleDragLeave,
|
|
1252
|
+
onDrop: handleDrop,
|
|
1253
|
+
children: [
|
|
1254
|
+
isDragging && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-drop-overlay", children: [
|
|
1255
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-drop-icon", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AttachIcon, {}) }),
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-drop-text", children: "Drop files to upload" })
|
|
1257
|
+
] }),
|
|
1258
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-header", children: [
|
|
1259
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-header-info", children: [
|
|
1260
|
+
config.operatorAvatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: config.operatorAvatar, alt: "", class: "pp-avatar" }),
|
|
1261
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-header-title", children: config.operatorName ?? "Support" }),
|
|
1263
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-header-status", children: operatorOnline ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1264
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-status-dot pp-online" }),
|
|
1265
|
+
" Online"
|
|
1266
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-status-dot" }),
|
|
1268
|
+
" Away"
|
|
1269
|
+
] }) })
|
|
595
1270
|
] })
|
|
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
|
-
|
|
1271
|
+
] }),
|
|
1272
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1273
|
+
"button",
|
|
1274
|
+
{
|
|
1275
|
+
class: "pp-close-btn",
|
|
1276
|
+
onClick: () => client2.setOpen(false),
|
|
1277
|
+
"aria-label": "Close chat",
|
|
1278
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {})
|
|
1279
|
+
}
|
|
1280
|
+
)
|
|
1281
|
+
] }),
|
|
1282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-messages", ref: messagesContainerRef, children: [
|
|
1283
|
+
config.welcomeMessage && messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-welcome", children: config.welcomeMessage }),
|
|
1284
|
+
messages.map((msg) => {
|
|
1285
|
+
const isDeleted = !!msg.deletedAt;
|
|
1286
|
+
const isEdited = !!msg.editedAt;
|
|
1287
|
+
const replyToMsg = msg.replyTo ? messages.find((m) => m.id === msg.replyTo) : null;
|
|
1288
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1289
|
+
"div",
|
|
1290
|
+
{
|
|
1291
|
+
class: `pp-message pp-message-${msg.sender} ${isDeleted ? "pp-message-deleted" : ""}`,
|
|
1292
|
+
onContextMenu: (e) => handleMessageContextMenu(e, msg),
|
|
1293
|
+
children: [
|
|
1294
|
+
replyToMsg && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-reply-quote", children: [
|
|
1295
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-reply-sender", children: replyToMsg.sender === "visitor" ? "You" : "Support" }),
|
|
1296
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { class: "pp-reply-content", children: [
|
|
1297
|
+
replyToMsg.deletedAt ? "Message deleted" : replyToMsg.content.slice(0, 50),
|
|
1298
|
+
replyToMsg.content.length > 50 ? "..." : ""
|
|
1299
|
+
] })
|
|
1300
|
+
] }),
|
|
1301
|
+
isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message-content pp-deleted-content", children: [
|
|
1302
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-deleted-icon", children: "\u{1F5D1}\uFE0F" }),
|
|
1303
|
+
" Message deleted"
|
|
1304
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1305
|
+
msg.content && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-message-content", children: msg.content }),
|
|
1306
|
+
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)) })
|
|
1307
|
+
] }),
|
|
1308
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message-time", children: [
|
|
1309
|
+
formatTime(msg.timestamp),
|
|
1310
|
+
isEdited && !isDeleted && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-edited-badge", children: "edited" }),
|
|
1311
|
+
msg.sender === "ai" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-ai-badge", children: "AI" }),
|
|
1312
|
+
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 }) })
|
|
1313
|
+
] })
|
|
1314
|
+
]
|
|
1315
|
+
},
|
|
1316
|
+
msg.id
|
|
1317
|
+
);
|
|
1318
|
+
}),
|
|
1319
|
+
isTyping && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-message pp-message-operator pp-typing", children: [
|
|
1320
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
|
|
1321
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {})
|
|
1323
|
+
] }),
|
|
1324
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: messagesEndRef })
|
|
1325
|
+
] }),
|
|
1326
|
+
messageMenu && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1327
|
+
"div",
|
|
1328
|
+
{
|
|
1329
|
+
class: "pp-message-menu",
|
|
1330
|
+
style: { top: `${messageMenu.y}px`, left: `${messageMenu.x}px` },
|
|
1331
|
+
children: [
|
|
1332
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => handleReply(messageMenu.message), children: [
|
|
1333
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplyIcon, {}),
|
|
1334
|
+
" Reply"
|
|
1335
|
+
] }),
|
|
1336
|
+
messageMenu.message.sender === "visitor" && !messageMenu.message.deletedAt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1337
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => handleStartEdit(messageMenu.message), children: [
|
|
1338
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditIcon, {}),
|
|
1339
|
+
" Edit"
|
|
1340
|
+
] }),
|
|
1341
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { class: "pp-menu-delete", onClick: () => handleDelete(messageMenu.message), children: [
|
|
1342
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteIcon, {}),
|
|
1343
|
+
" Delete"
|
|
1344
|
+
] })
|
|
1345
|
+
] })
|
|
1346
|
+
]
|
|
1347
|
+
}
|
|
1348
|
+
),
|
|
1349
|
+
editingMessage && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-edit-modal", children: [
|
|
1350
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-edit-header", children: [
|
|
1351
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Edit message" }),
|
|
1352
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: handleCancelEdit, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) })
|
|
1353
|
+
] }),
|
|
1354
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1355
|
+
"textarea",
|
|
1356
|
+
{
|
|
1357
|
+
class: "pp-edit-input",
|
|
1358
|
+
value: editContent,
|
|
1359
|
+
onInput: (e) => setEditContent(e.target.value),
|
|
1360
|
+
autoFocus: true
|
|
1361
|
+
}
|
|
1362
|
+
),
|
|
1363
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-edit-actions", children: [
|
|
1364
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { class: "pp-edit-cancel", onClick: handleCancelEdit, children: "Cancel" }),
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { class: "pp-edit-save", onClick: handleSaveEdit, disabled: !editContent.trim(), children: "Save" })
|
|
1366
|
+
] })
|
|
1367
|
+
] }),
|
|
1368
|
+
replyingTo && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-reply-preview", children: [
|
|
1369
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-reply-preview-content", children: [
|
|
1370
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-reply-label", children: "Replying to" }),
|
|
1371
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { class: "pp-reply-text", children: [
|
|
1372
|
+
replyingTo.content.slice(0, 50),
|
|
1373
|
+
replyingTo.content.length > 50 ? "..." : ""
|
|
1374
|
+
] })
|
|
1375
|
+
] }),
|
|
1376
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { class: "pp-reply-cancel", onClick: handleCancelReply, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {}) })
|
|
1377
|
+
] }),
|
|
1378
|
+
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: [
|
|
1379
|
+
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 }) }),
|
|
1380
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1381
|
+
"button",
|
|
1382
|
+
{
|
|
1383
|
+
class: "pp-remove-attachment",
|
|
1384
|
+
onClick: () => handleRemoveAttachment(pending.id),
|
|
1385
|
+
"aria-label": "Remove attachment",
|
|
1386
|
+
type: "button",
|
|
1387
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CloseIcon, {})
|
|
1388
|
+
}
|
|
1389
|
+
),
|
|
1390
|
+
pending.status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-upload-progress", style: { width: `${pending.progress}%` } }),
|
|
1391
|
+
pending.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { class: "pp-upload-error", title: pending.error, children: "!" })
|
|
1392
|
+
] }, pending.id)) }),
|
|
1393
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { class: "pp-input-form", onSubmit: handleSubmit, children: [
|
|
1394
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1395
|
+
"input",
|
|
1396
|
+
{
|
|
1397
|
+
ref: (el) => {
|
|
1398
|
+
fileInputRef.current = el;
|
|
1399
|
+
if (el) {
|
|
1400
|
+
el.onchange = handleFileSelect;
|
|
1401
|
+
}
|
|
1402
|
+
},
|
|
1403
|
+
type: "file",
|
|
1404
|
+
class: "pp-file-input",
|
|
1405
|
+
accept: "image/*,audio/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.txt",
|
|
1406
|
+
multiple: true
|
|
1407
|
+
}
|
|
1408
|
+
),
|
|
1409
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1410
|
+
"button",
|
|
1411
|
+
{
|
|
1412
|
+
type: "button",
|
|
1413
|
+
class: "pp-attach-btn",
|
|
1414
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1415
|
+
disabled: !isConnected || isUploading,
|
|
1416
|
+
"aria-label": "Attach file",
|
|
1417
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AttachIcon, {})
|
|
1418
|
+
}
|
|
1419
|
+
),
|
|
1420
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1421
|
+
"input",
|
|
1422
|
+
{
|
|
1423
|
+
ref: inputRef,
|
|
1424
|
+
type: "text",
|
|
1425
|
+
class: "pp-input",
|
|
1426
|
+
placeholder: config.placeholder ?? "Type a message...",
|
|
1427
|
+
value: inputValue,
|
|
1428
|
+
onInput: handleInputChange,
|
|
1429
|
+
disabled: !isConnected
|
|
1430
|
+
}
|
|
1431
|
+
),
|
|
1432
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1433
|
+
"button",
|
|
1434
|
+
{
|
|
1435
|
+
type: "submit",
|
|
1436
|
+
class: "pp-send-btn",
|
|
1437
|
+
disabled: !inputValue.trim() && pendingAttachments.filter((a) => a.status === "ready").length === 0 || !isConnected || isUploading,
|
|
1438
|
+
"aria-label": "Send message",
|
|
1439
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SendIcon, {})
|
|
1440
|
+
}
|
|
1441
|
+
)
|
|
1442
|
+
] }),
|
|
1443
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-footer", children: [
|
|
1444
|
+
"Powered by ",
|
|
1445
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: "https://pocketping.io", target: "_blank", rel: "noopener", children: "PocketPing" })
|
|
1446
|
+
] })
|
|
1447
|
+
]
|
|
1448
|
+
}
|
|
1449
|
+
)
|
|
636
1450
|
] });
|
|
637
1451
|
}
|
|
638
1452
|
function checkPageVisibility(config) {
|
|
@@ -688,6 +1502,86 @@ function StatusIcon({ status }) {
|
|
|
688
1502
|
}
|
|
689
1503
|
return null;
|
|
690
1504
|
}
|
|
1505
|
+
function AttachIcon() {
|
|
1506
|
+
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" }) });
|
|
1507
|
+
}
|
|
1508
|
+
function ReplyIcon() {
|
|
1509
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1510
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "9 17 4 12 9 7" }),
|
|
1511
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
1512
|
+
] });
|
|
1513
|
+
}
|
|
1514
|
+
function EditIcon() {
|
|
1515
|
+
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: "M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" }) });
|
|
1516
|
+
}
|
|
1517
|
+
function DeleteIcon() {
|
|
1518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1519
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3 6 5 6 21 6" }),
|
|
1520
|
+
/* @__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" })
|
|
1521
|
+
] });
|
|
1522
|
+
}
|
|
1523
|
+
function FileIcon({ mimeType }) {
|
|
1524
|
+
if (mimeType === "application/pdf") {
|
|
1525
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1526
|
+
/* @__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" }),
|
|
1527
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "14 2 14 8 20 8" }),
|
|
1528
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 15h6" }),
|
|
1529
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 11h6" })
|
|
1530
|
+
] });
|
|
1531
|
+
}
|
|
1532
|
+
if (mimeType.startsWith("audio/")) {
|
|
1533
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1534
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 18V5l12-2v13" }),
|
|
1535
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "18", r: "3" }),
|
|
1536
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "18", cy: "16", r: "3" })
|
|
1537
|
+
] });
|
|
1538
|
+
}
|
|
1539
|
+
if (mimeType.startsWith("video/")) {
|
|
1540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1541
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "2", width: "20", height: "20", rx: "2.18", ry: "2.18" }),
|
|
1542
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "7", y1: "2", x2: "7", y2: "22" }),
|
|
1543
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "17", y1: "2", x2: "17", y2: "22" }),
|
|
1544
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2", y1: "12", x2: "22", y2: "12" }),
|
|
1545
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2", y1: "7", x2: "7", y2: "7" }),
|
|
1546
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2", y1: "17", x2: "7", y2: "17" }),
|
|
1547
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "17", y1: "17", x2: "22", y2: "17" }),
|
|
1548
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "17", y1: "7", x2: "22", y2: "7" })
|
|
1549
|
+
] });
|
|
1550
|
+
}
|
|
1551
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1552
|
+
/* @__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" }),
|
|
1553
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "14 2 14 8 20 8" })
|
|
1554
|
+
] });
|
|
1555
|
+
}
|
|
1556
|
+
function AttachmentDisplay({ attachment }) {
|
|
1557
|
+
const isImage = attachment.mimeType.startsWith("image/");
|
|
1558
|
+
const isAudio = attachment.mimeType.startsWith("audio/");
|
|
1559
|
+
const isVideo = attachment.mimeType.startsWith("video/");
|
|
1560
|
+
const formatSize = (bytes) => {
|
|
1561
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1562
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1563
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1564
|
+
};
|
|
1565
|
+
if (isImage) {
|
|
1566
|
+
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 }) });
|
|
1567
|
+
}
|
|
1568
|
+
if (isAudio) {
|
|
1569
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-attachment pp-attachment-audio", children: [
|
|
1570
|
+
/* @__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 }) }),
|
|
1571
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-attachment-name", children: attachment.filename })
|
|
1572
|
+
] });
|
|
1573
|
+
}
|
|
1574
|
+
if (isVideo) {
|
|
1575
|
+
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 }) }) });
|
|
1576
|
+
}
|
|
1577
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("a", { href: attachment.url, target: "_blank", rel: "noopener", class: "pp-attachment pp-attachment-file", children: [
|
|
1578
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileIcon, { mimeType: attachment.mimeType }),
|
|
1579
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { class: "pp-attachment-info", children: [
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-attachment-name", children: attachment.filename }),
|
|
1581
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { class: "pp-attachment-size", children: formatSize(attachment.size) })
|
|
1582
|
+
] })
|
|
1583
|
+
] });
|
|
1584
|
+
}
|
|
691
1585
|
|
|
692
1586
|
// src/version.ts
|
|
693
1587
|
var VERSION = "0.3.6";
|
|
@@ -803,7 +1697,7 @@ var PocketPingClient = class {
|
|
|
803
1697
|
this.cleanupTrackedElements();
|
|
804
1698
|
this.disableInspectorMode();
|
|
805
1699
|
}
|
|
806
|
-
async sendMessage(content) {
|
|
1700
|
+
async sendMessage(content, attachmentIds, replyTo) {
|
|
807
1701
|
if (!this.session) {
|
|
808
1702
|
throw new Error("Not connected");
|
|
809
1703
|
}
|
|
@@ -814,7 +1708,8 @@ var PocketPingClient = class {
|
|
|
814
1708
|
content,
|
|
815
1709
|
sender: "visitor",
|
|
816
1710
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
817
|
-
status: "sending"
|
|
1711
|
+
status: "sending",
|
|
1712
|
+
replyTo
|
|
818
1713
|
};
|
|
819
1714
|
this.session.messages.push(tempMessage);
|
|
820
1715
|
this.emit("message", tempMessage);
|
|
@@ -824,7 +1719,9 @@ var PocketPingClient = class {
|
|
|
824
1719
|
body: JSON.stringify({
|
|
825
1720
|
sessionId: this.session.sessionId,
|
|
826
1721
|
content,
|
|
827
|
-
sender: "visitor"
|
|
1722
|
+
sender: "visitor",
|
|
1723
|
+
attachmentIds: attachmentIds || [],
|
|
1724
|
+
replyTo
|
|
828
1725
|
})
|
|
829
1726
|
});
|
|
830
1727
|
const messageIndex = this.session.messages.findIndex((m) => m.id === tempId);
|
|
@@ -832,6 +1729,9 @@ var PocketPingClient = class {
|
|
|
832
1729
|
this.session.messages[messageIndex].id = response.messageId;
|
|
833
1730
|
this.session.messages[messageIndex].timestamp = response.timestamp;
|
|
834
1731
|
this.session.messages[messageIndex].status = "sent";
|
|
1732
|
+
if (response.attachments && response.attachments.length > 0) {
|
|
1733
|
+
this.session.messages[messageIndex].attachments = response.attachments;
|
|
1734
|
+
}
|
|
835
1735
|
this.emit("message", this.session.messages[messageIndex]);
|
|
836
1736
|
}
|
|
837
1737
|
const message = this.session.messages[messageIndex] || {
|
|
@@ -840,7 +1740,8 @@ var PocketPingClient = class {
|
|
|
840
1740
|
content,
|
|
841
1741
|
sender: "visitor",
|
|
842
1742
|
timestamp: response.timestamp,
|
|
843
|
-
status: "sent"
|
|
1743
|
+
status: "sent",
|
|
1744
|
+
attachments: response.attachments
|
|
844
1745
|
};
|
|
845
1746
|
this.config.onMessage?.(message);
|
|
846
1747
|
return message;
|
|
@@ -853,6 +1754,110 @@ var PocketPingClient = class {
|
|
|
853
1754
|
throw error;
|
|
854
1755
|
}
|
|
855
1756
|
}
|
|
1757
|
+
/**
|
|
1758
|
+
* Upload a file attachment
|
|
1759
|
+
* Returns the attachment data after successful upload
|
|
1760
|
+
* @param file - File object to upload
|
|
1761
|
+
* @param onProgress - Optional callback for upload progress (0-100)
|
|
1762
|
+
* @example
|
|
1763
|
+
* const attachment = await PocketPing.uploadFile(file, (progress) => {
|
|
1764
|
+
* console.log(`Upload ${progress}% complete`)
|
|
1765
|
+
* })
|
|
1766
|
+
* await PocketPing.sendMessage('Check this file', [attachment.id])
|
|
1767
|
+
*/
|
|
1768
|
+
async uploadFile(file, onProgress) {
|
|
1769
|
+
if (!this.session) {
|
|
1770
|
+
throw new Error("Not connected");
|
|
1771
|
+
}
|
|
1772
|
+
this.emit("uploadStart", { filename: file.name, size: file.size });
|
|
1773
|
+
try {
|
|
1774
|
+
const initResponse = await this.fetch("/upload", {
|
|
1775
|
+
method: "POST",
|
|
1776
|
+
body: JSON.stringify({
|
|
1777
|
+
sessionId: this.session.sessionId,
|
|
1778
|
+
filename: file.name,
|
|
1779
|
+
mimeType: file.type || "application/octet-stream",
|
|
1780
|
+
size: file.size
|
|
1781
|
+
})
|
|
1782
|
+
});
|
|
1783
|
+
onProgress?.(10);
|
|
1784
|
+
this.emit("uploadProgress", { filename: file.name, progress: 10 });
|
|
1785
|
+
await this.uploadToPresignedUrl(initResponse.uploadUrl, file, (progress) => {
|
|
1786
|
+
const mappedProgress = 10 + progress * 0.8;
|
|
1787
|
+
onProgress?.(mappedProgress);
|
|
1788
|
+
this.emit("uploadProgress", { filename: file.name, progress: mappedProgress });
|
|
1789
|
+
});
|
|
1790
|
+
const completeResponse = await this.fetch("/upload/complete", {
|
|
1791
|
+
method: "POST",
|
|
1792
|
+
body: JSON.stringify({
|
|
1793
|
+
sessionId: this.session.sessionId,
|
|
1794
|
+
attachmentId: initResponse.attachmentId
|
|
1795
|
+
})
|
|
1796
|
+
});
|
|
1797
|
+
onProgress?.(100);
|
|
1798
|
+
this.emit("uploadComplete", completeResponse);
|
|
1799
|
+
return {
|
|
1800
|
+
id: completeResponse.id,
|
|
1801
|
+
filename: completeResponse.filename,
|
|
1802
|
+
mimeType: completeResponse.mimeType,
|
|
1803
|
+
size: completeResponse.size,
|
|
1804
|
+
url: completeResponse.url,
|
|
1805
|
+
thumbnailUrl: completeResponse.thumbnailUrl,
|
|
1806
|
+
status: completeResponse.status
|
|
1807
|
+
};
|
|
1808
|
+
} catch (error) {
|
|
1809
|
+
this.emit("uploadError", { filename: file.name, error });
|
|
1810
|
+
throw error;
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
/**
|
|
1814
|
+
* Upload multiple files at once
|
|
1815
|
+
* @param files - Array of File objects to upload
|
|
1816
|
+
* @param onProgress - Optional callback for overall progress (0-100)
|
|
1817
|
+
* @returns Array of uploaded attachments
|
|
1818
|
+
*/
|
|
1819
|
+
async uploadFiles(files, onProgress) {
|
|
1820
|
+
const attachments = [];
|
|
1821
|
+
const totalFiles = files.length;
|
|
1822
|
+
for (let i = 0; i < totalFiles; i++) {
|
|
1823
|
+
const file = files[i];
|
|
1824
|
+
const baseProgress = i / totalFiles * 100;
|
|
1825
|
+
const fileProgress = 100 / totalFiles;
|
|
1826
|
+
const attachment = await this.uploadFile(file, (progress) => {
|
|
1827
|
+
const totalProgress = baseProgress + progress / 100 * fileProgress;
|
|
1828
|
+
onProgress?.(totalProgress);
|
|
1829
|
+
});
|
|
1830
|
+
attachments.push(attachment);
|
|
1831
|
+
}
|
|
1832
|
+
return attachments;
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Upload file to presigned URL with progress tracking
|
|
1836
|
+
*/
|
|
1837
|
+
uploadToPresignedUrl(url, file, onProgress) {
|
|
1838
|
+
return new Promise((resolve, reject) => {
|
|
1839
|
+
const xhr = new XMLHttpRequest();
|
|
1840
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
1841
|
+
if (event.lengthComputable) {
|
|
1842
|
+
const progress = event.loaded / event.total * 100;
|
|
1843
|
+
onProgress?.(progress);
|
|
1844
|
+
}
|
|
1845
|
+
});
|
|
1846
|
+
xhr.addEventListener("load", () => {
|
|
1847
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
1848
|
+
resolve();
|
|
1849
|
+
} else {
|
|
1850
|
+
reject(new Error(`Upload failed with status ${xhr.status}`));
|
|
1851
|
+
}
|
|
1852
|
+
});
|
|
1853
|
+
xhr.addEventListener("error", () => {
|
|
1854
|
+
reject(new Error("Upload failed"));
|
|
1855
|
+
});
|
|
1856
|
+
xhr.open("PUT", url);
|
|
1857
|
+
xhr.setRequestHeader("Content-Type", file.type || "application/octet-stream");
|
|
1858
|
+
xhr.send(file);
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
856
1861
|
async fetchMessages(after) {
|
|
857
1862
|
if (!this.session) {
|
|
858
1863
|
throw new Error("Not connected");
|
|
@@ -906,6 +1911,54 @@ var PocketPingClient = class {
|
|
|
906
1911
|
console.error("[PocketPing] Failed to send read status:", err);
|
|
907
1912
|
}
|
|
908
1913
|
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Edit a message (visitor can only edit their own messages)
|
|
1916
|
+
* @param messageId - ID of the message to edit
|
|
1917
|
+
* @param content - New content for the message
|
|
1918
|
+
*/
|
|
1919
|
+
async editMessage(messageId, content) {
|
|
1920
|
+
if (!this.session) {
|
|
1921
|
+
throw new Error("Not connected");
|
|
1922
|
+
}
|
|
1923
|
+
const response = await this.fetch(`/message/${messageId}`, {
|
|
1924
|
+
method: "PATCH",
|
|
1925
|
+
body: JSON.stringify({
|
|
1926
|
+
sessionId: this.session.sessionId,
|
|
1927
|
+
content
|
|
1928
|
+
})
|
|
1929
|
+
});
|
|
1930
|
+
const messageIndex = this.session.messages.findIndex((m) => m.id === messageId);
|
|
1931
|
+
if (messageIndex >= 0) {
|
|
1932
|
+
this.session.messages[messageIndex].content = response.message.content;
|
|
1933
|
+
this.session.messages[messageIndex].editedAt = response.message.editedAt;
|
|
1934
|
+
this.emit("messageEdited", this.session.messages[messageIndex]);
|
|
1935
|
+
}
|
|
1936
|
+
return this.session.messages[messageIndex];
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Delete a message (soft delete - visitor can only delete their own messages)
|
|
1940
|
+
* @param messageId - ID of the message to delete
|
|
1941
|
+
*/
|
|
1942
|
+
async deleteMessage(messageId) {
|
|
1943
|
+
if (!this.session) {
|
|
1944
|
+
throw new Error("Not connected");
|
|
1945
|
+
}
|
|
1946
|
+
const response = await this.fetch(`/message/${messageId}`, {
|
|
1947
|
+
method: "DELETE",
|
|
1948
|
+
body: JSON.stringify({
|
|
1949
|
+
sessionId: this.session.sessionId
|
|
1950
|
+
})
|
|
1951
|
+
});
|
|
1952
|
+
if (response.deleted) {
|
|
1953
|
+
const messageIndex = this.session.messages.findIndex((m) => m.id === messageId);
|
|
1954
|
+
if (messageIndex >= 0) {
|
|
1955
|
+
this.session.messages[messageIndex].deletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1956
|
+
this.session.messages[messageIndex].content = "";
|
|
1957
|
+
this.emit("messageDeleted", this.session.messages[messageIndex]);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
return response.deleted;
|
|
1961
|
+
}
|
|
909
1962
|
async getPresence() {
|
|
910
1963
|
return this.fetch("/presence", { method: "GET" });
|
|
911
1964
|
}
|
|
@@ -1809,11 +2862,23 @@ function close() {
|
|
|
1809
2862
|
function toggle() {
|
|
1810
2863
|
client?.toggleOpen();
|
|
1811
2864
|
}
|
|
1812
|
-
function sendMessage(content) {
|
|
2865
|
+
function sendMessage(content, attachmentIds) {
|
|
2866
|
+
if (!client) {
|
|
2867
|
+
throw new Error("[PocketPing] Not initialized");
|
|
2868
|
+
}
|
|
2869
|
+
return client.sendMessage(content, attachmentIds);
|
|
2870
|
+
}
|
|
2871
|
+
async function uploadFile(file, onProgress) {
|
|
2872
|
+
if (!client) {
|
|
2873
|
+
throw new Error("[PocketPing] Not initialized");
|
|
2874
|
+
}
|
|
2875
|
+
return client.uploadFile(file, onProgress);
|
|
2876
|
+
}
|
|
2877
|
+
async function uploadFiles(files, onProgress) {
|
|
1813
2878
|
if (!client) {
|
|
1814
2879
|
throw new Error("[PocketPing] Not initialized");
|
|
1815
2880
|
}
|
|
1816
|
-
return client.
|
|
2881
|
+
return client.uploadFiles(files, onProgress);
|
|
1817
2882
|
}
|
|
1818
2883
|
function trigger(eventName, data, options) {
|
|
1819
2884
|
if (!client) {
|
|
@@ -1882,7 +2947,7 @@ if (typeof document !== "undefined") {
|
|
|
1882
2947
|
}
|
|
1883
2948
|
}
|
|
1884
2949
|
}
|
|
1885
|
-
var index_default = { init, destroy, open, close, toggle, sendMessage, trigger, onEvent, offEvent, on, identify, reset, getIdentity, setupTrackedElements, getTrackedElements };
|
|
2950
|
+
var index_default = { init, destroy, open, close, toggle, sendMessage, uploadFile, uploadFiles, trigger, onEvent, offEvent, on, identify, reset, getIdentity, setupTrackedElements, getTrackedElements };
|
|
1886
2951
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1887
2952
|
0 && (module.exports = {
|
|
1888
2953
|
close,
|
|
@@ -1899,5 +2964,7 @@ var index_default = { init, destroy, open, close, toggle, sendMessage, trigger,
|
|
|
1899
2964
|
sendMessage,
|
|
1900
2965
|
setupTrackedElements,
|
|
1901
2966
|
toggle,
|
|
1902
|
-
trigger
|
|
2967
|
+
trigger,
|
|
2968
|
+
uploadFile,
|
|
2969
|
+
uploadFiles
|
|
1903
2970
|
});
|