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
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@

MVP социальной сети для обмена фотографиями

В данном приложении было выполнено:

1. Интеграция верстки списка постов с API,
2. Реализация страницы добавления поста,
3. Реализация страницы с постами конкретного юзера,
4. Реализация функционала лайков,
5. Реализация приложение в комфортный и современный стиль.

## Первоначальная оценка

ХХХХ часов
100 часов

## Фактически затраченное время

YYYY часов
80 часов
108 changes: 105 additions & 3 deletions api.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Замени на свой, чтобы получить независимый от других набор данных.
// "боевая" версия инстапро лежит в ключе prod
const personalKey = "prod";
const baseHost = "https://webdev-hw-api.vercel.app";
const personalKey = "vasiliev_nikolay";
const baseHost = "https://wedev-api.sky.pro";
const postsHost = `${baseHost}/api/v1/${personalKey}/instapro`;

export function getPosts({ token }) {
Expand All @@ -15,17 +15,107 @@ export function getPosts({ token }) {
if (response.status === 401) {
throw new Error("Нет авторизации");
}
if (!response.ok) {
throw new Error(`Ошибка сервера: ${response.status}`);
}
return response.json();
})
.then((data) => {
return data.posts;
});
}

export function getUserPosts({ token, userId }) {
return fetch(`${postsHost}/user-posts/${userId}`, {
method: "GET",
headers: {
Authorization: token,
},
})
.then((response) => {
if (response.status === 401) {
throw new Error("Нет авторизации");
}
if (!response.ok) {
throw new Error(`Ошибка сервера: ${response.status}`);
}
return response.json();
})
.then((data) => {
return data.posts;
});
}


export function addPost({ token, description, imageUrl }) {
// Отправляем как JSON
return fetch(postsHost, {
method: "POST",
headers: {
Authorization: token,

},
body: JSON.stringify({
description: description,
imageUrl: imageUrl,
}),
})
.then(async (response) => {
const text = await response.text();
console.log("addPost response status:", response.status);
console.log("addPost response body:", text);

if (response.status === 401) {
throw new Error("Нет авторизации");
}
if (!response.ok) {
throw new Error(`Ошибка сервера: ${response.status} - ${text}`);
}
return JSON.parse(text);
});
}


export function likePost({ token, postId }) {
return fetch(`${postsHost}/${postId}/like`, {
method: "POST",
headers: {
Authorization: token,
},
}).then((response) => {
if (response.status === 401) {
throw new Error("Нет авторизации");
}
if (!response.ok) {
throw new Error(`Ошибка сервера: ${response.status}`);
}
return response.json();
});
}

export function dislikePost({ token, postId }) {
return fetch(`${postsHost}/${postId}/dislike`, {
method: "POST",
headers: {
Authorization: token,
},
}).then((response) => {
if (response.status === 401) {
throw new Error("Нет авторизации");
}
if (!response.ok) {
throw new Error(`Ошибка сервера: ${response.status}`);
}
return response.json();
});
}

export function registerUser({ login, password, name, imageUrl }) {
return fetch(baseHost + "/api/user", {
method: "POST",
headers: {

},
body: JSON.stringify({
login,
password,
Expand All @@ -36,13 +126,19 @@ export function registerUser({ login, password, name, imageUrl }) {
if (response.status === 400) {
throw new Error("Такой пользователь уже существует");
}
if (!response.ok) {
throw new Error(`Ошибка сервера: ${response.status}`);
}
return response.json();
});
}

export function loginUser({ login, password }) {
return fetch(baseHost + "/api/user/login", {
method: "POST",
headers: {

},
body: JSON.stringify({
login,
password,
Expand All @@ -51,6 +147,9 @@ export function loginUser({ login, password }) {
if (response.status === 400) {
throw new Error("Неверный логин или пароль");
}
if (!response.ok) {
throw new Error(`Ошибка сервера: ${response.status}`);
}
return response.json();
});
}
Expand All @@ -64,6 +163,9 @@ export function uploadImage({ file }) {
method: "POST",
body: data,
}).then((response) => {
if (!response.ok) {
throw new Error(`Ошибка загрузки: ${response.status}`);
}
return response.json();
});
}
}
89 changes: 75 additions & 14 deletions components/add-post-page-component.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,84 @@
import { renderHeaderComponent } from "./header-component.js";
import { renderUploadImageComponent } from "./upload-image-component.js";

// Функция для очистки HTML-тегов
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

export function renderAddPostPageComponent({ appEl, onAddPostClick }) {
let imageUrl = "";
let isLoading = false;

const render = () => {
// @TODO: Реализовать страницу добавления поста
const appHtml = `
<div class="page-container">
<div class="header-container"></div>
Cтраница добавления поста
<button class="button" id="add-button">Добавить</button>
</div>
`;
appEl.innerHTML = `
<div class="page-container">
<div class="header-container"></div>
<div class="form">
<h2 class="form-title">Новый пост</h2>
<div class="form-inputs">
<div id="upload-image-container"></div>
<textarea
id="post-description"
class="input"
placeholder="Введите описание поста..."
rows="4"
style="resize: vertical;"
></textarea>
<p class="form-error" id="form-error" style="display:none;"></p>
</div>
<div class="form-footer">
<button class="button" id="add-button" ${isLoading ? "disabled" : ""}>
${isLoading ? "Добавляю..." : "Опубликовать"}
</button>
</div>
</div>
</div>
`;

appEl.innerHTML = appHtml;
renderHeaderComponent({
element: document.querySelector(".header-container"),
});

renderUploadImageComponent({
element: document.getElementById("upload-image-container"),
onImageUrlChange(url) {
imageUrl = url;
},
});

document.getElementById("add-button").addEventListener("click", () => {
onAddPostClick({
description: "Описание картинки",
imageUrl: "https://image.png",
});
const descriptionInput = document.getElementById("post-description");
const description = descriptionInput ? descriptionInput.value.trim() : "";
const errorEl = document.getElementById("form-error");

if (!imageUrl) {
errorEl.textContent = "Пожалуйста, загрузите фото";
errorEl.style.display = "block";
return;
}

if (!description) {
errorEl.textContent = "Пожалуйста, введите описание";
errorEl.style.display = "block";
return;
}

// Экранируем HTML-теги из описания перед отправкой
const sanitizedDescription = escapeHtml(description);

errorEl.style.display = "none";
isLoading = true;
render();

onAddPostClick({ description: sanitizedDescription, imageUrl });
});
};

render();
}
}
Loading