@axos-web-dev/shared-components 2.2.29 → 2.2.30
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/Chatbot/ChatWindow.d.ts +1 -0
- package/dist/Chatbot/ChatWindow.js +5 -10
- package/dist/Chatbot/Chatbot.js +75 -42
- package/dist/Chatbot/ChatbotMessage.d.ts +1 -0
- package/dist/Chatbot/ChatbotMessage.js +2 -1
- package/dist/Chatbot/store/messages.d.ts +6 -1
- package/dist/Chatbot/store/messages.js +62 -3
- package/dist/assets/ImageBillboard/ImageBillboard.css +12 -0
- package/package.json +1 -1
|
@@ -18,7 +18,6 @@ import { useOpenChat } from "./store/chat.js";
|
|
|
18
18
|
import { ThankYouMessage } from "./ThankYouMessage.js";
|
|
19
19
|
const ChatWindow = ({
|
|
20
20
|
messages,
|
|
21
|
-
status = "connected",
|
|
22
21
|
onSend,
|
|
23
22
|
inputDisabled = false,
|
|
24
23
|
onClose,
|
|
@@ -43,7 +42,6 @@ const ChatWindow = ({
|
|
|
43
42
|
showThankyouMessage,
|
|
44
43
|
toggleThankyouMessage,
|
|
45
44
|
hasEscalated,
|
|
46
|
-
isBlockedInput,
|
|
47
45
|
isOpen,
|
|
48
46
|
hasOpenedOnce,
|
|
49
47
|
chatStatus,
|
|
@@ -65,7 +63,6 @@ const ChatWindow = ({
|
|
|
65
63
|
const handleSend = (e) => {
|
|
66
64
|
e.preventDefault();
|
|
67
65
|
const cleaned = cleanInput(input);
|
|
68
|
-
if (isBlockedInput) return;
|
|
69
66
|
if (cleaned != "") {
|
|
70
67
|
onSend(cleaned);
|
|
71
68
|
setInput("");
|
|
@@ -460,7 +457,7 @@ const ChatWindow = ({
|
|
|
460
457
|
}
|
|
461
458
|
) }) : null,
|
|
462
459
|
/* @__PURE__ */ jsxs("div", { className: clsx(messagesContainerStyle), children: [
|
|
463
|
-
|
|
460
|
+
!messages.some((msg) => !msg.pending) && /* @__PURE__ */ jsx(
|
|
464
461
|
"div",
|
|
465
462
|
{
|
|
466
463
|
className: clsx(),
|
|
@@ -525,9 +522,7 @@ const ChatWindow = ({
|
|
|
525
522
|
/* @__PURE__ */ jsx(
|
|
526
523
|
"div",
|
|
527
524
|
{
|
|
528
|
-
className: clsx(
|
|
529
|
-
focused && !inputDisabled && !isBlockedInput ? focusAnimation : ""
|
|
530
|
-
),
|
|
525
|
+
className: clsx(focused && !inputDisabled ? focusAnimation : ""),
|
|
531
526
|
style: {
|
|
532
527
|
position: "absolute",
|
|
533
528
|
left: "10%",
|
|
@@ -544,7 +539,7 @@ const ChatWindow = ({
|
|
|
544
539
|
style: {
|
|
545
540
|
display: "flex",
|
|
546
541
|
padding: "12px 16px",
|
|
547
|
-
background:
|
|
542
|
+
background: inputDisabled || escalationDeflected ? "light-dark(rgba(239, 239, 239, 0.3), rgba(59, 59, 59, 0.3))" : "#ffffff",
|
|
548
543
|
borderRadius: 12
|
|
549
544
|
},
|
|
550
545
|
children: [
|
|
@@ -568,7 +563,7 @@ const ChatWindow = ({
|
|
|
568
563
|
"aria-label": "Ask anything...",
|
|
569
564
|
className: clsx(inputStyle, autoResize),
|
|
570
565
|
autoFocus: true,
|
|
571
|
-
disabled:
|
|
566
|
+
disabled: inputDisabled || escalationDeflected,
|
|
572
567
|
rows: 1
|
|
573
568
|
}
|
|
574
569
|
),
|
|
@@ -582,7 +577,7 @@ const ChatWindow = ({
|
|
|
582
577
|
type: "submit",
|
|
583
578
|
title: "Send message",
|
|
584
579
|
"aria-label": "Send message",
|
|
585
|
-
disabled:
|
|
580
|
+
disabled: inputDisabled || !input.trim() || escalationDeflected,
|
|
586
581
|
children: /* @__PURE__ */ jsx(
|
|
587
582
|
"svg",
|
|
588
583
|
{
|
package/dist/Chatbot/Chatbot.js
CHANGED
|
@@ -40,17 +40,34 @@ const Chatbot = ({
|
|
|
40
40
|
hideDisplayThankyouMessage
|
|
41
41
|
} = useOpenChat();
|
|
42
42
|
const isVisible = usePageVisibility();
|
|
43
|
-
const {
|
|
43
|
+
const {
|
|
44
|
+
addMessage,
|
|
45
|
+
addMessages,
|
|
46
|
+
clearMessages,
|
|
47
|
+
messages,
|
|
48
|
+
addPendingMessage,
|
|
49
|
+
resolvePendingMessage
|
|
50
|
+
} = useMessages();
|
|
44
51
|
const clientRef = useRef(null);
|
|
45
52
|
const menuRef = useRef(null);
|
|
46
53
|
const isMountedRef = useRef(false);
|
|
47
54
|
const chatRef = useRef(null);
|
|
48
55
|
const chatLoading = useRef(false);
|
|
49
56
|
const agent_virtual = useRef(null);
|
|
57
|
+
const messageQueueRef = useRef([]);
|
|
58
|
+
const sendInFlightRef = useRef(false);
|
|
59
|
+
const flushingRef = useRef(false);
|
|
60
|
+
const statusRef = useRef("idle");
|
|
50
61
|
const [status, setStatus] = useState("idle");
|
|
51
62
|
const [menusLoaded, setMenusLoaded] = useState(false);
|
|
52
63
|
const [isTyping, setIsTyping] = useState(false);
|
|
53
64
|
const [scalationStarted, setScalationStarted] = useState(false);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
statusRef.current = status;
|
|
67
|
+
if (status === "connected") {
|
|
68
|
+
flushQueuedMessages();
|
|
69
|
+
}
|
|
70
|
+
}, [status]);
|
|
54
71
|
useEffect(() => {
|
|
55
72
|
if (messages.length === 0) return;
|
|
56
73
|
const hasScalation = messages.some(
|
|
@@ -67,6 +84,9 @@ const Chatbot = ({
|
|
|
67
84
|
["axos", 1],
|
|
68
85
|
["ufb", 3]
|
|
69
86
|
]);
|
|
87
|
+
const hasRealReply = () => useMessages.getState().messages.some(
|
|
88
|
+
(m) => !m.pending && m.$userType !== "end_user" && m.type !== "noti" && m.$sid !== "typing-1"
|
|
89
|
+
);
|
|
70
90
|
const typingMessage = {
|
|
71
91
|
$sid: "typing-1",
|
|
72
92
|
type: "system",
|
|
@@ -107,7 +127,7 @@ const Chatbot = ({
|
|
|
107
127
|
menuRef.current = await clientRef.current.getMenus();
|
|
108
128
|
setMenusLoaded(true);
|
|
109
129
|
console.log("menus:", menuRef.current);
|
|
110
|
-
if (menuRef.current !== null) {
|
|
130
|
+
if (menuRef.current !== null && useOpenChat.getState().isOpen) {
|
|
111
131
|
await startChat("onReady");
|
|
112
132
|
}
|
|
113
133
|
}
|
|
@@ -121,10 +141,11 @@ const Chatbot = ({
|
|
|
121
141
|
const onChatMessageHandler = async (message) => {
|
|
122
142
|
console.log("Received message:", message);
|
|
123
143
|
const { event, $userType } = message;
|
|
124
|
-
if (["
|
|
144
|
+
if (["virtual_agent", "user"].includes($userType) && event === void 0) {
|
|
125
145
|
addMessage(message);
|
|
126
146
|
if (!hasEscalated) {
|
|
127
147
|
unblockInput?.();
|
|
148
|
+
flushQueuedMessages();
|
|
128
149
|
}
|
|
129
150
|
return;
|
|
130
151
|
}
|
|
@@ -205,6 +226,7 @@ const Chatbot = ({
|
|
|
205
226
|
startEscalation?.();
|
|
206
227
|
}
|
|
207
228
|
}
|
|
229
|
+
flushQueuedMessages();
|
|
208
230
|
} catch (error) {
|
|
209
231
|
console.error("Error fetching messages on chat connected:", error);
|
|
210
232
|
}
|
|
@@ -226,7 +248,7 @@ const Chatbot = ({
|
|
|
226
248
|
const deregisterEventHandlers = () => {
|
|
227
249
|
clientRef.current?.off("ready", onReadyHandler);
|
|
228
250
|
clientRef.current?.off("authenticated", onAuthenticatedHandler);
|
|
229
|
-
clientRef.current?.off("chat.ongoing",
|
|
251
|
+
clientRef.current?.off("chat.ongoing", onChatOngoingHandler);
|
|
230
252
|
clientRef.current?.off("chat.message", onChatMessageHandler);
|
|
231
253
|
clientRef.current?.off("chat.typingStarted", onChatTypingStartedHandler);
|
|
232
254
|
clientRef.current?.off("chat.typingEnded", onChatTypingEndedHandler);
|
|
@@ -340,6 +362,41 @@ const Chatbot = ({
|
|
|
340
362
|
setChatStarted();
|
|
341
363
|
}
|
|
342
364
|
};
|
|
365
|
+
const sendNow = async (msg) => {
|
|
366
|
+
sendInFlightRef.current = true;
|
|
367
|
+
let success = true;
|
|
368
|
+
try {
|
|
369
|
+
await clientRef.current?.sendTextMessage(msg);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.log(error);
|
|
372
|
+
success = false;
|
|
373
|
+
} finally {
|
|
374
|
+
sendInFlightRef.current = false;
|
|
375
|
+
if (!hasEscalated) {
|
|
376
|
+
blockInput?.();
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return success;
|
|
380
|
+
};
|
|
381
|
+
const flushQueuedMessages = async () => {
|
|
382
|
+
if (flushingRef.current) return;
|
|
383
|
+
if (messageQueueRef.current.length === 0) return;
|
|
384
|
+
if (!clientRef.current || statusRef.current !== "connected") return;
|
|
385
|
+
if (!hasRealReply()) return;
|
|
386
|
+
flushingRef.current = true;
|
|
387
|
+
try {
|
|
388
|
+
while (messageQueueRef.current.length > 0 && !useOpenChat.getState().isBlockedInput && !sendInFlightRef.current) {
|
|
389
|
+
const next = messageQueueRef.current[0];
|
|
390
|
+
const success = await sendNow(next.text);
|
|
391
|
+
messageQueueRef.current.shift();
|
|
392
|
+
if (!success) {
|
|
393
|
+
resolvePendingMessage(next.id);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
} finally {
|
|
397
|
+
flushingRef.current = false;
|
|
398
|
+
}
|
|
399
|
+
};
|
|
343
400
|
const onSendMessage = async (msg) => {
|
|
344
401
|
const clientChatId = clientRef.current?.chat?.id;
|
|
345
402
|
const refChatId = chatRef.current?.id;
|
|
@@ -351,15 +408,16 @@ const Chatbot = ({
|
|
|
351
408
|
"| chatRef.id:",
|
|
352
409
|
refChatId
|
|
353
410
|
);
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
411
|
+
const notReady = !clientRef.current || statusRef.current !== "connected";
|
|
412
|
+
const isThinking = useOpenChat.getState().isBlockedInput;
|
|
413
|
+
const noReplyYet = !hasRealReply();
|
|
414
|
+
if (notReady || isThinking || sendInFlightRef.current || noReplyYet) {
|
|
415
|
+
const id = `pending-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
416
|
+
messageQueueRef.current.push({ id, text: msg });
|
|
417
|
+
addPendingMessage(id, msg);
|
|
418
|
+
return;
|
|
362
419
|
}
|
|
420
|
+
await sendNow(msg);
|
|
363
421
|
};
|
|
364
422
|
const onEndChat = async () => {
|
|
365
423
|
console.log(`Ending chat [end chat]`);
|
|
@@ -373,6 +431,9 @@ const Chatbot = ({
|
|
|
373
431
|
clearMessages();
|
|
374
432
|
chatRef.current = null;
|
|
375
433
|
resetChatStarted();
|
|
434
|
+
messageQueueRef.current = [];
|
|
435
|
+
sendInFlightRef.current = false;
|
|
436
|
+
flushingRef.current = false;
|
|
376
437
|
console.log("Chat ended");
|
|
377
438
|
endEscalation?.();
|
|
378
439
|
} finally {
|
|
@@ -399,6 +460,8 @@ const Chatbot = ({
|
|
|
399
460
|
console.log("Chatbot unmounted");
|
|
400
461
|
deregisterEventHandlers();
|
|
401
462
|
chatLoading.current = false;
|
|
463
|
+
messageQueueRef.current = [];
|
|
464
|
+
sendInFlightRef.current = false;
|
|
402
465
|
reset();
|
|
403
466
|
resetChatStarted();
|
|
404
467
|
setStatus("idle");
|
|
@@ -443,36 +506,6 @@ const Chatbot = ({
|
|
|
443
506
|
}
|
|
444
507
|
}
|
|
445
508
|
}, [isOpen, isVisible]);
|
|
446
|
-
useMount(() => {
|
|
447
|
-
const hasParams = () => {
|
|
448
|
-
return window.location.search.includes("utm_");
|
|
449
|
-
};
|
|
450
|
-
const tryStart = () => {
|
|
451
|
-
if (hasParams()) {
|
|
452
|
-
startChat("utm-ready");
|
|
453
|
-
return true;
|
|
454
|
-
}
|
|
455
|
-
return false;
|
|
456
|
-
};
|
|
457
|
-
if (tryStart()) return;
|
|
458
|
-
const original = history.replaceState;
|
|
459
|
-
history.replaceState = function(...args) {
|
|
460
|
-
original.apply(this, args);
|
|
461
|
-
if (tryStart()) {
|
|
462
|
-
history.replaceState = original;
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
const interval = setInterval(() => {
|
|
466
|
-
if (tryStart()) {
|
|
467
|
-
clearInterval(interval);
|
|
468
|
-
history.replaceState = original;
|
|
469
|
-
}
|
|
470
|
-
}, 100);
|
|
471
|
-
return () => {
|
|
472
|
-
clearInterval(interval);
|
|
473
|
-
history.replaceState = original;
|
|
474
|
-
};
|
|
475
|
-
});
|
|
476
509
|
return menusLoaded && /* @__PURE__ */ jsxs(
|
|
477
510
|
"div",
|
|
478
511
|
{
|
|
@@ -108,6 +108,7 @@ const ChatbotMessage = ({
|
|
|
108
108
|
messageStyle,
|
|
109
109
|
msg.$userType == "end_user" ? user_msg : agent_msg
|
|
110
110
|
),
|
|
111
|
+
style: msg.pending ? { opacity: 0.65 } : void 0,
|
|
111
112
|
children: /* @__PURE__ */ jsxs(
|
|
112
113
|
"div",
|
|
113
114
|
{
|
|
@@ -253,7 +254,7 @@ const ChatbotMessage = ({
|
|
|
253
254
|
textTransform: "capitalize",
|
|
254
255
|
fontFamily: "inherit"
|
|
255
256
|
},
|
|
256
|
-
children: timeText
|
|
257
|
+
children: msg.pending ? "Sending..." : timeText
|
|
257
258
|
}
|
|
258
259
|
)
|
|
259
260
|
]
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { MessageResponse } from '@ujet/websdk-headless';
|
|
2
2
|
|
|
3
|
+
export type PendingMessage = MessageResponse & {
|
|
4
|
+
pending?: boolean;
|
|
5
|
+
};
|
|
3
6
|
interface MessageStore {
|
|
4
|
-
messages: Array<
|
|
7
|
+
messages: Array<PendingMessage>;
|
|
5
8
|
addMessage: (message: MessageResponse) => void;
|
|
6
9
|
removeMessage: (id: string) => void;
|
|
7
10
|
addMessages: (newMessages: MessageResponse[]) => void;
|
|
8
11
|
clearMessages: () => void;
|
|
9
12
|
isEscalated: boolean;
|
|
13
|
+
addPendingMessage: (id: string, text: string) => void;
|
|
14
|
+
resolvePendingMessage: (id: string) => void;
|
|
10
15
|
}
|
|
11
16
|
export interface Message {
|
|
12
17
|
id: string;
|
|
@@ -3,18 +3,77 @@ const useMessages = create((set, get) => ({
|
|
|
3
3
|
messages: [],
|
|
4
4
|
addMessage: (message) => set((state) => {
|
|
5
5
|
const cleaned = state.messages.filter((m) => m.$sid !== "typing-1");
|
|
6
|
-
|
|
6
|
+
const isPending = message.pending;
|
|
7
|
+
if (!isPending && message.$userType === "end_user") {
|
|
8
|
+
const pendingIdx = cleaned.findIndex(
|
|
9
|
+
(m) => m.pending && m.content === message.content
|
|
10
|
+
);
|
|
11
|
+
if (pendingIdx !== -1) {
|
|
12
|
+
return {
|
|
13
|
+
messages: [
|
|
14
|
+
...cleaned.slice(0, pendingIdx),
|
|
15
|
+
message,
|
|
16
|
+
...cleaned.slice(pendingIdx + 1)
|
|
17
|
+
]
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return { messages: [...cleaned, message] };
|
|
21
|
+
}
|
|
22
|
+
const insertBeforeIdx = !isPending ? cleaned.findIndex((m) => m.pending) : -1;
|
|
23
|
+
if (insertBeforeIdx === -1) {
|
|
24
|
+
return { messages: [...cleaned, message] };
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
messages: [
|
|
28
|
+
...cleaned.slice(0, insertBeforeIdx),
|
|
29
|
+
message,
|
|
30
|
+
...cleaned.slice(insertBeforeIdx)
|
|
31
|
+
]
|
|
32
|
+
};
|
|
7
33
|
}),
|
|
8
34
|
removeMessage: (id) => set((state) => ({
|
|
9
35
|
messages: state.messages.filter(
|
|
10
36
|
(msg) => "$sid" in msg && msg.$sid !== id
|
|
11
37
|
)
|
|
12
38
|
})),
|
|
13
|
-
addMessages: (newMessages) => set((state) =>
|
|
39
|
+
addMessages: (newMessages) => set((state) => {
|
|
40
|
+
const pendingIdx = state.messages.findIndex(
|
|
41
|
+
(m) => m.pending
|
|
42
|
+
);
|
|
43
|
+
if (pendingIdx === -1) {
|
|
44
|
+
return { messages: [...state.messages, ...newMessages] };
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
messages: [
|
|
48
|
+
...state.messages.slice(0, pendingIdx),
|
|
49
|
+
...newMessages,
|
|
50
|
+
...state.messages.slice(pendingIdx)
|
|
51
|
+
]
|
|
52
|
+
};
|
|
53
|
+
}),
|
|
14
54
|
clearMessages: () => set({ messages: [] }),
|
|
15
55
|
isEscalated: get()?.messages?.some(
|
|
16
56
|
(msg) => ["escalationAccepted", "escalationStarted"].includes(msg.event)
|
|
17
|
-
)
|
|
57
|
+
),
|
|
58
|
+
addPendingMessage: (id, text) => set((state) => ({
|
|
59
|
+
messages: [
|
|
60
|
+
...state.messages,
|
|
61
|
+
{
|
|
62
|
+
$sid: id,
|
|
63
|
+
type: "text",
|
|
64
|
+
content: text,
|
|
65
|
+
sender: { id: "end_user", type: "end_user" },
|
|
66
|
+
$timestamp: /* @__PURE__ */ new Date(),
|
|
67
|
+
$userType: "end_user",
|
|
68
|
+
$index: Date.now(),
|
|
69
|
+
$userId: 0,
|
|
70
|
+
pending: true
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
})),
|
|
74
|
+
resolvePendingMessage: (id) => set((state) => ({
|
|
75
|
+
messages: state.messages.filter((msg) => msg.$sid !== id)
|
|
76
|
+
}))
|
|
18
77
|
}));
|
|
19
78
|
export {
|
|
20
79
|
useMessages
|
|
@@ -293,6 +293,12 @@ main > div:nth-last-child(2) > ._1m7m2a0:not(._1m7m2ax) {
|
|
|
293
293
|
.page_body_contents > div:has(._1m7m2ax._1m7m2a4) + div:has(._1m7m2ax._1m7m2a4) {
|
|
294
294
|
padding-top: 0;
|
|
295
295
|
}
|
|
296
|
+
.page_body_contents > div:has(.stacked) {
|
|
297
|
+
padding-bottom: 40px;
|
|
298
|
+
}
|
|
299
|
+
.page_body_contents > div:has(> ._1m7m2ax) {
|
|
300
|
+
padding-block: 56px;
|
|
301
|
+
}
|
|
296
302
|
.page_body_contents > div:has(._1m7m2ax) + div:has(._1m7m2ax) {
|
|
297
303
|
margin-top: 0;
|
|
298
304
|
}
|
|
@@ -459,6 +465,9 @@ main > div:nth-last-child(2) > ._1m7m2a0:not(._1m7m2ax) {
|
|
|
459
465
|
.stacked:first-child {
|
|
460
466
|
padding-top: 42px;
|
|
461
467
|
}
|
|
468
|
+
.page_body_contents > div:has(> ._1m7m2ax) {
|
|
469
|
+
padding-block: 42px;
|
|
470
|
+
}
|
|
462
471
|
}
|
|
463
472
|
@media screen and (max-width: 768px) {
|
|
464
473
|
._1m7m2av {
|
|
@@ -476,6 +485,9 @@ main > div:nth-last-child(2) > ._1m7m2a0:not(._1m7m2ax) {
|
|
|
476
485
|
.stacked:first-child {
|
|
477
486
|
padding-top: 2rem;
|
|
478
487
|
}
|
|
488
|
+
.page_body_contents > div:has(> ._1m7m2ax) {
|
|
489
|
+
padding-block: 2rem;
|
|
490
|
+
}
|
|
479
491
|
}
|
|
480
492
|
@media screen and (max-width: 327px) {
|
|
481
493
|
._1m7m2av {
|