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