source: reactapp/src/Components/OpinionTree.js@ af801e3

main
Last change on this file since af801e3 was af801e3, checked in by viktor <viktor@…>, 19 months ago

finished edit/delete/displace opinion/thread from report (react); todo reporting user/opinion/thread interface, public user pages and messaging (springboot)

  • Property mode set to 100644
File size: 12.5 KB
Line 
1import {
2 OpinionCard,
3 OpinionCardContent,
4 OpinionCardContentTime,
5 OpinionReplyCard,
6 OpinionReplyCardContent,
7 OpinionReplyCardContentTime,
8 StyledFontAwesomeIcon,
9 VoteCount,
10} from "./Styled/OpinionCard.style";
11import { solid } from "@fortawesome/fontawesome-svg-core/import.macro";
12import { dateConverter } from "../Util/dateConverter";
13import AuthApi from "../api/AuthApi";
14import { useNavigate } from "react-router-dom";
15import { useContext, useState, useEffect } from "react";
16import JSOG from "jsog";
17import {
18 Modal,
19 ModalContent,
20 ModalClose,
21 ModalHeader,
22 ModalBody,
23 ModalTextarea,
24 ModalFooter,
25} from "../Components/Styled/Modal.style";
26import axios from "../api/axios";
27
28function OpinionTree({ professor }) {
29 var renderedOpinionIds = [];
30 var postCount; // za da ne go pokazuva ispod postot
31
32 const { auth, setAuth } = useContext(AuthApi);
33 let navigate = useNavigate();
34
35 const [replyModalDisplay, setReplyModalDisplay] = useState("none");
36 const [replyContent, setReplyContent] = useState("");
37 const [postForModal, setPostForModal] = useState(null);
38 const [user, setUser] = useState(null);
39 const [loadedUser, setLoadedUser] = useState(false);
40 const [fetchError, setFetchError] = useState(false);
41 const [errorMessage, setErrorMessage] = useState("");
42
43 useEffect(() => {
44 const url = `http://192.168.0.29:8080/secure/currentUser`;
45
46 const fetchUser = async () => {
47 try {
48 const response = await axios.get(url, { withCredentials: true });
49 var cyclicGraph = await response.data;
50 var jsogStructure = JSOG.encode(cyclicGraph);
51 cyclicGraph = JSOG.decode(jsogStructure);
52 setUser(cyclicGraph);
53 setLoadedUser(true);
54 } catch (error) {
55 setFetchError(true);
56 }
57 };
58
59 if (auth) fetchUser();
60 }, []);
61
62 const handleLike = async (post) => {
63 if (auth) {
64 if (
65 loadedUser &&
66 user &&
67 !post.votes.some((e) => e.user.id === user.id)
68 ) {
69 const response = await axios(
70 `http://192.168.0.29:8080/secure/upvoteOpinion/${post.postId}`,
71 {
72 method: "get",
73 withCredentials: true,
74 }
75 );
76 window.location.reload();
77 } else {
78 return;
79 }
80 } else {
81 navigate("/login");
82 }
83 };
84
85 const handleDislike = async (post) => {
86 if (auth) {
87 if (
88 loadedUser &&
89 user &&
90 !post.votes.some((e) => e.user.id === user.id)
91 ) {
92 const response = await axios(
93 `http://192.168.0.29:8080/secure/downvoteOpinion/${post.postId}`,
94 {
95 method: "get",
96 withCredentials: true,
97 }
98 );
99
100 window.location.reload();
101 } else {
102 return;
103 }
104 } else {
105 navigate("/login");
106 }
107 };
108
109 const handleReply = (opinion) => {
110 if (auth) {
111 setReplyModalDisplay("block");
112 setPostForModal(opinion);
113 document.body.style.overflowY = "hidden";
114 } else {
115 navigate("/login");
116 }
117 };
118
119 const handleModalCloseClick = () => {
120 setReplyModalDisplay("none");
121 document.body.style.overflowY = "auto";
122 };
123
124 const handleContentChange = (e) => {
125 setReplyContent(e.target.value);
126 };
127
128 const handleReplySubmit = async (e, postId) => {
129 e.preventDefault();
130
131 if (!replyContent.length < 1) {
132 const response = await axios(
133 `http://192.168.0.29:8080/secure/professor/${professor.professorId}/replyToOpinion/${postId}`,
134 {
135 method: "post",
136 data: {
137 content: replyContent,
138 },
139 withCredentials: true,
140 }
141 );
142 setErrorMessage("");
143 window.location.reload();
144 } else {
145 setErrorMessage("Полето за содржина не смее да биде празно");
146 }
147 };
148
149 function displayChildPosts(child, parentPostAuthorUsername, replyIndent) {
150 if (child == null) return;
151 postCount = renderedOpinionIds.push(child.postId);
152 return (
153 <div key={child.postId}>
154 <OpinionReplyCard indent={replyIndent + "px"}>
155 <OpinionReplyCardContent>
156 <p style={{ fontStyle: "italic", marginBottom: "10px" }}>
157 <a href={"/user/" + child.author.id}>{child.author.username}</a>{" "}
158 му реплицирал на {parentPostAuthorUsername}
159 </p>
160 <p style={{ marginBottom: "10px", maxWidth: "90%" }}>
161 {child.content}
162 </p>
163 {new Date(child.timePosted).setMilliseconds(0) === new Date(child.timeLastEdited).setMilliseconds(0) ? (
164 <OpinionCardContentTime>
165 {dateConverter(
166 new Date(child.timePosted).toString().slice(4, -43)
167 )} <span style={{fontStyle:"normal",color:"blue"}}>#{child.postId}</span>
168 </OpinionCardContentTime>
169 ) : (
170 <OpinionCardContentTime>
171 {dateConverter(
172 new Date(child.timeLastEdited).toString().slice(4, -43)
173 )}{" "} <span style={{fontStyle:"normal",color:"blue"}}>#{child.postId}</span>{" "}
174 (едитирано од модератор)
175 </OpinionCardContentTime>
176 )}
177
178 <div
179 style={{
180 display:
181 !auth || (auth && loadedUser && user.id !== child.author.id)
182 ? "block"
183 : "none",
184 }}
185 >
186 <StyledFontAwesomeIcon
187 icon={solid("thumbs-up")}
188 right={50 + "px"}
189 color={
190 auth && loadedUser && user
191 ? child.votes.some(
192 (e) => e.vote === "UPVOTE" && e.user.id === user.id
193 )
194 ? "green"
195 : "darkgrey"
196 : "darkgrey"
197 }
198 onClick={() => handleLike(child)}
199 />
200
201 <VoteCount right={50 + "px"}>
202 {child.votes.filter((v) => v.vote === "UPVOTE").length}
203 </VoteCount>
204
205 <StyledFontAwesomeIcon
206 icon={solid("thumbs-down")}
207 right={10 + "px"}
208 color={
209 auth && loadedUser && user
210 ? child.votes.some(
211 (e) => e.vote === "DOWNVOTE" && e.user.id === user.id
212 )
213 ? "indianred"
214 : "darkgrey"
215 : "darkgrey"
216 }
217 onClick={() => handleDislike(child)}
218 />
219
220 <VoteCount right={10 + "px"}>
221 {child.votes.filter((v) => v.vote === "DOWNVOTE").length}
222 </VoteCount>
223
224 <StyledFontAwesomeIcon
225 icon={solid("reply")}
226 right={90 + "px"}
227 color="darkgrey"
228 onClick={() => handleReply(child)}
229 />
230 </div>
231 </OpinionReplyCardContent>
232 {child.children.map((childOfChild) =>
233 displayChildPosts(
234 childOfChild,
235 child.author.username,
236 replyIndent + 30
237 )
238 )}
239 </OpinionReplyCard>
240 </div>
241 );
242 }
243
244 return (
245 <div className="opinionTree">
246 {professor.relatedOpinions.map((opinion) => {
247 if (!renderedOpinionIds.includes(opinion.postId)) {
248 postCount = renderedOpinionIds.push(opinion.postId);
249 return (
250 <div key={opinion.postId}>
251 <OpinionCard>
252 <OpinionCardContent>
253 <p style={{ fontStyle: "italic", marginBottom: "10px" }}>
254 <a href={"/user/" + opinion.author.id}>
255 {opinion.author.username}
256 </a>{" "}
257 напишал
258 </p>
259 <p style={{ marginBottom: "10px", maxWidth: "90%" }}>
260 {opinion.content}
261 </p>
262 {new Date(opinion.timePosted).setMilliseconds(0) === new Date(opinion.timeLastEdited).setMilliseconds(0) ? (
263 <OpinionCardContentTime>
264 {dateConverter(
265 new Date(opinion.timePosted).toString().slice(4, -43)
266 )} <span style={{fontStyle:"normal",color:"blue"}}>#{opinion.postId}</span>
267 </OpinionCardContentTime>
268 ) : (
269 <OpinionCardContentTime>
270 {dateConverter(
271 new Date(opinion.timeLastEdited)
272 .toString()
273 .slice(4, -43)
274 )}{" "} <span style={{fontStyle:"normal",color:"blue"}}>#{opinion.postId}</span>{" "}
275 (едитирано од модератор)
276 </OpinionCardContentTime>
277 )}
278
279 <div
280 style={{
281 display:
282 !auth ||
283 (auth && loadedUser && user.id !== opinion.author.id)
284 ? "block"
285 : "none",
286 }}
287 >
288 <StyledFontAwesomeIcon
289 icon={solid("thumbs-up")}
290 right={50 + "px"}
291 color={
292 auth && loadedUser && user
293 ? opinion.votes.some(
294 (e) =>
295 e.vote === "UPVOTE" && e.user.id === user.id
296 )
297 ? "green"
298 : "darkgrey"
299 : "darkgrey"
300 }
301 onClick={() => handleLike(opinion)}
302 />
303
304 <VoteCount right={50 + "px"}>
305 {opinion.votes.filter((v) => v.vote === "UPVOTE").length}
306 </VoteCount>
307
308 <StyledFontAwesomeIcon
309 icon={solid("thumbs-down")}
310 right={10 + "px"}
311 color={
312 auth && loadedUser && user
313 ? opinion.votes.some(
314 (e) =>
315 e.vote === "DOWNVOTE" && e.user.id === user.id
316 )
317 ? "indianred"
318 : "darkgrey"
319 : "darkgrey"
320 }
321 onClick={() => handleDislike(opinion)}
322 />
323
324 <VoteCount right={10 + "px"}>
325 {
326 opinion.votes.filter((v) => v.vote === "DOWNVOTE")
327 .length
328 }
329 </VoteCount>
330
331 <StyledFontAwesomeIcon
332 icon={solid("reply")}
333 right={90 + "px"}
334 color="darkgrey"
335 onClick={() => handleReply(opinion)}
336 />
337 </div>
338 </OpinionCardContent>
339 {opinion.children.map((child) =>
340 displayChildPosts(child, opinion.author.username, 30)
341 )}
342 </OpinionCard>
343 </div>
344 );
345 }
346 return null;
347 })}
348 {postForModal && (
349 <Modal display={replyModalDisplay}>
350 <ModalContent>
351 <ModalHeader>
352 <ModalClose onClick={handleModalCloseClick}>&times;</ModalClose>
353 <h3 style={{ marginTop: "5px" }}>
354 Реплика на {postForModal.author.username}
355 </h3>
356 </ModalHeader>
357 <form onSubmit={(e) => handleReplySubmit(e, postForModal.postId)}>
358 <ModalBody>
359 <label htmlFor="content">
360 <b>Содржина</b>:
361 <ModalTextarea
362 id="content"
363 rows="8"
364 cols="100"
365 value={replyContent}
366 onChange={handleContentChange}
367 spellCheck={false}
368 />
369 </label>
370 </ModalBody>
371 <p
372 style={{ color: "red", marginLeft: "15px", marginTop: "10px" }}
373 >
374 {errorMessage}
375 </p>
376 <ModalFooter type="submit">РЕПЛИЦИРАЈ</ModalFooter>
377 </form>
378 </ModalContent>
379 </Modal>
380 )}
381 </div>
382 );
383}
384
385export default OpinionTree;
Note: See TracBrowser for help on using the repository browser.