Skip to content
Open
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
63 changes: 45 additions & 18 deletions JS modules/addComment.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,53 @@
import { saveComment, loadComments, getComments } from './state.js';
import { sanitize } from './utils.js';
import { renderComments } from './view.js';

export function initAddComment({ nameInput, textInput, button, listRoot }) {
button.addEventListener('click', async () => {
const nameValue = sanitize(nameInput.value);
const textValue = sanitize(textInput.value);
if (!nameValue || !textValue) {
alert('Напиши что-нибудь!');
const handlePostClick = (retryCount = 0) => {
const textValue = textInput.value.trim();

if (textValue.length < 3) {
alert('Комментарий должен быть не короче 3 символов');
return;
}

button.disabled = true;
try {
await saveComment({ name: nameValue, text: textValue });
await loadComments();
renderComments(listRoot, getComments());
nameInput.value = '';
textInput.value = '';
} catch (e) {
alert(e.message);
} finally {
button.disabled = false;
}
});
button.textContent = 'Отправляем...';

saveComment({ text: textValue })
.then(() => loadComments())
.then(() => {
renderComments(listRoot, getComments());
textInput.value = '';
})
.catch((e) => {
if (e.message === 'Failed to fetch') {
alert('Кажется, у вас сломался интернет, попробуйте позже');
return;
}

if (e.message === 'Ошибка сервера') {
if (retryCount < 3) {
setTimeout(() => {
handlePostClick(retryCount + 1);
}, 1000);
} else {
alert('Сервер сломался, попробуй позже');
}
return;
}

if (e.message === 'Ошибка запроса') {
alert('Ошибка запроса');
return;
}

alert(e.message);
})
.finally(() => {
button.disabled = false;
button.textContent = 'Написать';
});
};

button.addEventListener('click', () => handlePostClick());
}
15 changes: 13 additions & 2 deletions JS modules/likeHandler.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
// Лайки
import { toggleLike, getComments } from './state.js';
import { toggleLike, setLikeLoading, getComments } from './state.js';
import { renderComments } from './view.js';
import { delay } from './utils.js';

export function initLikeHandler({ listRoot }) {
listRoot.addEventListener('click', (e) => {
const likeBtn = e.target.closest('.like-button');
if (!likeBtn) return;
const i = Number(likeBtn.dataset.index);
if (Number.isNaN(i)) return;
toggleLike(i);

const comments = getComments();
if (comments[i].isLikeLoading) return;

setLikeLoading(i, true);
renderComments(listRoot, getComments());

delay(2000).then(() => {
toggleLike(i);
setLikeLoading(i, false);
renderComments(listRoot, getComments());
});
});
}
16 changes: 16 additions & 0 deletions JS modules/loginView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function renderLogin(root, onLogin) {
root.innerHTML = `
<div class="add-form">
<input id="login" placeholder="Логин" />
<input id="password" type="password" placeholder="Пароль" />
<button id="login-btn">Войти</button>
</div>
`;

document.getElementById('login-btn').addEventListener('click', () => {
const login = document.getElementById('login').value;
const password = document.getElementById('password').value;

onLogin({ login, password });
});
}
64 changes: 52 additions & 12 deletions JS modules/main.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,58 @@
import { getComments, loadComments } from './state.js';
import { getComments, loadComments, login, getUser } from './state.js';
import { renderComments } from './view.js';
import { initAddComment } from './addComment.js';
import { initLikeHandler } from './likeHandler.js';
import { initQuoteHandler } from './quoteHandler.js';
import { renderLogin } from './loginView.js';

const nameInput = document.getElementById('name');
const textInput = document.getElementById('comment');
const button = document.getElementById('button');
const listRoot = document.querySelector('.comments');
const appRoot = document.querySelector('.container');
const form = document.querySelector('.add-form');

initAddComment({ nameInput, textInput, button, listRoot });
initLikeHandler({ listRoot });
initQuoteHandler({ listRoot, textInput });
function renderApp() {
listRoot.innerHTML = '<div class="loading">Загружаем...</div>';

loadComments().then(() => {
renderComments(listRoot, getComments());
});
loadComments()
.then(() => {
renderComments(listRoot, getComments());

if (!getUser()) {
form.style.display = 'none';

const link = document.createElement('div');
link.innerHTML =
'<a href="#">Чтобы добавить комментарий, авторизуйтесь</a>';

link.addEventListener('click', (e) => {
e.preventDefault();
renderLogin(appRoot, handleLogin);
});

appRoot.appendChild(link);
} else {
form.style.display = 'block';

const nameInput = document.getElementById('name');
const textInput = document.getElementById('comment');
const button = document.getElementById('button');

nameInput.value = getUser().name;
nameInput.setAttribute('readonly', true);

initAddComment({ nameInput, textInput, button, listRoot });
}
})
.catch(() => {
alert('Ошибка загрузки');
});
}

function handleLogin({ login: userLogin, password }) {
login({ login: userLogin, password })
.then(() => {
location.reload();
})
.catch(() => {
alert('Неверные данные');
});
}

renderApp();
82 changes: 60 additions & 22 deletions JS modules/state.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,73 @@
const API_URL = 'https://wedev-api.sky.pro/api/v1/origami/comments';
const API_URL = 'https://wedev-api.sky.pro/api/v2/origami/comments';

let comments = [];
let token = null;
let user = null;

export const getComments = () => comments.slice();
export const getUser = () => user;

export const loadComments = async () => {
const res = await fetch(API_URL);
const data = await res.json();
comments = data.comments.map((c) => ({
name: c.author.name,
text: c.text,
date: new Date(c.date).toLocaleString('ru-RU'),
likes: c.likes,
isLiked: false, // лайки локальные, сбрасываем при загрузке
}));
};
export function loadComments() {
return fetch(API_URL)
.then((res) => {
if (!res.ok) {
if (res.status >= 500) throw new Error('Ошибка сервера');
throw new Error('Ошибка запроса');
}
return res.json();
})
.then((data) => {
comments = data.comments.map((c) => ({
name: c.author.name,
text: c.text,
date: new Date(c.date).toLocaleString('ru-RU'),
likes: c.likes,
isLiked: false,
isLikeLoading: false,
}));
});
}

export const saveComment = async ({ name, text }) => {
const res = await fetch(API_URL, {
export function saveComment({ text }) {
return fetch(API_URL, {
method: 'POST',
body: JSON.stringify({ name, text }),
headers: {
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ text }),
}).then((res) => {
if (!res.ok) {
if (res.status >= 500) throw new Error('Ошибка сервера');
if (res.status === 400) throw new Error('Ошибка запроса');
throw new Error('Ошибка');
}
});
if (!res.ok) {
const err = await res.json();
throw new Error(err.error || 'Ошибка сервера');
}
};
}

export const toggleLike = (index) => {
export function login({ login, password }) {
return fetch('https://wedev-api.sky.pro/api/v2/origami/login', {
method: 'POST',
body: JSON.stringify({ login, password }),
})
.then((res) => {
if (!res.ok) throw new Error('Неверные данные');
return res.json();
})
.then((data) => {
token = data.user.token;
user = data.user;
});
}

export function toggleLike(index) {
const c = comments[index];
if (!c) return;
c.isLiked = !c.isLiked;
c.likes += c.isLiked ? 1 : -1;
};
}

export function setLikeLoading(index, value) {
const c = comments[index];
if (!c) return;
c.isLikeLoading = value;
}
8 changes: 8 additions & 0 deletions JS modules/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@ export function sanitize(str) {
.replaceAll('>', '&gt;')
.trim();
}

export function delay(interval = 300) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, interval);
});
}
2 changes: 1 addition & 1 deletion JS modules/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function renderComments(rootEl, comments) {
<span class="likes-counter">${item.likes}</span>
<button class="like-button${
item.isLiked ? ' -active-like' : ''
}" data-index="${index}"></button>
}${item.isLikeLoading ? ' -loading-like' : ''}" data-index="${index}"></button>
</div>
</div>
</li>`,
Expand Down
Loading