frontPanel/src/ui_kit/FloatingSupportChat/index.tsx

294 lines
8.3 KiB
TypeScript
Raw Normal View History

import {
createTicket,
TicketMessage,
useSSESubscription,
useTicketMessages,
useTicketsFetcher,
} from "@frontend/kitui";
2024-05-13 13:24:41 +00:00
import { makeRequest } from "@api/makeRequest";
import FloatingSupportChat from "./FloatingSupportChat";
import { useUserStore } from "@root/user";
import { useCallback, useEffect, useMemo, useState } from "react";
import { sendTicketMessage, shownMessage } from "../../api/ticket";
import { useSSETab } from "../../utils/hooks/useSSETab";
import {
addOrUpdateUnauthMessages,
cleanAuthTicketData,
cleanUnauthTicketData,
setIsMessageSending,
setTicketData,
setUnauthIsPreventAutoscroll,
setUnauthTicketMessageFetchState,
useTicketStore,
} from "@root/ticket";
import { enqueueSnackbar } from "notistack";
import { getMessageFromFetchError } from "@utils/backendMessageHandler";
type ModalWarningType =
| "errorType"
| "errorSize"
| "picture"
| "video"
| "audio"
| "document"
| null;
const MAX_FILE_SIZE = 419430400;
const ACCEPT_SEND_FILE_TYPES_MAP = [
".jpeg",
".jpg",
".png",
".mp4",
".doc",
".docx",
".pdf",
".txt",
".xlsx",
".csv",
] as const;
export default () => {
const user = useUserStore((state) => state.user?._id);
const ticket = useTicketStore(
(state) => state[user ? "authData" : "unauthData"],
);
const { isActiveSSETab, updateSSEValue } = useSSETab<TicketMessage[]>(
"ticket",
addOrUpdateUnauthMessages,
);
const [modalWarningType, setModalWarningType] =
useState<ModalWarningType>(null);
const [isChatOpened, setIsChatOpened] = useState<boolean>(false);
const [sseEnabled, setSseEnabled] = useState(true);
const handleChatClickOpen = () => {
setIsChatOpened(true);
};
const handleChatClickClose = () => {
setIsChatOpened(false);
};
const handleChatClickSwitch = () => {
setIsChatOpened((state) => !state);
};
const getGreetingMessage: TicketMessage = useMemo(() => {
const workingHoursMessage =
"Здравствуйте, задайте ваш вопрос и наш оператор вам ответит в течение 10 минут";
const offHoursMessage =
"Здравствуйте, к сожалению, сейчас операторы не работают. Задайте ваш вопрос, и вам ответят в 10:00 по московскому времени";
const date = new Date();
const currentHourUTC = date.getUTCHours();
const MscTime = 3; // Москва UTC+3;
const moscowHour = (currentHourUTC + MscTime) % 24;
const greetingMessage =
moscowHour >= 3 && moscowHour < 10
? offHoursMessage
: workingHoursMessage;
return {
created_at: new Date().toISOString(),
files: [],
id: "111",
message: greetingMessage,
request_screenshot: "",
session_id: "greetingMessage",
shown: { me: 1 },
ticket_id: "111",
user_id: "greetingMessage",
};
}, [isChatOpened]);
useTicketsFetcher({
url: process.env.REACT_APP_DOMAIN + "/heruvym/getTickets",
ticketsPerPage: 10,
ticketApiPage: 0,
onSuccess: (result) => {
if (result.data?.length) {
const currentTicket = result.data.find(
({ origin }) => !origin.includes("/support"),
);
if (!currentTicket) {
return;
}
setTicketData({
ticketId: currentTicket.id,
sessionId: currentTicket.sess,
});
}
},
onError: (error: Error) => {
const message = getMessageFromFetchError(error);
if (message) enqueueSnackbar(message);
},
2024-05-13 13:24:41 +00:00
onFetchStateChange: () => {},
enabled: Boolean(user),
});
useTicketMessages({
url: process.env.REACT_APP_DOMAIN + "/heruvym/getMessages",
isUnauth: true,
ticketId: ticket.sessionData?.ticketId,
messagesPerPage: ticket.messagesPerPage,
messageApiPage: ticket.apiPage,
onSuccess: useCallback((messages) => {
addOrUpdateUnauthMessages(messages);
}, []),
onError: useCallback((error: Error) => {
const message = getMessageFromFetchError(error);
if (message) enqueueSnackbar(message);
}, []),
onFetchStateChange: setUnauthTicketMessageFetchState,
});
useSSESubscription<TicketMessage>({
enabled:
sseEnabled && isActiveSSETab && Boolean(ticket.sessionData?.sessionId),
url:
process.env.REACT_APP_DOMAIN +
`/heruvym/ticket?ticket=${ticket.sessionData?.ticketId}&s=${ticket.sessionData?.sessionId}`,
onNewData: (ticketMessages) => {
const isTicketClosed = ticketMessages.some(
(message) => message.session_id === "close",
);
if (isTicketClosed) {
cleanAuthTicketData();
addOrUpdateUnauthMessages([getGreetingMessage]);
if (!user) {
cleanUnauthTicketData();
localStorage.removeItem("unauth-ticket");
}
return;
}
updateSSEValue(ticketMessages);
addOrUpdateUnauthMessages(ticketMessages);
},
onDisconnect: useCallback(() => {
setUnauthIsPreventAutoscroll(false);
setSseEnabled(false);
}, []),
marker: "ticket",
});
useEffect(() => {
cleanAuthTicketData();
setSseEnabled(true);
}, [user]);
useEffect(() => {
if (isChatOpened) {
const newMessages = ticket.messages.filter(
({ shown }) => shown?.me !== 1,
);
newMessages.map(async ({ id }) => {
await shownMessage(id);
});
}
}, [isChatOpened, ticket.messages]);
const sendMessage = async (messageField: string) => {
if (!messageField || ticket.isMessageSending) return false;
setSseEnabled(true);
let successful = false;
setIsMessageSending(true);
if (!ticket.sessionData?.ticketId) {
try {
const data = await createTicket({
url: process.env.REACT_APP_DOMAIN + "/heruvym/create",
body: {
Title: "Unauth title",
Message: messageField,
},
useToken: Boolean(user),
});
successful = true;
setTicketData({
ticketId: data.Ticket,
sessionId: data.sess,
});
} catch (error: any) {
successful = false;
const errorMessage = getMessageFromFetchError(error);
if (errorMessage) enqueueSnackbar(errorMessage);
}
setIsMessageSending(false);
} else {
const [_, sendTicketMessageError] = await sendTicketMessage(
ticket.sessionData?.ticketId,
messageField,
);
successful = true;
if (sendTicketMessageError) {
successful = false;
enqueueSnackbar(sendTicketMessageError);
}
setIsMessageSending(false);
}
return successful;
};
const sendFile = async (file: File) => {
if (file === undefined) return true;
let data;
if (!ticket.sessionData?.ticketId) {
try {
data = await createTicket({
url: process.env.REACT_APP_DOMAIN + "/heruvym/create",
body: {
Title: "Unauth title",
Message: "",
},
useToken: Boolean(user),
});
setTicketData({
ticketId: data.Ticket,
sessionId: data.sess,
});
} catch (error: any) {
const errorMessage = getMessageFromFetchError(error);
if (errorMessage) enqueueSnackbar(errorMessage);
}
setIsMessageSending(false);
}
const ticketId = ticket.sessionData?.ticketId || data?.Ticket;
if (ticketId !== undefined) {
if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize");
try {
const body = new FormData();
body.append(file.name, file);
body.append("ticket", ticketId);
await makeRequest({
url: process.env.REACT_APP_DOMAIN + "/heruvym/sendFiles",
body: body,
method: "POST",
});
} catch (error: any) {
const errorMessage = getMessageFromFetchError(error);
if (errorMessage) enqueueSnackbar(errorMessage);
}
return true;
}
};
return (
<FloatingSupportChat
isChatOpened={isChatOpened}
handleChatClickOpen={handleChatClickOpen}
handleChatClickClose={handleChatClickClose}
handleChatClickSwitch={handleChatClickSwitch}
sendMessage={sendMessage}
sendFile={sendFile}
modalWarningType={modalWarningType}
setModalWarningType={setModalWarningType}
greetingMessage={getGreetingMessage}
/>
);
};