123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
- <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
- <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
- <jsp:include page="/WEB-INF/jsp/include/head.jsp"></jsp:include>
- <script>
- let targetFeed = null;
- let params = {
- articleId: -1,
- feed: 'your-feed'
- };
- let query = Object.keys(params)
- .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))
- .join('&');
- let url = '/article/page/users/${user.id}?' + query;
- let loadingArticles = null;
- let articleList = null;
- const tagSet = new Set();
- const options = { method: 'GET' };
- const follow = 'Follow ' + "${user.username}";
- const unfollow = 'Unfollow ' + "${user.username}";
- // feed를 바꾼 경우
- const filterArticle = (option) => {
- const { clickedFeed } = option;
- const articles = document.querySelectorAll('.article-preview');
- articles.forEach(article => {
- let display = 'none';
- // display 기본 값은 none이고 값이 block으로 변경되는 경우만 조건으로 건다.
- if(clickedFeed === 'favorite-feed') {
- display = 'block';
- } else if(clickedFeed === 'your-feed') {
- display = 'block';
- }
- });
- }
- // 게시글 목록 로딩
- const setLoading = (type) => {
- let loadingDisplay = null;
- let contentDisplay = null;
- if(type === 'on') {
- loadingDisplay = 'block';
- contentDisplay = 'none';
- } else if(type === 'off') {
- loadingDisplay = 'none';
- contentDisplay = 'block';
- }
-
- loadingArticles.style.display = loadingDisplay;
- articleList.style.display = contentDisplay;
- }
- // 자신의 게시글이 없는 경우
- const noContent = () => {
- if(articleList.firstChild === null) {
- const p = document.createElement('p');
- p.classList.add('article-preview');
- p.textContent = 'No articles are here... yet.';
- articleList.appendChild(p);
- }
- }
- const focusFeed = () => {
- // 이미 포커싱되었으면 중단
- if(event.target.parentNode.classList.contains('active')) {
- return ;
- }
- const currentFeed = event.target.parentNode; // 포커싱할 피드
- const focusedFeed = (currentFeed.id === 'favorite-feed') ? document.querySelector('#your-feed') : document.querySelector('#favorite-feed'); // 현재 포커싱되어있는 피드
-
- currentFeed.classList.add('active');
- focusedFeed.classList.remove('active');
- // targetFeed 초기화
- targetFeed = currentFeed;
- setLoading('on');
- // 게시글 초기화
- while(articleList.firstChild) {
- articleList.firstChild.remove();
- }
- params = {
- articleId: -1,
- feed: targetFeed.id
- }
- query = Object.keys(params)
- .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))
- .join('&');
- url = '/article/page/users/${user.id}?' + query;
- fetch(url, options)
- .then(response => response.json())
- .then(json => {
- const { articles, paging } = json;
- username = "${ssUsername}";
- displayArticles(articles);
- nextPageLoad(articles, paging);
- })
- filterArticle({ 'clickedFeed': currentFeed.id });
- setTimeout(() => {
- setLoading('off');
- noContent();
- }, 1000);
- }
- // 좋아요 버튼
- const favoriteBtn = (indexNumber) => {
- const favoriteNum = event.target.querySelector('.count').textContent;
- const favorite = event.target.querySelector('.favorite-btn');
- let counts = parseInt(favoriteNum);
- let newCounts = counts + 1;
- // 로그인한 경우에만 실행
- if("${ssIsLogin}" === "true") {
- if(event.target.classList.contains('active')) { // 좋아요 버튼 해지
- event.target.querySelector('.count').textContent = counts - 1;
- event.target.classList.remove('active');
- fetch('/article/' + indexNumber + '/favorite', { method: 'DELETE' })
- .then(response => {
- if(response.status === 200) {
- return ;
- }
- });
- } else { // 좋아요 버튼 활성
- event.target.querySelector('.count').textContent = newCounts;
- event.target.classList.add('active');
- fetch('/article/' + indexNumber + '/favorite', {
- body: JSON.stringify({
- articleId: "${article.id}",
- userId: "${ssId}",
- created: new Date()
- }),
- method: 'POST',
- headers: {
- "Content-Type": "application/json"
- }
- })
- .then(response => {
- if(response.status === 201) {
- return;
- }
- })
- }
- } else {
- location.href = "/user/signin";
- }
- }
- // 사용자 팔로우
- const userFollow = () => {
- const followBtn = document.querySelector('.follow-btn');
- if("${ssIsLogin}" === "true") { // 로그인한 경우에만 팔로우 기능 사용 가능
- if(followBtn.classList.contains('active')) { // 팔로우 취소
- followBtn.querySelector('.follow').textContent = follow;
- fetch("/user/unfollow", {
- body: JSON.stringify({
- fromUser: "${ssId}",
- toUser: "${user.id}"
- }),
- method: 'DELETE',
- headers: {
- "Content-Type": "application/json"
- }
- })
- .then(response => {
- if(response.status === 200) {
- return followBtn.classList.remove('active');
- }
- })
- } else { // 팔로우하기
- followBtn.querySelector('.follow').textContent = unfollow;
- fetch("/user/follow", {
- body: JSON.stringify({
- fromUser: "${ssId}",
- toUser: "${user.id}"
- }),
- method: 'POST',
- headers: {
- "Content-Type": 'application/json'
- }
- })
- .then(response => {
- if(response.status === 201) {
- return followBtn.classList.add('active');
- }
- })
- }
- } else { // 로그인하지 않은 경우 로그인화면으로 이동
- location.href = "/user/signin"
- }
- }
- // 게시글 목록 조회
- const displayArticles = (articles) => {
- articles.forEach(article => {
- console.log(JSON.stringify(article))
- const domParser = new DOMParser();
- // 게시글 정보
- const domStrArticleMeta =
- `
- <div class="article-meta">
- <div class="metadata">
- <a href="/user/\${article.writerId}">
- <img src="/resources/images/avatar.png" alt="avatar">
- </a>
- <div class="article-info">
- <a href="/user/\${article.writerId}" class="name">\${article.writerName}</a>
- <span class="date"></span>
- </div>
- </div>
- <div>
- <button class="favorite-btn" onclick="favoriteBtn(\${article.id})">
- <i class="fas fa-heart"></i>
- <span class="count">\${article.favoriteNum}</span>
- </button>
- </div>
- </div>
- `;
- const divArticleMeta = domParser.parseFromString(domStrArticleMeta, 'text/html').body.firstChild;
- divArticleMeta.querySelector('.date').textContent = new Date(article.created).toLocaleString();
- // 게시글 좋아요한 경우 active
- if (article.favorite) {
- divArticleMeta.querySelector('.favorite-btn').classList.add('active');
- }
- // 게시글 내용 및 게시글태그
- const domStrPreviewLink =
- `
- <a href="/article/\${article.id}" class="preview-link">
- <h1 class="preview-title">\${article.title}</h1>
- <p>\${article.subtitle}</p>
- <div class="tag-data">
- <span>Read more...</span>
- <ul class="tag-list">
- </ul>
- </div>
- </a>
- `;
- const aPreviewLink = domParser.parseFromString(domStrPreviewLink, 'text/html').body.firstChild;
- const ul = aPreviewLink.querySelector('.tag-list');
- if (article.tags !== '') {
- article.tags.split(',').forEach(tag => {
- if(!tagSet.has(tag)) {
- tagSet.add(tag);
- }
- const li = document.createElement('li');
- li.classList.add('tag');
- li.textContent = tag;
- ul.appendChild(li);
- });
- }
- // 게시글 완성
- const articlePreview = document.createElement('article');
- articlePreview.classList.add('article-preview');
- articlePreview.append(divArticleMeta, aPreviewLink);
- articlePreview.style.display = 'block';
- articleList.appendChild(articlePreview);
- })
- }
- const nextPageLoad = (articles, paging) => {
- // 더 조회될 수 있는 게시글이 있는지 여부
- if(paging.isNext) {
- const moreButton = document.createElement('button');
- const articleList = document.querySelector('#article-list');
- const loading = document.createElement('div');
- moreButton.textContent = '더보기';
- moreButton.classList.add('more-button');
- loading.setAttribute('id', 'loading');
- articleList.appendChild(moreButton);
- // more 버튼 클릭 시 다음페이지 게시물을 가져온다
- moreButton.onclick = () => {
- articleList.removeChild(moreButton);
- articleList.appendChild(loading); // 로딩 생성
- // 현재 페이지의 마지막 게시글 id
- params['articleId'] = articles[articles.length - 1].id;
- query = Object.keys(params)
- .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))
- .join('&');
- url = '/article/page/users/${user.id}?' + query;
- fetch(url, options)
- .then(response => response.json())
- .then(json => {
- const {
- articles,
- paging
- } = json;
- username = "${ssUsername}";
- setTimeout(() => {
- articleList.removeChild(loading);
- displayArticles(articles);
- nextPageLoad(articles, paging);
- }, 1000);
- })
- }
- }
- }
- window.onload = () => {
- targetFeed = document.querySelector('.nav-item.active');
- loadingArticles = window.document.querySelector('#article-loading');
- articleList = document.querySelector('#article-list');
- const followString = document.querySelector('.follow-btn');
- if("${isFollow}" === "true"){
- followString.classList.add('active');
- followString.querySelector('.follow').textContent = unfollow;
- }
-
- setLoading('on');
- // fetch api call
- fetch(url, options)
- .then(response => response.json())
- .then(json => {
- const { articles, paging } = json;
- username = "${ssUsername}";
- displayArticles(articles);
- nextPageLoad(articles, paging);
- // hide loadings
- setTimeout(() => {
- setLoading('off');
- noContent();
- }, 1000);
- });
- }
- </script>
- </head>
- <body>
- <jsp:include page="/WEB-INF/jsp/include/header.jsp"></jsp:include>
- <!-- user-page content -->
- <div class="user-page">
- <!-- User Info -->
- <section class="user-info">
- <div class="container">
- <div class="row">
- <div class="col-10">
- <img src="/resources/images/avatar.png" alt="" class="user-img">
- <h4 class="user-name"><c:out value="${user.username}"></c:out></h4>
- <p class="profile-bio">
- <pre>
- <c:out value="${user.shortBio}"></c:out>
- </pre>
- </p>
- <c:choose>
- <c:when test="${user.id eq ssId}">
- <a href="/user/settings/${user.id}" class="action-btn btn-sm">
- <i class="fas fa-cog"></i> Edit Profile Settings
- </a>
- </c:when>
- <c:otherwise>
- <button class="action-btn btn-sm follow-btn" onclick="userFollow()">
- <i class="fas fa-plus"></i>
- <span class="follow"> Follow <c:out value="${user.username}"/></span>
- </button>
- </c:otherwise>
- </c:choose>
- </div>
- </div>
- </div>
- </section>
- <!-- Body -->
- <div class="container main">
- <div class="row">
- <div class="col-10">
- <!-- 토글 버튼으로 피드 내용 보기 -->
- <div class="toggle">
- <ul class="nav">
- <li id="your-feed" class="nav-item active">
- <a href="javascript:void(0);" onclick="focusFeed()">My Articles</a>
- </li>
- <li id="favorite-feed" class="nav-item">
- <a href="javascript:void(0);" onclick="focusFeed()">Favorited Articles</a>
- </li>
- </ul>
- </div>
- <!-- Content -->
- <div class="article">
- <!-- Article Loading -->
- <article id="article-loading">
- Loading articles...
- </article>
- <!-- Article List -->
- <div id="article-list" style="display: block;"></div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </body>
- </html>
|