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
|
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';
/**
* @type { import('../api').Message }
*/
const emptyMessage = {};
/**
* @param {{
visitor: import('../api').SecureUser
connection: EventSource?
}} props
*/
export default function Thread(props) {
const location = useLocation();
const params = useParams();
const [message, setMessage] = useState((location.state || {}).msg || {});
const [replies, setReplies] = useState([]);
const [loading, setLoading] = useState(false);
const [active, setActive] = useState(0);
const [editing, setEditing] = useState(emptyMessage);
const [hash] = useState(props.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} visitor={props.visitor}>
{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 : ''} visitor={props.visitor} active={active} setActive={setActive} onStartEditing={startEditing} postComment={postComment} />
</li>
)) : (
<>
{
Array(loaders).fill().map((it, i) => <Spinner key={i} />)
}
</>
)
}
</ul>
}
</>
);
}
|