123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- <%@ 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 loadingTag = null;
- let loadingArticle = null;
- let articleList = null;
- let ssIsLogin = null;
- let targetFeed = null;
- const options = {
- method: 'GET'
- };
- const tagSet = new Set();
- let tagArray = [];
- // fetch url info
- const selectFeed = ("${ssIsLogin}" === "true") ? 'your-feed' : 'flobal-feed';
- let params = {
- articleId: -1,
- feed: selectFeed
- };
- let query = Object.keys(params)
- .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))
- .join('&');
- let url = '/article/page?' + query;
- // 1. feed를 바꾼 경우
- // 2. 태그를 클릭한 경우
- const filterArticle = (option) => {
- const {
- clickedTag,
- clickedFeed
- } = option;
- const articles = window.document.querySelectorAll('.article-preview');
- const articleList = document.querySelector('#article-list');
- articles.forEach(article => {
- const writerName = article.querySelector('.name').textContent;
- let display = 'none';
- // display 기본 값은 none이고 값이 block으로 변경되는 경우만 조건으로 건다
- if (clickedFeed === 'global-feed') {
- display = 'block';
- } else if (clickedFeed === 'your-feed' && "${ssUsername}" === writerName) {
- display = 'block';
- } else if (clickedTag !== undefined) { // 태그가 클릭된 경우
- const tags = article.querySelectorAll('.tag');
- const tagSet = new Set(Array.from(tags).map(tag => tag.textContent));
- if (tagSet.has(clickedTag)) {
- display = 'block';
- }
- }
- article.style.display = display;
- });
- };
- const setLoading = (type) => {
- let loadingDisplay = null;
- let contentDisplay = null;
- if (type === 'on') {
- loadingDisplay = 'block';
- contentDisplay = 'none';
- } else if (type === 'off') {
- loadingDisplay = 'none';
- contentDisplay = 'block';
- }
- loadingTag.style.display = loadingDisplay;
- loadingArticle.style.display = loadingDisplay;
- articleList.style.display = contentDisplay;
- tagList.style.display = contentDisplay;
- }
- // 자신의 게시글이 없는 경우
- const noContent = () => {
- articleList = document.querySelector('#article-list');
- tagList = document.querySelector('#tag-list');
- if (articleList.firstChild === null) {
- const p = document.createElement('p');
- const pTag = document.createElement('p');
- p.classList.add('article-preview');
- p.textContent = 'No articles are here... yet.';
- pTag.textContent = 'No tags are here... yet.';
- articleList.appendChild(p);
- tagList.appendChild(pTag);
- }
- }
- const focusFeed = () => {
- // 이미 포커싱된 탭이라면 중지
- const parentNode = event.target.parentNode;
- if (parentNode.classList.contains('active')) {
- return;
- }
- const unFocusedFeed = parentNode;
- const currentFeed = (unFocusedFeed.id === 'global-feed') ? window.document.querySelector('#your-feed') :
- window.document.querySelector('#global-feed');
- unFocusedFeed.classList.add('active');
- if ("${ssIsLogin}" === "true") {
- currentFeed.classList.remove('active');
- }
- // your-feed 또는 global-feed 클릭시 tag-feed 없애기
- const tagToggle = document.querySelector('#tag-feed');
- const spanTag = document.querySelector('#tag-feed span');
- if (tagToggle.classList.contains('active')) {
- tagToggle.classList.remove('active');
- spanTag.textContent = null;
- document.querySelector('.fa-hashtag').style.display = 'none';
- }
- // targetFeed 초기화
- targetFeed = unFocusedFeed;
- setLoading('on');
- // 태그와 게시글 초기화
- tagArray = [];
- while(tagList.firstChild) {
- tagList.firstChild.remove();
- }
- articleList = document.querySelector('#article-list');
- 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?' + query;
- fetch(url, options)
- .then(response => response.json())
- .then(json => {
- const {
- articles,
- paging
- } = json;
- loadArticle(articles);
- nextPageLoad(articles, paging);
- });
- filterArticle({
- 'clickedFeed': unFocusedFeed.id
- })
- setTimeout(() => {
- setLoading('off');
- noContent();
- }, 2000);
- }
- // 좋아요 버튼
- 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: indexNumber,
- userId: "${ssId}",
- created: new Date()
- }),
- method: 'POST',
- headers: {
- "Content-Type": "application/json"
- }
- })
- .then(response => {
- if (response.status === 201) {
- return;
- }
- })
- }
- } else {
- location.href = "/user/signin"
- }
- }
- // 태그 정렬 https://curryyou.tistory.com/229
- const getSortedArr = (array) => {
- // 출현 빈도 구하기
- const counts = array.reduce((pv, cv) => {
- pv[cv] = (pv[cv] || 0) + 1;
- return pv;
- }, {});
- // 배열 생성 => [ [ key: 개수 ], [ key: 개수 ], ...]
- const result = [];
- for (let key in counts) {
- result.push([key, counts[key]]);
- };
- // 빈도별로 정렬
- result.sort((first, second) => {
- return second[1] - first[1];
- });
- // 최대 10개까지만 표시
- return result.slice(0, 10);
- }
- // 현재 클릭한 태그 표시
- const focusTag = () => {
- let currentValue = document.querySelector('.tag.active');
- if (currentValue !== null) {
- currentValue.classList.remove('active');
- }
- event.target.classList.add('active');
- }
- // 게시글 및 태그 표시
- const loadArticle = (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}" class="profile-link">
- <img src="/resources/images/avatar.png" alt="avatar">
- </a>
- <div class="article-info">
- <a href="/user/\${article.writerId}" class="name"></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('.name').textContent = article.writerName;
- divArticleMeta.querySelector('.date').textContent = new Date(article.created).toLocaleString();
- // 좋아요한 게시글의 경우
- if (article.favorite) {
- divArticleMeta.querySelector('.favorite-btn').classList.add('active');
- }
- // 게시글 내용 및 게시글태그 표시
- const domStrPreviewLink =
- `
- <a href="/article/\${article.id}" class="preview-link">
- <h1 class="preview-title"></h1>
- <p></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;
- aPreviewLink.querySelector('.preview-title').textContent = article.title;
- aPreviewLink.querySelector('p').textContent = article.subtitle;
- const ul = aPreviewLink.querySelector('.tag-list');
- if (article.tags !== '') {
- article.tags.split(',').forEach(tag => {
- if (!tagSet.has(tag)) {
- tagSet.add(tag);
- }
- const li = window.document.createElement('li');
- li.classList.add('tag');
- li.textContent = tag;
- ul.appendChild(li);
- })
- }
- // article 완성
- const articlePreview = window.document.createElement('article');
- articlePreview.classList.add('article-preview');
- articlePreview.appendChild(divArticleMeta);
- articlePreview.appendChild(aPreviewLink);
- if (targetFeed.id === 'your-feed') {
- articlePreview.style.display = ("${ssUsername}" === article.writerName) ? 'block' : 'none';
- }
- articleList.appendChild(articlePreview);
- // Popular tag 배열 생성
- let tagsArray = article.tags.split(',');
- const tagList = document.querySelector('#tag-list');
- while (tagList.firstChild) {
- tagList.firstChild.remove();
- }
- tagsArray.forEach(tag => {
- if (tag !== '') {
- tagArray.push(tag);
- }
- })
- });
- const tags = Array.from(tagSet);
- const tagList = window.document.querySelector('#tag-list');
- // Popular tag 표시
- getSortedArr(tagArray).forEach(tag => {
- const a = document.createElement('a');
- const tagString = tag.join(',');
- const tagValue = tagString.substring(0, tagString.lastIndexOf(','));
- const tagCount = tagString.substring(tagString.lastIndexOf(',') + 1);
- a.classList.add("tag");
- a.textContent = `\${tagValue} (\${tagCount})`
- a.onclick = (event) => {
- const tagToggle = document.querySelector('#tag-feed');
- const yourToggle = document.querySelector('#your-feed');
- const globalToggle = document.querySelector('#global-feed');
- const moreBtn = document.querySelector('.more-button');
- const spanTag = document.querySelector('#tag-feed span');
- // 해당 태그의 게시물 표시
- filterArticle({
- 'clickedTag': tagValue
- });
- // 해당 태그의 토글버튼 생성
- document.querySelector('.fa-hashtag').style.display = 'block';
- spanTag.textContent = '\u00A0' + tagValue;
- tagToggle.classList.add('active');
- globalToggle.classList.remove('active');
- // 로그인한 경우에만 your-feed가 생기므로 조건문 처리
- if ("${ssIsLogin}" === "true") {
- yourToggle.classList.remove('active');
- }
- // more 버튼 삭제
- if (moreBtn !== null) {
- moreBtn.remove();
- }
- }
- tagList.appendChild(a);
- });
- }
- // 다음페이지 로드
- 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?' + query;
- fetch(url, options)
- .then(response => response.json())
- .then(json => {
- const {
- articles,
- paging
- } = json;
- setTimeout(() => {
- articleList.removeChild(loading);
- loadArticle(articles);
- nextPageLoad(articles, paging);
- }, 1000);
- })
- }
- }
- }
- window.onload = (event) => {
- ssIsLogin = "${ssIsLogin}";
- targetFeed = (ssIsLogin === "true") ? window.document.querySelector('#your-feed') : window.document
- .querySelector('#global-feed');
- targetFeed.classList.add('active');
- loadingTag = window.document.querySelector('#tag-loading');
- loadingArticle = window.document.querySelector('#article-loading');
- articleList = window.document.querySelector('#article-list');
- noTag = window.document.querySelector('#no-tag');
- tagList = window.document.querySelector('#tag-list');
- setLoading('on');
- //fetch api call
- fetch(url, options)
- .then(response => response.json())
- .then(json => {
- const {
- articles,
- paging
- } = json;
- loadArticle(articles);
- nextPageLoad(articles, paging);
- // hide loadings
- setTimeout(() => {
- setLoading('off');
- noContent();
- }, 2000);
- });
- }
- </script>
- <style>
- </style>
- </head>
- <body>
- <jsp:include page="/WEB-INF/jsp/include/header.jsp"></jsp:include>
- <!-- home-page content -->
- <div class="home-page">
- <!-- Banner -->
- <section class="banner">
- <div class="container">
- <h1 class="banner-title">conduit</h1>
- <p class="banner-description">A place to share your knowledge.</p>
- </div>
- </section>
- <!-- Main - Contents & aside-->
- <div class="container main">
- <div class="row">
- <div class="col-9">
- <!-- 토글 버튼으로 피드 내용 보기 -->
- <!-- toggle -->
- <div class="toggle">
- <ul class="nav">
- <!-- 로그인 상태인 경우에만 your feed on -->
- <c:if test="${ssIsLogin eq true}">
- <li id="your-feed" class="nav-item">
- <a href="javascript:void(0);" onclick="focusFeed()">Your
- Feed</a>
- </li>
- </c:if>
- <li id="global-feed" class="nav-item">
- <a href="javascript:void(0);" onclick="focusFeed()">Global
- Feed</a>
- </li>
- <li id="tag-feed" class="nav-item">
- <a href="javascript:void(0);">
- <i class="fas fa-hashtag" style="display: none;"></i>
- <span></span>
- </a>
- </li>
- </ul>
- </div>
- <!-- content list -->
- <div class="article">
- <!-- loading -->
- <article id="article-loading">
- loading articles...
- </article>
- <!-- article list -->
- <div id="article-list" style="display: none;"></div>
- </div>
- </div>
- <!-- aside -->
- <div class="col-3">
- <div class="aside">
- <p>Popular Tags</p>
- <!-- loading -->
- <div id="tag-loading">
- loading tags...
- </div>
- <!-- 태그 -->
- <div id="tag-list" class="tag-list" style="display: none;" onclick="focusTag()">
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </body>
- </html>
|