diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..b512c09d47 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 0000000000..0c03d914b1 --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,3 @@ +tabWidth: 4 +semi: false +singleQuote: true diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000000..a5dc7820be --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,12 @@ +import globals from 'globals' +import pluginJs from '@eslint/js' +import config from 'eslint-config-prettier' +import plugin from 'eslint-plugin-prettier/recommended' + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + { languageOptions: { globals: globals.browser } }, + pluginJs.configs.recommended, + config, + plugin, +] diff --git a/index.html b/index.html index 6f14ae14aa..9ba0007d6f 100644 --- a/index.html +++ b/index.html @@ -1,71 +1,36 @@ - + -
-
Пожалуйста подождите, загружаю комментарии...
' + } + + fetchComments().then((data) => { + updateComments(data) + + renderComments() + }) +} + +fetchAndRenderComments(true) diff --git a/modules/api.js b/modules/api.js new file mode 100644 index 0000000000..6ce2c1d11d --- /dev/null +++ b/modules/api.js @@ -0,0 +1,75 @@ +// const host = 'https://webdev-hw-api.vercel.app/api/v1/vladislav-chechulin' +const host = ' https://wedev-api.sky.pro/api/v2/vladislav-chechulin' +const authHost = 'https://wedev-api.sky.pro/api/user' + +export let token = '' +export let name = '' + +export const setToken = (newToken) => { + token = newToken +} + +export const setName = (newName) => { + name = newName +} + +export const login = (login, password) => { + return fetch(authHost + '/login', { + method: 'POST', + body: JSON.stringify({ login: login, password: password }), + }) +} + +export const registration = (name, login, password) => { + return fetch(authHost, { + method: 'POST', + body: JSON.stringify({ name: name, login: login, password: password }), + }) +} + +export const fetchComments = () => { + return fetch(host + '/comments') + .then((res) => res.json()) + .then((responseData) => { + const appComments = responseData.comments.map((comment) => { + return { + name: comment.author.name, + date: new Date(comment.date), + text: comment.text, + likes: comment.likes, + isLiked: false, + } + }) + + return appComments + }) +} + +export const postComment = (text, name) => { + return fetch(host + '/comments', { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + text, + name, + }), + }) + .then((response) => { + if (response.status === 500) { + throw new Error('Ошибка сервера') + } + + if (response.status === 401) { + throw new Error('Пользователь не авторизован') + } + + if (response.status === 400) { + throw new Error('Неверный запрос') + } + }) + .then(() => { + return fetchComments() + }) +} diff --git a/modules/comments.js b/modules/comments.js new file mode 100644 index 0000000000..2893d2aacb --- /dev/null +++ b/modules/comments.js @@ -0,0 +1,20 @@ +export let comments = [ + // { + // name: 'Глеб Фокин', + // date: new Date(), + // text: 'Это будет первый комментарий на этой странице', + // likes: 3, + // isLiked: false, + // }, + // { + // name: 'Варвара Н.', + // date: new Date(), + // text: 'Мне нравится как оформлена эта страница! ❤', + // likes: 75, + // isLiked: true, + // }, +] + +export const updateComments = (newComments) => { + comments = newComments +} diff --git a/modules/initListeners.js b/modules/initListeners.js new file mode 100644 index 0000000000..3489c99c94 --- /dev/null +++ b/modules/initListeners.js @@ -0,0 +1,85 @@ +import { postComment } from './api.js' +import { comments, updateComments } from './comments.js' +import { sanitizeHtml } from './sanitizeHtml.js' + +export const initLikeListeners = (renderComments) => { + const likeButtons = document.querySelectorAll('.like-button') + + for (const likeButton of likeButtons) { + likeButton.addEventListener('click', (event) => { + event.stopPropagation() + + const index = likeButton.dataset.index + const comment = comments[index] + + comment.likes = comment.isLiked + ? comment.likes - 1 + : comment.likes + 1 + + comment.isLiked = !comment.isLiked + + renderComments() + }) + } +} + +export const initReplyListeners = () => { + const text = document.getElementById('text-input') + const commentsElements = document.querySelectorAll('.comment') + + for (const commentElement of commentsElements) { + commentElement.addEventListener('click', () => { + const currentComment = comments[commentElement.dataset.index] + text.value = `${currentComment.name}: ${currentComment.text}` + }) + } +} + +export const initAddCommentListener = (renderComments) => { + const name = document.getElementById('name-input') + const text = document.getElementById('text-input') + const addButton = document.querySelector('.add-form-button') + + addButton.addEventListener('click', () => { + if (!name.value || !text.value) { + console.error('заполните форму') + return + } + + document.querySelector('.form-loading').style.display = 'block' + document.querySelector('.add-form').style.display = 'none' + + postComment(sanitizeHtml(text.value), sanitizeHtml(name.value)) + .then((data) => { + updateComments(data) + renderComments() + + name.value = '' + text.value = '' + }) + .catch((error) => { + document.querySelector('.form-loading').style.display = 'none' + document.querySelector('.add-form').style.display = 'flex' + + if (error.message === 'Ошибка сервера') { + alert('Сервер сломался, попробуй позже') + } + + if (error.message === 'Failed to fetch') { + alert('Нет интернета, попробуй позже') + } + + if (error.message === 'Неверный запрос') { + alert('Имя и комментарий должны быть не короче 3х символов') + + name.classList.add('-error') + text.classList.add('-error') + + setTimeout(() => { + name.classList.remove('-error') + text.classList.remove('-error') + }, 2000) + } + }) + }) +} diff --git a/modules/renderComments.js b/modules/renderComments.js new file mode 100644 index 0000000000..3ba6f6acc4 --- /dev/null +++ b/modules/renderComments.js @@ -0,0 +1,82 @@ +import { name, token } from './api.js' +import { comments } from './comments.js' +import { + initAddCommentListener, + initLikeListeners, + initReplyListeners, +} from './initListeners.js' +import { renderLogin } from './renderLogin.js' + +export const renderComments = () => { + const container = document.querySelector('.container') + + const commentsHtml = comments + .map((comment, index) => { + return ` +чтобы отправить комментарий, войдите
` + + const baseHtml = ` +${commentsHtml}
+ ${token ? addCommentHtml : linkToLoginText} + ` + + container.innerHTML = baseHtml + + initLikeListeners(renderComments) + + if (token) { + initReplyListeners() + initAddCommentListener(renderComments) + } else { + document.querySelector('.link-login').addEventListener('click', () => { + renderLogin() + }) + } +} diff --git a/modules/renderLogin.js b/modules/renderLogin.js new file mode 100644 index 0000000000..9022f830c4 --- /dev/null +++ b/modules/renderLogin.js @@ -0,0 +1,56 @@ +import { fetchAndRenderComments } from '../index.js' +import { login, setName, setToken } from './api.js' +import { renderRegistration } from './renderRegistration.js' + +export const renderLogin = () => { + const container = document.querySelector('.container') + + const loginHtml = ` +Форма входа
+ + + +Форма регистрации
+ + + + +