aboutsummaryrefslogtreecommitdiff
path: root/vnext/src/ui/Comment.js
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2022-10-28 14:29:54 +0300
committerGravatar Vitaly Takmazov2023-01-13 10:37:58 +0300
commit6e2f663ef80a784dd9b51fae76b17c042bbb46ee (patch)
tree474b07af4187301cfedf2d6abb5c34bb72fd87bf /vnext/src/ui/Comment.js
parentffb5d1beae77661b505a110e717c88aa44e1c912 (diff)
Split `Thread` and `Comment` components
Diffstat (limited to 'vnext/src/ui/Comment.js')
-rw-r--r--vnext/src/ui/Comment.js124
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>&nbsp;&middot;&nbsp;</span>
+ <span style={linkStyle} onClick={() => onStartEditing(msg)}>Edit</span>
+ </>
+ }
+ </>
+ ) : (
+ <>
+ <span>&nbsp;&middot;&nbsp;</span>{active === msg.rid || <Button>Reply</Button>}
+ </>
+ )
+ }
+ </div>
+ </div>
+ );
+}
+/**
+ * @type React.CSSProperties
+ */
+ const linkStyle = {
+ cursor: 'pointer'
+};