source: reactapp/src/Components/OpinionTree.js

main
Last change on this file was 8dffe02, checked in by unknown <mlviktor23@…>, 18 months ago

prefinal reproducible

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