diff options
Diffstat (limited to 'vnext/src/ui/Comment.js')
-rw-r--r-- | vnext/src/ui/Comment.js | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/vnext/src/ui/Comment.js b/vnext/src/ui/Comment.js new file mode 100644 index 00000000..756b3487 --- /dev/null +++ b/vnext/src/ui/Comment.js @@ -0,0 +1,124 @@ +import { useEffect, useRef, useState } from 'react'; + +import Avatar from './Avatar'; +import { UserLink } from './UserInfo'; +import Button from './Button'; +import defaultAvatar from '../assets/av-96.png'; + +import MessageInput from './MessageInput'; +import { fetchUserUri } from '../api'; +import { chatItemStyle } from './helpers/BubbleStyle'; +import { format, embedUrls } from '../utils/embed'; + +let isMounted; + +/** + * @param {{ + msg: import('../api').Message, + draft: string, + visitor: import('../api').User, + active: number, + setActive: function, + onStartEditing: function, + postComment: function + }} props + */ +export default function Comment({ msg, draft, visitor, active, setActive, onStartEditing, postComment }) { + const embedRef = useRef(); + const msgRef = useRef(); + const [author, setAuthor] = useState(msg.user); + useEffect(() => { + if (msgRef.current) { + embedUrls(msgRef.current.querySelectorAll('a'), embedRef.current); + if (!embedRef.current.hasChildNodes()) { + embedRef.current.style.display = 'none'; + } + } + }, []); + const userRef = useRef(author); + useEffect(() => { + isMounted = true; + if (userRef.current.uri) { + fetchUserUri(userRef.current.uri).then(remote_user => { + if (isMounted) { + setAuthor({ + uid: 0, + uname: remote_user.preferredUsername, + avatar: remote_user.icon && remote_user.icon.url, + uri: author.uri + }); + } + }).catch(e => { + setAuthor({ + uid: 0, + uname: userRef.current.uri, + uri: author.uri, + avatar: defaultAvatar + }); + }); + } + return () => { + isMounted = false; + }; + }, [author.uri, msg.user]); + return ( + <div style={chatItemStyle(visitor, msg)}> + <div className="msg-header"> + <Avatar user={author} link={author.uri}> + <div className="msg-ts"> + {msg.replyto > 0 && + ( + <UserLink user={msg.to} /> + )} + </div> + </Avatar> + </div> + { + msg.body && + <div className={visitor.uid === msg.user.uid ? 'msg-bubble msg-bubble-my' : 'msg-bubble'}> + <div ref={msgRef}> + <p dangerouslySetInnerHTML={{ __html: format(msg.body, msg.mid.toString(), (msg.tags || []).indexOf('code') >= 0) }} /> + </div> + </div> + } + { + msg.photo && + <div className="msg-media"> + <a href={`//i.juick.com/p/${msg.mid}-${msg.rid}.${msg.attach}`} data-fname={`${msg.mid}-${msg.rid}.${msg.attach}`}> + <img src={`//i.juick.com/photos-512/${msg.mid}-${msg.rid}.${msg.attach}`} alt="" /> + </a> + </div> + } + <div className="embedContainer" ref={embedRef} /> + { + active === msg.rid && <MessageInput data={msg} text={draft || ''} onSend={postComment}>Write a comment...</MessageInput> + } + <div className="msg-links"> + { + visitor.uid > 0 ? ( + <> + {active === msg.rid || <span style={linkStyle} onClick={() => setActive(msg.rid)}>Reply</span>} + { + visitor.uid == msg.user.uid && + <> + <span> · </span> + <span style={linkStyle} onClick={() => onStartEditing(msg)}>Edit</span> + </> + } + </> + ) : ( + <> + <span> · </span>{active === msg.rid || <Button>Reply</Button>} + </> + ) + } + </div> + </div> + ); +} +/** + * @type React.CSSProperties + */ + const linkStyle = { + cursor: 'pointer' +}; |