Remove chat ui library and implement replacement
This commit is contained in:
parent
fa931f35b2
commit
a6d3d1d8a8
5 changed files with 161 additions and 78 deletions
|
|
@ -0,0 +1,20 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FC } from "react";
|
||||||
|
import { Box, BoxProps } from "@mui/material";
|
||||||
|
|
||||||
|
interface MessageProps extends BoxProps {
|
||||||
|
message: string;
|
||||||
|
sentTime: string;
|
||||||
|
senderName: string;
|
||||||
|
direction: "outgoing" | "incoming";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Message: FC<MessageProps> = ({ message, sentTime, senderName, direction, ...props }) => {
|
||||||
|
return (
|
||||||
|
<Box {...props}>
|
||||||
|
<Box className="cs-message__time">{sentTime}{senderName ? ` - ${senderName}` : ''}</Box>
|
||||||
|
<Box className="cs-message__content" dangerouslySetInnerHTML={{ __html: message }} />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FC, useEffect, useRef, useCallback } from "react";
|
||||||
|
import { Box, styled, BoxProps } from "@mui/material";
|
||||||
|
|
||||||
|
interface MessageListProps extends BoxProps {
|
||||||
|
ticketId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MessageList: FC<MessageListProps> = ({
|
||||||
|
ticketId,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: MessageListProps) => {
|
||||||
|
const messageListRef = useRef<HTMLElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sessionStorage.removeItem("messageListScroll");
|
||||||
|
}, [ticketId]);
|
||||||
|
|
||||||
|
const onRefChange = useCallback(node => {
|
||||||
|
if (node !== null) {
|
||||||
|
// DOM node referenced by ref has changed and exists
|
||||||
|
const scrollTop = parseFloat(sessionStorage.getItem("messageListScroll") ?? "1000000");
|
||||||
|
node.scrollTop = Math.min(scrollTop, node.scrollHeight - node.clientHeight);
|
||||||
|
node.onscroll = () => {
|
||||||
|
sessionStorage.setItem("messageListScroll", node.scrollTop.toPrecision(6));
|
||||||
|
}
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
node.scrollTo({
|
||||||
|
top: node.scrollHeight - node.clientHeight,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const InternalMessageList = styled((props: any) => (
|
||||||
|
<Box key="messageList" ref={onRefChange} {...props} />
|
||||||
|
))(({ theme }) => ({
|
||||||
|
overflowX: "hidden",
|
||||||
|
overflowY: "auto",
|
||||||
|
padding: "20px",
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InternalMessageList {...props}>
|
||||||
|
{children}
|
||||||
|
</InternalMessageList>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,18 +1,27 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC, useState, useEffect } from "react";
|
import { FC, useState, useEffect, useRef, memo } from "react";
|
||||||
import { getTicketAction, getTicketArticlesAction } from "app/_actions/tickets";
|
import { getTicketAction, getTicketArticlesAction } from "app/_actions/tickets";
|
||||||
import { Grid, Box, Typography } from "@mui/material";
|
import { Grid, Box, Typography, styled, BoxProps } from "@mui/material";
|
||||||
import { Button, fonts, colors } from "@link-stack/ui";
|
import { Button, fonts, colors } from "@link-stack/ui";
|
||||||
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
|
|
||||||
import {
|
|
||||||
MainContainer,
|
|
||||||
ChatContainer,
|
|
||||||
MessageList,
|
|
||||||
Message,
|
|
||||||
ConversationHeader,
|
|
||||||
} from "@chatscope/chat-ui-kit-react";
|
|
||||||
import { ArticleCreateDialog } from "./ArticleCreateDialog";
|
import { ArticleCreateDialog } from "./ArticleCreateDialog";
|
||||||
|
import { Message } from "./Message";
|
||||||
|
import { MessageList } from "./MessageList";
|
||||||
|
|
||||||
|
const ChatContainer = styled((props: BoxProps) => <Box {...props} />)(
|
||||||
|
({ theme }) => ({
|
||||||
|
overflow: "hidden",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const ChatHeader = styled((props: any) => <Box {...props} />)(({ theme }) => ({
|
||||||
|
padding: "20px",
|
||||||
|
textAlign: "center",
|
||||||
|
}));
|
||||||
|
|
||||||
interface TicketDetailProps {
|
interface TicketDetailProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -26,9 +35,22 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
const [articleKind, setArticleKind] = useState("note");
|
const [articleKind, setArticleKind] = useState("note");
|
||||||
|
|
||||||
|
const messageListRef = useRef<HTMLElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Reset remembered state
|
||||||
|
sessionStorage.setItem("ticketDetailTicket", "");
|
||||||
|
sessionStorage.setItem("ticketDetailArticles", "");
|
||||||
|
}, [id])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchTicket = async () => {
|
const fetchTicket = async () => {
|
||||||
const result = await getTicketAction(id);
|
const result = await getTicketAction(id);
|
||||||
|
const resultRaw = JSON.stringify(result);
|
||||||
|
if (resultRaw == sessionStorage.getItem("ticketDetailTicket")) {
|
||||||
|
return; // No change
|
||||||
|
}
|
||||||
|
sessionStorage.setItem("ticketDetailTicket", resultRaw);
|
||||||
setTicket(result);
|
setTicket(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -42,6 +64,11 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchTicketArticles = async () => {
|
const fetchTicketArticles = async () => {
|
||||||
const result = await getTicketArticlesAction(id);
|
const result = await getTicketArticlesAction(id);
|
||||||
|
const resultRaw = JSON.stringify(result);
|
||||||
|
if (resultRaw == sessionStorage.getItem("ticketDetailArticles")) {
|
||||||
|
return; // No change
|
||||||
|
}
|
||||||
|
sessionStorage.setItem("ticketDetailArticles", resultRaw);
|
||||||
setTicketArticles(result);
|
setTicketArticles(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -64,17 +91,8 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
||||||
<Box sx={{ height: "100%", width: "100%", background: veryLightGray }}>
|
<Box sx={{ height: "100%", width: "100%", background: veryLightGray }}>
|
||||||
{shouldRender && (
|
{shouldRender && (
|
||||||
<>
|
<>
|
||||||
<MainContainer>
|
<ChatContainer className="cs-main-container">
|
||||||
<ChatContainer>
|
<ChatHeader className="cs-conversation-header">
|
||||||
<ConversationHeader>
|
|
||||||
<ConversationHeader.Content>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
textAlign: "center",
|
|
||||||
fontWeight: "bold",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
<Typography
|
||||||
variant="h5"
|
variant="h5"
|
||||||
sx={{
|
sx={{
|
||||||
|
|
@ -93,10 +111,8 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
||||||
>{`Ticket #${ticket.number} (created ${new Date(
|
>{`Ticket #${ticket.number} (created ${new Date(
|
||||||
ticket.createdAt,
|
ticket.createdAt,
|
||||||
).toLocaleDateString()})`}</Typography>
|
).toLocaleDateString()})`}</Typography>
|
||||||
</Box>
|
</ChatHeader>
|
||||||
</ConversationHeader.Content>
|
<MessageList ticketId={id}>
|
||||||
</ConversationHeader>
|
|
||||||
<MessageList style={{ marginBottom: 80 }}>
|
|
||||||
{ticketArticles.edges.map(({ node: article }: any) => (
|
{ticketArticles.edges.map(({ node: article }: any) => (
|
||||||
<Message
|
<Message
|
||||||
key={article.id}
|
key={article.id}
|
||||||
|
|
@ -107,28 +123,20 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
||||||
? "outgoing-message"
|
? "outgoing-message"
|
||||||
: "incoming-message"
|
: "incoming-message"
|
||||||
}
|
}
|
||||||
model={{
|
message={article.bodyWithUrls}
|
||||||
message: article.bodyWithUrls,
|
sentTime={new Date(article.updatedAt).toLocaleString()}
|
||||||
sentTime: article.updatedAt,
|
senderName={article.sender?.name}
|
||||||
sender: article.from,
|
direction={
|
||||||
direction:
|
article.sender === "Agent" ? "outgoing" : "incoming"
|
||||||
article.sender === "Agent" ? "outgoing" : "incoming",
|
}
|
||||||
position: "single",
|
/>
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Message.Header sender={article.sender?.name} sentTime={new Date(article.updatedAt).toLocaleString()} />
|
|
||||||
</Message>
|
|
||||||
))}
|
))}
|
||||||
</MessageList>
|
</MessageList>
|
||||||
</ChatContainer>
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
height: 80,
|
flex: "0 0 80",
|
||||||
background: veryLightGray,
|
background: veryLightGray,
|
||||||
borderTop: `1px solid ${lightGray}`,
|
borderTop: `1px solid ${lightGray}`,
|
||||||
position: "absolute",
|
|
||||||
bottom: 0,
|
|
||||||
width: "100%",
|
|
||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -162,7 +170,7 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
</MainContainer>
|
</ChatContainer>
|
||||||
<ArticleCreateDialog
|
<ArticleCreateDialog
|
||||||
ticketID={ticket.internalId}
|
ticketID={ticket.internalId}
|
||||||
open={dialogOpen}
|
open={dialogOpen}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,15 @@ body {
|
||||||
text-size-adjust: none;
|
text-size-adjust: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cs-message__time {
|
||||||
|
color: #888;
|
||||||
|
font-family: Roboto;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
left: 12px;
|
||||||
|
top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.internal-note .cs-message__content {
|
.internal-note .cs-message__content {
|
||||||
background-color: #FFB62088 !important;
|
background-color: #FFB62088 !important;
|
||||||
border: 2px solid #FFB620 !important;
|
border: 2px solid #FFB620 !important;
|
||||||
|
|
@ -75,8 +84,6 @@ body {
|
||||||
|
|
||||||
.cs-conversation-header {
|
.cs-conversation-header {
|
||||||
background-color: #ddd !important;
|
background-color: #ddd !important;
|
||||||
border: 0 !important;
|
|
||||||
padding: 20px !important;
|
|
||||||
border-bottom: 1px solid #ccc !important;
|
border-bottom: 1px solid #ccc !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,6 +96,5 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cs-main-container {
|
.cs-main-container {
|
||||||
border: 0 !important;
|
border-right: 1px solid #ccc;
|
||||||
border-right: 1px solid #ccc !important;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chatscope/chat-ui-kit-react": "^2.0.3",
|
|
||||||
"@chatscope/chat-ui-kit-styles": "^1.4.0",
|
|
||||||
"@emotion/cache": "^11.13.1",
|
"@emotion/cache": "^11.13.1",
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.13.3",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue