articleDetails.jsp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
  2. <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
  3. <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
  4. <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
  5. <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
  6. <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
  7. <jsp:include page="/WEB-INF/jsp/include/head.jsp"></jsp:include>
  8. <script>
  9. const follow = 'Follow ' + "${articleDetail.writerName}";
  10. const unfollow = 'Unfollow ' + "${articleDetail.writerName}";
  11. const favorite = 'Favorite Article';
  12. const unfavorite = 'Unfavorite Article';
  13. function userFollow() {
  14. const followBtn = document.querySelectorAll('.follow-btn');
  15. if ("${ssIsLogin}" === "true") {
  16. followBtn.forEach(button => {
  17. if (button.classList.contains('active')) {
  18. button.querySelector('.follow').textContent = follow;
  19. fetch("/user/unfollow", {
  20. method: 'DELETE',
  21. body: JSON.stringify({
  22. fromUser: "${ssId}",
  23. toUser: "${articleDetail.writerId}"
  24. }),
  25. headers: {
  26. "Content-Type": "application/json"
  27. }
  28. })
  29. .then(response => {
  30. if (response.status === 200) {
  31. return button.classList.remove('active');
  32. }
  33. })
  34. } else {
  35. button.querySelector('.follow').textContent = unfollow;
  36. fetch("/user/follow", {
  37. body: JSON.stringify({
  38. fromUser: "${ssId}",
  39. toUser: "${articleDetail.writerId}"
  40. }),
  41. method: 'POST',
  42. headers: {
  43. "Content-Type": "application/json"
  44. }
  45. })
  46. .then(response => {
  47. if (response.status === 201) {
  48. return button.classList.add('active');
  49. }
  50. })
  51. }
  52. })
  53. } else {
  54. location.href = "/user/signin"
  55. }
  56. }
  57. function articleFavorite() {
  58. const favoriteBtn = document.querySelectorAll('.favorite-btn');
  59. let counts = parseInt(event.target.querySelector('.count').textContent);
  60. let newCounts = counts + 1;
  61. if ("${ssIsLogin}" === "true") {
  62. favoriteBtn.forEach(button => {
  63. if (button.classList.contains('active')) {
  64. button.querySelector('.favorite').textContent = favorite;
  65. button.classList.remove('active');
  66. fetch("/article/${articleDetail.id}/favorite", { method: 'DELETE' })
  67. .then(response => {
  68. if (response.status === 200) {
  69. return button.querySelector('.count').textContent = counts - 1;
  70. }
  71. })
  72. } else {
  73. button.querySelector('.favorite').textContent = unfavorite;
  74. button.classList.add('active');
  75. fetch("/article/${articleDetail.id}/favorite", {
  76. body: JSON.stringify({
  77. artilceId: "${articleDetail.id}",
  78. userId: "${ssId}",
  79. created: new Date()
  80. }),
  81. method: 'POST',
  82. headers: {
  83. "Content-Type": "application/json"
  84. }
  85. })
  86. .then(response => {
  87. if (response.status === 201) {
  88. return button.querySelector('.count').textContent = newCounts;
  89. }
  90. })
  91. }
  92. })
  93. } else {
  94. location.href = "/user/signin"
  95. }
  96. }
  97. const deleteArticle = (targetArticleId) => {
  98. fetch("/article/" + targetArticleId, { method: 'DELETE' })
  99. .then(response => {
  100. console.table(response);
  101. if (response.status === 200) {
  102. location.href = "/";
  103. }
  104. })
  105. }
  106. const postComments = () => {
  107. const commentInputArea = document.querySelector('#comment-input');
  108. const req = {
  109. userId: "${ssId}",
  110. articleId: "${articleDetail.id}",
  111. content: commentInputArea.value,
  112. created: new Date()
  113. }
  114. const postOptions = {
  115. body: JSON.stringify(req),
  116. method: 'POST',
  117. headers: {
  118. "Content-Type": "application/json"
  119. }
  120. };
  121. if (commentInputArea.value !== '') {
  122. fetch('/article/${articleDetail.id}/comment', postOptions)
  123. .then(response => {
  124. if (response.status === 201) {
  125. return displayComments();
  126. }
  127. })
  128. }
  129. commentInputArea.value = '';
  130. }
  131. function deleteComment(targetCommentId) {
  132. fetch("/article/${articleDetail.id}/comment/" + targetCommentId, { method: 'DELETE' })
  133. .then(response => {
  134. if (response.status === 200) {
  135. return displayComments();
  136. }
  137. })
  138. }
  139. function displayComments() {
  140. fetch('/article/${articleDetail.id}/comment/all', { method: 'GET' })
  141. .then(response => {
  142. if (response.status === 200) {
  143. return response.json();
  144. }
  145. })
  146. .then(json => {
  147. const { comments } = json;
  148. const container = document.querySelector('.comments');
  149. while (container.firstChild) {
  150. container.firstChild.remove();
  151. }
  152. comments.forEach(comment => {
  153. const domParser = new DOMParser();
  154. const domStrComment =
  155. `<div class="card">
  156. <div class="card-block">
  157. <p class="card-text">
  158. \${comment.content}
  159. </p>
  160. </div>
  161. <div class="card-footer comment-footer">
  162. <div class="user-info">
  163. <a href="userpage-others.html" class="comment-author">
  164. <img class="comment-img" src="/resources/images/avatar.png" alt="">
  165. </a>
  166. <a href="userpage-others.html" class="comment-author">\${comment.username}</a>
  167. <span class="date-posted"></span>
  168. </div>
  169. <i style="display: none" class="fas fa-trash-alt" onclick="deleteComment('\${comment.id}')"></i>
  170. </div>
  171. </div>`;
  172. const divComment = domParser.parseFromString(domStrComment, 'text/html').body.firstChild;
  173. divComment.querySelector('.date-posted').textContent = new Date(comment.created).toLocaleString();
  174. container.appendChild(divComment);
  175. const icons = document.querySelectorAll('.comment-footer .fa-trash-alt');
  176. icons.forEach(icon => {
  177. if (comment.userId == "${ssId}") {
  178. icon.style.display = 'block';
  179. }
  180. })
  181. })
  182. })
  183. }
  184. window.onload = () => {
  185. const tags = document.querySelector('.tag-list');
  186. const tagString = "${articleDetail.tags}"
  187. const tagList = tagString.split(',');
  188. if (tagString !== '') {
  189. tags.innerHTML = tagList.map(tag =>
  190. `<li class="tag">\${tag}</li>`
  191. ).join('');
  192. }
  193. const date = "${articleDetail.created}"
  194. document.querySelectorAll('.date').forEach(value =>
  195. value.textContent = new Date(date).toLocaleString()
  196. )
  197. const count = "${articleDetail.favoriteNum}";
  198. document.querySelectorAll('.count').forEach(value => {
  199. value.textContent = count;
  200. })
  201. const followString = document.querySelectorAll('.follow-btn');
  202. followString.forEach(value => {
  203. if(value.classList.contains('active')) {
  204. value.querySelector('.follow').textContent = unfollow;
  205. }
  206. })
  207. const favoriteString = document.querySelectorAll('.favorite-btn');
  208. favoriteString.forEach(value => {
  209. if (value.classList.contains('active')) {
  210. value.querySelector('.favorite').textContent = unfavorite;
  211. }
  212. })
  213. displayComments();
  214. }
  215. </script>
  216. </head>
  217. <body>
  218. <jsp:include page="/WEB-INF/jsp/include/header.jsp"></jsp:include>
  219. <div class="article-page">
  220. <!-- Article Banner -->
  221. <section class="banner">
  222. <div class="container">
  223. <h1 class="article-banner-title">
  224. <c:out value="${articleDetail.title}"></c:out>
  225. </h1>
  226. <div class="article-meta">
  227. <div class="metadata">
  228. <a href="userpage-my.html" class="profile-link">
  229. <img src="/resources/images/avatar.png" alt="avatar">
  230. </a>
  231. <div class="article-info">
  232. <a href="userpage-my.html" class="name">
  233. <c:out value="${articleDetail.writerName}"></c:out>
  234. </a>
  235. <span class="date">
  236. </span>
  237. </div>
  238. <c:choose>
  239. <c:when test="${articleDetail.writerId eq ssId}">
  240. <div class="buttons">
  241. <a href="/article/${articleDetail.id}/edit"
  242. class="edit-article btn-sm">
  243. <i class="fas fa-pencil-alt"> Edit Article</i>
  244. </a>
  245. <button class="delete-article btn-sm"
  246. onclick="deleteArticle('${articleDetail.id}')">
  247. <i class="fas fa-trash-alt"> Delete Article</i>
  248. </button>
  249. </div>
  250. </c:when>
  251. <c:otherwise>
  252. <div class="buttons">
  253. <button class="follow-btn btn-sm btn-meta <c:if test="${articleDetail.writerFollow eq true}">active</c:if>"
  254. onclick="userFollow()"
  255. >
  256. <i class="fas fa-plus"></i>
  257. <span class="follow">Follow <c:out
  258. value="${articleDetail.writerName}"></c:out>
  259. </span>
  260. </button>
  261. <button class="favorite-btn btn-sm btn-meta <c:if test="${articleDetail.favorite eq true}">active</c:if>"
  262. onclick="articleFavorite()">
  263. <i class="fas fa-heart"></i>
  264. <span class="favorite"> Favorite Article </span> (
  265. <span class="count">
  266. </span>
  267. )
  268. </button>
  269. </div>
  270. </c:otherwise>
  271. </c:choose>
  272. </div>
  273. </div>
  274. </div>
  275. </section>
  276. <div class="container main">
  277. <div class="article-content">
  278. <div class="col-12">
  279. <pre>
  280. <c:out value="${articleDetail.content}"></c:out>
  281. </pre>
  282. <ul class="tag-list">
  283. </ul>
  284. </div>
  285. </div>
  286. <hr>
  287. <div class="article-actions">
  288. <div class="article-meta">
  289. <div class="metadata">
  290. <a href="userpage-my.html" class="profile-link">
  291. <img src="/resources/images/avatar.png" alt="avatar">
  292. </a>
  293. <div class="article-info">
  294. <a href="userpage-my.html" class="name">
  295. <c:out value="${articleDetail.writerName}"></c:out>
  296. </a>
  297. <span class="date">
  298. </span>
  299. </div>
  300. <div class="buttons">
  301. </div>
  302. <c:choose>
  303. <c:when test="${articleDetail.writerId eq ssId}">
  304. <div class="buttons">
  305. <a href="/article/${articleDetail.id}/edit"
  306. class="edit-article btn-sm">
  307. <i class="fas fa-pencil-alt"> Edit Article</i>
  308. </a>
  309. <button class="delete-article btn-sm"
  310. onclick="deleteArticle('${articleDetail.id}')">
  311. <i class="fas fa-trash-alt"> Delete Article</i>
  312. </button>
  313. </div>
  314. </c:when>
  315. <c:otherwise>
  316. <div class="buttons">
  317. <button class="follow-btn btn-sm btn-meta <c:if test="${articleDetail.writerFollow eq true}">active</c:if>" onclick="userFollow()"
  318. >
  319. <i class="fas fa-plus"></i>
  320. <span class="follow">Follow <c:out
  321. value="${articleDetail.writerName}"></c:out>
  322. </span>
  323. </button>
  324. <button class="favorite-btn btn-sm btn-meta <c:if test="${articleDetail.favorite eq true}">active</c:if>"
  325. onclick="articleFavorite()">
  326. <i class="fas fa-heart"></i>
  327. <span class="favorite"> Favorite Article </span> (
  328. <span class="count">
  329. </span>
  330. )
  331. </button>
  332. </div>
  333. </c:otherwise>
  334. </c:choose>
  335. </div>
  336. </div>
  337. </div>
  338. <!-- comment -->
  339. <div class="row">
  340. <div class="col-8">
  341. <div class="card comment-form">
  342. <div class="card-block">
  343. <textarea id="comment-input" class="form-control"
  344. placeholder="Write a comment..." rows="3"></textarea>
  345. </div>
  346. <div class="card-footer">
  347. <img class="comment-img" src="/resources/images/avatar.png" alt="">
  348. <button class="btn btn-card btn-sm" onclick="postComments()">Post
  349. Comment</button>
  350. </div>
  351. </div>
  352. <div class="comments">
  353. </div>
  354. </div>
  355. </div>
  356. </div>
  357. </div>
  358. </body>
  359. </html>