@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.js
CHANGED
|
@@ -190,6 +190,12 @@ function styles(primaryColor, theme) {
|
|
|
190
190
|
border-radius: 4px;
|
|
191
191
|
opacity: 0.8;
|
|
192
192
|
transition: opacity 0.2s;
|
|
193
|
+
flex-shrink: 0;
|
|
194
|
+
width: 28px;
|
|
195
|
+
height: 28px;
|
|
196
|
+
display: flex;
|
|
197
|
+
align-items: center;
|
|
198
|
+
justify-content: center;
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
.pp-close-btn:hover {
|
|
@@ -197,8 +203,8 @@ function styles(primaryColor, theme) {
|
|
|
197
203
|
}
|
|
198
204
|
|
|
199
205
|
.pp-close-btn svg {
|
|
200
|
-
width:
|
|
201
|
-
height:
|
|
206
|
+
width: 16px;
|
|
207
|
+
height: 16px;
|
|
202
208
|
}
|
|
203
209
|
|
|
204
210
|
.pp-messages {
|
|
@@ -372,6 +378,486 @@ function styles(primaryColor, theme) {
|
|
|
372
378
|
.pp-footer a:hover {
|
|
373
379
|
text-decoration: underline;
|
|
374
380
|
}
|
|
381
|
+
|
|
382
|
+
/* Attachment Styles */
|
|
383
|
+
.pp-file-input {
|
|
384
|
+
/* Use offscreen positioning instead of display:none for better browser compatibility */
|
|
385
|
+
position: absolute;
|
|
386
|
+
width: 1px;
|
|
387
|
+
height: 1px;
|
|
388
|
+
padding: 0;
|
|
389
|
+
margin: -1px;
|
|
390
|
+
overflow: hidden;
|
|
391
|
+
clip: rect(0, 0, 0, 0);
|
|
392
|
+
white-space: nowrap;
|
|
393
|
+
border: 0;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.pp-attach-btn {
|
|
397
|
+
width: 40px;
|
|
398
|
+
height: 40px;
|
|
399
|
+
border-radius: 50%;
|
|
400
|
+
background: transparent;
|
|
401
|
+
color: ${colors.textSecondary};
|
|
402
|
+
border: 1px solid ${colors.border};
|
|
403
|
+
cursor: pointer;
|
|
404
|
+
display: flex;
|
|
405
|
+
align-items: center;
|
|
406
|
+
justify-content: center;
|
|
407
|
+
transition: color 0.2s, border-color 0.2s;
|
|
408
|
+
flex-shrink: 0;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.pp-attach-btn:hover:not(:disabled) {
|
|
412
|
+
color: ${primaryColor};
|
|
413
|
+
border-color: ${primaryColor};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.pp-attach-btn:disabled {
|
|
417
|
+
opacity: 0.5;
|
|
418
|
+
cursor: not-allowed;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.pp-attach-btn svg {
|
|
422
|
+
width: 18px;
|
|
423
|
+
height: 18px;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.pp-attachments-preview {
|
|
427
|
+
display: flex;
|
|
428
|
+
gap: 8px;
|
|
429
|
+
padding: 8px 12px;
|
|
430
|
+
border-top: 1px solid ${colors.border};
|
|
431
|
+
overflow-x: auto;
|
|
432
|
+
background: ${colors.bgSecondary};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.pp-attachment-preview {
|
|
436
|
+
position: relative;
|
|
437
|
+
width: 60px;
|
|
438
|
+
height: 60px;
|
|
439
|
+
border-radius: 8px;
|
|
440
|
+
overflow: hidden;
|
|
441
|
+
flex-shrink: 0;
|
|
442
|
+
background: ${colors.bg};
|
|
443
|
+
border: 1px solid ${colors.border};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.pp-preview-img {
|
|
447
|
+
width: 100%;
|
|
448
|
+
height: 100%;
|
|
449
|
+
object-fit: cover;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.pp-preview-file {
|
|
453
|
+
width: 100%;
|
|
454
|
+
height: 100%;
|
|
455
|
+
display: flex;
|
|
456
|
+
align-items: center;
|
|
457
|
+
justify-content: center;
|
|
458
|
+
color: ${colors.textSecondary};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.pp-preview-file svg {
|
|
462
|
+
width: 24px;
|
|
463
|
+
height: 24px;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.pp-remove-attachment {
|
|
467
|
+
position: absolute;
|
|
468
|
+
top: 2px;
|
|
469
|
+
right: 2px;
|
|
470
|
+
width: 18px;
|
|
471
|
+
height: 18px;
|
|
472
|
+
border-radius: 50%;
|
|
473
|
+
background: rgba(0, 0, 0, 0.6);
|
|
474
|
+
color: white;
|
|
475
|
+
border: none;
|
|
476
|
+
cursor: pointer;
|
|
477
|
+
display: flex;
|
|
478
|
+
align-items: center;
|
|
479
|
+
justify-content: center;
|
|
480
|
+
padding: 0;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.pp-remove-attachment svg {
|
|
484
|
+
width: 10px;
|
|
485
|
+
height: 10px;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.pp-upload-progress {
|
|
489
|
+
position: absolute;
|
|
490
|
+
bottom: 0;
|
|
491
|
+
left: 0;
|
|
492
|
+
height: 3px;
|
|
493
|
+
background: ${primaryColor};
|
|
494
|
+
transition: width 0.1s;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.pp-upload-error {
|
|
498
|
+
position: absolute;
|
|
499
|
+
top: 50%;
|
|
500
|
+
left: 50%;
|
|
501
|
+
transform: translate(-50%, -50%);
|
|
502
|
+
width: 24px;
|
|
503
|
+
height: 24px;
|
|
504
|
+
border-radius: 50%;
|
|
505
|
+
background: #ef4444;
|
|
506
|
+
color: white;
|
|
507
|
+
font-weight: bold;
|
|
508
|
+
display: flex;
|
|
509
|
+
align-items: center;
|
|
510
|
+
justify-content: center;
|
|
511
|
+
font-size: 14px;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.pp-attachment-uploading {
|
|
515
|
+
opacity: 0.7;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.pp-attachment-error {
|
|
519
|
+
border-color: #ef4444;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* Message Attachments */
|
|
523
|
+
.pp-message-attachments {
|
|
524
|
+
display: flex;
|
|
525
|
+
flex-direction: column;
|
|
526
|
+
gap: 8px;
|
|
527
|
+
margin-top: 4px;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.pp-attachment {
|
|
531
|
+
display: block;
|
|
532
|
+
text-decoration: none;
|
|
533
|
+
color: inherit;
|
|
534
|
+
border-radius: 8px;
|
|
535
|
+
overflow: hidden;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.pp-attachment-image img {
|
|
539
|
+
max-width: 200px;
|
|
540
|
+
max-height: 200px;
|
|
541
|
+
border-radius: 8px;
|
|
542
|
+
display: block;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.pp-attachment-audio {
|
|
546
|
+
display: flex;
|
|
547
|
+
flex-direction: column;
|
|
548
|
+
gap: 4px;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.pp-attachment-audio audio {
|
|
552
|
+
width: 200px;
|
|
553
|
+
height: 36px;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.pp-attachment-audio .pp-attachment-name {
|
|
557
|
+
font-size: 11px;
|
|
558
|
+
opacity: 0.7;
|
|
559
|
+
white-space: nowrap;
|
|
560
|
+
overflow: hidden;
|
|
561
|
+
text-overflow: ellipsis;
|
|
562
|
+
max-width: 200px;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.pp-attachment-video video {
|
|
566
|
+
max-width: 200px;
|
|
567
|
+
max-height: 200px;
|
|
568
|
+
border-radius: 8px;
|
|
569
|
+
display: block;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.pp-attachment-file {
|
|
573
|
+
display: flex;
|
|
574
|
+
align-items: center;
|
|
575
|
+
gap: 8px;
|
|
576
|
+
padding: 8px 12px;
|
|
577
|
+
background: ${isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
|
|
578
|
+
border-radius: 8px;
|
|
579
|
+
transition: background 0.2s;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.pp-attachment-file:hover {
|
|
583
|
+
background: ${isDark ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.08)"};
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.pp-attachment-file svg {
|
|
587
|
+
width: 24px;
|
|
588
|
+
height: 24px;
|
|
589
|
+
flex-shrink: 0;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.pp-attachment-info {
|
|
593
|
+
display: flex;
|
|
594
|
+
flex-direction: column;
|
|
595
|
+
min-width: 0;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.pp-attachment-name {
|
|
599
|
+
font-size: 13px;
|
|
600
|
+
font-weight: 500;
|
|
601
|
+
white-space: nowrap;
|
|
602
|
+
overflow: hidden;
|
|
603
|
+
text-overflow: ellipsis;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.pp-attachment-size {
|
|
607
|
+
font-size: 11px;
|
|
608
|
+
opacity: 0.7;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/* Drag & Drop */
|
|
612
|
+
.pp-dragging {
|
|
613
|
+
position: relative;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.pp-drop-overlay {
|
|
617
|
+
position: absolute;
|
|
618
|
+
inset: 0;
|
|
619
|
+
background: ${isDark ? "rgba(0,0,0,0.9)" : "rgba(255,255,255,0.95)"};
|
|
620
|
+
display: flex;
|
|
621
|
+
flex-direction: column;
|
|
622
|
+
align-items: center;
|
|
623
|
+
justify-content: center;
|
|
624
|
+
gap: 12px;
|
|
625
|
+
z-index: 100;
|
|
626
|
+
border: 3px dashed ${primaryColor};
|
|
627
|
+
border-radius: 16px;
|
|
628
|
+
margin: 4px;
|
|
629
|
+
pointer-events: none;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.pp-drop-icon svg {
|
|
633
|
+
width: 48px;
|
|
634
|
+
height: 48px;
|
|
635
|
+
color: ${primaryColor};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.pp-drop-text {
|
|
639
|
+
font-size: 16px;
|
|
640
|
+
font-weight: 500;
|
|
641
|
+
color: ${colors.text};
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/* Message Context Menu */
|
|
645
|
+
.pp-message-menu {
|
|
646
|
+
position: fixed;
|
|
647
|
+
background: ${colors.bg};
|
|
648
|
+
border: 1px solid ${colors.border};
|
|
649
|
+
border-radius: 8px;
|
|
650
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
651
|
+
padding: 4px;
|
|
652
|
+
z-index: 200;
|
|
653
|
+
min-width: 120px;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.pp-message-menu button {
|
|
657
|
+
display: flex;
|
|
658
|
+
align-items: center;
|
|
659
|
+
gap: 8px;
|
|
660
|
+
width: 100%;
|
|
661
|
+
padding: 8px 12px;
|
|
662
|
+
border: none;
|
|
663
|
+
background: transparent;
|
|
664
|
+
color: ${colors.text};
|
|
665
|
+
font-size: 13px;
|
|
666
|
+
cursor: pointer;
|
|
667
|
+
border-radius: 4px;
|
|
668
|
+
text-align: left;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
.pp-message-menu button:hover {
|
|
672
|
+
background: ${isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.pp-message-menu button svg {
|
|
676
|
+
width: 16px;
|
|
677
|
+
height: 16px;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.pp-menu-delete {
|
|
681
|
+
color: #ef4444 !important;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/* Edit Modal */
|
|
685
|
+
.pp-edit-modal {
|
|
686
|
+
position: absolute;
|
|
687
|
+
bottom: 80px;
|
|
688
|
+
left: 12px;
|
|
689
|
+
right: 12px;
|
|
690
|
+
background: ${colors.bg};
|
|
691
|
+
border: 1px solid ${colors.border};
|
|
692
|
+
border-radius: 12px;
|
|
693
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
|
694
|
+
z-index: 150;
|
|
695
|
+
overflow: hidden;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.pp-edit-header {
|
|
699
|
+
display: flex;
|
|
700
|
+
justify-content: space-between;
|
|
701
|
+
align-items: center;
|
|
702
|
+
padding: 12px 16px;
|
|
703
|
+
border-bottom: 1px solid ${colors.border};
|
|
704
|
+
font-weight: 500;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
.pp-edit-header button {
|
|
708
|
+
background: transparent;
|
|
709
|
+
border: none;
|
|
710
|
+
color: ${colors.textSecondary};
|
|
711
|
+
cursor: pointer;
|
|
712
|
+
padding: 4px;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
.pp-edit-header button svg {
|
|
716
|
+
width: 18px;
|
|
717
|
+
height: 18px;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
.pp-edit-input {
|
|
721
|
+
width: 100%;
|
|
722
|
+
padding: 12px 16px;
|
|
723
|
+
border: none;
|
|
724
|
+
background: transparent;
|
|
725
|
+
color: ${colors.text};
|
|
726
|
+
font-size: 14px;
|
|
727
|
+
resize: none;
|
|
728
|
+
min-height: 80px;
|
|
729
|
+
outline: none;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.pp-edit-actions {
|
|
733
|
+
display: flex;
|
|
734
|
+
justify-content: flex-end;
|
|
735
|
+
gap: 8px;
|
|
736
|
+
padding: 12px 16px;
|
|
737
|
+
border-top: 1px solid ${colors.border};
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
.pp-edit-cancel {
|
|
741
|
+
padding: 8px 16px;
|
|
742
|
+
border: 1px solid ${colors.border};
|
|
743
|
+
border-radius: 6px;
|
|
744
|
+
background: transparent;
|
|
745
|
+
color: ${colors.text};
|
|
746
|
+
font-size: 13px;
|
|
747
|
+
cursor: pointer;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.pp-edit-save {
|
|
751
|
+
padding: 8px 16px;
|
|
752
|
+
border: none;
|
|
753
|
+
border-radius: 6px;
|
|
754
|
+
background: ${primaryColor};
|
|
755
|
+
color: white;
|
|
756
|
+
font-size: 13px;
|
|
757
|
+
cursor: pointer;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.pp-edit-save:disabled {
|
|
761
|
+
opacity: 0.5;
|
|
762
|
+
cursor: not-allowed;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/* Reply Preview */
|
|
766
|
+
.pp-reply-preview {
|
|
767
|
+
display: flex;
|
|
768
|
+
align-items: center;
|
|
769
|
+
gap: 8px;
|
|
770
|
+
padding: 8px 12px;
|
|
771
|
+
background: ${isDark ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
|
|
772
|
+
border-top: 1px solid ${colors.border};
|
|
773
|
+
border-left: 3px solid ${primaryColor};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.pp-reply-preview-content {
|
|
777
|
+
flex: 1;
|
|
778
|
+
min-width: 0;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.pp-reply-label {
|
|
782
|
+
display: block;
|
|
783
|
+
font-size: 11px;
|
|
784
|
+
color: ${primaryColor};
|
|
785
|
+
font-weight: 500;
|
|
786
|
+
margin-bottom: 2px;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.pp-reply-text {
|
|
790
|
+
display: block;
|
|
791
|
+
font-size: 12px;
|
|
792
|
+
color: ${colors.textSecondary};
|
|
793
|
+
white-space: nowrap;
|
|
794
|
+
overflow: hidden;
|
|
795
|
+
text-overflow: ellipsis;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.pp-reply-cancel {
|
|
799
|
+
background: transparent;
|
|
800
|
+
border: none;
|
|
801
|
+
color: ${colors.textSecondary};
|
|
802
|
+
cursor: pointer;
|
|
803
|
+
padding: 4px;
|
|
804
|
+
flex-shrink: 0;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.pp-reply-cancel svg {
|
|
808
|
+
width: 16px;
|
|
809
|
+
height: 16px;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/* Reply Quote in Message */
|
|
813
|
+
.pp-reply-quote {
|
|
814
|
+
background: ${isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
|
|
815
|
+
border-left: 2px solid ${primaryColor};
|
|
816
|
+
padding: 4px 8px;
|
|
817
|
+
margin-bottom: 6px;
|
|
818
|
+
border-radius: 0 4px 4px 0;
|
|
819
|
+
font-size: 12px;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.pp-reply-sender {
|
|
823
|
+
display: block;
|
|
824
|
+
font-weight: 500;
|
|
825
|
+
color: ${primaryColor};
|
|
826
|
+
margin-bottom: 2px;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.pp-reply-content {
|
|
830
|
+
display: block;
|
|
831
|
+
color: ${colors.textSecondary};
|
|
832
|
+
white-space: nowrap;
|
|
833
|
+
overflow: hidden;
|
|
834
|
+
text-overflow: ellipsis;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/* Deleted Message */
|
|
838
|
+
.pp-message-deleted {
|
|
839
|
+
opacity: 0.6;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
.pp-deleted-content {
|
|
843
|
+
font-style: italic;
|
|
844
|
+
color: ${colors.textSecondary};
|
|
845
|
+
display: flex;
|
|
846
|
+
align-items: center;
|
|
847
|
+
gap: 4px;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.pp-deleted-icon {
|
|
851
|
+
font-size: 12px;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/* Edited Badge */
|
|
855
|
+
.pp-edited-badge {
|
|
856
|
+
font-size: 10px;
|
|
857
|
+
color: ${colors.textSecondary};
|
|
858
|
+
margin-left: 4px;
|
|
859
|
+
font-style: italic;
|
|
860
|
+
}
|
|
375
861
|
`;
|
|
376
862
|
}
|
|
377
863
|
|
|
@@ -385,9 +871,18 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
385
871
|
const [operatorOnline, setOperatorOnline] = useState(false);
|
|
386
872
|
const [isConnected, setIsConnected] = useState(false);
|
|
387
873
|
const [unreadCount, setUnreadCount] = useState(0);
|
|
874
|
+
const [pendingAttachments, setPendingAttachments] = useState([]);
|
|
875
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
876
|
+
const [replyingTo, setReplyingTo] = useState(null);
|
|
877
|
+
const [editingMessage, setEditingMessage] = useState(null);
|
|
878
|
+
const [editContent, setEditContent] = useState("");
|
|
879
|
+
const [messageMenu, setMessageMenu] = useState(null);
|
|
880
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
388
881
|
const [config, setConfig] = useState(initialConfig);
|
|
389
882
|
const messagesEndRef = useRef(null);
|
|
390
883
|
const inputRef = useRef(null);
|
|
884
|
+
const fileInputRef = useRef(null);
|
|
885
|
+
const messagesContainerRef = useRef(null);
|
|
391
886
|
useEffect(() => {
|
|
392
887
|
const unsubOpen = client2.on("openChange", setIsOpen);
|
|
393
888
|
const unsubMessage = client2.on("message", () => {
|
|
@@ -484,11 +979,17 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
484
979
|
if (!shouldShow) return null;
|
|
485
980
|
const handleSubmit = async (e) => {
|
|
486
981
|
e.preventDefault();
|
|
487
|
-
|
|
982
|
+
const hasContent = inputValue.trim().length > 0;
|
|
983
|
+
const readyAttachments = pendingAttachments.filter((a) => a.status === "ready" && a.attachment);
|
|
984
|
+
if (!hasContent && readyAttachments.length === 0) return;
|
|
488
985
|
const content = inputValue;
|
|
986
|
+
const attachmentIds = readyAttachments.map((a) => a.attachment.id);
|
|
987
|
+
const replyToId = replyingTo?.id;
|
|
489
988
|
setInputValue("");
|
|
989
|
+
setPendingAttachments([]);
|
|
990
|
+
setReplyingTo(null);
|
|
490
991
|
try {
|
|
491
|
-
await client2.sendMessage(content);
|
|
992
|
+
await client2.sendMessage(content, attachmentIds, replyToId);
|
|
492
993
|
} catch (err) {
|
|
493
994
|
console.error("[PocketPing] Failed to send message:", err);
|
|
494
995
|
}
|
|
@@ -498,6 +999,190 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
498
999
|
setInputValue(target.value);
|
|
499
1000
|
client2.sendTyping(true);
|
|
500
1001
|
};
|
|
1002
|
+
const handleFileSelect = async (e) => {
|
|
1003
|
+
const target = e.target;
|
|
1004
|
+
const files = target.files;
|
|
1005
|
+
if (!files || files.length === 0) return;
|
|
1006
|
+
const newPending = [];
|
|
1007
|
+
for (let i = 0; i < files.length; i++) {
|
|
1008
|
+
const file = files[i];
|
|
1009
|
+
const id = `pending-${Date.now()}-${i}`;
|
|
1010
|
+
let preview;
|
|
1011
|
+
if (file.type.startsWith("image/")) {
|
|
1012
|
+
preview = URL.createObjectURL(file);
|
|
1013
|
+
}
|
|
1014
|
+
newPending.push({
|
|
1015
|
+
id,
|
|
1016
|
+
file,
|
|
1017
|
+
preview,
|
|
1018
|
+
progress: 0,
|
|
1019
|
+
status: "pending"
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
setPendingAttachments((prev) => [...prev, ...newPending]);
|
|
1023
|
+
target.value = "";
|
|
1024
|
+
setIsUploading(true);
|
|
1025
|
+
for (const pending of newPending) {
|
|
1026
|
+
try {
|
|
1027
|
+
setPendingAttachments(
|
|
1028
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, status: "uploading" } : a)
|
|
1029
|
+
);
|
|
1030
|
+
const attachment = await client2.uploadFile(pending.file, (progress) => {
|
|
1031
|
+
setPendingAttachments(
|
|
1032
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, progress } : a)
|
|
1033
|
+
);
|
|
1034
|
+
});
|
|
1035
|
+
setPendingAttachments(
|
|
1036
|
+
(prev) => prev.map(
|
|
1037
|
+
(a) => a.id === pending.id ? { ...a, status: "ready", progress: 100, attachment } : a
|
|
1038
|
+
)
|
|
1039
|
+
);
|
|
1040
|
+
} catch (err) {
|
|
1041
|
+
console.error("[PocketPing] Failed to upload file:", err);
|
|
1042
|
+
setPendingAttachments(
|
|
1043
|
+
(prev) => prev.map(
|
|
1044
|
+
(a) => a.id === pending.id ? { ...a, status: "error", error: "Upload failed" } : a
|
|
1045
|
+
)
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
setIsUploading(false);
|
|
1050
|
+
};
|
|
1051
|
+
const handleRemoveAttachment = (id) => {
|
|
1052
|
+
setPendingAttachments((prev) => {
|
|
1053
|
+
const removed = prev.find((a) => a.id === id);
|
|
1054
|
+
if (removed?.preview) {
|
|
1055
|
+
URL.revokeObjectURL(removed.preview);
|
|
1056
|
+
}
|
|
1057
|
+
return prev.filter((a) => a.id !== id);
|
|
1058
|
+
});
|
|
1059
|
+
};
|
|
1060
|
+
const handleReply = (message) => {
|
|
1061
|
+
setReplyingTo(message);
|
|
1062
|
+
setMessageMenu(null);
|
|
1063
|
+
inputRef.current?.focus();
|
|
1064
|
+
};
|
|
1065
|
+
const handleCancelReply = () => {
|
|
1066
|
+
setReplyingTo(null);
|
|
1067
|
+
};
|
|
1068
|
+
const handleStartEdit = (message) => {
|
|
1069
|
+
if (message.sender !== "visitor") return;
|
|
1070
|
+
setEditingMessage(message);
|
|
1071
|
+
setEditContent(message.content);
|
|
1072
|
+
setMessageMenu(null);
|
|
1073
|
+
};
|
|
1074
|
+
const handleCancelEdit = () => {
|
|
1075
|
+
setEditingMessage(null);
|
|
1076
|
+
setEditContent("");
|
|
1077
|
+
};
|
|
1078
|
+
const handleSaveEdit = async () => {
|
|
1079
|
+
if (!editingMessage || !editContent.trim()) return;
|
|
1080
|
+
try {
|
|
1081
|
+
await client2.editMessage(editingMessage.id, editContent.trim());
|
|
1082
|
+
setEditingMessage(null);
|
|
1083
|
+
setEditContent("");
|
|
1084
|
+
} catch (err) {
|
|
1085
|
+
console.error("[PocketPing] Failed to edit message:", err);
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
const handleDelete = async (message) => {
|
|
1089
|
+
if (message.sender !== "visitor") return;
|
|
1090
|
+
setMessageMenu(null);
|
|
1091
|
+
if (confirm("Delete this message?")) {
|
|
1092
|
+
try {
|
|
1093
|
+
await client2.deleteMessage(message.id);
|
|
1094
|
+
} catch (err) {
|
|
1095
|
+
console.error("[PocketPing] Failed to delete message:", err);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
const handleMessageContextMenu = (e, message) => {
|
|
1100
|
+
e.preventDefault();
|
|
1101
|
+
const mouseEvent = e;
|
|
1102
|
+
setMessageMenu({
|
|
1103
|
+
message,
|
|
1104
|
+
x: mouseEvent.clientX,
|
|
1105
|
+
y: mouseEvent.clientY
|
|
1106
|
+
});
|
|
1107
|
+
};
|
|
1108
|
+
useEffect(() => {
|
|
1109
|
+
if (!messageMenu) return;
|
|
1110
|
+
const handleClickOutside = () => setMessageMenu(null);
|
|
1111
|
+
document.addEventListener("click", handleClickOutside);
|
|
1112
|
+
return () => document.removeEventListener("click", handleClickOutside);
|
|
1113
|
+
}, [messageMenu]);
|
|
1114
|
+
const dragCounterRef = useRef(0);
|
|
1115
|
+
const handleDragEnter = (e) => {
|
|
1116
|
+
e.preventDefault();
|
|
1117
|
+
e.stopPropagation();
|
|
1118
|
+
dragCounterRef.current++;
|
|
1119
|
+
if (dragCounterRef.current === 1) {
|
|
1120
|
+
setIsDragging(true);
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
const handleDragOver = (e) => {
|
|
1124
|
+
e.preventDefault();
|
|
1125
|
+
e.stopPropagation();
|
|
1126
|
+
};
|
|
1127
|
+
const handleDragLeave = (e) => {
|
|
1128
|
+
e.preventDefault();
|
|
1129
|
+
e.stopPropagation();
|
|
1130
|
+
dragCounterRef.current--;
|
|
1131
|
+
if (dragCounterRef.current === 0) {
|
|
1132
|
+
setIsDragging(false);
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
const handleDrop = async (e) => {
|
|
1136
|
+
e.preventDefault();
|
|
1137
|
+
e.stopPropagation();
|
|
1138
|
+
dragCounterRef.current = 0;
|
|
1139
|
+
setIsDragging(false);
|
|
1140
|
+
const files = e.dataTransfer?.files;
|
|
1141
|
+
if (!files || files.length === 0) return;
|
|
1142
|
+
const newPending = [];
|
|
1143
|
+
for (let i = 0; i < files.length; i++) {
|
|
1144
|
+
const file = files[i];
|
|
1145
|
+
const id = `pending-${Date.now()}-${i}`;
|
|
1146
|
+
let preview;
|
|
1147
|
+
if (file.type.startsWith("image/")) {
|
|
1148
|
+
preview = URL.createObjectURL(file);
|
|
1149
|
+
}
|
|
1150
|
+
newPending.push({
|
|
1151
|
+
id,
|
|
1152
|
+
file,
|
|
1153
|
+
preview,
|
|
1154
|
+
progress: 0,
|
|
1155
|
+
status: "pending"
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
setPendingAttachments((prev) => [...prev, ...newPending]);
|
|
1159
|
+
setIsUploading(true);
|
|
1160
|
+
for (const pending of newPending) {
|
|
1161
|
+
try {
|
|
1162
|
+
setPendingAttachments(
|
|
1163
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, status: "uploading" } : a)
|
|
1164
|
+
);
|
|
1165
|
+
const attachment = await client2.uploadFile(pending.file, (progress) => {
|
|
1166
|
+
setPendingAttachments(
|
|
1167
|
+
(prev) => prev.map((a) => a.id === pending.id ? { ...a, progress } : a)
|
|
1168
|
+
);
|
|
1169
|
+
});
|
|
1170
|
+
setPendingAttachments(
|
|
1171
|
+
(prev) => prev.map(
|
|
1172
|
+
(a) => a.id === pending.id ? { ...a, status: "ready", progress: 100, attachment } : a
|
|
1173
|
+
)
|
|
1174
|
+
);
|
|
1175
|
+
} catch (err) {
|
|
1176
|
+
console.error("[PocketPing] Failed to upload dropped file:", err);
|
|
1177
|
+
setPendingAttachments(
|
|
1178
|
+
(prev) => prev.map(
|
|
1179
|
+
(a) => a.id === pending.id ? { ...a, status: "error", error: "Upload failed" } : a
|
|
1180
|
+
)
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
setIsUploading(false);
|
|
1185
|
+
};
|
|
501
1186
|
const position = config.position ?? "bottom-right";
|
|
502
1187
|
const theme = getTheme(config.theme ?? "auto");
|
|
503
1188
|
const primaryColor = config.primaryColor ?? "#6366f1";
|
|
@@ -516,84 +1201,211 @@ function ChatWidget({ client: client2, config: initialConfig }) {
|
|
|
516
1201
|
]
|
|
517
1202
|
}
|
|
518
1203
|
),
|
|
519
|
-
isOpen && /* @__PURE__ */ jsxs(
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
/* @__PURE__ */ jsxs("div", { class: "pp-messages", children: [
|
|
545
|
-
config.welcomeMessage && messages.length === 0 && /* @__PURE__ */ jsx("div", { class: "pp-welcome", children: config.welcomeMessage }),
|
|
546
|
-
messages.map((msg) => /* @__PURE__ */ jsxs(
|
|
547
|
-
"div",
|
|
548
|
-
{
|
|
549
|
-
class: `pp-message pp-message-${msg.sender}`,
|
|
550
|
-
children: [
|
|
551
|
-
/* @__PURE__ */ jsx("div", { class: "pp-message-content", children: msg.content }),
|
|
552
|
-
/* @__PURE__ */ jsxs("div", { class: "pp-message-time", children: [
|
|
553
|
-
formatTime(msg.timestamp),
|
|
554
|
-
msg.sender === "ai" && /* @__PURE__ */ jsx("span", { class: "pp-ai-badge", children: "AI" }),
|
|
555
|
-
msg.sender === "visitor" && /* @__PURE__ */ jsx("span", { class: `pp-status pp-status-${msg.status ?? "sent"}`, children: /* @__PURE__ */ jsx(StatusIcon, { status: msg.status }) })
|
|
1204
|
+
isOpen && /* @__PURE__ */ jsxs(
|
|
1205
|
+
"div",
|
|
1206
|
+
{
|
|
1207
|
+
class: `pp-window pp-${position} pp-theme-${theme} ${isDragging ? "pp-dragging" : ""}`,
|
|
1208
|
+
onDragEnter: handleDragEnter,
|
|
1209
|
+
onDragOver: handleDragOver,
|
|
1210
|
+
onDragLeave: handleDragLeave,
|
|
1211
|
+
onDrop: handleDrop,
|
|
1212
|
+
children: [
|
|
1213
|
+
isDragging && /* @__PURE__ */ jsxs("div", { class: "pp-drop-overlay", children: [
|
|
1214
|
+
/* @__PURE__ */ jsx("div", { class: "pp-drop-icon", children: /* @__PURE__ */ jsx(AttachIcon, {}) }),
|
|
1215
|
+
/* @__PURE__ */ jsx("div", { class: "pp-drop-text", children: "Drop files to upload" })
|
|
1216
|
+
] }),
|
|
1217
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-header", children: [
|
|
1218
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-header-info", children: [
|
|
1219
|
+
config.operatorAvatar && /* @__PURE__ */ jsx("img", { src: config.operatorAvatar, alt: "", class: "pp-avatar" }),
|
|
1220
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1221
|
+
/* @__PURE__ */ jsx("div", { class: "pp-header-title", children: config.operatorName ?? "Support" }),
|
|
1222
|
+
/* @__PURE__ */ jsx("div", { class: "pp-header-status", children: operatorOnline ? /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1223
|
+
/* @__PURE__ */ jsx("span", { class: "pp-status-dot pp-online" }),
|
|
1224
|
+
" Online"
|
|
1225
|
+
] }) : /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1226
|
+
/* @__PURE__ */ jsx("span", { class: "pp-status-dot" }),
|
|
1227
|
+
" Away"
|
|
1228
|
+
] }) })
|
|
556
1229
|
] })
|
|
557
|
-
]
|
|
558
|
-
|
|
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
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
1230
|
+
] }),
|
|
1231
|
+
/* @__PURE__ */ jsx(
|
|
1232
|
+
"button",
|
|
1233
|
+
{
|
|
1234
|
+
class: "pp-close-btn",
|
|
1235
|
+
onClick: () => client2.setOpen(false),
|
|
1236
|
+
"aria-label": "Close chat",
|
|
1237
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
1238
|
+
}
|
|
1239
|
+
)
|
|
1240
|
+
] }),
|
|
1241
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-messages", ref: messagesContainerRef, children: [
|
|
1242
|
+
config.welcomeMessage && messages.length === 0 && /* @__PURE__ */ jsx("div", { class: "pp-welcome", children: config.welcomeMessage }),
|
|
1243
|
+
messages.map((msg) => {
|
|
1244
|
+
const isDeleted = !!msg.deletedAt;
|
|
1245
|
+
const isEdited = !!msg.editedAt;
|
|
1246
|
+
const replyToMsg = msg.replyTo ? messages.find((m) => m.id === msg.replyTo) : null;
|
|
1247
|
+
return /* @__PURE__ */ jsxs(
|
|
1248
|
+
"div",
|
|
1249
|
+
{
|
|
1250
|
+
class: `pp-message pp-message-${msg.sender} ${isDeleted ? "pp-message-deleted" : ""}`,
|
|
1251
|
+
onContextMenu: (e) => handleMessageContextMenu(e, msg),
|
|
1252
|
+
children: [
|
|
1253
|
+
replyToMsg && /* @__PURE__ */ jsxs("div", { class: "pp-reply-quote", children: [
|
|
1254
|
+
/* @__PURE__ */ jsx("span", { class: "pp-reply-sender", children: replyToMsg.sender === "visitor" ? "You" : "Support" }),
|
|
1255
|
+
/* @__PURE__ */ jsxs("span", { class: "pp-reply-content", children: [
|
|
1256
|
+
replyToMsg.deletedAt ? "Message deleted" : replyToMsg.content.slice(0, 50),
|
|
1257
|
+
replyToMsg.content.length > 50 ? "..." : ""
|
|
1258
|
+
] })
|
|
1259
|
+
] }),
|
|
1260
|
+
isDeleted ? /* @__PURE__ */ jsxs("div", { class: "pp-message-content pp-deleted-content", children: [
|
|
1261
|
+
/* @__PURE__ */ jsx("span", { class: "pp-deleted-icon", children: "\u{1F5D1}\uFE0F" }),
|
|
1262
|
+
" Message deleted"
|
|
1263
|
+
] }) : /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1264
|
+
msg.content && /* @__PURE__ */ jsx("div", { class: "pp-message-content", children: msg.content }),
|
|
1265
|
+
msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ jsx("div", { class: "pp-message-attachments", children: msg.attachments.map((att) => /* @__PURE__ */ jsx(AttachmentDisplay, { attachment: att }, att.id)) })
|
|
1266
|
+
] }),
|
|
1267
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-message-time", children: [
|
|
1268
|
+
formatTime(msg.timestamp),
|
|
1269
|
+
isEdited && !isDeleted && /* @__PURE__ */ jsx("span", { class: "pp-edited-badge", children: "edited" }),
|
|
1270
|
+
msg.sender === "ai" && /* @__PURE__ */ jsx("span", { class: "pp-ai-badge", children: "AI" }),
|
|
1271
|
+
msg.sender === "visitor" && !isDeleted && /* @__PURE__ */ jsx("span", { class: `pp-status pp-status-${msg.status ?? "sent"}`, children: /* @__PURE__ */ jsx(StatusIcon, { status: msg.status }) })
|
|
1272
|
+
] })
|
|
1273
|
+
]
|
|
1274
|
+
},
|
|
1275
|
+
msg.id
|
|
1276
|
+
);
|
|
1277
|
+
}),
|
|
1278
|
+
isTyping && /* @__PURE__ */ jsxs("div", { class: "pp-message pp-message-operator pp-typing", children: [
|
|
1279
|
+
/* @__PURE__ */ jsx("span", {}),
|
|
1280
|
+
/* @__PURE__ */ jsx("span", {}),
|
|
1281
|
+
/* @__PURE__ */ jsx("span", {})
|
|
1282
|
+
] }),
|
|
1283
|
+
/* @__PURE__ */ jsx("div", { ref: messagesEndRef })
|
|
1284
|
+
] }),
|
|
1285
|
+
messageMenu && /* @__PURE__ */ jsxs(
|
|
1286
|
+
"div",
|
|
1287
|
+
{
|
|
1288
|
+
class: "pp-message-menu",
|
|
1289
|
+
style: { top: `${messageMenu.y}px`, left: `${messageMenu.x}px` },
|
|
1290
|
+
children: [
|
|
1291
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => handleReply(messageMenu.message), children: [
|
|
1292
|
+
/* @__PURE__ */ jsx(ReplyIcon, {}),
|
|
1293
|
+
" Reply"
|
|
1294
|
+
] }),
|
|
1295
|
+
messageMenu.message.sender === "visitor" && !messageMenu.message.deletedAt && /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1296
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => handleStartEdit(messageMenu.message), children: [
|
|
1297
|
+
/* @__PURE__ */ jsx(EditIcon, {}),
|
|
1298
|
+
" Edit"
|
|
1299
|
+
] }),
|
|
1300
|
+
/* @__PURE__ */ jsxs("button", { class: "pp-menu-delete", onClick: () => handleDelete(messageMenu.message), children: [
|
|
1301
|
+
/* @__PURE__ */ jsx(DeleteIcon, {}),
|
|
1302
|
+
" Delete"
|
|
1303
|
+
] })
|
|
1304
|
+
] })
|
|
1305
|
+
]
|
|
1306
|
+
}
|
|
1307
|
+
),
|
|
1308
|
+
editingMessage && /* @__PURE__ */ jsxs("div", { class: "pp-edit-modal", children: [
|
|
1309
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-edit-header", children: [
|
|
1310
|
+
/* @__PURE__ */ jsx("span", { children: "Edit message" }),
|
|
1311
|
+
/* @__PURE__ */ jsx("button", { onClick: handleCancelEdit, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
1312
|
+
] }),
|
|
1313
|
+
/* @__PURE__ */ jsx(
|
|
1314
|
+
"textarea",
|
|
1315
|
+
{
|
|
1316
|
+
class: "pp-edit-input",
|
|
1317
|
+
value: editContent,
|
|
1318
|
+
onInput: (e) => setEditContent(e.target.value),
|
|
1319
|
+
autoFocus: true
|
|
1320
|
+
}
|
|
1321
|
+
),
|
|
1322
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-edit-actions", children: [
|
|
1323
|
+
/* @__PURE__ */ jsx("button", { class: "pp-edit-cancel", onClick: handleCancelEdit, children: "Cancel" }),
|
|
1324
|
+
/* @__PURE__ */ jsx("button", { class: "pp-edit-save", onClick: handleSaveEdit, disabled: !editContent.trim(), children: "Save" })
|
|
1325
|
+
] })
|
|
1326
|
+
] }),
|
|
1327
|
+
replyingTo && /* @__PURE__ */ jsxs("div", { class: "pp-reply-preview", children: [
|
|
1328
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-reply-preview-content", children: [
|
|
1329
|
+
/* @__PURE__ */ jsx("span", { class: "pp-reply-label", children: "Replying to" }),
|
|
1330
|
+
/* @__PURE__ */ jsxs("span", { class: "pp-reply-text", children: [
|
|
1331
|
+
replyingTo.content.slice(0, 50),
|
|
1332
|
+
replyingTo.content.length > 50 ? "..." : ""
|
|
1333
|
+
] })
|
|
1334
|
+
] }),
|
|
1335
|
+
/* @__PURE__ */ jsx("button", { class: "pp-reply-cancel", onClick: handleCancelReply, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
1336
|
+
] }),
|
|
1337
|
+
pendingAttachments.length > 0 && /* @__PURE__ */ jsx("div", { class: "pp-attachments-preview", children: pendingAttachments.map((pending) => /* @__PURE__ */ jsxs("div", { class: `pp-attachment-preview pp-attachment-${pending.status}`, children: [
|
|
1338
|
+
pending.preview ? /* @__PURE__ */ jsx("img", { src: pending.preview, alt: pending.file.name, class: "pp-preview-img" }) : /* @__PURE__ */ jsx("div", { class: "pp-preview-file", children: /* @__PURE__ */ jsx(FileIcon, { mimeType: pending.file.type }) }),
|
|
1339
|
+
/* @__PURE__ */ jsx(
|
|
1340
|
+
"button",
|
|
1341
|
+
{
|
|
1342
|
+
class: "pp-remove-attachment",
|
|
1343
|
+
onClick: () => handleRemoveAttachment(pending.id),
|
|
1344
|
+
"aria-label": "Remove attachment",
|
|
1345
|
+
type: "button",
|
|
1346
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
1347
|
+
}
|
|
1348
|
+
),
|
|
1349
|
+
pending.status === "uploading" && /* @__PURE__ */ jsx("div", { class: "pp-upload-progress", style: { width: `${pending.progress}%` } }),
|
|
1350
|
+
pending.status === "error" && /* @__PURE__ */ jsx("div", { class: "pp-upload-error", title: pending.error, children: "!" })
|
|
1351
|
+
] }, pending.id)) }),
|
|
1352
|
+
/* @__PURE__ */ jsxs("form", { class: "pp-input-form", onSubmit: handleSubmit, children: [
|
|
1353
|
+
/* @__PURE__ */ jsx(
|
|
1354
|
+
"input",
|
|
1355
|
+
{
|
|
1356
|
+
ref: (el) => {
|
|
1357
|
+
fileInputRef.current = el;
|
|
1358
|
+
if (el) {
|
|
1359
|
+
el.onchange = handleFileSelect;
|
|
1360
|
+
}
|
|
1361
|
+
},
|
|
1362
|
+
type: "file",
|
|
1363
|
+
class: "pp-file-input",
|
|
1364
|
+
accept: "image/*,audio/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.txt",
|
|
1365
|
+
multiple: true
|
|
1366
|
+
}
|
|
1367
|
+
),
|
|
1368
|
+
/* @__PURE__ */ jsx(
|
|
1369
|
+
"button",
|
|
1370
|
+
{
|
|
1371
|
+
type: "button",
|
|
1372
|
+
class: "pp-attach-btn",
|
|
1373
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1374
|
+
disabled: !isConnected || isUploading,
|
|
1375
|
+
"aria-label": "Attach file",
|
|
1376
|
+
children: /* @__PURE__ */ jsx(AttachIcon, {})
|
|
1377
|
+
}
|
|
1378
|
+
),
|
|
1379
|
+
/* @__PURE__ */ jsx(
|
|
1380
|
+
"input",
|
|
1381
|
+
{
|
|
1382
|
+
ref: inputRef,
|
|
1383
|
+
type: "text",
|
|
1384
|
+
class: "pp-input",
|
|
1385
|
+
placeholder: config.placeholder ?? "Type a message...",
|
|
1386
|
+
value: inputValue,
|
|
1387
|
+
onInput: handleInputChange,
|
|
1388
|
+
disabled: !isConnected
|
|
1389
|
+
}
|
|
1390
|
+
),
|
|
1391
|
+
/* @__PURE__ */ jsx(
|
|
1392
|
+
"button",
|
|
1393
|
+
{
|
|
1394
|
+
type: "submit",
|
|
1395
|
+
class: "pp-send-btn",
|
|
1396
|
+
disabled: !inputValue.trim() && pendingAttachments.filter((a) => a.status === "ready").length === 0 || !isConnected || isUploading,
|
|
1397
|
+
"aria-label": "Send message",
|
|
1398
|
+
children: /* @__PURE__ */ jsx(SendIcon, {})
|
|
1399
|
+
}
|
|
1400
|
+
)
|
|
1401
|
+
] }),
|
|
1402
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-footer", children: [
|
|
1403
|
+
"Powered by ",
|
|
1404
|
+
/* @__PURE__ */ jsx("a", { href: "https://pocketping.io", target: "_blank", rel: "noopener", children: "PocketPing" })
|
|
1405
|
+
] })
|
|
1406
|
+
]
|
|
1407
|
+
}
|
|
1408
|
+
)
|
|
597
1409
|
] });
|
|
598
1410
|
}
|
|
599
1411
|
function checkPageVisibility(config) {
|
|
@@ -649,6 +1461,86 @@ function StatusIcon({ status }) {
|
|
|
649
1461
|
}
|
|
650
1462
|
return null;
|
|
651
1463
|
}
|
|
1464
|
+
function AttachIcon() {
|
|
1465
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: /* @__PURE__ */ 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" }) });
|
|
1466
|
+
}
|
|
1467
|
+
function ReplyIcon() {
|
|
1468
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1469
|
+
/* @__PURE__ */ jsx("polyline", { points: "9 17 4 12 9 7" }),
|
|
1470
|
+
/* @__PURE__ */ jsx("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
1471
|
+
] });
|
|
1472
|
+
}
|
|
1473
|
+
function EditIcon() {
|
|
1474
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: /* @__PURE__ */ jsx("path", { d: "M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" }) });
|
|
1475
|
+
}
|
|
1476
|
+
function DeleteIcon() {
|
|
1477
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1478
|
+
/* @__PURE__ */ jsx("polyline", { points: "3 6 5 6 21 6" }),
|
|
1479
|
+
/* @__PURE__ */ 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" })
|
|
1480
|
+
] });
|
|
1481
|
+
}
|
|
1482
|
+
function FileIcon({ mimeType }) {
|
|
1483
|
+
if (mimeType === "application/pdf") {
|
|
1484
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1485
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
1486
|
+
/* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" }),
|
|
1487
|
+
/* @__PURE__ */ jsx("path", { d: "M9 15h6" }),
|
|
1488
|
+
/* @__PURE__ */ jsx("path", { d: "M9 11h6" })
|
|
1489
|
+
] });
|
|
1490
|
+
}
|
|
1491
|
+
if (mimeType.startsWith("audio/")) {
|
|
1492
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1493
|
+
/* @__PURE__ */ jsx("path", { d: "M9 18V5l12-2v13" }),
|
|
1494
|
+
/* @__PURE__ */ jsx("circle", { cx: "6", cy: "18", r: "3" }),
|
|
1495
|
+
/* @__PURE__ */ jsx("circle", { cx: "18", cy: "16", r: "3" })
|
|
1496
|
+
] });
|
|
1497
|
+
}
|
|
1498
|
+
if (mimeType.startsWith("video/")) {
|
|
1499
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1500
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "20", height: "20", rx: "2.18", ry: "2.18" }),
|
|
1501
|
+
/* @__PURE__ */ jsx("line", { x1: "7", y1: "2", x2: "7", y2: "22" }),
|
|
1502
|
+
/* @__PURE__ */ jsx("line", { x1: "17", y1: "2", x2: "17", y2: "22" }),
|
|
1503
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "12", x2: "22", y2: "12" }),
|
|
1504
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "7", x2: "7", y2: "7" }),
|
|
1505
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "17", x2: "7", y2: "17" }),
|
|
1506
|
+
/* @__PURE__ */ jsx("line", { x1: "17", y1: "17", x2: "22", y2: "17" }),
|
|
1507
|
+
/* @__PURE__ */ jsx("line", { x1: "17", y1: "7", x2: "22", y2: "7" })
|
|
1508
|
+
] });
|
|
1509
|
+
}
|
|
1510
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", children: [
|
|
1511
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
1512
|
+
/* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" })
|
|
1513
|
+
] });
|
|
1514
|
+
}
|
|
1515
|
+
function AttachmentDisplay({ attachment }) {
|
|
1516
|
+
const isImage = attachment.mimeType.startsWith("image/");
|
|
1517
|
+
const isAudio = attachment.mimeType.startsWith("audio/");
|
|
1518
|
+
const isVideo = attachment.mimeType.startsWith("video/");
|
|
1519
|
+
const formatSize = (bytes) => {
|
|
1520
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1521
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1522
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1523
|
+
};
|
|
1524
|
+
if (isImage) {
|
|
1525
|
+
return /* @__PURE__ */ jsx("a", { href: attachment.url, target: "_blank", rel: "noopener", class: "pp-attachment pp-attachment-image", children: /* @__PURE__ */ jsx("img", { src: attachment.thumbnailUrl || attachment.url, alt: attachment.filename }) });
|
|
1526
|
+
}
|
|
1527
|
+
if (isAudio) {
|
|
1528
|
+
return /* @__PURE__ */ jsxs("div", { class: "pp-attachment pp-attachment-audio", children: [
|
|
1529
|
+
/* @__PURE__ */ jsx("audio", { controls: true, preload: "metadata", children: /* @__PURE__ */ jsx("source", { src: attachment.url, type: attachment.mimeType }) }),
|
|
1530
|
+
/* @__PURE__ */ jsx("span", { class: "pp-attachment-name", children: attachment.filename })
|
|
1531
|
+
] });
|
|
1532
|
+
}
|
|
1533
|
+
if (isVideo) {
|
|
1534
|
+
return /* @__PURE__ */ jsx("div", { class: "pp-attachment pp-attachment-video", children: /* @__PURE__ */ jsx("video", { controls: true, preload: "metadata", children: /* @__PURE__ */ jsx("source", { src: attachment.url, type: attachment.mimeType }) }) });
|
|
1535
|
+
}
|
|
1536
|
+
return /* @__PURE__ */ jsxs("a", { href: attachment.url, target: "_blank", rel: "noopener", class: "pp-attachment pp-attachment-file", children: [
|
|
1537
|
+
/* @__PURE__ */ jsx(FileIcon, { mimeType: attachment.mimeType }),
|
|
1538
|
+
/* @__PURE__ */ jsxs("div", { class: "pp-attachment-info", children: [
|
|
1539
|
+
/* @__PURE__ */ jsx("span", { class: "pp-attachment-name", children: attachment.filename }),
|
|
1540
|
+
/* @__PURE__ */ jsx("span", { class: "pp-attachment-size", children: formatSize(attachment.size) })
|
|
1541
|
+
] })
|
|
1542
|
+
] });
|
|
1543
|
+
}
|
|
652
1544
|
|
|
653
1545
|
// src/version.ts
|
|
654
1546
|
var VERSION = "0.3.6";
|
|
@@ -764,7 +1656,7 @@ var PocketPingClient = class {
|
|
|
764
1656
|
this.cleanupTrackedElements();
|
|
765
1657
|
this.disableInspectorMode();
|
|
766
1658
|
}
|
|
767
|
-
async sendMessage(content) {
|
|
1659
|
+
async sendMessage(content, attachmentIds, replyTo) {
|
|
768
1660
|
if (!this.session) {
|
|
769
1661
|
throw new Error("Not connected");
|
|
770
1662
|
}
|
|
@@ -775,7 +1667,8 @@ var PocketPingClient = class {
|
|
|
775
1667
|
content,
|
|
776
1668
|
sender: "visitor",
|
|
777
1669
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
778
|
-
status: "sending"
|
|
1670
|
+
status: "sending",
|
|
1671
|
+
replyTo
|
|
779
1672
|
};
|
|
780
1673
|
this.session.messages.push(tempMessage);
|
|
781
1674
|
this.emit("message", tempMessage);
|
|
@@ -785,7 +1678,9 @@ var PocketPingClient = class {
|
|
|
785
1678
|
body: JSON.stringify({
|
|
786
1679
|
sessionId: this.session.sessionId,
|
|
787
1680
|
content,
|
|
788
|
-
sender: "visitor"
|
|
1681
|
+
sender: "visitor",
|
|
1682
|
+
attachmentIds: attachmentIds || [],
|
|
1683
|
+
replyTo
|
|
789
1684
|
})
|
|
790
1685
|
});
|
|
791
1686
|
const messageIndex = this.session.messages.findIndex((m) => m.id === tempId);
|
|
@@ -793,6 +1688,9 @@ var PocketPingClient = class {
|
|
|
793
1688
|
this.session.messages[messageIndex].id = response.messageId;
|
|
794
1689
|
this.session.messages[messageIndex].timestamp = response.timestamp;
|
|
795
1690
|
this.session.messages[messageIndex].status = "sent";
|
|
1691
|
+
if (response.attachments && response.attachments.length > 0) {
|
|
1692
|
+
this.session.messages[messageIndex].attachments = response.attachments;
|
|
1693
|
+
}
|
|
796
1694
|
this.emit("message", this.session.messages[messageIndex]);
|
|
797
1695
|
}
|
|
798
1696
|
const message = this.session.messages[messageIndex] || {
|
|
@@ -801,7 +1699,8 @@ var PocketPingClient = class {
|
|
|
801
1699
|
content,
|
|
802
1700
|
sender: "visitor",
|
|
803
1701
|
timestamp: response.timestamp,
|
|
804
|
-
status: "sent"
|
|
1702
|
+
status: "sent",
|
|
1703
|
+
attachments: response.attachments
|
|
805
1704
|
};
|
|
806
1705
|
this.config.onMessage?.(message);
|
|
807
1706
|
return message;
|
|
@@ -814,6 +1713,110 @@ var PocketPingClient = class {
|
|
|
814
1713
|
throw error;
|
|
815
1714
|
}
|
|
816
1715
|
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Upload a file attachment
|
|
1718
|
+
* Returns the attachment data after successful upload
|
|
1719
|
+
* @param file - File object to upload
|
|
1720
|
+
* @param onProgress - Optional callback for upload progress (0-100)
|
|
1721
|
+
* @example
|
|
1722
|
+
* const attachment = await PocketPing.uploadFile(file, (progress) => {
|
|
1723
|
+
* console.log(`Upload ${progress}% complete`)
|
|
1724
|
+
* })
|
|
1725
|
+
* await PocketPing.sendMessage('Check this file', [attachment.id])
|
|
1726
|
+
*/
|
|
1727
|
+
async uploadFile(file, onProgress) {
|
|
1728
|
+
if (!this.session) {
|
|
1729
|
+
throw new Error("Not connected");
|
|
1730
|
+
}
|
|
1731
|
+
this.emit("uploadStart", { filename: file.name, size: file.size });
|
|
1732
|
+
try {
|
|
1733
|
+
const initResponse = await this.fetch("/upload", {
|
|
1734
|
+
method: "POST",
|
|
1735
|
+
body: JSON.stringify({
|
|
1736
|
+
sessionId: this.session.sessionId,
|
|
1737
|
+
filename: file.name,
|
|
1738
|
+
mimeType: file.type || "application/octet-stream",
|
|
1739
|
+
size: file.size
|
|
1740
|
+
})
|
|
1741
|
+
});
|
|
1742
|
+
onProgress?.(10);
|
|
1743
|
+
this.emit("uploadProgress", { filename: file.name, progress: 10 });
|
|
1744
|
+
await this.uploadToPresignedUrl(initResponse.uploadUrl, file, (progress) => {
|
|
1745
|
+
const mappedProgress = 10 + progress * 0.8;
|
|
1746
|
+
onProgress?.(mappedProgress);
|
|
1747
|
+
this.emit("uploadProgress", { filename: file.name, progress: mappedProgress });
|
|
1748
|
+
});
|
|
1749
|
+
const completeResponse = await this.fetch("/upload/complete", {
|
|
1750
|
+
method: "POST",
|
|
1751
|
+
body: JSON.stringify({
|
|
1752
|
+
sessionId: this.session.sessionId,
|
|
1753
|
+
attachmentId: initResponse.attachmentId
|
|
1754
|
+
})
|
|
1755
|
+
});
|
|
1756
|
+
onProgress?.(100);
|
|
1757
|
+
this.emit("uploadComplete", completeResponse);
|
|
1758
|
+
return {
|
|
1759
|
+
id: completeResponse.id,
|
|
1760
|
+
filename: completeResponse.filename,
|
|
1761
|
+
mimeType: completeResponse.mimeType,
|
|
1762
|
+
size: completeResponse.size,
|
|
1763
|
+
url: completeResponse.url,
|
|
1764
|
+
thumbnailUrl: completeResponse.thumbnailUrl,
|
|
1765
|
+
status: completeResponse.status
|
|
1766
|
+
};
|
|
1767
|
+
} catch (error) {
|
|
1768
|
+
this.emit("uploadError", { filename: file.name, error });
|
|
1769
|
+
throw error;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Upload multiple files at once
|
|
1774
|
+
* @param files - Array of File objects to upload
|
|
1775
|
+
* @param onProgress - Optional callback for overall progress (0-100)
|
|
1776
|
+
* @returns Array of uploaded attachments
|
|
1777
|
+
*/
|
|
1778
|
+
async uploadFiles(files, onProgress) {
|
|
1779
|
+
const attachments = [];
|
|
1780
|
+
const totalFiles = files.length;
|
|
1781
|
+
for (let i = 0; i < totalFiles; i++) {
|
|
1782
|
+
const file = files[i];
|
|
1783
|
+
const baseProgress = i / totalFiles * 100;
|
|
1784
|
+
const fileProgress = 100 / totalFiles;
|
|
1785
|
+
const attachment = await this.uploadFile(file, (progress) => {
|
|
1786
|
+
const totalProgress = baseProgress + progress / 100 * fileProgress;
|
|
1787
|
+
onProgress?.(totalProgress);
|
|
1788
|
+
});
|
|
1789
|
+
attachments.push(attachment);
|
|
1790
|
+
}
|
|
1791
|
+
return attachments;
|
|
1792
|
+
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Upload file to presigned URL with progress tracking
|
|
1795
|
+
*/
|
|
1796
|
+
uploadToPresignedUrl(url, file, onProgress) {
|
|
1797
|
+
return new Promise((resolve, reject) => {
|
|
1798
|
+
const xhr = new XMLHttpRequest();
|
|
1799
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
1800
|
+
if (event.lengthComputable) {
|
|
1801
|
+
const progress = event.loaded / event.total * 100;
|
|
1802
|
+
onProgress?.(progress);
|
|
1803
|
+
}
|
|
1804
|
+
});
|
|
1805
|
+
xhr.addEventListener("load", () => {
|
|
1806
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
1807
|
+
resolve();
|
|
1808
|
+
} else {
|
|
1809
|
+
reject(new Error(`Upload failed with status ${xhr.status}`));
|
|
1810
|
+
}
|
|
1811
|
+
});
|
|
1812
|
+
xhr.addEventListener("error", () => {
|
|
1813
|
+
reject(new Error("Upload failed"));
|
|
1814
|
+
});
|
|
1815
|
+
xhr.open("PUT", url);
|
|
1816
|
+
xhr.setRequestHeader("Content-Type", file.type || "application/octet-stream");
|
|
1817
|
+
xhr.send(file);
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
817
1820
|
async fetchMessages(after) {
|
|
818
1821
|
if (!this.session) {
|
|
819
1822
|
throw new Error("Not connected");
|
|
@@ -867,6 +1870,54 @@ var PocketPingClient = class {
|
|
|
867
1870
|
console.error("[PocketPing] Failed to send read status:", err);
|
|
868
1871
|
}
|
|
869
1872
|
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Edit a message (visitor can only edit their own messages)
|
|
1875
|
+
* @param messageId - ID of the message to edit
|
|
1876
|
+
* @param content - New content for the message
|
|
1877
|
+
*/
|
|
1878
|
+
async editMessage(messageId, content) {
|
|
1879
|
+
if (!this.session) {
|
|
1880
|
+
throw new Error("Not connected");
|
|
1881
|
+
}
|
|
1882
|
+
const response = await this.fetch(`/message/${messageId}`, {
|
|
1883
|
+
method: "PATCH",
|
|
1884
|
+
body: JSON.stringify({
|
|
1885
|
+
sessionId: this.session.sessionId,
|
|
1886
|
+
content
|
|
1887
|
+
})
|
|
1888
|
+
});
|
|
1889
|
+
const messageIndex = this.session.messages.findIndex((m) => m.id === messageId);
|
|
1890
|
+
if (messageIndex >= 0) {
|
|
1891
|
+
this.session.messages[messageIndex].content = response.message.content;
|
|
1892
|
+
this.session.messages[messageIndex].editedAt = response.message.editedAt;
|
|
1893
|
+
this.emit("messageEdited", this.session.messages[messageIndex]);
|
|
1894
|
+
}
|
|
1895
|
+
return this.session.messages[messageIndex];
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Delete a message (soft delete - visitor can only delete their own messages)
|
|
1899
|
+
* @param messageId - ID of the message to delete
|
|
1900
|
+
*/
|
|
1901
|
+
async deleteMessage(messageId) {
|
|
1902
|
+
if (!this.session) {
|
|
1903
|
+
throw new Error("Not connected");
|
|
1904
|
+
}
|
|
1905
|
+
const response = await this.fetch(`/message/${messageId}`, {
|
|
1906
|
+
method: "DELETE",
|
|
1907
|
+
body: JSON.stringify({
|
|
1908
|
+
sessionId: this.session.sessionId
|
|
1909
|
+
})
|
|
1910
|
+
});
|
|
1911
|
+
if (response.deleted) {
|
|
1912
|
+
const messageIndex = this.session.messages.findIndex((m) => m.id === messageId);
|
|
1913
|
+
if (messageIndex >= 0) {
|
|
1914
|
+
this.session.messages[messageIndex].deletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1915
|
+
this.session.messages[messageIndex].content = "";
|
|
1916
|
+
this.emit("messageDeleted", this.session.messages[messageIndex]);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
return response.deleted;
|
|
1920
|
+
}
|
|
870
1921
|
async getPresence() {
|
|
871
1922
|
return this.fetch("/presence", { method: "GET" });
|
|
872
1923
|
}
|
|
@@ -1770,11 +2821,23 @@ function close() {
|
|
|
1770
2821
|
function toggle() {
|
|
1771
2822
|
client?.toggleOpen();
|
|
1772
2823
|
}
|
|
1773
|
-
function sendMessage(content) {
|
|
2824
|
+
function sendMessage(content, attachmentIds) {
|
|
2825
|
+
if (!client) {
|
|
2826
|
+
throw new Error("[PocketPing] Not initialized");
|
|
2827
|
+
}
|
|
2828
|
+
return client.sendMessage(content, attachmentIds);
|
|
2829
|
+
}
|
|
2830
|
+
async function uploadFile(file, onProgress) {
|
|
2831
|
+
if (!client) {
|
|
2832
|
+
throw new Error("[PocketPing] Not initialized");
|
|
2833
|
+
}
|
|
2834
|
+
return client.uploadFile(file, onProgress);
|
|
2835
|
+
}
|
|
2836
|
+
async function uploadFiles(files, onProgress) {
|
|
1774
2837
|
if (!client) {
|
|
1775
2838
|
throw new Error("[PocketPing] Not initialized");
|
|
1776
2839
|
}
|
|
1777
|
-
return client.
|
|
2840
|
+
return client.uploadFiles(files, onProgress);
|
|
1778
2841
|
}
|
|
1779
2842
|
function trigger(eventName, data, options) {
|
|
1780
2843
|
if (!client) {
|
|
@@ -1843,7 +2906,7 @@ if (typeof document !== "undefined") {
|
|
|
1843
2906
|
}
|
|
1844
2907
|
}
|
|
1845
2908
|
}
|
|
1846
|
-
var index_default = { init, destroy, open, close, toggle, sendMessage, trigger, onEvent, offEvent, on, identify, reset, getIdentity, setupTrackedElements, getTrackedElements };
|
|
2909
|
+
var index_default = { init, destroy, open, close, toggle, sendMessage, uploadFile, uploadFiles, trigger, onEvent, offEvent, on, identify, reset, getIdentity, setupTrackedElements, getTrackedElements };
|
|
1847
2910
|
export {
|
|
1848
2911
|
close,
|
|
1849
2912
|
index_default as default,
|
|
@@ -1860,5 +2923,7 @@ export {
|
|
|
1860
2923
|
sendMessage,
|
|
1861
2924
|
setupTrackedElements,
|
|
1862
2925
|
toggle,
|
|
1863
|
-
trigger
|
|
2926
|
+
trigger,
|
|
2927
|
+
uploadFile,
|
|
2928
|
+
uploadFiles
|
|
1864
2929
|
};
|