Merge branch 'support' into support-fixes
This commit is contained in:
commit
4f3a15c735
@ -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={{
|
||||
|
Loading…
Reference in New Issue
Block a user