aboutsummaryrefslogtreecommitdiff
path: root/vnext/src/ui/Thread.js
blob: b2d1782401039422205615ad9ffd72291d0f5d7f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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 { getMessages, comment, update } from '../api';
import { useVisitor } from './VisitorContext';
/**
 * @type { import('../api').Message }
 */
const emptyMessage = {};

/**
 * @param {{
     connection: EventSource?
  }} props
 */
export default function Thread(props) {
  const location = useLocation();
  const params = useParams();
  const [message, setMessage] = useState((location.state || {}).data || {});
  const [replies, setReplies] = useState([]);
  const [loading, setLoading] = useState(false);
  const [active, setActive] = useState(0);

  const [editing, setEditing] = useState(emptyMessage);
  const [visitor] = useVisitor();
  const [hash] = useState(visitor.hash);
  const { mid } = params;

  let loadReplies = useCallback(() => {
    document.body.scrollTop = 0;
    document.documentElement.scrollTop = 0;
    setReplies([]);
    setLoading(true);
    let params = {
      mid: mid
    };
    params.hash = hash;
    getMessages('/api/thread', params)
      .then(response => {
        let updatedMessage = response.data.shift();
        if (!message.mid) {
          setMessage(updatedMessage);
        }
        setReplies(response.data);
        setLoading(false);
        setActive(0);
      }
      ).catch(ex => {
        console.log(ex);
      });
  }, [hash, message.mid, mid]);
  let onReply = useCallback((json) => {
    const msg = JSON.parse(json.data);
    if (msg.mid == message.mid) {
      setReplies(oldReplies => {
        return [...oldReplies, msg];
      });
    }
  }, [message]);

  let postComment = useCallback((template) => {
    const { mid, rid, body, attach } = template;
    let commentAction = editing.rid ? update(mid, editing.rid, body) : comment(mid, rid, body, attach);
    commentAction.then(() => {
      setEditing(emptyMessage);
      loadReplies();
    })
      .catch(console.log);
  }, [editing.rid, loadReplies]);

  let startEditing = (reply) => {
    setActive(reply.replyto);
    setEditing(reply);
  };

  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]);

  const loaders = Math.min(message.replies || 0, 10);
  return (
    <>
      {
        message.mid ? (
          <Message data={message}>
            {active === (message.rid || 0) && <MessageInput data={message} text={editing.body || ''} onSend={postComment}>Write a comment...</MessageInput>}
          </Message>
        ) : (
          <Spinner />
        )
      }
      {
        message.replies && <ul id="replies">
          {
            !loading ? replies.map((msg) => (
              <li id={msg.rid} key={msg.rid}>
                <Comment msg={msg} draft={msg.rid === editing.replyto ? editing.body : ''} active={active} setActive={setActive} onStartEditing={startEditing} postComment={postComment} />
              </li>
            )) : (
          <>
                {
                  Array(loaders).fill().map((it, i) => <Spinner key={i} />)
                }
              </>
            )
          }
        </ul>
      }
    </>
  );
}