diff options
Diffstat (limited to 'vnext/src/ui/Message.js')
-rw-r--r-- | vnext/src/ui/Message.js | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/vnext/src/ui/Message.js b/vnext/src/ui/Message.js new file mode 100644 index 00000000..eb008bfe --- /dev/null +++ b/vnext/src/ui/Message.js @@ -0,0 +1,142 @@ +import React, { useEffect, useRef } from 'react'; +import PropTypes from 'prop-types'; +import { Link } from 'react-router-dom'; +import moment from 'moment'; + +import { UserType, MessageType } from './Types'; +import Icon from './Icon'; +import Avatar from './Avatar'; +import { UserLink } from './UserInfo'; + +import { format, embedUrls } from '../utils/embed'; + +import './Message.css'; + +export default function Message({ data, visitor, children, ...rest }) { + const isCode = (data.tags || []).indexOf('code') >= 0; + const likesSummary = data.likes ? `${data.likes}` : 'Recommend'; + const commentsSummary = data.replies ? `${data.replies}` : 'Comment'; + const embedRef = useRef(); + const msgRef = useRef(); + useEffect(() => { + if (msgRef.current) { + embedUrls(msgRef.current.querySelectorAll('a'), embedRef.current); + if (!embedRef.current.hasChildNodes()) { + embedRef.current.style.display = 'none'; + } + } + }, []); + return ( + <div className="msg-cont"> + <Recommendations forMessage={data} /> + <header className="h"> + <Avatar user={data.user}> + <div className="msg-ts"> + <Link to={{ pathname: `/${data.user.uname}/${data.mid}`, state: { msg: data } }}> + <time dateTime={data.timestamp} + title={moment.utc(data.timestamp).local().format('lll')}> + {moment.utc(data.timestamp).fromNow()} + </time> + </Link> + </div> + </Avatar> + <TagsList user={data.user} data={data.tags || []} /> + </header> + { + data.body && + <div className="msg-txt" ref={msgRef}> + <MessageContainer isCode={isCode} data={{ __html: format(data.body, data.mid, isCode) }} /> + </div> + } + { + data.photo && + <div className="msg-media"> + <a href={`//i.juick.com/p/${data.mid}.${data.attach}`} data-fname={`${data.mid}.${data.attach}`}> + <img src={`//i.juick.com/photos-512/${data.mid}.${data.attach}`} alt="" /> + </a> + </div> + } + <div className="embedContainer" ref={embedRef} /> + <nav className="l"> + {visitor.uid === data.user.uid ? ( + <Link to={{ pathname: `/${data.user.uname}/${data.mid}` }} className="a-like msg-button"> + <Icon name="ei-heart" size="s" /> + <span>{likesSummary}</span> + </Link> + ) : visitor.uid > 0 ? ( + <Link to={{ pathname: '/post', search: `?body=!+%23${data.mid}` }} className="a-like msg-button"> + <Icon name="ei-heart" size="s" /> + <span>{likesSummary}</span> + </Link> + ) : ( + <a href="/login" className="a-login msg-button"> + <Icon name="ei-heart" size="s" /> + <span>{likesSummary}</span> + </a> + )} + {!data.ReadOnly | (visitor.uid === data.user.uid) && ( + <> + <Link to={{ pathname: `/${data.user.uname}/${data.mid}`, state: { msg: data } }} className="a-comment msg-button"> + <Icon name="ei-comment" size="s" /> + <span>{commentsSummary}</span> + </Link> + </> + )} + </nav> + {children} + </div> + ); +} + +function MessageContainer({ isCode, data }) { + return isCode ? (<pre dangerouslySetInnerHTML={data} />) : (<p dangerouslySetInnerHTML={data} />); +} + +function Tags({ data, user, ...rest }) { + return data.length > 0 && ( + <div className="msg-tags"> + { + data.map(tag => { + return (<Link key={tag} to={{ pathname: `/${user.uname}`, search: `?tag=${tag}` }} title={tag}>{tag}</Link>); + }).reduce((prev, curr) => [prev, ', ', curr]) + } + </div> + ); +} + +const TagsList = React.memo(Tags); + +function Recommends({ forMessage, ...rest }) { + const { likes, recommendations } = forMessage; + return recommendations && recommendations.length > 0 && ( + <div className="msg-recomms">{'♡ by '} + { + recommendations.map(it => ( + <UserLink key={it.uri || it.uid} user={it} /> + )).reduce((prev, curr) => [prev, ', ', curr]) + } + { + likes > recommendations.length && (<span> and {likes - recommendations.length} others</span>) + } + </div> + ) || null; +} + +const Recommendations = React.memo(Recommends); + +Message.propTypes = { + data: MessageType, + visitor: UserType.isRequired, + children: PropTypes.node +}; + +MessageContainer.propTypes = { + isCode: PropTypes.bool.isRequired, + data: PropTypes.object.isRequired +}; + +Tags.propTypes = { + user: UserType.isRequired, + data: PropTypes.array +}; + |