feat: added markdown styles in messaages

feat: added option to show / hide agent name
This commit is contained in:
canove
2020-11-29 13:37:55 -03:00
parent 36098bf12d
commit ce1a0f48e2
10 changed files with 106 additions and 32 deletions

View File

@@ -15,11 +15,11 @@
"formik": "^2.2.0", "formik": "^2.2.0",
"i18next": "^19.8.2", "i18next": "^19.8.2",
"i18next-browser-languagedetector": "^6.0.1", "i18next-browser-languagedetector": "^6.0.1",
"markdown-to-jsx": "^7.1.0",
"mic-recorder-to-mp3": "^2.2.2", "mic-recorder-to-mp3": "^2.2.2",
"qrcode.react": "^1.0.0", "qrcode.react": "^1.0.0",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-linkify": "^1.0.0-alpha",
"react-modal-image": "^2.5.0", "react-modal-image": "^2.5.0",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "3.4.3", "react-scripts": "3.4.3",

View File

@@ -12,9 +12,10 @@ import Button from "@material-ui/core/Button";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n";
import LinkifyWithTargetBlank from "../LinkifyWithTargetBlank";
import ContactModal from "../ContactModal"; import ContactModal from "../ContactModal";
import ContactDrawerSkeleton from "../ContactDrawerSkeleton"; import ContactDrawerSkeleton from "../ContactDrawerSkeleton";
import MarkdownWrapper from "../MarkdownWrapper";
const drawerWidth = 320; const drawerWidth = 320;
@@ -149,11 +150,9 @@ const ContactDrawer = ({ open, handleDrawerClose, contact, loading }) => {
className={classes.contactExtraInfo} className={classes.contactExtraInfo}
> >
<InputLabel>{info.name}</InputLabel> <InputLabel>{info.name}</InputLabel>
<LinkifyWithTargetBlank> <Typography component="div" noWrap style={{ paddingTop: 2 }}>
<Typography noWrap style={{ paddingTop: 2 }}> <MarkdownWrapper>{info.value}</MarkdownWrapper>
{info.value} </Typography>
</Typography>
</LinkifyWithTargetBlank>
</Paper> </Paper>
))} ))}
</Paper> </Paper>

View File

@@ -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;

View 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;

View File

@@ -20,6 +20,7 @@ import ClearIcon from "@material-ui/icons/Clear";
import MicIcon from "@material-ui/icons/Mic"; import MicIcon from "@material-ui/icons/Mic";
import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline"; import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline";
import HighlightOffIcon from "@material-ui/icons/HighlightOff"; import HighlightOffIcon from "@material-ui/icons/HighlightOff";
import { FormControlLabel, Switch } from "@material-ui/core";
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n";
import api from "../../services/api"; import api from "../../services/api";
@@ -175,6 +176,17 @@ const MessageInput = ({ ticketStatus }) => {
ReplyMessageContext 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(() => { useEffect(() => {
inputRef.current.focus(); inputRef.current.focus();
}, [replyingMessage]); }, [replyingMessage]);
@@ -251,7 +263,9 @@ const MessageInput = ({ ticketStatus }) => {
read: 1, read: 1,
fromMe: true, fromMe: true,
mediaUrl: "", mediaUrl: "",
body: `${username}: ${inputMessage.trim()}`, body: signMessage
? `*${username}:*\n${inputMessage.trim()}`
: inputMessage.trim(),
quotedMsg: replyingMessage, quotedMsg: replyingMessage,
}; };
try { try {
@@ -433,6 +447,22 @@ const MessageInput = ({ ticketStatus }) => {
<AttachFileIcon className={classes.sendMessageIcons} /> <AttachFileIcon className={classes.sendMessageIcons} />
</IconButton> </IconButton>
</label> </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}> <div className={classes.messageInputWrapper}>
<InputBase <InputBase
inputRef={input => { inputRef={input => {

View File

@@ -21,7 +21,7 @@ import {
GetApp, GetApp,
} from "@material-ui/icons"; } from "@material-ui/icons";
import LinkifyWithTargetBlank from "../LinkifyWithTargetBlank"; import MarkdownWrapper from "../MarkdownWrapper";
import ModalImageCors from "../ModalImageCors"; import ModalImageCors from "../ModalImageCors";
import MessageOptionsMenu from "../MessageOptionsMenu"; import MessageOptionsMenu from "../MessageOptionsMenu";
import whatsBackground from "../../assets/wa-background.png"; import whatsBackground from "../../assets/wa-background.png";
@@ -556,7 +556,7 @@ const MessagesList = ({ ticketId, isGroup, setReplyingMessage }) => {
const viewMessagesList = messagesList.map((message, index) => { const viewMessagesList = messagesList.map((message, index) => {
if (!message.fromMe) { if (!message.fromMe) {
return ( return (
<LinkifyWithTargetBlank key={message.id}> <React.Fragment key={message.id}>
{renderDailyTimestamps(message, index)} {renderDailyTimestamps(message, index)}
{renderMessageDivider(message, index)} {renderMessageDivider(message, index)}
<div className={classes.messageLeft}> <div className={classes.messageLeft}>
@@ -578,17 +578,17 @@ const MessagesList = ({ ticketId, isGroup, setReplyingMessage }) => {
{message.mediaUrl && checkMessageMedia(message)} {message.mediaUrl && checkMessageMedia(message)}
<div className={classes.textContentItem}> <div className={classes.textContentItem}>
{message.quotedMsg && renderQuotedMessage(message)} {message.quotedMsg && renderQuotedMessage(message)}
{message.body} <MarkdownWrapper>{message.body}</MarkdownWrapper>
<span className={classes.timestamp}> <span className={classes.timestamp}>
{format(parseISO(message.createdAt), "HH:mm")} {format(parseISO(message.createdAt), "HH:mm")}
</span> </span>
</div> </div>
</div> </div>
</LinkifyWithTargetBlank> </React.Fragment>
); );
} else { } else {
return ( return (
<LinkifyWithTargetBlank key={message.id}> <React.Fragment key={message.id}>
{renderDailyTimestamps(message, index)} {renderDailyTimestamps(message, index)}
{renderMessageDivider(message, index)} {renderMessageDivider(message, index)}
<div className={classes.messageRight}> <div className={classes.messageRight}>
@@ -616,14 +616,14 @@ const MessagesList = ({ ticketId, isGroup, setReplyingMessage }) => {
/> />
)} )}
{message.quotedMsg && renderQuotedMessage(message)} {message.quotedMsg && renderQuotedMessage(message)}
{message.body} <MarkdownWrapper>{message.body}</MarkdownWrapper>
<span className={classes.timestamp}> <span className={classes.timestamp}>
{format(parseISO(message.createdAt), "HH:mm")} {format(parseISO(message.createdAt), "HH:mm")}
{renderMessageAck(message)} {renderMessageAck(message)}
</span> </span>
</div> </div>
</div> </div>
</LinkifyWithTargetBlank> </React.Fragment>
); );
} }
}); });

View File

@@ -18,6 +18,7 @@ import { i18n } from "../../translate/i18n";
import api from "../../services/api"; import api from "../../services/api";
import ButtonWithSpinner from "../ButtonWithSpinner"; import ButtonWithSpinner from "../ButtonWithSpinner";
import MarkdownWrapper from "../MarkdownWrapper";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
ticket: { ticket: {
@@ -143,6 +144,7 @@ const TicketListItem = ({ ticket }) => {
></Avatar> ></Avatar>
</ListItemAvatar> </ListItemAvatar>
<ListItemText <ListItemText
disableTypography
primary={ primary={
<span className={classes.contactNameWrapper}> <span className={classes.contactNameWrapper}>
<Typography <Typography
@@ -185,7 +187,11 @@ const TicketListItem = ({ ticket }) => {
variant="body2" variant="body2"
color="textSecondary" color="textSecondary"
> >
{ticket.lastMessage || <br />} {ticket.lastMessage ? (
<MarkdownWrapper>{ticket.lastMessage}</MarkdownWrapper>
) : (
<br />
)}
</Typography> </Typography>
<Badge <Badge

View File

@@ -287,6 +287,7 @@ const messages = {
messagesInput: { messagesInput: {
placeholderOpen: "Type a message", placeholderOpen: "Type a message",
placeholderClosed: "Reopen or accept this ticket to send a message.", placeholderClosed: "Reopen or accept this ticket to send a message.",
signMessage: "Sign",
}, },
contactDrawer: { contactDrawer: {
header: "Contact details", header: "Contact details",
@@ -323,6 +324,8 @@ const messages = {
}, },
}, },
backendErrors: { backendErrors: {
ERR_NO_OTHER_WHATSAPP:
"There must be at lest one default WhatsApp connection.",
ERR_NO_DEF_WAPP_FOUND: ERR_NO_DEF_WAPP_FOUND:
"No default WhatsApp found. Check connections page.", "No default WhatsApp found. Check connections page.",
ERR_WAPP_NOT_INITIALIZED: ERR_WAPP_NOT_INITIALIZED:

View File

@@ -292,6 +292,7 @@ const messages = {
placeholderOpen: "Escribe un mensaje", placeholderOpen: "Escribe un mensaje",
placeholderClosed: placeholderClosed:
"Vuelva a abrir o acepte este ticket para enviar un mensaje.", "Vuelva a abrir o acepte este ticket para enviar un mensaje.",
signMessage: "Firmar",
}, },
contactDrawer: { contactDrawer: {
header: "Detalles del contacto", header: "Detalles del contacto",
@@ -329,6 +330,8 @@ const messages = {
}, },
}, },
backendErrors: { backendErrors: {
ERR_NO_OTHER_WHATSAPP:
"Debe haber al menos una conexión de WhatsApp predeterminada.",
ERR_NO_DEF_WAPP_FOUND: ERR_NO_DEF_WAPP_FOUND:
"No se encontró WhatsApp predeterminado. Verifique la página de conexiones.", "No se encontró WhatsApp predeterminado. Verifique la página de conexiones.",
ERR_WAPP_NOT_INITIALIZED: ERR_WAPP_NOT_INITIALIZED:

View File

@@ -290,6 +290,7 @@ const messages = {
placeholderOpen: "Digite uma mensagem", placeholderOpen: "Digite uma mensagem",
placeholderClosed: placeholderClosed:
"Reabra ou aceite esse ticket para enviar uma mensagem.", "Reabra ou aceite esse ticket para enviar uma mensagem.",
signMessage: "Assinar",
}, },
contactDrawer: { contactDrawer: {
header: "Dados do contato", header: "Dados do contato",
@@ -302,8 +303,7 @@ const messages = {
delete: "Deletar", delete: "Deletar",
transfer: "Transferir", transfer: "Transferir",
confirmationModal: { confirmationModal: {
title: "Deletar o ticket #", title: "Deletar o ticket do contato",
titleFrom: "do contato ",
message: message:
"Atenção! Todas as mensagens relacionadas ao ticket serão perdidas.", "Atenção! Todas as mensagens relacionadas ao ticket serão perdidas.",
}, },
@@ -327,6 +327,7 @@ const messages = {
}, },
}, },
backendErrors: { backendErrors: {
ERR_NO_OTHER_WHATSAPP: "Deve haver pelo menos um WhatsApp padrão.",
ERR_NO_DEF_WAPP_FOUND: ERR_NO_DEF_WAPP_FOUND:
"Nenhum WhatsApp padrão encontrado. Verifique a página de conexões.", "Nenhum WhatsApp padrão encontrado. Verifique a página de conexões.",
ERR_WAPP_NOT_INITIALIZED: ERR_WAPP_NOT_INITIALIZED: