2022-08-27 21:25:38 -05:00
|
|
|
import { HTMLAttributes } from 'react'
|
|
|
|
import Box from '@mui/material/Box'
|
2022-10-29 14:20:56 -05:00
|
|
|
import Tooltip from '@mui/material/Tooltip'
|
2022-08-27 21:25:38 -05:00
|
|
|
import Typography, { TypographyProps } from '@mui/material/Typography'
|
|
|
|
import Link, { LinkProps } from '@mui/material/Link'
|
2022-08-28 18:19:14 -05:00
|
|
|
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
|
|
|
import { materialDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
2022-09-19 11:51:49 -05:00
|
|
|
|
|
|
|
// These imports need to be ts-ignored to prevent spurious errors that look
|
|
|
|
// like this:
|
|
|
|
//
|
|
|
|
// Module 'react-markdown' cannot be imported using this construct. The
|
|
|
|
// specifier only resolves to an ES module, which cannot be imported
|
|
|
|
// synchronously. Use dynamic import instead. (tsserver 1471)
|
|
|
|
//
|
|
|
|
// @ts-ignore
|
|
|
|
import Markdown from 'react-markdown'
|
|
|
|
// @ts-ignore
|
|
|
|
import { CodeProps } from 'react-markdown/lib/ast-to-react'
|
|
|
|
// @ts-ignore
|
2022-09-08 09:37:38 -05:00
|
|
|
import remarkGfm from 'remark-gfm'
|
2022-08-23 21:46:07 -05:00
|
|
|
|
2022-11-28 21:18:41 -06:00
|
|
|
import {
|
|
|
|
InlineMedia as I_InlineMedia,
|
|
|
|
Message as IMessage,
|
|
|
|
isMessageReceived,
|
|
|
|
isInlineMedia,
|
|
|
|
} from 'models/chat'
|
2022-09-04 09:39:18 -05:00
|
|
|
import { PeerNameDisplay } from 'components/PeerNameDisplay'
|
2022-08-23 21:46:07 -05:00
|
|
|
|
2022-11-28 21:18:41 -06:00
|
|
|
import { InlineMedia } from './InlineMedia'
|
2022-10-01 11:46:46 -05:00
|
|
|
import './Message.sass'
|
|
|
|
|
2022-08-23 21:46:07 -05:00
|
|
|
export interface MessageProps {
|
2022-11-28 21:18:41 -06:00
|
|
|
message: IMessage | I_InlineMedia
|
2022-10-03 21:29:28 -05:00
|
|
|
showAuthor: boolean
|
2022-08-23 21:46:07 -05:00
|
|
|
userId: string
|
|
|
|
}
|
|
|
|
|
2022-08-27 21:25:38 -05:00
|
|
|
const typographyFactory =
|
|
|
|
(overrides: TypographyProps) => (args: HTMLAttributes<HTMLElement>) => {
|
|
|
|
return <Typography {...args} {...overrides} />
|
|
|
|
}
|
|
|
|
|
|
|
|
const linkFactory =
|
|
|
|
(overrides: LinkProps) => (args: HTMLAttributes<HTMLElement>) => {
|
|
|
|
return <Link {...args} {...overrides} />
|
|
|
|
}
|
|
|
|
|
|
|
|
const componentMap = {
|
|
|
|
h1: typographyFactory({ variant: 'h1' }),
|
|
|
|
h2: typographyFactory({ variant: 'h2' }),
|
|
|
|
h3: typographyFactory({ variant: 'h3' }),
|
|
|
|
h4: typographyFactory({ variant: 'h4' }),
|
|
|
|
h5: typographyFactory({ variant: 'h5' }),
|
|
|
|
h6: typographyFactory({ variant: 'h6' }),
|
|
|
|
p: typographyFactory({ variant: 'body1' }),
|
|
|
|
a: linkFactory({
|
|
|
|
variant: 'body1',
|
|
|
|
underline: 'always',
|
2022-09-08 09:37:38 -05:00
|
|
|
color: 'primary.contrastText',
|
2022-08-27 21:25:38 -05:00
|
|
|
}),
|
2022-08-28 18:19:14 -05:00
|
|
|
// https://github.com/remarkjs/react-markdown#use-custom-components-syntax-highlight
|
|
|
|
code({ node, inline, className, children, style, ...props }: CodeProps) {
|
|
|
|
const match = /language-(\w+)/.exec(className || '')
|
|
|
|
return !inline && match ? (
|
|
|
|
<SyntaxHighlighter
|
|
|
|
children={String(children).replace(/\n$/, '')}
|
|
|
|
language={match[1]}
|
|
|
|
style={materialDark}
|
|
|
|
PreTag="div"
|
|
|
|
{...props}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
<code className={className} {...props}>
|
|
|
|
{children}
|
|
|
|
</code>
|
|
|
|
)
|
|
|
|
},
|
2022-08-27 21:25:38 -05:00
|
|
|
}
|
|
|
|
|
2022-10-29 14:20:56 -05:00
|
|
|
const spaceNeededForSideDateTooltip = 850
|
|
|
|
|
2022-10-03 21:29:28 -05:00
|
|
|
export const Message = ({ message, showAuthor, userId }: MessageProps) => {
|
2022-08-23 21:46:07 -05:00
|
|
|
let backgroundColor: string
|
|
|
|
|
|
|
|
if (message.authorId === userId) {
|
|
|
|
backgroundColor = isMessageReceived(message)
|
2022-09-04 11:04:54 -05:00
|
|
|
? 'primary.main'
|
2022-09-02 09:49:00 -05:00
|
|
|
: 'primary.light'
|
2022-08-23 21:46:07 -05:00
|
|
|
} else {
|
2022-09-04 11:04:54 -05:00
|
|
|
backgroundColor = 'secondary.main'
|
2022-08-23 21:46:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2022-08-27 21:38:08 -05:00
|
|
|
<Box className="Message">
|
2022-10-03 21:29:28 -05:00
|
|
|
{showAuthor && (
|
|
|
|
<Typography
|
|
|
|
variant="caption"
|
|
|
|
display="block"
|
|
|
|
sx={{
|
|
|
|
textAlign: message.authorId === userId ? 'right' : 'left',
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<PeerNameDisplay>{message.authorId}</PeerNameDisplay>
|
|
|
|
</Typography>
|
|
|
|
)}
|
2022-10-29 14:20:56 -05:00
|
|
|
<Tooltip
|
|
|
|
placement={
|
|
|
|
window.innerWidth >= spaceNeededForSideDateTooltip ? 'left' : 'top'
|
|
|
|
}
|
|
|
|
title={String(
|
|
|
|
Intl.DateTimeFormat(undefined, {
|
|
|
|
dateStyle: 'short',
|
|
|
|
timeStyle: 'short',
|
|
|
|
}).format(message.timeSent)
|
|
|
|
)}
|
2022-08-23 21:46:07 -05:00
|
|
|
>
|
2022-10-29 14:20:56 -05:00
|
|
|
<Box
|
|
|
|
sx={{
|
|
|
|
color: 'primary.contrastText',
|
|
|
|
backgroundColor,
|
|
|
|
margin: 0.5,
|
|
|
|
padding: '0.5em 0.75em',
|
|
|
|
borderRadius: 6,
|
|
|
|
float: message.authorId === userId ? 'right' : 'left',
|
|
|
|
transition: 'background-color 1s',
|
|
|
|
wordBreak: 'break-word',
|
|
|
|
}}
|
|
|
|
maxWidth="85%"
|
2022-09-08 09:37:38 -05:00
|
|
|
>
|
2022-11-28 21:18:41 -06:00
|
|
|
{isInlineMedia(message) ? (
|
|
|
|
<InlineMedia magnetURI={message.magnetURI} />
|
|
|
|
) : (
|
|
|
|
<Markdown
|
|
|
|
components={componentMap}
|
|
|
|
remarkPlugins={[remarkGfm]}
|
|
|
|
linkTarget="_blank"
|
|
|
|
>
|
|
|
|
{message.text}
|
|
|
|
</Markdown>
|
|
|
|
)}
|
2022-10-29 14:20:56 -05:00
|
|
|
</Box>
|
|
|
|
</Tooltip>
|
2022-08-27 21:38:08 -05:00
|
|
|
</Box>
|
2022-08-23 21:46:07 -05:00
|
|
|
)
|
|
|
|
}
|