Skip to content
Closed

Js api4 #1361

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 267 additions & 44 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,287 @@

<body>
<div class="container">
<ul class="comments">
<li class="comment">
<div class="comment-header">
<div>Глеб Фокин</div>
<div>12.02.22 12:18</div>
</div>
<div class="comment-body">
<div class="comment-text">
Это будет первый комментарий на этой странице
</div>
</div>
<div class="comment-footer">
<div class="likes">
<span class="likes-counter">3</span>
<button class="like-button"></button>
</div>
</div>
</li>
<li class="comment">
<div class="comment-header">
<div>Варвара Н.</div>
<div>13.02.22 19:22</div>
</div>
<div class="comment-body">
<div class="comment-text">
Мне нравится как оформлена эта страница! ❤
</div>
</div>
<div class="comment-footer">
<div class="likes">
<span class="likes-counter">75</span>
<button class="like-button -active-like"></button>
</div>
</div>
</li>
</ul>
<div class="loading">Загрузка комментариев...</div>
<ul class="comments"></ul>
<div class="add-form">
<input
type="text"
class="add-form-name"
placeholder="Введите ваше имя"
/>
placeholder="Введите ваше имя" />
<textarea
type="textarea"
class="add-form-text"
placeholder="Введите ваш коментарий"
rows="4"
></textarea>
placeholder="Введите ваш комментарий"
rows="4"></textarea>
<div class="add-form-row">
<button class="add-form-button">Написать</button>
</div>
</div>

<div class="add-form-loading" style="display: none">
Комментарий добавляется...
</div>
</div>
</body>

<script>
"use strict";
// Код писать здесь
console.log("It works!");
const commentsList = document.querySelector(".comments");
const nameInput = document.querySelector(".add-form-name");
const textInput = document.querySelector(".add-form-text");
const addButton = document.querySelector(".add-form-button");
const loadingElement = document.querySelector(".loading");
const addFormElement = document.querySelector(".add-form");
const addFormLoadingElement = document.querySelector(".add-form-loading");

const API_URL = "https://wedev-api.sky.pro/api/v1/ivan-ivanov/comments";
let comments = [];
let formData = {
name: "",
text: "",
};

nameInput.addEventListener("input", () => {
formData.name = nameInput.value;
});

textInput.addEventListener("input", () => {
formData.text = textInput.value;
});

function restoreFormData() {
nameInput.value = formData.name;
textInput.value = formData.text;
}

async function fetchComments() {
try {
loadingElement.style.display = "";
addFormElement.style.display = "none";
addFormLoadingElement.style.display = "none";

const response = await fetch(API_URL);

if (response.status === 500) {
throw new Error("SERVER_ERROR");
}

if (!response.ok) {
throw new Error("Ошибка при загрузке комментариев");
}

const data = await response.json();
comments = data.comments.map((comment) => ({
author: comment.author.name,
date: new Date(comment.date),
text: comment.text,
likes: comment.likes,
isLiked: comment.isLiked,
id: comment.id,
}));
renderComments();
} catch (error) {
console.error("Ошибка:", error);

if (error.message === "Failed to fetch") {
alert("Кажется, у вас сломался интернет, попробуйте позже");
} else if (error.message === "SERVER_ERROR") {
alert("Сервер сломался, попробуй позже");
} else {
alert("Не удалось загрузить комментарии. Попробуйте позже.");
}
} finally {
loadingElement.style.display = "none";
addFormElement.style.display = "";
restoreFormData();
}
}

async function postComment() {
const author = nameInput.value.trim();
const text = textInput.value.trim();

if (author === "" || text === "") {
alert("Пожалуйста, заполните все поля");
return;
}

if (author.length < 3 || text.length < 3) {
alert("Имя и комментарий должны быть не короче 3 символов");
return;
}

try {
addFormElement.style.display = "none";
addFormLoadingElement.style.display = "";

const response = await fetch(API_URL, {
method: "POST",
body: JSON.stringify({
text: text,
name: author,
}),
});

if (response.status === 400) {
throw new Error("VALIDATION_ERROR");
}

if (response.status === 500) {
throw new Error("SERVER_ERROR");
}

if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "Ошибка при отправке комментария");
}

formData.name = "";
formData.text = "";
await fetchComments();
} catch (error) {
console.error("Ошибка:", error);

if (error.message === "Failed to fetch") {
alert("Кажется, у вас сломался интернет, попробуйте позже");
} else if (error.message === "SERVER_ERROR") {
alert("Сервер сломался, попробуй позже");
} else if (error.message === "VALIDATION_ERROR") {
alert("Ошибка валидации. Проверьте введенные данные.");
} else {
alert(error.message);
}
} finally {
addFormLoadingElement.style.display = "none";
addFormElement.style.display = "";
restoreFormData();
}
}

function formatDate(date) {
const day = date.getDate();
const month = date.toLocaleString("ru-RU", { month: "long" });
const year = date.getFullYear();
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
return `${day} ${month} ${year}, ${hours}:${minutes}`;
}

function escapeHTML(str) {
return str
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}

function renderComments() {
commentsList.innerHTML = "";

comments.forEach((comment, index) => {
const commentHTML = `
<li class="comment" data-index="${index}">
<div class="comment-header">
<div class="comment-author">${escapeHTML(comment.author)}</div>
<div>${formatDate(comment.date)}</div>
</div>
<div class="comment-body">
<div class="comment-text">
${escapeHTML(comment.text).replace(/\n/g, "<br>")}
</div>
</div>
<div class="comment-footer">
<div class="likes">
<span class="likes-counter">${comment.likes}</span>
<button class="like-button ${
comment.isLiked ? "-active-like" : ""
}" data-index="${index}"></button>
</div>
</div>
</li>
`;
commentsList.insertAdjacentHTML("beforeend", commentHTML);
});

setupCommentReplyHandlers();
setupLikeHandlers();
}

function setupCommentReplyHandlers() {
document.querySelectorAll(".comment").forEach((commentElement) => {
commentElement.addEventListener("click", function (event) {
if (
event.target.closest(".like-button") ||
event.target.closest(".likes-counter")
) {
return;
}

const index = this.getAttribute("data-index");
const author = comments[index].author;
const mention = `@${author} `;

if (textInput.value.trim() === "") {
textInput.value = mention;
formData.text = mention;
} else {
let newValue = textInput.value.replace(/^@[^@\s]+\s?/g, "").trim();
const newText = mention + (newValue ? newValue + " " : "");
textInput.value = newText;
formData.text = newText;
}

textInput.focus();
});
});
}

function setupLikeHandlers() {
document.querySelectorAll(".like-button").forEach((button) => {
button.addEventListener("click", async function (event) {
event.stopPropagation();

const index = this.getAttribute("data-index");
const comment = comments[index];

if (button.classList.contains("-loading-like")) {
return;
}

button.classList.add("-loading-like");

try {
await delay(1000);

if (comment.isLiked) {
comment.likes--;
} else {
comment.likes++;
}
comment.isLiked = !comment.isLiked;

renderComments();
} catch (error) {
console.error("Ошибка при установке лайка:", error);
alert("Не удалось поставить лайк. Попробуйте позже.");
renderComments();
}
});
});
}

function delay(interval = 300) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, interval);
});
}

addButton.addEventListener("click", function () {
postComment();
});

fetchComments();
</script>
</html>
20 changes: 19 additions & 1 deletion styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,22 @@ body {

.add-form-button:hover {
opacity: 0.9;
}
}

@keyframes rotating {
from {
transform: rotate(0deg);
}

25% {
transform: rotate(30deg);
}

75% {
transform: rotate(-30deg);
}

to {
transform: rotate(0deg);
}
}