@minhnd1010/chat-widget 1.0.1 → 1.0.4
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/index.js +282 -178
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
class ChatWidget extends HTMLElement {
|
|
2
2
|
constructor() {
|
|
3
3
|
super();
|
|
4
|
-
this.attachShadow({ mode:
|
|
4
|
+
this.attachShadow({ mode: "open" });
|
|
5
5
|
this.connection = null;
|
|
6
6
|
}
|
|
7
7
|
|
|
@@ -15,17 +15,19 @@ class ChatWidget extends HTMLElement {
|
|
|
15
15
|
|
|
16
16
|
this.initLogic();
|
|
17
17
|
|
|
18
|
-
if (!customElements.get(
|
|
18
|
+
if (!customElements.get("emoji-picker")) {
|
|
19
19
|
// Nếu chưa load thư viện → load từ CDN
|
|
20
|
-
const script = document.createElement(
|
|
21
|
-
script.type =
|
|
22
|
-
script.src =
|
|
20
|
+
const script = document.createElement("script");
|
|
21
|
+
script.type = "module";
|
|
22
|
+
script.src =
|
|
23
|
+
"https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js";
|
|
23
24
|
script.onload = () => {
|
|
24
|
-
console.log(
|
|
25
|
+
console.log("emoji-picker-element loaded");
|
|
25
26
|
// Sau khi load xong, khởi tạo picker (nếu cần)
|
|
26
27
|
this.setupEmojiPickerAfterLoad();
|
|
27
28
|
};
|
|
28
|
-
script.onerror = () =>
|
|
29
|
+
script.onerror = () =>
|
|
30
|
+
console.error("Failed to load emoji-picker-element");
|
|
29
31
|
document.head.appendChild(script);
|
|
30
32
|
} else {
|
|
31
33
|
// Nếu đã load rồi (trang có nhiều widget) → khởi tạo luôn
|
|
@@ -34,29 +36,32 @@ class ChatWidget extends HTMLElement {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
render() {
|
|
37
|
-
const position = this.getAttribute(
|
|
39
|
+
const position = this.getAttribute("position") || "bottom-left";
|
|
38
40
|
|
|
39
|
-
let containerStyles =
|
|
40
|
-
let boxStyles =
|
|
41
|
+
let containerStyles = "bottom: 20px; left: 20px;";
|
|
42
|
+
let boxStyles = "top: -78vh; left: 42px;";
|
|
41
43
|
|
|
42
44
|
switch (position) {
|
|
43
|
-
case
|
|
44
|
-
containerStyles =
|
|
45
|
-
boxStyles =
|
|
45
|
+
case "bottom-right":
|
|
46
|
+
containerStyles = "bottom: 20px; right: 20px; left: auto;";
|
|
47
|
+
boxStyles =
|
|
48
|
+
"top: -78vh; right: 42px; left: auto; transform: translateX(-100%);";
|
|
46
49
|
break;
|
|
47
|
-
case
|
|
48
|
-
containerStyles =
|
|
49
|
-
boxStyles =
|
|
50
|
+
case "top-left":
|
|
51
|
+
containerStyles = "top: 20px; left: 20px; bottom: auto;";
|
|
52
|
+
boxStyles =
|
|
53
|
+
"bottom: 80px; top: auto; left: 42px; transform: translateY(100%);";
|
|
50
54
|
break;
|
|
51
|
-
case
|
|
52
|
-
containerStyles =
|
|
53
|
-
boxStyles =
|
|
55
|
+
case "top-right":
|
|
56
|
+
containerStyles = "top: 20px; right: 20px; left: auto; bottom: auto;";
|
|
57
|
+
boxStyles =
|
|
58
|
+
"bottom: 80px; top: auto; right: 42px; left: auto; transform: translateX(-100%) translateY(100%);";
|
|
54
59
|
break;
|
|
55
60
|
default:
|
|
56
61
|
// bottom-left
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
const template = document.createElement(
|
|
64
|
+
const template = document.createElement("template");
|
|
60
65
|
template.innerHTML = `
|
|
61
66
|
<style>
|
|
62
67
|
* {
|
|
@@ -381,6 +386,7 @@ class ChatWidget extends HTMLElement {
|
|
|
381
386
|
padding: 10px;
|
|
382
387
|
border-radius: 12px;
|
|
383
388
|
margin: 0;
|
|
389
|
+
word-break: break-word;
|
|
384
390
|
}
|
|
385
391
|
|
|
386
392
|
.partner .message {
|
|
@@ -461,7 +467,7 @@ class ChatWidget extends HTMLElement {
|
|
|
461
467
|
align-items: center;
|
|
462
468
|
background-color: #e0e0e0ff;
|
|
463
469
|
border-radius: 50px;
|
|
464
|
-
padding:
|
|
470
|
+
padding: 8px 20px;
|
|
465
471
|
overflow-y: hidden;
|
|
466
472
|
}
|
|
467
473
|
|
|
@@ -503,15 +509,38 @@ class ChatWidget extends HTMLElement {
|
|
|
503
509
|
background-color: #4b4bda;
|
|
504
510
|
}
|
|
505
511
|
|
|
512
|
+
/* ================================================================================
|
|
513
|
+
File attach styling
|
|
514
|
+
================================================================================ */
|
|
515
|
+
.file-attach {
|
|
516
|
+
background: rgb(224, 224, 224);
|
|
517
|
+
border: none;
|
|
518
|
+
cursor: pointer;
|
|
519
|
+
border-radius: 50%;
|
|
520
|
+
display: flex;
|
|
521
|
+
align-items: center;
|
|
522
|
+
justify-content: center;
|
|
523
|
+
padding: 6px;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.file-attach:hover {
|
|
527
|
+
background-color: rgba(201, 201, 201, 1);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.file-attach svg {
|
|
531
|
+
width: 26px;
|
|
532
|
+
height: 26px;
|
|
533
|
+
fill: #5b5bdfff;
|
|
534
|
+
}
|
|
535
|
+
|
|
506
536
|
/* ================================================================================
|
|
507
537
|
EMOJI PICKER STYLING
|
|
508
538
|
================================================================================ */
|
|
509
539
|
#emoji-button {
|
|
510
540
|
background: none;
|
|
511
541
|
border: none;
|
|
512
|
-
font-size:
|
|
542
|
+
font-size: 18px;
|
|
513
543
|
cursor: pointer;
|
|
514
|
-
padding: 8px;
|
|
515
544
|
border-radius: 50%;
|
|
516
545
|
display: flex;
|
|
517
546
|
align-items: center;
|
|
@@ -522,6 +551,12 @@ class ChatWidget extends HTMLElement {
|
|
|
522
551
|
background-color: rgba(0, 0, 0, 0.1);
|
|
523
552
|
}
|
|
524
553
|
|
|
554
|
+
#emoji-button svg {
|
|
555
|
+
width: 25px;
|
|
556
|
+
height: 25px;
|
|
557
|
+
fill: #5b5bdfff;
|
|
558
|
+
}
|
|
559
|
+
|
|
525
560
|
emoji-picker {
|
|
526
561
|
position: absolute;
|
|
527
562
|
bottom: 70px;
|
|
@@ -754,6 +789,13 @@ class ChatWidget extends HTMLElement {
|
|
|
754
789
|
|
|
755
790
|
<!-- Message input -->
|
|
756
791
|
<div class="message-sending">
|
|
792
|
+
<div class="file-attach" title="Đính kèm file">
|
|
793
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640">
|
|
794
|
+
<path
|
|
795
|
+
d="M352 128C352 110.3 337.7 96 320 96C302.3 96 288 110.3 288 128L288 288L128 288C110.3 288 96 302.3 96 320C96 337.7 110.3 352 128 352L288 352L288 512C288 529.7 302.3 544 320 544C337.7 544 352 529.7 352 512L352 352L512 352C529.7 352 544 337.7 544 320C544 302.3 529.7 288 512 288L352 288L352 128z"
|
|
796
|
+
/>
|
|
797
|
+
</svg>
|
|
798
|
+
</div>
|
|
757
799
|
<div class="message-content">
|
|
758
800
|
<textarea
|
|
759
801
|
id="message-input"
|
|
@@ -761,7 +803,13 @@ class ChatWidget extends HTMLElement {
|
|
|
761
803
|
placeholder="Nhập tin nhắn..."
|
|
762
804
|
></textarea>
|
|
763
805
|
</div>
|
|
764
|
-
<button id="emoji-button"
|
|
806
|
+
<button id="emoji-button">
|
|
807
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640">
|
|
808
|
+
<path
|
|
809
|
+
d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM229.4 385.9C249.8 413.9 282.8 432 320 432C357.2 432 390.2 413.9 410.6 385.9C418.4 375.2 433.4 372.8 444.1 380.6C454.8 388.4 457.2 403.4 449.4 414.1C420.3 454 373.2 480 320 480C266.8 480 219.7 454 190.6 414.1C182.8 403.4 185.2 388.4 195.9 380.6C206.6 372.8 221.6 375.2 229.4 385.9zM208 272C208 254.3 222.3 240 240 240C257.7 240 272 254.3 272 272C272 289.7 257.7 304 240 304C222.3 304 208 289.7 208 272zM400 240C417.7 240 432 254.3 432 272C432 289.7 417.7 304 400 304C382.3 304 368 289.7 368 272C368 254.3 382.3 240 400 240z"
|
|
810
|
+
/>
|
|
811
|
+
</svg>
|
|
812
|
+
</button>
|
|
765
813
|
<button id="btn-send-message">
|
|
766
814
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640">
|
|
767
815
|
<path
|
|
@@ -783,24 +831,20 @@ class ChatWidget extends HTMLElement {
|
|
|
783
831
|
const shadowRoot = this.shadowRoot;
|
|
784
832
|
|
|
785
833
|
// ================== CONFIGURATION ==================
|
|
786
|
-
const BASE_URL = this.getAttribute(
|
|
834
|
+
const BASE_URL = this.getAttribute("base-url");
|
|
787
835
|
const PAGE_SIZE = 10;
|
|
788
836
|
const SCROLL_THRESHOLD = 20;
|
|
789
837
|
const SCROLL_LOAD_MORE_THRESHOLD = 100;
|
|
790
838
|
const DEBOUNCE_DELAY = 200;
|
|
791
839
|
|
|
792
840
|
// ================== USER INPUT ==================
|
|
793
|
-
let token =
|
|
794
|
-
if (!token) token = "Anonymous";
|
|
841
|
+
let token = this.getAttribute("token");
|
|
795
842
|
|
|
796
|
-
let userId =
|
|
797
|
-
if (!userId) userId = "Anonymous";
|
|
843
|
+
let userId = this.getAttribute("ma_doi_tac_nsd");
|
|
798
844
|
|
|
799
|
-
let fullName =
|
|
800
|
-
if (!fullName) fullName = "Khách hàng";
|
|
845
|
+
let fullName = this.getAttribute("ten");
|
|
801
846
|
|
|
802
|
-
let ePartnerCode =
|
|
803
|
-
if (!ePartnerCode) ePartnerCode = "PORTAL";
|
|
847
|
+
let ePartnerCode = this.getAttribute("e-partner-code");
|
|
804
848
|
|
|
805
849
|
// ================== STATE MANAGEMENT ==================
|
|
806
850
|
let selectedChat = null;
|
|
@@ -820,35 +864,35 @@ class ChatWidget extends HTMLElement {
|
|
|
820
864
|
|
|
821
865
|
// ================== DOM ELEMENTS ==================
|
|
822
866
|
const listEl = shadowRoot.querySelector("#list-items ul");
|
|
823
|
-
const infoTitle = shadowRoot.querySelector(
|
|
824
|
-
const chatBoxIcon = shadowRoot.querySelector(
|
|
825
|
-
const chatBoxContainer = shadowRoot.querySelector(
|
|
826
|
-
const chatBoxFilter = shadowRoot.querySelector(
|
|
827
|
-
const searchChatInput = shadowRoot.querySelector(
|
|
828
|
-
const searchChatIcon = shadowRoot.querySelector(
|
|
829
|
-
const chatBoxListContainer = shadowRoot.querySelector(
|
|
830
|
-
const chatBoxInputInfo = shadowRoot.querySelector(
|
|
831
|
-
const turnBackButton = shadowRoot.querySelector(
|
|
832
|
-
const continueButton = shadowRoot.querySelector(
|
|
833
|
-
const chatDetails = shadowRoot.querySelector(
|
|
834
|
-
const btnX = shadowRoot.querySelector(
|
|
835
|
-
const detailsBackButton = shadowRoot.querySelector(
|
|
836
|
-
const messageContainer = shadowRoot.querySelector(
|
|
837
|
-
const hsblInput = shadowRoot.querySelector(
|
|
838
|
-
const userNameInput = shadowRoot.querySelector(
|
|
839
|
-
const errorInfoInput = shadowRoot.querySelector(
|
|
840
|
-
const btnSendMessage = shadowRoot.querySelector(
|
|
841
|
-
const textarea = shadowRoot.getElementById(
|
|
842
|
-
const emojiButton = shadowRoot.getElementById(
|
|
843
|
-
const messageInput = shadowRoot.getElementById(
|
|
867
|
+
const infoTitle = shadowRoot.querySelector("#info-input .info-header h4");
|
|
868
|
+
const chatBoxIcon = shadowRoot.querySelector("#chat-icon");
|
|
869
|
+
const chatBoxContainer = shadowRoot.querySelector("#chat-box");
|
|
870
|
+
const chatBoxFilter = shadowRoot.querySelector("#chat-box-header");
|
|
871
|
+
const searchChatInput = shadowRoot.querySelector("#search-chat-input");
|
|
872
|
+
const searchChatIcon = shadowRoot.querySelector("#search-chat-icon");
|
|
873
|
+
const chatBoxListContainer = shadowRoot.querySelector("#list-items");
|
|
874
|
+
const chatBoxInputInfo = shadowRoot.querySelector("#info-input");
|
|
875
|
+
const turnBackButton = shadowRoot.querySelector("#turn-back-button");
|
|
876
|
+
const continueButton = shadowRoot.querySelector("#continue-button");
|
|
877
|
+
const chatDetails = shadowRoot.querySelector("#chat-details");
|
|
878
|
+
const btnX = shadowRoot.querySelector("#btn-x");
|
|
879
|
+
const detailsBackButton = shadowRoot.querySelector("#btn-details-back");
|
|
880
|
+
const messageContainer = shadowRoot.querySelector("#message-container");
|
|
881
|
+
const hsblInput = shadowRoot.querySelector("#input-shsbl");
|
|
882
|
+
const userNameInput = shadowRoot.querySelector("#input-username");
|
|
883
|
+
const errorInfoInput = shadowRoot.querySelector("#error-info-input");
|
|
884
|
+
const btnSendMessage = shadowRoot.querySelector("#btn-send-message");
|
|
885
|
+
const textarea = shadowRoot.getElementById("message-input");
|
|
886
|
+
const emojiButton = shadowRoot.getElementById("emoji-button");
|
|
887
|
+
const messageInput = shadowRoot.getElementById("message-input");
|
|
844
888
|
|
|
845
889
|
userNameInput.value = fullName;
|
|
846
890
|
|
|
847
891
|
// ================== UTILITY FUNCTIONS ==================
|
|
848
892
|
function parseCustomDateString(str) {
|
|
849
893
|
// Expected format: dd/MM/yyyy HH:mm:ss
|
|
850
|
-
const [date, time] = str.split(
|
|
851
|
-
const [day, month, year] = date.split(
|
|
894
|
+
const [date, time] = str.split(" ");
|
|
895
|
+
const [day, month, year] = date.split("/");
|
|
852
896
|
return new Date(`${year}-${month}-${day}T${time}`);
|
|
853
897
|
}
|
|
854
898
|
|
|
@@ -865,8 +909,8 @@ class ChatWidget extends HTMLElement {
|
|
|
865
909
|
}
|
|
866
910
|
|
|
867
911
|
function autoResize() {
|
|
868
|
-
this.style.height =
|
|
869
|
-
this.style.height = this.scrollHeight +
|
|
912
|
+
this.style.height = "auto";
|
|
913
|
+
this.style.height = this.scrollHeight + "px";
|
|
870
914
|
}
|
|
871
915
|
|
|
872
916
|
function scrollToBottom() {
|
|
@@ -878,12 +922,14 @@ class ChatWidget extends HTMLElement {
|
|
|
878
922
|
// ================== CHAT LIST FUNCTIONS ==================
|
|
879
923
|
function renderChatList(items, append = false) {
|
|
880
924
|
const htmlContent = items
|
|
881
|
-
.map(
|
|
925
|
+
.map(
|
|
926
|
+
(item) => `
|
|
882
927
|
<li data-id="${item.MA}">
|
|
883
928
|
<img class="logo-item" src="${item.LINK_LOGO}" alt="Logo" />
|
|
884
929
|
<h4 class="title-item">${item.TEN}</h4>
|
|
885
930
|
</li>
|
|
886
|
-
`
|
|
931
|
+
`
|
|
932
|
+
)
|
|
887
933
|
.join("");
|
|
888
934
|
|
|
889
935
|
if (append) {
|
|
@@ -897,8 +943,12 @@ class ChatWidget extends HTMLElement {
|
|
|
897
943
|
const scrollHeight = chatBoxListContainer.scrollHeight;
|
|
898
944
|
const clientHeight = chatBoxListContainer.clientHeight;
|
|
899
945
|
|
|
900
|
-
if (
|
|
901
|
-
|
|
946
|
+
if (
|
|
947
|
+
scrollHeight <= clientHeight &&
|
|
948
|
+
chatListHasMore &&
|
|
949
|
+
!chatListIsLoading
|
|
950
|
+
) {
|
|
951
|
+
console.log("Danh sách chưa đủ để scroll, load thêm...");
|
|
902
952
|
await loadMoreChatList();
|
|
903
953
|
setTimeout(() => ensureChatListFilled(), 100);
|
|
904
954
|
}
|
|
@@ -909,7 +959,7 @@ class ChatWidget extends HTMLElement {
|
|
|
909
959
|
const body = {
|
|
910
960
|
trang: page,
|
|
911
961
|
so_dong: PAGE_SIZE,
|
|
912
|
-
nd_tim: searchTerm || ""
|
|
962
|
+
nd_tim: searchTerm || "",
|
|
913
963
|
};
|
|
914
964
|
|
|
915
965
|
const header = {
|
|
@@ -918,17 +968,20 @@ class ChatWidget extends HTMLElement {
|
|
|
918
968
|
};
|
|
919
969
|
|
|
920
970
|
const response = await fetch(`${BASE_URL}api/chat/execute`, {
|
|
921
|
-
method:
|
|
971
|
+
method: "POST",
|
|
922
972
|
headers: header,
|
|
923
|
-
body: JSON.stringify(body)
|
|
973
|
+
body: JSON.stringify(body),
|
|
924
974
|
});
|
|
925
975
|
|
|
926
|
-
if (!response.ok) throw new Error(
|
|
976
|
+
if (!response.ok) throw new Error("Failed to fetch chat list");
|
|
927
977
|
|
|
928
978
|
const data = await response.json();
|
|
929
979
|
|
|
930
980
|
// Check if there are more items
|
|
931
|
-
if (
|
|
981
|
+
if (
|
|
982
|
+
data.data.length === 0 ||
|
|
983
|
+
data.data.length >= data.output.tong_so_dong
|
|
984
|
+
) {
|
|
932
985
|
chatListHasMore = false;
|
|
933
986
|
} else {
|
|
934
987
|
chatListHasMore = true;
|
|
@@ -944,9 +997,10 @@ class ChatWidget extends HTMLElement {
|
|
|
944
997
|
|
|
945
998
|
return data.data.length;
|
|
946
999
|
} catch (error) {
|
|
947
|
-
console.error(
|
|
1000
|
+
console.error("Error loading chat list:", error);
|
|
948
1001
|
if (!append) {
|
|
949
|
-
listEl.innerHTML =
|
|
1002
|
+
listEl.innerHTML =
|
|
1003
|
+
'<li style="padding: 20px; text-align: center;">Không thể tải danh sách chat</li>';
|
|
950
1004
|
}
|
|
951
1005
|
return 0;
|
|
952
1006
|
}
|
|
@@ -961,14 +1015,18 @@ class ChatWidget extends HTMLElement {
|
|
|
961
1015
|
|
|
962
1016
|
try {
|
|
963
1017
|
chatListCurrentPage++;
|
|
964
|
-
const count = await loadChatList(
|
|
1018
|
+
const count = await loadChatList(
|
|
1019
|
+
currentSearchTerm,
|
|
1020
|
+
chatListCurrentPage,
|
|
1021
|
+
true
|
|
1022
|
+
);
|
|
965
1023
|
|
|
966
1024
|
if (count === 0) {
|
|
967
1025
|
chatListHasMore = false;
|
|
968
|
-
console.log(
|
|
1026
|
+
console.log("Đã tải hết danh sách chat");
|
|
969
1027
|
}
|
|
970
1028
|
} catch (error) {
|
|
971
|
-
console.error(
|
|
1029
|
+
console.error("Lỗi khi load thêm chat list:", error);
|
|
972
1030
|
chatListCurrentPage--;
|
|
973
1031
|
} finally {
|
|
974
1032
|
chatListIsLoading = false;
|
|
@@ -976,22 +1034,29 @@ class ChatWidget extends HTMLElement {
|
|
|
976
1034
|
}
|
|
977
1035
|
|
|
978
1036
|
function wireChatItemClicks() {
|
|
979
|
-
const chatBoxListItem = shadowRoot.querySelectorAll(
|
|
1037
|
+
const chatBoxListItem = shadowRoot.querySelectorAll("#list-items li");
|
|
980
1038
|
chatBoxListItem.forEach((item) => {
|
|
981
|
-
item.addEventListener(
|
|
1039
|
+
item.addEventListener("click", function () {
|
|
982
1040
|
if (chatBoxInputInfo) {
|
|
983
1041
|
chatBoxListContainerDisplay = !chatBoxListContainerDisplay;
|
|
984
|
-
chatBoxListContainer.style.display = chatBoxListContainerDisplay
|
|
985
|
-
|
|
1042
|
+
chatBoxListContainer.style.display = chatBoxListContainerDisplay
|
|
1043
|
+
? "none"
|
|
1044
|
+
: "flex";
|
|
1045
|
+
chatBoxFilter.style.display = chatBoxListContainerDisplay
|
|
1046
|
+
? "none"
|
|
1047
|
+
: "flex";
|
|
986
1048
|
|
|
987
1049
|
chatBoxInputInfoDisplay = !chatBoxInputInfoDisplay;
|
|
988
|
-
chatBoxInputInfo.style.display = chatBoxInputInfoDisplay
|
|
1050
|
+
chatBoxInputInfo.style.display = chatBoxInputInfoDisplay
|
|
1051
|
+
? "none"
|
|
1052
|
+
: "flex";
|
|
989
1053
|
|
|
990
1054
|
const { id } = item.dataset;
|
|
991
|
-
const name =
|
|
1055
|
+
const name =
|
|
1056
|
+
item.querySelector(".title-item")?.textContent?.trim() || "";
|
|
992
1057
|
|
|
993
1058
|
selectedChat = { id, name };
|
|
994
|
-
infoTitle.textContent = name ||
|
|
1059
|
+
infoTitle.textContent = name || "Không tìm thấy cuộc trò chuyện!";
|
|
995
1060
|
userNameInput.value = fullName;
|
|
996
1061
|
}
|
|
997
1062
|
});
|
|
@@ -1004,7 +1069,7 @@ class ChatWidget extends HTMLElement {
|
|
|
1004
1069
|
const body = {
|
|
1005
1070
|
trang: page,
|
|
1006
1071
|
so_dong: PAGE_SIZE,
|
|
1007
|
-
nhom: groupName || ""
|
|
1072
|
+
nhom: groupName || "",
|
|
1008
1073
|
};
|
|
1009
1074
|
|
|
1010
1075
|
const header = {
|
|
@@ -1013,23 +1078,23 @@ class ChatWidget extends HTMLElement {
|
|
|
1013
1078
|
};
|
|
1014
1079
|
|
|
1015
1080
|
const response = await fetch(`${BASE_URL}api/chat/execute`, {
|
|
1016
|
-
method:
|
|
1081
|
+
method: "POST",
|
|
1017
1082
|
headers: header,
|
|
1018
|
-
body: JSON.stringify(body)
|
|
1083
|
+
body: JSON.stringify(body),
|
|
1019
1084
|
});
|
|
1020
1085
|
|
|
1021
|
-
if (!response.ok) throw new Error(
|
|
1086
|
+
if (!response.ok) throw new Error("Failed to fetch chat list");
|
|
1022
1087
|
|
|
1023
1088
|
const data = await response.json();
|
|
1024
1089
|
|
|
1025
1090
|
// Sort messages by NGAY_GUI ascending
|
|
1026
|
-
console.log(
|
|
1091
|
+
console.log("Loaded messages:", data.data);
|
|
1027
1092
|
const sortedMessages = data.data.sort((a, b) => {
|
|
1028
1093
|
const dateA = parseCustomDateString(a.NGAY_GUI);
|
|
1029
1094
|
const dateB = parseCustomDateString(b.NGAY_GUI);
|
|
1030
1095
|
return dateA - dateB;
|
|
1031
1096
|
});
|
|
1032
|
-
console.log(
|
|
1097
|
+
console.log("Sorted messages:", sortedMessages);
|
|
1033
1098
|
|
|
1034
1099
|
return sortedMessages;
|
|
1035
1100
|
} catch {
|
|
@@ -1043,22 +1108,22 @@ class ChatWidget extends HTMLElement {
|
|
|
1043
1108
|
for (const msg of messages) {
|
|
1044
1109
|
const isMe = userId === msg.MA_DOI_TAC_NSD;
|
|
1045
1110
|
const msgDiv = document.createElement("div");
|
|
1046
|
-
msgDiv.classList.add(isMe ?
|
|
1111
|
+
msgDiv.classList.add(isMe ? "me" : "partner");
|
|
1047
1112
|
msgDiv.innerHTML = `
|
|
1048
1113
|
<div class="info">
|
|
1049
|
-
${
|
|
1050
|
-
|
|
1114
|
+
${
|
|
1115
|
+
isMe
|
|
1116
|
+
? `<div class="message-title">
|
|
1051
1117
|
<h5>${msg.TEN}</h5>
|
|
1052
1118
|
<p class="message-time">${msg.NGAY_GUI}</p>
|
|
1053
1119
|
</div>
|
|
1054
1120
|
<img class="logo-item" src="${msg.LINK_LOGO}" alt="Logo" />`
|
|
1055
|
-
|
|
1056
|
-
`<img class="logo-item" src="${msg.LINK_LOGO}" alt="Logo" />
|
|
1121
|
+
: `<img class="logo-item" src="${msg.LINK_LOGO}" alt="Logo" />
|
|
1057
1122
|
<div class="message-title">
|
|
1058
1123
|
<h5>${msg.TEN}</h5>
|
|
1059
1124
|
<p class="message-time">${msg.NGAY_GUI}</p>
|
|
1060
1125
|
</div>`
|
|
1061
|
-
|
|
1126
|
+
}
|
|
1062
1127
|
</div>
|
|
1063
1128
|
<div class="message">
|
|
1064
1129
|
<p>${msg.NOI_DUNG_TIN_NHAN}</p>
|
|
@@ -1086,21 +1151,24 @@ class ChatWidget extends HTMLElement {
|
|
|
1086
1151
|
|
|
1087
1152
|
try {
|
|
1088
1153
|
currentPage++;
|
|
1089
|
-
const messages = await loadMessageHistory(
|
|
1154
|
+
const messages = await loadMessageHistory(
|
|
1155
|
+
selectedChat.hsbl,
|
|
1156
|
+
currentPage
|
|
1157
|
+
);
|
|
1090
1158
|
|
|
1091
1159
|
if (messages.length === 0) {
|
|
1092
1160
|
hasMoreMessages = false;
|
|
1093
|
-
console.log(
|
|
1161
|
+
console.log("Đã tải hết tin nhắn");
|
|
1094
1162
|
return;
|
|
1095
1163
|
}
|
|
1096
1164
|
|
|
1097
1165
|
await appendMessageHistory(messages, true);
|
|
1098
1166
|
|
|
1099
1167
|
const newScrollHeight = messageContainer.scrollHeight;
|
|
1100
|
-
messageContainer.scrollTop =
|
|
1101
|
-
|
|
1168
|
+
messageContainer.scrollTop =
|
|
1169
|
+
newScrollHeight - oldScrollHeight + oldScrollTop;
|
|
1102
1170
|
} catch (error) {
|
|
1103
|
-
console.error(
|
|
1171
|
+
console.error("Lỗi khi load thêm tin nhắn:", error);
|
|
1104
1172
|
currentPage--;
|
|
1105
1173
|
} finally {
|
|
1106
1174
|
isLoading = false;
|
|
@@ -1112,17 +1180,17 @@ class ChatWidget extends HTMLElement {
|
|
|
1112
1180
|
let isValid = true;
|
|
1113
1181
|
|
|
1114
1182
|
if (!hsblInput.value.trim()) {
|
|
1115
|
-
hsblInput.classList.add(
|
|
1183
|
+
hsblInput.classList.add("error");
|
|
1116
1184
|
isValid = false;
|
|
1117
1185
|
} else {
|
|
1118
|
-
hsblInput.classList.remove(
|
|
1186
|
+
hsblInput.classList.remove("error");
|
|
1119
1187
|
}
|
|
1120
1188
|
|
|
1121
1189
|
if (!userNameInput.value.trim()) {
|
|
1122
|
-
userNameInput.classList.add(
|
|
1190
|
+
userNameInput.classList.add("error");
|
|
1123
1191
|
isValid = false;
|
|
1124
1192
|
} else {
|
|
1125
|
-
userNameInput.classList.remove(
|
|
1193
|
+
userNameInput.classList.remove("error");
|
|
1126
1194
|
}
|
|
1127
1195
|
|
|
1128
1196
|
return isValid;
|
|
@@ -1130,7 +1198,7 @@ class ChatWidget extends HTMLElement {
|
|
|
1130
1198
|
|
|
1131
1199
|
function validateContractId(contractId) {
|
|
1132
1200
|
return fetch(`${BASE_URL}api/chat/execute`)
|
|
1133
|
-
.then(response => {
|
|
1201
|
+
.then((response) => {
|
|
1134
1202
|
if (!response.ok) {
|
|
1135
1203
|
errorInfoInput.textContent = `Mã hợp đồng không tồn tại.`;
|
|
1136
1204
|
return false;
|
|
@@ -1145,7 +1213,7 @@ class ChatWidget extends HTMLElement {
|
|
|
1145
1213
|
|
|
1146
1214
|
// ================== EVENT LISTENERS ==================
|
|
1147
1215
|
// Auto resize textarea
|
|
1148
|
-
textarea.addEventListener(
|
|
1216
|
+
textarea.addEventListener("input", autoResize, false);
|
|
1149
1217
|
|
|
1150
1218
|
// Scroll handlers
|
|
1151
1219
|
const handleScroll = debounce(function () {
|
|
@@ -1154,64 +1222,71 @@ class ChatWidget extends HTMLElement {
|
|
|
1154
1222
|
}
|
|
1155
1223
|
}, DEBOUNCE_DELAY);
|
|
1156
1224
|
|
|
1157
|
-
messageContainer.addEventListener(
|
|
1225
|
+
messageContainer.addEventListener("scroll", handleScroll);
|
|
1158
1226
|
|
|
1159
1227
|
const handleChatListScroll = debounce(function () {
|
|
1160
|
-
console.log(
|
|
1228
|
+
console.log("Scroll chat list:", chatBoxListContainer.scrollTop);
|
|
1161
1229
|
const scrollTop = chatBoxListContainer.scrollTop;
|
|
1162
1230
|
const scrollHeight = chatBoxListContainer.scrollHeight;
|
|
1163
1231
|
const clientHeight = chatBoxListContainer.clientHeight;
|
|
1164
1232
|
|
|
1165
|
-
if (
|
|
1233
|
+
if (
|
|
1234
|
+
scrollTop + clientHeight >=
|
|
1235
|
+
scrollHeight - SCROLL_LOAD_MORE_THRESHOLD
|
|
1236
|
+
) {
|
|
1166
1237
|
loadMoreChatList();
|
|
1167
1238
|
}
|
|
1168
1239
|
}, DEBOUNCE_DELAY);
|
|
1169
1240
|
|
|
1170
|
-
chatBoxListContainer.addEventListener(
|
|
1241
|
+
chatBoxListContainer.addEventListener("scroll", handleChatListScroll);
|
|
1171
1242
|
|
|
1172
1243
|
// Input validation listeners
|
|
1173
|
-
hsblInput.addEventListener(
|
|
1244
|
+
hsblInput.addEventListener("input", function () {
|
|
1174
1245
|
if (this.value.trim()) {
|
|
1175
|
-
this.classList.remove(
|
|
1246
|
+
this.classList.remove("error");
|
|
1176
1247
|
}
|
|
1177
1248
|
});
|
|
1178
1249
|
|
|
1179
|
-
userNameInput.addEventListener(
|
|
1250
|
+
userNameInput.addEventListener("input", function () {
|
|
1180
1251
|
if (this.value.trim()) {
|
|
1181
|
-
this.classList.remove(
|
|
1252
|
+
this.classList.remove("error");
|
|
1182
1253
|
}
|
|
1183
1254
|
});
|
|
1184
1255
|
|
|
1185
1256
|
// Display initialization
|
|
1186
|
-
chatBoxContainer.style.display = chatBoxContainerDisplay ?
|
|
1187
|
-
chatBoxListContainer.style.display = chatBoxListContainerDisplay
|
|
1188
|
-
|
|
1189
|
-
|
|
1257
|
+
chatBoxContainer.style.display = chatBoxContainerDisplay ? "none" : "flex";
|
|
1258
|
+
chatBoxListContainer.style.display = chatBoxListContainerDisplay
|
|
1259
|
+
? "none"
|
|
1260
|
+
: "flex";
|
|
1261
|
+
chatBoxInputInfo.style.display = chatBoxInputInfoDisplay ? "none" : "flex";
|
|
1262
|
+
chatDetails.style.display = chatDetailsDisplay ? "none" : "flex";
|
|
1190
1263
|
|
|
1191
1264
|
// Chat box icon click
|
|
1192
|
-
chatBoxIcon.addEventListener(
|
|
1265
|
+
chatBoxIcon.addEventListener("click", function () {
|
|
1193
1266
|
if (chatBoxContainer) {
|
|
1194
1267
|
chatListCurrentPage = 1;
|
|
1195
1268
|
chatListIsLoading = false;
|
|
1196
1269
|
currentSearchTerm = "";
|
|
1197
|
-
searchChatInput.value =
|
|
1270
|
+
searchChatInput.value = "";
|
|
1198
1271
|
|
|
1199
1272
|
loadChatList();
|
|
1200
1273
|
chatBoxContainerDisplay = !chatBoxContainerDisplay;
|
|
1201
|
-
chatBoxContainer.style.display = chatBoxContainerDisplay
|
|
1274
|
+
chatBoxContainer.style.display = chatBoxContainerDisplay
|
|
1275
|
+
? "none"
|
|
1276
|
+
: "flex";
|
|
1202
1277
|
}
|
|
1203
1278
|
});
|
|
1204
1279
|
|
|
1205
1280
|
// Close button
|
|
1206
|
-
btnX.addEventListener(
|
|
1281
|
+
btnX.addEventListener("click", function () {
|
|
1207
1282
|
if (chatBoxContainer) {
|
|
1208
1283
|
chatBoxContainerDisplay = true;
|
|
1209
|
-
chatBoxContainer.style.display =
|
|
1284
|
+
chatBoxContainer.style.display = "none";
|
|
1210
1285
|
}
|
|
1211
1286
|
});
|
|
1212
1287
|
|
|
1213
1288
|
// Search handlers
|
|
1214
|
-
searchChatIcon.addEventListener(
|
|
1289
|
+
searchChatIcon.addEventListener("click", function () {
|
|
1215
1290
|
const searchTerm = searchChatInput.value.trim();
|
|
1216
1291
|
currentSearchTerm = searchTerm;
|
|
1217
1292
|
|
|
@@ -1221,8 +1296,8 @@ class ChatWidget extends HTMLElement {
|
|
|
1221
1296
|
loadChatList(currentSearchTerm);
|
|
1222
1297
|
});
|
|
1223
1298
|
|
|
1224
|
-
searchChatInput.addEventListener(
|
|
1225
|
-
if (event.key ===
|
|
1299
|
+
searchChatInput.addEventListener("keypress", function (event) {
|
|
1300
|
+
if (event.key === "Enter") {
|
|
1226
1301
|
const searchTerm = searchChatInput.value.trim();
|
|
1227
1302
|
currentSearchTerm = searchTerm;
|
|
1228
1303
|
|
|
@@ -1234,24 +1309,30 @@ class ChatWidget extends HTMLElement {
|
|
|
1234
1309
|
});
|
|
1235
1310
|
|
|
1236
1311
|
// Turn back button
|
|
1237
|
-
turnBackButton.addEventListener(
|
|
1312
|
+
turnBackButton.addEventListener("click", function () {
|
|
1238
1313
|
if (chatBoxInputInfo) {
|
|
1239
1314
|
chatBoxListContainerDisplay = !chatBoxListContainerDisplay;
|
|
1240
|
-
chatBoxListContainer.style.display = chatBoxListContainerDisplay
|
|
1241
|
-
|
|
1315
|
+
chatBoxListContainer.style.display = chatBoxListContainerDisplay
|
|
1316
|
+
? "none"
|
|
1317
|
+
: "flex";
|
|
1318
|
+
chatBoxFilter.style.display = chatBoxListContainerDisplay
|
|
1319
|
+
? "none"
|
|
1320
|
+
: "flex";
|
|
1242
1321
|
|
|
1243
1322
|
chatBoxInputInfoDisplay = !chatBoxInputInfoDisplay;
|
|
1244
|
-
chatBoxInputInfo.style.display = chatBoxInputInfoDisplay
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
hsblInput.
|
|
1323
|
+
chatBoxInputInfo.style.display = chatBoxInputInfoDisplay
|
|
1324
|
+
? "none"
|
|
1325
|
+
: "flex";
|
|
1326
|
+
|
|
1327
|
+
userNameInput.classList.remove("error");
|
|
1328
|
+
hsblInput.classList.remove("error");
|
|
1329
|
+
errorInfoInput.textContent = "";
|
|
1330
|
+
hsblInput.value = "";
|
|
1250
1331
|
}
|
|
1251
1332
|
});
|
|
1252
1333
|
|
|
1253
1334
|
// Continue button
|
|
1254
|
-
continueButton.addEventListener(
|
|
1335
|
+
continueButton.addEventListener("click", async function () {
|
|
1255
1336
|
if (chatDetails) {
|
|
1256
1337
|
if (!validateInfoInputs()) {
|
|
1257
1338
|
return;
|
|
@@ -1262,59 +1343,74 @@ class ChatWidget extends HTMLElement {
|
|
|
1262
1343
|
selectedChat.userName = userNameInput.value.trim();
|
|
1263
1344
|
}
|
|
1264
1345
|
|
|
1265
|
-
errorInfoInput.textContent =
|
|
1346
|
+
errorInfoInput.textContent = "";
|
|
1266
1347
|
|
|
1267
1348
|
// Join group
|
|
1268
|
-
connection
|
|
1269
|
-
|
|
1270
|
-
|
|
1349
|
+
connection
|
|
1350
|
+
.invoke("JoinGroupAsync", selectedChat.hsbl)
|
|
1351
|
+
.then(() => {
|
|
1352
|
+
console.log(`Đã join nhóm: ${selectedChat.hsbl}`);
|
|
1353
|
+
})
|
|
1354
|
+
.catch((err) => console.error(err.toString()));
|
|
1271
1355
|
|
|
1272
1356
|
var messages = await loadMessageHistory(selectedChat.hsbl, 1);
|
|
1273
1357
|
await appendMessageHistory(messages);
|
|
1274
1358
|
|
|
1275
1359
|
chatBoxInputInfoDisplay = !chatBoxInputInfoDisplay;
|
|
1276
|
-
chatBoxInputInfo.style.display = chatBoxInputInfoDisplay
|
|
1360
|
+
chatBoxInputInfo.style.display = chatBoxInputInfoDisplay
|
|
1361
|
+
? "none"
|
|
1362
|
+
: "flex";
|
|
1277
1363
|
|
|
1278
1364
|
chatDetailsDisplay = !chatDetailsDisplay;
|
|
1279
|
-
chatDetails.style.display = chatDetailsDisplay ?
|
|
1365
|
+
chatDetails.style.display = chatDetailsDisplay ? "none" : "flex";
|
|
1280
1366
|
|
|
1281
1367
|
setTimeout(scrollToBottom, 0);
|
|
1282
1368
|
}
|
|
1283
1369
|
});
|
|
1284
1370
|
|
|
1285
1371
|
// Details back button
|
|
1286
|
-
detailsBackButton.addEventListener(
|
|
1372
|
+
detailsBackButton.addEventListener("click", function () {
|
|
1287
1373
|
if (chatDetails) {
|
|
1288
1374
|
// Leave group
|
|
1289
|
-
connection
|
|
1290
|
-
|
|
1291
|
-
|
|
1375
|
+
connection
|
|
1376
|
+
.invoke("RemoveFromGroupAsync", selectedChat.hsbl)
|
|
1377
|
+
.then(() => {
|
|
1378
|
+
console.log(`Đã rời nhóm: ${selectedChat.hsbl}`);
|
|
1379
|
+
})
|
|
1380
|
+
.catch((err) => console.error(err.toString()));
|
|
1292
1381
|
|
|
1293
1382
|
// Reset state before go back
|
|
1294
1383
|
currentPage = 1;
|
|
1295
1384
|
hasMoreMessages = true;
|
|
1296
1385
|
isLoading = false;
|
|
1297
|
-
messageContainer.innerHTML =
|
|
1386
|
+
messageContainer.innerHTML = "";
|
|
1298
1387
|
|
|
1299
1388
|
chatBoxInputInfoDisplay = !chatBoxInputInfoDisplay;
|
|
1300
|
-
chatBoxInputInfo.style.display = chatBoxInputInfoDisplay
|
|
1389
|
+
chatBoxInputInfo.style.display = chatBoxInputInfoDisplay
|
|
1390
|
+
? "none"
|
|
1391
|
+
: "flex";
|
|
1301
1392
|
|
|
1302
1393
|
chatDetailsDisplay = !chatDetailsDisplay;
|
|
1303
|
-
chatDetails.style.display = chatDetailsDisplay ?
|
|
1394
|
+
chatDetails.style.display = chatDetailsDisplay ? "none" : "flex";
|
|
1304
1395
|
}
|
|
1305
1396
|
});
|
|
1306
1397
|
|
|
1307
1398
|
// Send message button
|
|
1308
|
-
btnSendMessage.addEventListener(
|
|
1309
|
-
const message = shadowRoot.getElementById(
|
|
1310
|
-
console.log(
|
|
1399
|
+
btnSendMessage.addEventListener("click", async function () {
|
|
1400
|
+
const message = shadowRoot.getElementById("message-input").value;
|
|
1401
|
+
console.log("Gửi tin nhắn:", message);
|
|
1311
1402
|
if (message) {
|
|
1312
1403
|
try {
|
|
1313
|
-
await connection.invoke(
|
|
1404
|
+
await connection.invoke(
|
|
1405
|
+
"SendMessageToGroupAsync",
|
|
1406
|
+
selectedChat.hsbl,
|
|
1407
|
+
message,
|
|
1408
|
+
userNameInput.value.trim()
|
|
1409
|
+
);
|
|
1314
1410
|
shadowRoot.getElementById("message-input").value = "";
|
|
1315
1411
|
|
|
1316
|
-
const textarea = shadowRoot.getElementById(
|
|
1317
|
-
textarea.style.height =
|
|
1412
|
+
const textarea = shadowRoot.getElementById("message-input");
|
|
1413
|
+
textarea.style.height = "auto";
|
|
1318
1414
|
} catch (err) {
|
|
1319
1415
|
console.error("Lỗi gửi tin nhắn:", err);
|
|
1320
1416
|
}
|
|
@@ -1362,34 +1458,37 @@ class ChatWidget extends HTMLElement {
|
|
|
1362
1458
|
|
|
1363
1459
|
// ================== SIGNALR CONNECTION ==================
|
|
1364
1460
|
const connection = new signalR.HubConnectionBuilder()
|
|
1365
|
-
.withUrl(
|
|
1366
|
-
|
|
1367
|
-
|
|
1461
|
+
.withUrl(
|
|
1462
|
+
`${BASE_URL}chatHub?token=${token}&ePartnerCode=${ePartnerCode}`,
|
|
1463
|
+
{
|
|
1464
|
+
withCredentials: false,
|
|
1465
|
+
}
|
|
1466
|
+
)
|
|
1368
1467
|
.configureLogging(signalR.LogLevel.Information)
|
|
1369
1468
|
.withAutomaticReconnect()
|
|
1370
1469
|
.build();
|
|
1371
1470
|
|
|
1372
1471
|
// Nhận tin nhắn từ server
|
|
1373
1472
|
connection.on("ReceiveGroupMessage", (data) => {
|
|
1374
|
-
console.log(
|
|
1473
|
+
console.log("Tin nhắn nhận được:", data);
|
|
1375
1474
|
const isMe = userId === data.ma_doi_tac_nsd;
|
|
1376
1475
|
const msg = document.createElement("div");
|
|
1377
|
-
msg.classList.add(isMe ?
|
|
1476
|
+
msg.classList.add(isMe ? "me" : "partner");
|
|
1378
1477
|
msg.innerHTML = `
|
|
1379
1478
|
<div class="info">
|
|
1380
|
-
${
|
|
1381
|
-
|
|
1479
|
+
${
|
|
1480
|
+
isMe
|
|
1481
|
+
? `<div class="message-title">
|
|
1382
1482
|
<h5>${data.ten}</h5>
|
|
1383
1483
|
<p class="message-time">${data.ngay_gui}</p>
|
|
1384
1484
|
</div>
|
|
1385
1485
|
<img class="logo-item" src="${data.link_logo}" alt="Logo" />`
|
|
1386
|
-
|
|
1387
|
-
`<img class="logo-item" src="${data.link_logo}" alt="Logo" />
|
|
1486
|
+
: `<img class="logo-item" src="${data.link_logo}" alt="Logo" />
|
|
1388
1487
|
<div class="message-title">
|
|
1389
1488
|
<h5>${data.ten}</h5>
|
|
1390
1489
|
<p class="message-time">${data.ngay_gui}</p>
|
|
1391
1490
|
</div>`
|
|
1392
|
-
|
|
1491
|
+
}
|
|
1393
1492
|
</div>
|
|
1394
1493
|
<div class="message">
|
|
1395
1494
|
<p>${data.noi_dung_tin_nhan}</p>
|
|
@@ -1410,7 +1509,7 @@ class ChatWidget extends HTMLElement {
|
|
|
1410
1509
|
}
|
|
1411
1510
|
|
|
1412
1511
|
async initSignalR() {
|
|
1413
|
-
const signalR = window.signalR || await this.loadSignalR();
|
|
1512
|
+
const signalR = window.signalR || (await this.loadSignalR());
|
|
1414
1513
|
|
|
1415
1514
|
this.connection = new signalR.HubConnectionBuilder()
|
|
1416
1515
|
.withUrl(`${BASE_URL}chatHub`, { withCredentials: false })
|
|
@@ -1432,8 +1531,9 @@ class ChatWidget extends HTMLElement {
|
|
|
1432
1531
|
return new Promise((resolve, reject) => {
|
|
1433
1532
|
if (typeof signalR !== "undefined") return resolve();
|
|
1434
1533
|
|
|
1435
|
-
const script = document.createElement(
|
|
1436
|
-
script.src =
|
|
1534
|
+
const script = document.createElement("script");
|
|
1535
|
+
script.src =
|
|
1536
|
+
"https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.0/signalr.min.js";
|
|
1437
1537
|
script.onload = () => {
|
|
1438
1538
|
console.log("SignalR đã tải xong.");
|
|
1439
1539
|
resolve();
|
|
@@ -1445,45 +1545,49 @@ class ChatWidget extends HTMLElement {
|
|
|
1445
1545
|
|
|
1446
1546
|
setupEmojiPickerAfterLoad() {
|
|
1447
1547
|
const shadow = this.shadowRoot;
|
|
1448
|
-
const messageSending = shadow.querySelector(
|
|
1449
|
-
const emojiButton = shadow.querySelector(
|
|
1450
|
-
const messageInput = shadow.querySelector(
|
|
1548
|
+
const messageSending = shadow.querySelector(".message-sending");
|
|
1549
|
+
const emojiButton = shadow.querySelector("#emoji-button");
|
|
1550
|
+
const messageInput = shadow.querySelector("#message-input");
|
|
1451
1551
|
|
|
1452
1552
|
if (!messageSending || !emojiButton || !messageInput) return;
|
|
1453
1553
|
|
|
1454
1554
|
// Tạo emoji-picker element
|
|
1455
|
-
let picker = messageSending.querySelector(
|
|
1555
|
+
let picker = messageSending.querySelector("emoji-picker");
|
|
1456
1556
|
if (!picker) {
|
|
1457
|
-
picker = document.createElement(
|
|
1557
|
+
picker = document.createElement("emoji-picker");
|
|
1458
1558
|
messageSending.appendChild(picker);
|
|
1459
1559
|
}
|
|
1460
1560
|
|
|
1461
1561
|
// Xử lý chọn emoji
|
|
1462
|
-
picker.addEventListener(
|
|
1562
|
+
picker.addEventListener("emoji-click", (event) => {
|
|
1463
1563
|
const emoji = event.detail.unicode;
|
|
1464
1564
|
const start = messageInput.selectionStart;
|
|
1465
1565
|
const end = messageInput.selectionEnd;
|
|
1466
|
-
messageInput.value =
|
|
1566
|
+
messageInput.value =
|
|
1567
|
+
messageInput.value.substring(0, start) +
|
|
1568
|
+
emoji +
|
|
1569
|
+
messageInput.value.substring(end);
|
|
1467
1570
|
const newPos = start + emoji.length;
|
|
1468
1571
|
messageInput.setSelectionRange(newPos, newPos);
|
|
1469
1572
|
messageInput.focus();
|
|
1470
|
-
picker.style.display =
|
|
1471
|
-
messageInput.dispatchEvent(new Event(
|
|
1573
|
+
picker.style.display = "none";
|
|
1574
|
+
messageInput.dispatchEvent(new Event("input")); // resize textarea
|
|
1472
1575
|
});
|
|
1473
1576
|
|
|
1474
1577
|
// Toggle picker
|
|
1475
|
-
emojiButton.addEventListener(
|
|
1578
|
+
emojiButton.addEventListener("click", (e) => {
|
|
1476
1579
|
e.stopPropagation();
|
|
1477
|
-
picker.style.display =
|
|
1580
|
+
picker.style.display =
|
|
1581
|
+
picker.style.display === "block" ? "none" : "block";
|
|
1478
1582
|
});
|
|
1479
1583
|
|
|
1480
1584
|
// Đóng khi click ngoài
|
|
1481
|
-
document.addEventListener(
|
|
1585
|
+
document.addEventListener("click", (e) => {
|
|
1482
1586
|
if (!picker.contains(e.target) && e.target !== emojiButton) {
|
|
1483
|
-
picker.style.display =
|
|
1587
|
+
picker.style.display = "none";
|
|
1484
1588
|
}
|
|
1485
1589
|
});
|
|
1486
1590
|
}
|
|
1487
1591
|
}
|
|
1488
1592
|
|
|
1489
|
-
customElements.define(
|
|
1593
|
+
customElements.define("chat-widget", ChatWidget);
|