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.html ?
+ :
+
+ }
+ {
+ 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