mirror of
https://github.com/cheveguerra/whaticket-community.git
synced 2026-04-18 19:59:20 +00:00
feat: added markdown styles in messaages
feat: added option to show / hide agent name
This commit is contained in:
@@ -12,9 +12,10 @@ import Button from "@material-ui/core/Button";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
import { i18n } from "../../translate/i18n";
|
||||
import LinkifyWithTargetBlank from "../LinkifyWithTargetBlank";
|
||||
|
||||
import ContactModal from "../ContactModal";
|
||||
import ContactDrawerSkeleton from "../ContactDrawerSkeleton";
|
||||
import MarkdownWrapper from "../MarkdownWrapper";
|
||||
|
||||
const drawerWidth = 320;
|
||||
|
||||
@@ -149,11 +150,9 @@ const ContactDrawer = ({ open, handleDrawerClose, contact, loading }) => {
|
||||
className={classes.contactExtraInfo}
|
||||
>
|
||||
<InputLabel>{info.name}</InputLabel>
|
||||
<LinkifyWithTargetBlank>
|
||||
<Typography noWrap style={{ paddingTop: 2 }}>
|
||||
{info.value}
|
||||
</Typography>
|
||||
</LinkifyWithTargetBlank>
|
||||
<Typography component="div" noWrap style={{ paddingTop: 2 }}>
|
||||
<MarkdownWrapper>{info.value}</MarkdownWrapper>
|
||||
</Typography>
|
||||
</Paper>
|
||||
))}
|
||||
</Paper>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from "react";
|
||||
import Linkify from "react-linkify";
|
||||
|
||||
const componentDecorator = (href, text, key) => (
|
||||
<a href={href} key={key} target="_blank" rel="noopener noreferrer">
|
||||
{text}
|
||||
</a>
|
||||
);
|
||||
|
||||
const LinkifyWithTargetBlank = ({ children }) => {
|
||||
return <Linkify componentDecorator={componentDecorator}>{children}</Linkify>;
|
||||
};
|
||||
|
||||
export default LinkifyWithTargetBlank;
|
||||
46
frontend/src/components/MarkdownWrapper/index.js
Normal file
46
frontend/src/components/MarkdownWrapper/index.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
import Markdown from "markdown-to-jsx";
|
||||
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
markdownP: {
|
||||
marginBlockStart: 0,
|
||||
marginBlockEnd: 0,
|
||||
},
|
||||
}));
|
||||
|
||||
const CustomLink = ({ children, ...props }) => (
|
||||
<a {...props} target="_blank" rel="noopener noreferrer">
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
|
||||
const MarkdownWrapper = ({ children }) => {
|
||||
const classes = useStyles();
|
||||
const boldRegex = /\*(.*?)\*/g;
|
||||
|
||||
if (children && boldRegex.test(children)) {
|
||||
children = children.replace(boldRegex, "**$1**");
|
||||
}
|
||||
|
||||
return (
|
||||
<Markdown
|
||||
options={{
|
||||
disableParsingRawHTML: true,
|
||||
overrides: {
|
||||
a: { component: CustomLink },
|
||||
p: {
|
||||
props: {
|
||||
className: classes.markdownP,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Markdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownWrapper;
|
||||
@@ -20,6 +20,7 @@ import ClearIcon from "@material-ui/icons/Clear";
|
||||
import MicIcon from "@material-ui/icons/Mic";
|
||||
import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline";
|
||||
import HighlightOffIcon from "@material-ui/icons/HighlightOff";
|
||||
import { FormControlLabel, Switch } from "@material-ui/core";
|
||||
|
||||
import { i18n } from "../../translate/i18n";
|
||||
import api from "../../services/api";
|
||||
@@ -175,6 +176,17 @@ const MessageInput = ({ ticketStatus }) => {
|
||||
ReplyMessageContext
|
||||
);
|
||||
|
||||
const [signMessage, setSignMessage] = useState(false);
|
||||
const storedSignOption = localStorage.getItem("signOption");
|
||||
|
||||
useEffect(() => {
|
||||
if (storedSignOption === "true") setSignMessage(true);
|
||||
}, [storedSignOption]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem("signOption", signMessage);
|
||||
}, [signMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
inputRef.current.focus();
|
||||
}, [replyingMessage]);
|
||||
@@ -251,7 +263,9 @@ const MessageInput = ({ ticketStatus }) => {
|
||||
read: 1,
|
||||
fromMe: true,
|
||||
mediaUrl: "",
|
||||
body: `${username}: ${inputMessage.trim()}`,
|
||||
body: signMessage
|
||||
? `*${username}:*\n${inputMessage.trim()}`
|
||||
: inputMessage.trim(),
|
||||
quotedMsg: replyingMessage,
|
||||
};
|
||||
try {
|
||||
@@ -433,6 +447,22 @@ const MessageInput = ({ ticketStatus }) => {
|
||||
<AttachFileIcon className={classes.sendMessageIcons} />
|
||||
</IconButton>
|
||||
</label>
|
||||
<FormControlLabel
|
||||
style={{ marginRight: 7, color: "gray" }}
|
||||
label={i18n.t("messagesInput.signMessage")}
|
||||
labelPlacement="start"
|
||||
control={
|
||||
<Switch
|
||||
size="small"
|
||||
checked={signMessage}
|
||||
onChange={() => {
|
||||
setSignMessage(prevState => !prevState);
|
||||
}}
|
||||
name="showAllTickets"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<div className={classes.messageInputWrapper}>
|
||||
<InputBase
|
||||
inputRef={input => {
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
GetApp,
|
||||
} from "@material-ui/icons";
|
||||
|
||||
import LinkifyWithTargetBlank from "../LinkifyWithTargetBlank";
|
||||
import MarkdownWrapper from "../MarkdownWrapper";
|
||||
import ModalImageCors from "../ModalImageCors";
|
||||
import MessageOptionsMenu from "../MessageOptionsMenu";
|
||||
import whatsBackground from "../../assets/wa-background.png";
|
||||
@@ -556,7 +556,7 @@ const MessagesList = ({ ticketId, isGroup, setReplyingMessage }) => {
|
||||
const viewMessagesList = messagesList.map((message, index) => {
|
||||
if (!message.fromMe) {
|
||||
return (
|
||||
<LinkifyWithTargetBlank key={message.id}>
|
||||
<React.Fragment key={message.id}>
|
||||
{renderDailyTimestamps(message, index)}
|
||||
{renderMessageDivider(message, index)}
|
||||
<div className={classes.messageLeft}>
|
||||
@@ -578,17 +578,17 @@ const MessagesList = ({ ticketId, isGroup, setReplyingMessage }) => {
|
||||
{message.mediaUrl && checkMessageMedia(message)}
|
||||
<div className={classes.textContentItem}>
|
||||
{message.quotedMsg && renderQuotedMessage(message)}
|
||||
{message.body}
|
||||
<MarkdownWrapper>{message.body}</MarkdownWrapper>
|
||||
<span className={classes.timestamp}>
|
||||
{format(parseISO(message.createdAt), "HH:mm")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</LinkifyWithTargetBlank>
|
||||
</React.Fragment>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<LinkifyWithTargetBlank key={message.id}>
|
||||
<React.Fragment key={message.id}>
|
||||
{renderDailyTimestamps(message, index)}
|
||||
{renderMessageDivider(message, index)}
|
||||
<div className={classes.messageRight}>
|
||||
@@ -616,14 +616,14 @@ const MessagesList = ({ ticketId, isGroup, setReplyingMessage }) => {
|
||||
/>
|
||||
)}
|
||||
{message.quotedMsg && renderQuotedMessage(message)}
|
||||
{message.body}
|
||||
<MarkdownWrapper>{message.body}</MarkdownWrapper>
|
||||
<span className={classes.timestamp}>
|
||||
{format(parseISO(message.createdAt), "HH:mm")}
|
||||
{renderMessageAck(message)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</LinkifyWithTargetBlank>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@ import { i18n } from "../../translate/i18n";
|
||||
|
||||
import api from "../../services/api";
|
||||
import ButtonWithSpinner from "../ButtonWithSpinner";
|
||||
import MarkdownWrapper from "../MarkdownWrapper";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
ticket: {
|
||||
@@ -143,6 +144,7 @@ const TicketListItem = ({ ticket }) => {
|
||||
></Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
disableTypography
|
||||
primary={
|
||||
<span className={classes.contactNameWrapper}>
|
||||
<Typography
|
||||
@@ -185,7 +187,11 @@ const TicketListItem = ({ ticket }) => {
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
>
|
||||
{ticket.lastMessage || <br />}
|
||||
{ticket.lastMessage ? (
|
||||
<MarkdownWrapper>{ticket.lastMessage}</MarkdownWrapper>
|
||||
) : (
|
||||
<br />
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<Badge
|
||||
|
||||
Reference in New Issue
Block a user