From f470636a70943a8ecad8bddc791a1c2dddd28e1e Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Sat, 4 May 2019 21:13:12 +0300 Subject: Components -> UI --- vnext/src/ui/Thread.js | 181 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 vnext/src/ui/Thread.js (limited to 'vnext/src/ui/Thread.js') diff --git a/vnext/src/ui/Thread.js b/vnext/src/ui/Thread.js new file mode 100644 index 00000000..e7ccb032 --- /dev/null +++ b/vnext/src/ui/Thread.js @@ -0,0 +1,181 @@ +import React, { useEffect, useState, useRef, useCallback } from 'react'; +import PropTypes from 'prop-types'; + +import ReactRouterPropTypes from 'react-router-prop-types'; +import { UserType, MessageType } from './Types'; + +import Message from './Message'; +import MessageInput from './MessageInput'; +import Spinner from './Spinner'; +import Avatar from './Avatar'; +import Button from './Button'; + +import { format, embedUrls } from '../utils/embed'; + +import { getMessages, comment, markReadTracker } from '../api'; + +function Comment({ msg, visitor, active, setActive, postComment }) { + 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 ( +
+
+ +
+ {msg.replyto > 0 && + ( + {msg.to.uname}  + )} +
+
+
+ { + msg.html ?
+ : +
+

= 0) }} /> +

+ } + { + msg.photo && +
+ + + +
+ } +
+
+ { + visitor.uid > 0 ? ( + <> + {active === msg.rid || setActive(msg.rid)}>Reply} + {active === msg.rid && Write a comment...} + + ) : ( + <> +  · {active === msg.rid || } + + ) + } +
+
+ ); +} + +Comment.propTypes = { + msg: MessageType.isRequired, + visitor: UserType.isRequired, + active: PropTypes.number.isRequired, + setActive: PropTypes.func.isRequired, + postComment: PropTypes.func.isRequired +}; + +export default function Thread(props) { + const [message, setMessage] = useState((props.location.state || {}).msg || {}); + const [replies, setReplies] = useState([]); + const [loading, setLoading] = useState(false); + const [active, setActive] = useState(0); + useEffect(() => { + setActive(0); + loadReplies(); + }, [loadReplies]); + useEffect(() => { + if (props.connection.addEventListener && message.mid) { + props.connection.addEventListener('msg', onReply); + } + return () => { + if (props.connection.removeEventListener && message.mid) { + props.connection.removeEventListener('msg', onReply); + } + }; + }, [props.connection, message.mid, onReply]); + + let loadReplies = useCallback(() => { + document.body.scrollTop = 0; + document.documentElement.scrollTop = 0; + + setReplies([]); + setLoading(true); + const { mid } = props.match.params; + let params = { + mid: mid + }; + if (props.visitor && props.visitor.hash) { + params.hash = props.visitor.hash; + } + getMessages('/api/thread', params) + .then(response => { + setMessage(response.data.shift()); + setReplies(response.data); + setLoading(false); + setActive(0); + } + ).catch(ex => { + console.log(ex); + }); + }, [props.visitor, props.match.params]); + let onReply = useCallback((json) => { + const msg = JSON.parse(json.data); + if (msg.mid == message.mid) { + setReplies(oldReplies => { + return [...oldReplies, msg]; + }); + } + }, [message]); + let postComment = (template) => { + const { mid, rid, body, attach } = template; + comment(mid, rid, body, attach).then(res => { + loadReplies(); + }) + .catch(console.log); + }; + + const loaders = Math.min(message.replies || 0, 10); + return ( + <> + { + message.mid ? ( + + {active === (message.rid || 0) && Write a comment...} + + ) : ( + + ) + } +
    + { + !loading ? replies.map((msg) => ( +
  • + +
  • + )) : ( + <> + {Array(loaders).fill().map((it, i) => )} + + ) + } +
+ + ); +} + +const linkStyle = { + cursor: 'pointer' +}; + +Thread.propTypes = { + location: ReactRouterPropTypes.location, + history: ReactRouterPropTypes.history, + match: ReactRouterPropTypes.match, + visitor: UserType.isRequired, + connection: PropTypes.object.isRequired +}; -- cgit v1.2.3