aboutsummaryrefslogtreecommitdiff
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
parentffb5d1beae77661b505a110e717c88aa44e1c912 (diff)
Split `Thread` and `Comment` components
-rw-r--r--vnext/src/ui/Comment.js124
-rw-r--r--vnext/src/ui/Thread.js127
2 files changed, 127 insertions, 124 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'
+};
diff --git a/vnext/src/ui/Thread.js b/vnext/src/ui/Thread.js
index 15c169fc..52a2baf1 100644
--- a/vnext/src/ui/Thread.js
+++ b/vnext/src/ui/Thread.js
@@ -1,23 +1,14 @@
-import { useEffect, useState, useRef, useCallback } from 'react';
+import { useEffect, useState, useCallback } from 'react';
import { useLocation, useParams } from 'react-router-dom';
+import Comment from './Comment';
import Message from './Message';
import MessageInput from './MessageInput';
import Spinner from './Spinner';
-import Avatar from './Avatar';
-import { UserLink } from './UserInfo';
-import Button from './Button';
-import defaultAvatar from '../assets/av-96.png';
-import { format, embedUrls } from '../utils/embed';
-
-import { getMessages, comment, update, fetchUserUri } from '../api';
-
-import { chatItemStyle } from './helpers/BubbleStyle';
+import { getMessages, comment, update } from '../api';
import './Thread.css';
-
-let isMounted;
/**
* @type import('../api').Message
*/
@@ -25,111 +16,6 @@ const emptyMessage = {};
/**
* @param {{
- msg: import('../api').Message,
- draft: string,
- visitor: import('../api').User,
- active: number,
- setActive: function,
- onStartEditing: function,
- postComment: function
- }} props
- */
-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>
- );
-}
-
-/**
- * @param {{
visitor: import('../api').SecureUser
connection: EventSource?
}} props
@@ -241,10 +127,3 @@ export default function Thread(props) {
</>
);
}
-
-/**
- * @type React.CSSProperties
- */
-const linkStyle = {
- cursor: 'pointer'
-};