chore: initial commit
This commit is contained in:
114
components/Messages/Message/MessageContent/MessageFile.tsx
Normal file
114
components/Messages/Message/MessageContent/MessageFile.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import prettyBytes from 'pretty-bytes'
|
||||
|
||||
import { useAuthentication } from 'utils/authentication'
|
||||
import { MessageContentProps } from '.'
|
||||
import { Loader } from 'components/design/Loader'
|
||||
import { IconButton } from 'components/design/IconButton'
|
||||
|
||||
export interface FileData {
|
||||
blob: Blob
|
||||
url: string
|
||||
}
|
||||
|
||||
export const MessageFile: React.FC<MessageContentProps> = (props) => {
|
||||
const { authentication } = useAuthentication()
|
||||
const [file, setFile] = useState<FileData | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async (): Promise<void> => {
|
||||
const { data } = await authentication.api.get(props.value, {
|
||||
responseType: 'blob'
|
||||
})
|
||||
const fileURL = URL.createObjectURL(data)
|
||||
setFile({ blob: data, url: fileURL })
|
||||
}
|
||||
fetchData().catch(() => {})
|
||||
}, [])
|
||||
|
||||
if (file == null) {
|
||||
return <Loader />
|
||||
}
|
||||
if (props.mimetype.startsWith('image/')) {
|
||||
return (
|
||||
<>
|
||||
<a href={file.url} target='_blank' rel='noreferrer'>
|
||||
<img src={file.url} />
|
||||
</a>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
img {
|
||||
max-width: 30vw;
|
||||
max-height: 30vw;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
||||
if (props.mimetype.startsWith('audio/')) {
|
||||
return (
|
||||
<audio controls>
|
||||
<source src={file.url} type={props.mimetype} />
|
||||
</audio>
|
||||
)
|
||||
}
|
||||
if (props.mimetype.startsWith('video/')) {
|
||||
return (
|
||||
<>
|
||||
<video controls>
|
||||
<source src={file.url} type={props.mimetype} />
|
||||
</video>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
video {
|
||||
max-width: 250px;
|
||||
max-height: 250px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className='message-file'>
|
||||
<div className='file-informations'>
|
||||
<div className='file-icon'>
|
||||
<img src='/images/svg/icons/file.svg' alt='file' />
|
||||
</div>
|
||||
<div className='file-title'>
|
||||
<div className='file-name'>{file.blob.type}</div>
|
||||
<div className='file-size'>{prettyBytes(file.blob.size)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='download-button'>
|
||||
<a href={file.url} download>
|
||||
<IconButton icon='download' />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.message-file {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.file-informations {
|
||||
display: flex;
|
||||
}
|
||||
.file-title {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.file-size {
|
||||
color: var(--color-tertiary);
|
||||
margin-top: 5px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
62
components/Messages/Message/MessageContent/MessageText.tsx
Normal file
62
components/Messages/Message/MessageContent/MessageText.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { useMemo } from 'react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import gfm from 'remark-gfm'
|
||||
import Tex from '@matejmazur/react-katex'
|
||||
import math from 'remark-math'
|
||||
import 'katex/dist/katex.min.css'
|
||||
|
||||
import { Emoji, emojiPlugin, isStringWithOnlyOneEmoji } from 'components/Emoji'
|
||||
|
||||
export interface MessageTextProps {
|
||||
value: string
|
||||
}
|
||||
|
||||
export const MessageText: React.FC<MessageTextProps> = (props) => {
|
||||
const isMessageWithOnlyOneEmoji = useMemo(() => {
|
||||
return isStringWithOnlyOneEmoji(props.value)
|
||||
}, [props.value])
|
||||
|
||||
if (isMessageWithOnlyOneEmoji) {
|
||||
return (
|
||||
<div className='message-content'>
|
||||
<p>
|
||||
<Emoji value={props.value} size={40} />
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReactMarkdown
|
||||
disallowedTypes={['heading', 'table']}
|
||||
unwrapDisallowed
|
||||
plugins={[[gfm], [emojiPlugin], [math]]}
|
||||
linkTarget='_blank'
|
||||
renderers={{
|
||||
inlineMath: ({ value }) => <Tex math={value} />,
|
||||
math: ({ value }) => <Tex block math={value} />,
|
||||
emoji: ({ value }) => {
|
||||
return <Emoji value={value} size={20} />
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.value}
|
||||
</ReactMarkdown>
|
||||
|
||||
<style jsx global>
|
||||
{`
|
||||
.message-content p {
|
||||
margin: 0;
|
||||
line-height: 30px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.message-content .katex,
|
||||
.message-content .katex-display {
|
||||
text-align: initial;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
40
components/Messages/Message/MessageContent/index.tsx
Normal file
40
components/Messages/Message/MessageContent/index.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { Loader } from 'components/design/Loader'
|
||||
import { MessageType } from 'contexts/Messages'
|
||||
import { MessageFile } from './MessageFile'
|
||||
import { MessageText } from './MessageText'
|
||||
|
||||
export interface MessageContentProps {
|
||||
value: string
|
||||
type: MessageType
|
||||
mimetype: string
|
||||
}
|
||||
|
||||
export const MessageContent: React.FC<MessageContentProps> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<div className='message-content'>
|
||||
{props.type === 'text' ? (
|
||||
<MessageText value={props.value} />
|
||||
) : props.type === 'file' ? (
|
||||
<MessageFile {...props} />
|
||||
) : (
|
||||
<Loader />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.message-content {
|
||||
font-family: 'Roboto', 'Arial', 'sans-serif';
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
position: relative;
|
||||
margin-left: -75px;
|
||||
padding-left: 75px;
|
||||
overflow: hidden;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user