Merge branch 'support' into support-fixes

This commit is contained in:
Nastya 2024-02-13 21:32:53 +03:00
commit 4f3a15c735
2 changed files with 305 additions and 302 deletions

@ -9,8 +9,8 @@ import {
Typography,
useMediaQuery,
useTheme,
} from "@mui/material"
import { TicketMessage } from "@frontend/kitui"
} from "@mui/material";
import { TicketMessage } from "@frontend/kitui";
import {
addOrUpdateUnauthMessages,
useUnauthTicketStore,
@ -19,47 +19,48 @@ import {
setUnauthSessionData,
setIsMessageSending,
setUnauthTicketMessageFetchState,
} from "@root/stores/unauthTicket"
import { enqueueSnackbar } from "notistack"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import ChatMessage from "../ChatMessage"
import SendIcon from "../icons/SendIcon"
import UserCircleIcon from "./UserCircleIcon"
import { throttle } from "@frontend/kitui"
} from "@root/stores/unauthTicket";
import { enqueueSnackbar } from "notistack";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import ChatMessage from "../ChatMessage";
import SendIcon from "../icons/SendIcon";
import UserCircleIcon from "./UserCircleIcon";
import { throttle } from "@frontend/kitui";
import {
useTicketMessages,
getMessageFromFetchError,
useSSESubscription,
useEventListener,
createTicket,
} from "@frontend/kitui"
import { sendTicketMessage } from "@root/api/ticket"
} from "@frontend/kitui";
import { sendTicketMessage } from "@root/api/ticket";
interface Props {
open: boolean;
sx?: SxProps<Theme>;
}
export default function Chat({ sx }: Props) {
const theme = useTheme()
const upMd = useMediaQuery(theme.breakpoints.up("md"))
const [messageField, setMessageField] = useState<string>("")
const sessionData = useUnauthTicketStore((state) => state.sessionData)
const messages = useUnauthTicketStore((state) => state.messages)
const messageApiPage = useUnauthTicketStore((state) => state.apiPage)
export default function Chat({ open = false, sx }: Props) {
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const [messageField, setMessageField] = useState<string>("");
const sessionData = useUnauthTicketStore((state) => state.sessionData);
const messages = useUnauthTicketStore((state) => state.messages);
const messageApiPage = useUnauthTicketStore((state) => state.apiPage);
const messagesPerPage = useUnauthTicketStore(
(state) => state.messagesPerPage
)
);
const isMessageSending = useUnauthTicketStore(
(state) => state.isMessageSending
)
);
const isPreventAutoscroll = useUnauthTicketStore(
(state) => state.isPreventAutoscroll
)
const lastMessageId = useUnauthTicketStore((state) => state.lastMessageId)
);
const lastMessageId = useUnauthTicketStore((state) => state.lastMessageId);
const fetchState = useUnauthTicketStore(
(state) => state.unauthTicketMessageFetchState
)
const chatBoxRef = useRef<HTMLDivElement>(null)
);
const chatBoxRef = useRef<HTMLDivElement>(null);
useTicketMessages({
url: process.env.REACT_APP_DOMAIN + "/heruvym/getMessages",
@ -69,67 +70,69 @@ export default function Chat({ sx }: Props) {
messageApiPage,
onSuccess: useCallback((messages) => {
if (chatBoxRef.current && chatBoxRef.current.scrollTop < 1)
chatBoxRef.current.scrollTop = 1
addOrUpdateUnauthMessages(messages)
chatBoxRef.current.scrollTop = 1;
addOrUpdateUnauthMessages(messages);
}, []),
onError: useCallback((error: Error) => {
const message = getMessageFromFetchError(error)
if (message) enqueueSnackbar(message)
const message = getMessageFromFetchError(error);
if (message) enqueueSnackbar(message);
}, []),
onFetchStateChange: setUnauthTicketMessageFetchState,
})
});
useSSESubscription<TicketMessage>({
enabled: Boolean(sessionData),
url: process.env.REACT_APP_DOMAIN + `/heruvym/ticket?ticket=${sessionData?.ticketId}&s=${sessionData?.sessionId}`,
url:
process.env.REACT_APP_DOMAIN +
`/heruvym/ticket?ticket=${sessionData?.ticketId}&s=${sessionData?.sessionId}`,
onNewData: addOrUpdateUnauthMessages,
onDisconnect: useCallback(() => {
setUnauthIsPreventAutoscroll(false)
setUnauthIsPreventAutoscroll(false);
}, []),
marker: "ticket",
})
});
const throttledScrollHandler = useMemo(
() =>
throttle(() => {
const chatBox = chatBoxRef.current
if (!chatBox) return
const chatBox = chatBoxRef.current;
if (!chatBox) return;
const scrollBottom =
chatBox.scrollHeight - chatBox.scrollTop - chatBox.clientHeight
const isPreventAutoscroll = scrollBottom > chatBox.clientHeight
setUnauthIsPreventAutoscroll(isPreventAutoscroll)
chatBox.scrollHeight - chatBox.scrollTop - chatBox.clientHeight;
const isPreventAutoscroll = scrollBottom > chatBox.clientHeight;
setUnauthIsPreventAutoscroll(isPreventAutoscroll);
if (fetchState !== "idle") return
if (fetchState !== "idle") return;
if (chatBox.scrollTop < chatBox.clientHeight) {
incrementUnauthMessageApiPage()
incrementUnauthMessageApiPage();
}
}, 200),
[fetchState]
)
);
useEventListener("scroll", throttledScrollHandler, chatBoxRef)
useEventListener("scroll", throttledScrollHandler, chatBoxRef);
useEffect(
function scrollOnNewMessage() {
if (!chatBoxRef.current) return
if (!chatBoxRef.current) return;
if (!isPreventAutoscroll) {
setTimeout(() => {
scrollToBottom()
}, 50)
scrollToBottom();
}, 50);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[lastMessageId]
)
);
async function handleSendMessage() {
if (!messageField || isMessageSending) return
if (!messageField || isMessageSending) return;
if (!sessionData) {
setIsMessageSending(true)
setIsMessageSending(true);
createTicket({
url: process.env.REACT_APP_DOMAIN + "/heruvym/create",
body: {
@ -142,54 +145,56 @@ export default function Chat({ sx }: Props) {
setUnauthSessionData({
ticketId: response.Ticket,
sessionId: response.sess,
})
});
})
.catch((error) => {
const errorMessage = getMessageFromFetchError(error)
if (errorMessage) enqueueSnackbar(errorMessage)
const errorMessage = getMessageFromFetchError(error);
if (errorMessage) enqueueSnackbar(errorMessage);
})
.finally(() => {
setMessageField("")
setIsMessageSending(false)
})
setMessageField("");
setIsMessageSending(false);
});
} else {
setIsMessageSending(true)
setIsMessageSending(true);
const [_, sendTicketMessageError] = await sendTicketMessage(
sessionData.ticketId,
messageField
)
);
if (sendTicketMessageError) {
enqueueSnackbar(sendTicketMessageError)
enqueueSnackbar(sendTicketMessageError);
}
setMessageField("")
setIsMessageSending(false)
setMessageField("");
setIsMessageSending(false);
}
}
function scrollToBottom(behavior?: ScrollBehavior) {
if (!chatBoxRef.current) return
if (!chatBoxRef.current) return;
const chatBox = chatBoxRef.current
const chatBox = chatBoxRef.current;
chatBox.scroll({
left: 0,
top: chatBox.scrollHeight,
behavior,
})
});
}
const handleTextfieldKeyPress: React.KeyboardEventHandler<
HTMLInputElement | HTMLTextAreaElement
> = (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
handleSendMessage()
}
e.preventDefault();
handleSendMessage();
}
};
return (
<>
{open && (
<Box
sx={{
display: "flex",
@ -315,5 +320,7 @@ export default function Chat({ sx }: Props) {
</FormControl>
</Box>
</Box>
)
)}
</>
);
}

@ -43,14 +43,10 @@ export default function FloatingSupportChat() {
zIndex: 10,
}}
>
{isChatOpened && (
<Chat
sx={{
alignSelf: "start",
width: "clamp(200px, 100%, 400px)",
}}
open={isChatOpened}
sx={{ alignSelf: "start", width: "clamp(200px, 100%, 400px)" }}
/>
)}
<Fab
disableRipple
sx={{