From 23b43568d6cb2da782961c97bbffda900e2c0502 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 20:34:29 +0500
Subject: [PATCH 001/202] Update api.js
---
api.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 73 insertions(+), 1 deletion(-)
diff --git a/api.js b/api.js
index 123a9e3b6..a00b363f7 100644
--- a/api.js
+++ b/api.js
@@ -55,7 +55,7 @@ export function loginUser({ login, password }) {
});
}
-// Загружает картинку в облако, возвращает url загруженной картинки
+
export function uploadImage({ file }) {
const data = new FormData();
data.append("file", file);
@@ -67,3 +67,75 @@ export function uploadImage({ file }) {
return response.json();
});
}
+
+
+export function addPost({ token, description, imageUrl }) {
+ return fetch(postsHost, {
+ method: "POST",
+ headers: {
+ Authorization: token,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ description,
+ imageUrl,
+ }),
+ }).then((response) => {
+ if (response.status === 401) {
+ throw new Error("Нет авторизации");
+ }
+ if (response.status === 400) {
+ throw new Error("Не переданы обязательные данные");
+ }
+ return response.json();
+ });
+}
+
+
+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("Нет авторизации");
+ }
+ return response.json();
+ })
+ .then((data) => {
+ return data.posts;
+ });
+}
+
+
+export function likePost({ token, postId }) {
+ return fetch(`${postsHost}/${postId}/like`, {
+ method: "POST",
+ headers: {
+ Authorization: token,
+ },
+ }).then((response) => {
+ if (response.status === 401) {
+ throw new Error("Нет авторизации");
+ }
+ 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("Нет авторизации");
+ }
+ return response.json();
+ });
+}
From 8fbca97542519317e7ac94fab7470ad05ae106a9 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 20:37:27 +0500
Subject: [PATCH 002/202] Update index.js
---
index.js | 101 ++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 85 insertions(+), 16 deletions(-)
diff --git a/index.js b/index.js
index 7f1817c75..09059893a 100644
--- a/index.js
+++ b/index.js
@@ -1,4 +1,10 @@
-import { getPosts } from "./api.js";
+import {
+ getPosts,
+ addPost,
+ getUserPosts,
+ likePost,
+ dislikePost
+} from "./api.js";
import { renderAddPostPageComponent } from "./components/add-post-page-component.js";
import { renderAuthPageComponent } from "./components/auth-page-component.js";
import {
@@ -31,9 +37,9 @@ export const logout = () => {
goToPage(POSTS_PAGE);
};
-/**
- * Включает страницу приложения
- */
+
+
+
export const goToPage = (newPage, data) => {
if (
[
@@ -45,7 +51,7 @@ export const goToPage = (newPage, data) => {
].includes(newPage)
) {
if (newPage === ADD_POSTS_PAGE) {
- /* Если пользователь не авторизован, то отправляем его на страницу авторизации перед добавлением поста */
+
page = user ? ADD_POSTS_PAGE : AUTH_PAGE;
return renderApp();
}
@@ -67,11 +73,19 @@ export const goToPage = (newPage, data) => {
}
if (newPage === USER_POSTS_PAGE) {
- // @@TODO: реализовать получение постов юзера из API
- console.log("Открываю страницу пользователя: ", data.userId);
- page = USER_POSTS_PAGE;
- posts = [];
- return renderApp();
+ page = LOADING_PAGE;
+ renderApp();
+
+ return getUserPosts({ token: getToken(), userId: data.userId })
+ .then((userPosts) => {
+ page = USER_POSTS_PAGE;
+ posts = userPosts;
+ renderApp();
+ })
+ .catch((error) => {
+ console.error(error);
+ goToPage(POSTS_PAGE);
+ });
}
page = newPage;
@@ -83,6 +97,31 @@ export const goToPage = (newPage, data) => {
throw new Error("страницы не существует");
};
+
+export const toggleLike = (postId, isLiked) => {
+ const token = getToken();
+ if (!token) {
+ goToPage(AUTH_PAGE);
+ return Promise.resolve();
+ }
+
+ const likePromise = isLiked ? dislikePost : likePost;
+
+ return likePromise({ token, postId })
+ .then((responseData) => {
+
+ const postIndex = posts.findIndex(post => post.id === postId);
+ if (postIndex !== -1) {
+ posts[postIndex] = responseData.post;
+ }
+ renderApp();
+ })
+ .catch((error) => {
+ console.error("Ошибка при работе с лайком:", error);
+ alert("Не удалось выполнить действие");
+ });
+};
+
const renderApp = () => {
const appEl = document.getElementById("app");
if (page === LOADING_PAGE) {
@@ -110,9 +149,30 @@ const renderApp = () => {
return renderAddPostPageComponent({
appEl,
onAddPostClick({ description, imageUrl }) {
- // @TODO: реализовать добавление поста в API
- console.log("Добавляю пост...", { description, imageUrl });
- goToPage(POSTS_PAGE);
+ const token = getToken();
+ if (!token) {
+ goToPage(AUTH_PAGE);
+ return;
+ }
+
+ if (!description.trim()) {
+ alert("Введите описание поста");
+ return;
+ }
+
+ if (!imageUrl) {
+ alert("Загрузите изображение");
+ return;
+ }
+
+ addPost({ token, description, imageUrl })
+ .then(() => {
+ goToPage(POSTS_PAGE);
+ })
+ .catch((error) => {
+ console.error("Ошибка при добавлении поста:", error);
+ alert("Не удалось добавить пост: " + error.message);
+ });
},
});
}
@@ -120,13 +180,22 @@ const renderApp = () => {
if (page === POSTS_PAGE) {
return renderPostsPageComponent({
appEl,
+ posts,
+ user,
+ goToPage,
+ toggleLike,
});
}
if (page === USER_POSTS_PAGE) {
- // @TODO: реализовать страницу с фотографиями отдельного пользвателя
- appEl.innerHTML = "Здесь будет страница фотографий пользователя";
- return;
+ return renderPostsPageComponent({
+ appEl,
+ posts,
+ user,
+ goToPage,
+ toggleLike,
+ isUserPostsPage: true,
+ });
}
};
From 9f465d7d0e379462952bd00542041cb64fee0f1c Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 20:45:51 +0500
Subject: [PATCH 003/202] Update api.js
---
api.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/api.js b/api.js
index a00b363f7..d40c3506b 100644
--- a/api.js
+++ b/api.js
@@ -1,5 +1,4 @@
-// Замени на свой, чтобы получить независимый от других набор данных.
-// "боевая" версия инстапро лежит в ключе prod
+
const personalKey = "prod";
const baseHost = "https://webdev-hw-api.vercel.app";
const postsHost = `${baseHost}/api/v1/${personalKey}/instapro`;
From 4a92adf7c67e293fc0c5879ba0d3359bd01ca577 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 20:47:14 +0500
Subject: [PATCH 004/202] Update index.js
---
index.js | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/index.js b/index.js
index 09059893a..eea646094 100644
--- a/index.js
+++ b/index.js
@@ -37,9 +37,6 @@ export const logout = () => {
goToPage(POSTS_PAGE);
};
-
-
-
export const goToPage = (newPage, data) => {
if (
[
@@ -51,7 +48,7 @@ export const goToPage = (newPage, data) => {
].includes(newPage)
) {
if (newPage === ADD_POSTS_PAGE) {
-
+
page = user ? ADD_POSTS_PAGE : AUTH_PAGE;
return renderApp();
}
@@ -109,16 +106,18 @@ export const toggleLike = (postId, isLiked) => {
return likePromise({ token, postId })
.then((responseData) => {
-
+
const postIndex = posts.findIndex(post => post.id === postId);
if (postIndex !== -1) {
posts[postIndex] = responseData.post;
}
+
+
renderApp();
})
.catch((error) => {
console.error("Ошибка при работе с лайком:", error);
- alert("Не удалось выполнить действие");
+ alert("Не удалось выполнить действие: " + error.message);
});
};
From 942a65101b42fe6b482c98f4a562c7ca2d139247 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:01:39 +0500
Subject: [PATCH 005/202] Update index.js
---
index.js | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/index.js b/index.js
index eea646094..1a138e927 100644
--- a/index.js
+++ b/index.js
@@ -37,6 +37,9 @@ export const logout = () => {
goToPage(POSTS_PAGE);
};
+/**
+ * Включает страницу приложения
+ */
export const goToPage = (newPage, data) => {
if (
[
@@ -48,7 +51,7 @@ export const goToPage = (newPage, data) => {
].includes(newPage)
) {
if (newPage === ADD_POSTS_PAGE) {
-
+ /* Если пользователь не авторизован, то отправляем его на страницу авторизации перед добавлением поста */
page = user ? ADD_POSTS_PAGE : AUTH_PAGE;
return renderApp();
}
@@ -59,6 +62,7 @@ export const goToPage = (newPage, data) => {
return getPosts({ token: getToken() })
.then((newPosts) => {
+ console.log("Посты из API:", newPosts); // Для отладки
page = POSTS_PAGE;
posts = newPosts;
renderApp();
@@ -94,7 +98,7 @@ export const goToPage = (newPage, data) => {
throw new Error("страницы не существует");
};
-
+// Функция для обработки лайков
export const toggleLike = (postId, isLiked) => {
const token = getToken();
if (!token) {
@@ -106,13 +110,13 @@ export const toggleLike = (postId, isLiked) => {
return likePromise({ token, postId })
.then((responseData) => {
-
+ // Обновляем конкретный пост в массиве posts
const postIndex = posts.findIndex(post => post.id === postId);
if (postIndex !== -1) {
posts[postIndex] = responseData.post;
}
-
+ // Также обновляем страницу для отображения изменений
renderApp();
})
.catch((error) => {
From 480a59ec82da2ae5682833a1bee32e171b76452e Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:03:27 +0500
Subject: [PATCH 006/202] Update posts-page-component.js
---
components/posts-page-component.js | 170 ++++++++++++++---------------
1 file changed, 79 insertions(+), 91 deletions(-)
diff --git a/components/posts-page-component.js b/components/posts-page-component.js
index 662ccdbdc..5f81de629 100644
--- a/components/posts-page-component.js
+++ b/components/posts-page-component.js
@@ -1,97 +1,68 @@
import { USER_POSTS_PAGE } from "../routes.js";
import { renderHeaderComponent } from "./header-component.js";
-import { posts, goToPage } from "../index.js";
+import { formatDate } from "../date-utils.js";
-export function renderPostsPageComponent({ appEl }) {
- // @TODO: реализовать рендер постов из api
- console.log("Актуальный список постов:", posts);
+export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleLike, isUserPostsPage = false }) {
+
+ let pageHeader = '';
+ if (isUserPostsPage && posts.length > 0) {
+ const firstPost = posts[0];
+ pageHeader = `
+
+ `;
+ }
+
+ const postsHtml = posts.map((post) => {
+ const isLiked = post.isLiked;
+ const likeImage = isLiked
+ ? "./assets/images/like-active.svg"
+ : "./assets/images/like-not-active.svg";
+
+
+ const imageSrc = post.imageUrl && post.imageUrl.trim() !== ''
+ ? post.imageUrl
+ : "./assets/images/default-image.jpg";
+
+ return `
+
+
+
+
+
+
+
+
+
+
+ Нравится: ${post.likes.length}
+
+
+
+ ${post.user.name}
+ ${post.description || ''}
+
+
+ ${formatDate(post.createdAt)}
+
+
+ `;
+ }).join("");
- /**
- * @TODO: чтобы отформатировать дату создания поста в виде "19 минут назад"
- * можно использовать https://date-fns.org/v2.29.3/docs/formatDistanceToNow
- */
const appHtml = `
-
-
-
-
-
-
-
-
-
-
-
-
-
- Нравится: 2
-
-
-
- Иван Иваныч
- Ромашка, ромашка...
-
-
- 19 минут назад
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Нравится: 35
-
-
-
- Варварва Н.
- Нарисовала картину, посмотрите какая красивая
-
-
- 3 часа назад
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Нравится: 0
-
-
-
- Варварва Н.
- Голова
-
-
- 8 дней назад
-
-
-
-
`;
+
+
+ ${pageHeader}
+
+ ${postsHtml || 'Постов пока нет
'}
+
+
+ `;
appEl.innerHTML = appHtml;
@@ -99,11 +70,28 @@ export function renderPostsPageComponent({ appEl }) {
element: document.querySelector(".header-container"),
});
- for (let userEl of document.querySelectorAll(".post-header")) {
+
+ document.querySelectorAll(".post-header").forEach(userEl => {
userEl.addEventListener("click", () => {
goToPage(USER_POSTS_PAGE, {
userId: userEl.dataset.userId,
});
});
- }
+ });
+
+
+ document.querySelectorAll('.like-button').forEach(button => {
+ button.addEventListener('click', (event) => {
+ event.stopPropagation();
+ const postId = button.dataset.postId;
+ const isLiked = button.dataset.isLiked === 'true';
+
+
+ button.dataset.isLiked = !isLiked;
+
+ toggleLike(postId, isLiked);
+ });
+ });
+
+
}
From d57a7c9e47abb3679a70c9898b94de700249d4fb Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:12:03 +0500
Subject: [PATCH 007/202] Update posts-page-component.js
---
components/posts-page-component.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/components/posts-page-component.js b/components/posts-page-component.js
index 5f81de629..c9063d329 100644
--- a/components/posts-page-component.js
+++ b/components/posts-page-component.js
@@ -1,6 +1,6 @@
import { USER_POSTS_PAGE } from "../routes.js";
import { renderHeaderComponent } from "./header-component.js";
-import { formatDate } from "../date-utils.js";
+
export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleLike, isUserPostsPage = false }) {
@@ -48,8 +48,8 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
${post.description || ''}
- ${formatDate(post.createdAt)}
-
+ ${post.createdAt}
+
`;
}).join("");
From f9d4c456a92810f69d0c7aa67b3edc5bb73a002e Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:14:48 +0500
Subject: [PATCH 008/202] Update posts-page-component.js
---
components/posts-page-component.js | 97 ++++++++++++++++++++++++++----
1 file changed, 86 insertions(+), 11 deletions(-)
diff --git a/components/posts-page-component.js b/components/posts-page-component.js
index c9063d329..2f0bccd2b 100644
--- a/components/posts-page-component.js
+++ b/components/posts-page-component.js
@@ -2,8 +2,83 @@ import { USER_POSTS_PAGE } from "../routes.js";
import { renderHeaderComponent } from "./header-component.js";
-export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleLike, isUserPostsPage = false }) {
+function simpleFormatDate(dateString) {
+ try {
+ const date = new Date(dateString);
+ const now = new Date();
+ const timeDiff = now - date;
+ const minutes = Math.floor(timeDiff / (1000 * 60));
+ const hours = Math.floor(timeDiff / (1000 * 60 * 60));
+ const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
+
+ if (minutes < 1) {
+ return "менее минуты назад";
+ } else if (minutes < 60) {
+ return `${minutes} ${getMinutesWord(minutes)} назад`;
+ } else if (hours < 24) {
+ return `${hours} ${getHoursWord(hours)} назад`;
+ } else {
+ return `${days} ${getDaysWord(days)} назад`;
+ }
+ } catch (e) {
+ return "недавно";
+ }
+}
+
+
+function getMinutesWord(minutes) {
+ const lastDigit = minutes % 10;
+ const lastTwoDigits = minutes % 100;
+
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
+ return "минут";
+ }
+
+ switch (lastDigit) {
+ case 1: return "минуту";
+ case 2:
+ case 3:
+ case 4: return "минуты";
+ default: return "минут";
+ }
+}
+
+function getHoursWord(hours) {
+ const lastDigit = hours % 10;
+ const lastTwoDigits = hours % 100;
+
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
+ return "часов";
+ }
+
+ switch (lastDigit) {
+ case 1: return "час";
+ case 2:
+ case 3:
+ case 4: return "часа";
+ default: return "часов";
+ }
+}
+
+function getDaysWord(days) {
+ const lastDigit = days % 10;
+ const lastTwoDigits = days % 100;
+
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
+ return "дней";
+ }
+ switch (lastDigit) {
+ case 1: return "день";
+ case 2:
+ case 3:
+ case 4: return "дня";
+ default: return "дней";
+ }
+}
+
+export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleLike, isUserPostsPage = false }) {
+
let pageHeader = '';
if (isUserPostsPage && posts.length > 0) {
const firstPost = posts[0];
@@ -21,7 +96,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
? "./assets/images/like-active.svg"
: "./assets/images/like-not-active.svg";
-
+
const imageSrc = post.imageUrl && post.imageUrl.trim() !== ''
? post.imageUrl
: "./assets/images/default-image.jpg";
@@ -48,8 +123,8 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
${post.description || ''}
- ${post.createdAt}
-
+ ${simpleFormatDate(post.createdAt)}
+
`;
}).join("");
@@ -70,7 +145,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
element: document.querySelector(".header-container"),
});
-
+
document.querySelectorAll(".post-header").forEach(userEl => {
userEl.addEventListener("click", () => {
goToPage(USER_POSTS_PAGE, {
@@ -79,19 +154,19 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
});
});
-
+
document.querySelectorAll('.like-button').forEach(button => {
button.addEventListener('click', (event) => {
- event.stopPropagation();
+ event.stopPropagation(); // Предотвращаем всплытие события
const postId = button.dataset.postId;
const isLiked = button.dataset.isLiked === 'true';
-
+
button.dataset.isLiked = !isLiked;
- toggleLike(postId, isLiked);
+ if (typeof toggleLike === 'function') {
+ toggleLike(postId, isLiked);
+ }
});
});
-
-
}
From ad11cd449bba3958635cfa088ca85ff92244cecc Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:25:02 +0500
Subject: [PATCH 009/202] Update add-post-page-component.js
---
components/add-post-page-component.js | 76 +++++++++++++++++++++++----
1 file changed, 67 insertions(+), 9 deletions(-)
diff --git a/components/add-post-page-component.js b/components/add-post-page-component.js
index 8277f093b..5fe8b0b82 100644
--- a/components/add-post-page-component.js
+++ b/components/add-post-page-component.js
@@ -1,20 +1,78 @@
+import { uploadImage } from "../api.js";
+import { renderUploadImageComponent } from "./upload-image-component.js";
+import { renderHeaderComponent } from "./header-component.js";
+
export function renderAddPostPageComponent({ appEl, onAddPostClick }) {
+ let imageUrl = "";
+
const render = () => {
- // @TODO: Реализовать страницу добавления поста
const appHtml = `
-
-
- Cтраница добавления поста
- Добавить
-
- `;
+
+ `;
appEl.innerHTML = appHtml;
+
+ renderHeaderComponent({
+ element: document.querySelector(".header-container"),
+ });
+
+
+ renderUploadImageComponent({
+ element: document.getElementById("upload-image-conrainer"),
+ onImageUpload: (imageFile) => {
+ document.getElementById("add-button").setAttribute("disabled", true);
+
+ uploadImage({ file: imageFile })
+ .then(({ imageUrl: uploadedImageUrl }) => {
+ imageUrl = uploadedImageUrl;
+ document.getElementById("add-button").removeAttribute("disabled");
+ })
+ .catch((error) => {
+ console.error("Ошибка загрузки изображения:", error);
+ document.getElementById("form-error").textContent = "Ошибка загрузки изображения";
+ document.getElementById("add-button").removeAttribute("disabled");
+ });
+ },
+ });
+
+
document.getElementById("add-button").addEventListener("click", () => {
+ const description = document.getElementById("post-description").value;
+
+
+ if (!description.trim()) {
+ document.getElementById("form-error").textContent = "Введите описание поста";
+ return;
+ }
+
+ if (!imageUrl) {
+ document.getElementById("form-error").textContent = "Загрузите изображение";
+ return;
+ }
+
+
onAddPostClick({
- description: "Описание картинки",
- imageUrl: "https://image.png",
+ description: description.trim(),
+ imageUrl,
});
});
};
From 271aa779675a0d2a8780cd5c71e0387ade2fc2ed Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:26:22 +0500
Subject: [PATCH 010/202] Update styles.css
---
styles.css | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 85 insertions(+)
diff --git a/styles.css b/styles.css
index 57bc08b16..1f6fbab25 100644
--- a/styles.css
+++ b/styles.css
@@ -196,3 +196,88 @@
width: 100%;
text-align: center;
}
+
+
+.form {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ max-width: 500px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+.form-title {
+ font-size: 28px;
+ line-height: 35px;
+ text-align: center;
+ margin-bottom: 20px;
+}
+
+.form-inputs {
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+}
+
+.form-textarea {
+ width: 100%;
+ min-height: 100px;
+ padding: 10px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ font-family: inherit;
+ resize: vertical;
+}
+
+.form-error {
+ color: red;
+ font-size: 14px;
+ text-align: center;
+}
+
+.form-footer {
+ margin-top: 20px;
+ display: flex;
+ justify-content: center;
+}
+
+.button {
+ background-color: #0095f6;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 500;
+ font-size: 16px;
+}
+
+.button:disabled {
+ background-color: #b2dffc;
+ cursor: not-allowed;
+}
+
+.button:hover:not(:disabled) {
+ background-color: #0081d1;
+}
+
+.upload-image-container {
+ margin-bottom: 15px;
+}
+
+
+.uploaded-image-container {
+ display: flex;
+ align-items: center;
+ margin-bottom: 15px;
+}
+
+.uploaded-image {
+ width: 100px;
+ height: 100px;
+ object-fit: cover;
+ margin-right: 10px;
+ border: 1px solid gray;
+ border-radius: 5px;
+}
From eea767d8cb038b613e71f435cca988f592883db7 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:27:34 +0500
Subject: [PATCH 011/202] Update index.js
---
index.js | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/index.js b/index.js
index 1a138e927..6fbdd5c96 100644
--- a/index.js
+++ b/index.js
@@ -3,7 +3,8 @@ import {
addPost,
getUserPosts,
likePost,
- dislikePost
+ dislikePost,
+ uploadImage
} from "./api.js";
import { renderAddPostPageComponent } from "./components/add-post-page-component.js";
import { renderAuthPageComponent } from "./components/auth-page-component.js";
@@ -37,9 +38,7 @@ export const logout = () => {
goToPage(POSTS_PAGE);
};
-/**
- * Включает страницу приложения
- */
+
export const goToPage = (newPage, data) => {
if (
[
@@ -51,7 +50,7 @@ export const goToPage = (newPage, data) => {
].includes(newPage)
) {
if (newPage === ADD_POSTS_PAGE) {
- /* Если пользователь не авторизован, то отправляем его на страницу авторизации перед добавлением поста */
+
page = user ? ADD_POSTS_PAGE : AUTH_PAGE;
return renderApp();
}
@@ -62,7 +61,7 @@ export const goToPage = (newPage, data) => {
return getPosts({ token: getToken() })
.then((newPosts) => {
- console.log("Посты из API:", newPosts); // Для отладки
+ console.log("Посты из API:", newPosts);
page = POSTS_PAGE;
posts = newPosts;
renderApp();
@@ -98,7 +97,7 @@ export const goToPage = (newPage, data) => {
throw new Error("страницы не существует");
};
-// Функция для обработки лайков
+
export const toggleLike = (postId, isLiked) => {
const token = getToken();
if (!token) {
@@ -110,13 +109,13 @@ export const toggleLike = (postId, isLiked) => {
return likePromise({ token, postId })
.then((responseData) => {
- // Обновляем конкретный пост в массиве posts
+
const postIndex = posts.findIndex(post => post.id === postId);
if (postIndex !== -1) {
posts[postIndex] = responseData.post;
}
- // Также обновляем страницу для отображения изменений
+
renderApp();
})
.catch((error) => {
@@ -158,6 +157,7 @@ const renderApp = () => {
return;
}
+
if (!description.trim()) {
alert("Введите описание поста");
return;
@@ -168,8 +168,10 @@ const renderApp = () => {
return;
}
+
addPost({ token, description, imageUrl })
.then(() => {
+
goToPage(POSTS_PAGE);
})
.catch((error) => {
From f4cea14667bd4997e6166c8491ed3b5cdc2bd852 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:43:16 +0500
Subject: [PATCH 012/202] Update loading-page-component.js
---
components/loading-page-component.js | 27 ++++++++-------------------
1 file changed, 8 insertions(+), 19 deletions(-)
diff --git a/components/loading-page-component.js b/components/loading-page-component.js
index c93522573..45b6e3890 100644
--- a/components/loading-page-component.js
+++ b/components/loading-page-component.js
@@ -1,34 +1,23 @@
import { renderHeaderComponent } from "./header-component.js";
-/**
- * Компонент страницы загрузки.
- * Этот компонент отображает страницу с индикатором загрузки и заголовком.
- * Используется для отображения промежуточного состояния, пока выполняется загрузка данных или другой процесс.
- *
- * @param {HTMLElement} params.appEl - Корневой элемент приложения, в который будет рендериться страница загрузки.
- * @param {Object} params.user - Объект пользователя, содержащий данные о текущем авторизованном пользователе (если он есть).
- * @param {Function} params.goToPage - Функция для навигации по страницам.
- */
+
export function renderLoadingPageComponent({ appEl, user, goToPage }) {
- /**
- * HTML-разметка страницы загрузки.
- * Содержит контейнер заголовка и индикатор загрузки.
- */
+
const appHtml = `
-
+
+
+
Загружаем данные...
+
`;
- // Устанавливаем разметку в корневой элемент приложения
+
appEl.innerHTML = appHtml;
- /**
- * Рендеринг заголовка с использованием компонента `renderHeaderComponent`.
- * Передаются данные пользователя и функция навигации.
- */
+
renderHeaderComponent({
user,
element: document.querySelector(".header-container"),
From f054e95e008ffb368648ddc352a1cc48fcde4b9b Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:44:31 +0500
Subject: [PATCH 013/202] Update styles.css
---
styles.css | 328 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 328 insertions(+)
diff --git a/styles.css b/styles.css
index 1f6fbab25..176241db7 100644
--- a/styles.css
+++ b/styles.css
@@ -281,3 +281,331 @@
border: 1px solid gray;
border-radius: 5px;
}
+
+.page-container {
+ padding: 0 20px;
+}
+
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ border-bottom: 1px solid #e0e0e0;
+ padding: 20px 16px;
+ margin-left: -20px;
+ margin-right: -20px;
+}
+
+.logo {
+ margin: 0;
+ font-size: 28px;
+ cursor: pointer;
+ width: 130px;
+}
+
+.header-container {
+ margin-bottom: 8px;
+}
+
+.header-button {
+ padding: 0;
+ border: none;
+ background-color: transparent;
+ font-size: 14px;
+ line-height: 18px;
+ font-weight: 500;
+ cursor: pointer;
+}
+
+.logout-button {
+ width: 130px;
+ text-align: right;
+}
+
+.posts {
+ display: flex;
+ flex-direction: column;
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.post-header {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ margin-bottom: 10px;
+ cursor: pointer;
+}
+
+.post-header__user-image {
+ width: 40px;
+ height: 40px;
+ object-fit: cover;
+ border-radius: 40px;
+ margin-right: 10px;
+}
+
+.post-image-container {
+ margin-left: -20px;
+ margin-right: -20px;
+ height: 500px;
+ display: flex;
+ justify-content: center;
+ background-color: #e0e0e0;
+}
+
+.post-image {
+ width: 100%;
+ height: 100%;
+ max-width: 500px;
+ object-fit: cover;
+}
+
+.post-likes {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.like-button {
+ border: none;
+ background-color: transparent;
+ padding: 8px;
+ padding-left: 0px;
+ padding-bottom: 5px;
+ cursor: pointer;
+ transition: transform 0.2s ease;
+}
+
+.like-button:active {
+ transform: scale(1.2);
+}
+
+.user-name {
+ font-weight: 500;
+}
+
+.post-text {
+ font-size: 14px;
+ line-height: 18px;
+ margin-top: 5px;
+}
+
+.post-date {
+ color: #8a8a8a;
+}
+
+.post + .post {
+ margin-top: 20px;
+}
+
+.posts-user-header {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ margin-bottom: 12px;
+}
+
+.posts-user-header__user-image {
+ width: 70px;
+ height: 70px;
+ object-fit: cover;
+ border-radius: 40px;
+ margin-right: 10px;
+}
+
+.posts-user-header__user-name {
+ font-size: 28px;
+ line-height: 35px;
+}
+
+.loading-page {
+ display: flex;
+ justify-content: center;
+ margin-top: 100px;
+}
+
+.add-post-sign {
+ background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 122.88 122.88'%3E%3Ctitle%3Eadd%3C/title%3E%3Cpath d='M61.44,0A61.46,61.46,0,1,1,18,18,61.25,61.25,0,0,1,61.44,0ZM88.6,56.82v9.24a4,4,0,0,1-4,4H70V84.62a4,4,0,0,1-4,4H56.82a4,4,0,0,1-4-4V70H38.26a4,4,0,0,1-4-4V56.82a4,4,0,0,1,4-4H52.84V38.26a4,4,0,0,1,4-4h9.24a4,4,0,0,1,4,4V52.84H84.62a4,4,0,0,1,4,4Zm8.83-31.37a50.92,50.92,0,1,0,14.9,36,50.78,50.78,0,0,0-14.9-36Z'/%3E%3C/svg%3E");
+ height: 30px;
+ width: 30px;
+}
+
+.form {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+}
+
+.form-title {
+ font-size: 28px;
+ line-height: 35px;
+ text-align: center;
+}
+
+.form-inputs {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.form-error {
+ color: red;
+}
+
+.form-footer {
+ margin-top: 50px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.form-footer-title {
+ text-align: center;
+}
+
+.file-upload-image-conrainer {
+ display: flex;
+ align-items: center;
+}
+
+.file-upload-image {
+ width: 100px;
+ height: 100px;
+ object-fit: cover;
+ margin-right: 10px;
+ border: 1px solid gray;
+ border-radius: 5px;
+}
+
+.file-upload-label {
+ display: inline-block;
+ box-sizing: border-box;
+ width: 100%;
+ text-align: center;
+}
+
+
+
+
+.page-container {
+ opacity: 1;
+ transition: opacity 0.3s ease;
+}
+
+
+.loading-spinner {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 20px;
+}
+
+.spinner {
+ width: 50px;
+ height: 50px;
+ border: 5px solid #f3f3f3;
+ border-top: 5px solid #0095f6;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+
+.post {
+ animation: fadeIn 0.5s ease;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(20px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+
+.like-button.liked {
+ animation: pulse 0.3s ease;
+}
+
+@keyframes pulse {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.3); }
+ 100% { transform: scale(1); }
+}
+
+
+.form-error {
+ padding: 10px;
+ border-radius: 4px;
+ background-color: #ffebee;
+ border-left: 4px solid #f44336;
+ animation: shake 0.5s ease;
+ margin-top: 10px;
+}
+
+@keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 25% { transform: translateX(-5px); }
+ 75% { transform: translateX(5px); }
+}
+
+
+.button {
+ transition: all 0.2s ease;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.button:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
+}
+
+.button:active {
+ transform: translateY(0);
+}
+
+
+.page-transition {
+ opacity: 0;
+ transform: translateY(20px);
+ transition: all 0.3s ease;
+}
+
+.page-transition.active {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+
+.form-textarea {
+ width: 100%;
+ min-height: 100px;
+ padding: 10px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ font-family: inherit;
+ resize: vertical;
+}
+
+.button {
+ background-color: #0095f6;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 500;
+}
+
+.button:disabled {
+ background-color: #b2dffc;
+ cursor: not-allowed;
+}
+
+.upload-image-container {
+ margin-bottom: 15px;
+}
From 0c074cf37e1681214b3e0560f092ba7296aa2d7b Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:45:48 +0500
Subject: [PATCH 014/202] Update posts-page-component.js
---
components/posts-page-component.js | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/components/posts-page-component.js b/components/posts-page-component.js
index 2f0bccd2b..4c5e37ea5 100644
--- a/components/posts-page-component.js
+++ b/components/posts-page-component.js
@@ -96,7 +96,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
? "./assets/images/like-active.svg"
: "./assets/images/like-not-active.svg";
-
+
const imageSrc = post.imageUrl && post.imageUrl.trim() !== ''
? post.imageUrl
: "./assets/images/default-image.jpg";
@@ -145,7 +145,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
element: document.querySelector(".header-container"),
});
-
+
document.querySelectorAll(".post-header").forEach(userEl => {
userEl.addEventListener("click", () => {
goToPage(USER_POSTS_PAGE, {
@@ -154,14 +154,20 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
});
});
-
+
document.querySelectorAll('.like-button').forEach(button => {
button.addEventListener('click', (event) => {
- event.stopPropagation(); // Предотвращаем всплытие события
+ event.stopPropagation();
+
+
+ button.classList.add('liked');
+ setTimeout(() => {
+ button.classList.remove('liked');
+ }, 300);
+
const postId = button.dataset.postId;
const isLiked = button.dataset.isLiked === 'true';
-
button.dataset.isLiked = !isLiked;
if (typeof toggleLike === 'function') {
@@ -169,4 +175,13 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
}
});
});
+
+
+ const container = document.querySelector('.page-container');
+ if (container) {
+ container.classList.add('page-transition');
+ setTimeout(() => {
+ container.classList.add('active');
+ }, 10);
+ }
}
From 772b60260499e451772127ef551706081a53e863 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:46:45 +0500
Subject: [PATCH 015/202] Update index.js
---
index.js | 42 +++++++++++++++++++++++++++++++++---------
1 file changed, 33 insertions(+), 9 deletions(-)
diff --git a/index.js b/index.js
index 6fbdd5c96..c03358d42 100644
--- a/index.js
+++ b/index.js
@@ -39,6 +39,30 @@ export const logout = () => {
};
+const smoothPageTransition = (callback) => {
+ const appEl = document.getElementById("app");
+ const container = appEl.querySelector('.page-container');
+
+ if (container) {
+ container.classList.remove('active');
+ setTimeout(() => {
+ callback();
+ setTimeout(() => {
+ const newContainer = appEl.querySelector('.page-container');
+ if (newContainer) {
+ newContainer.classList.add('page-transition');
+ setTimeout(() => {
+ newContainer.classList.add('active');
+ }, 10);
+ }
+ }, 10);
+ }, 300);
+ } else {
+ callback();
+ }
+};
+
+
export const goToPage = (newPage, data) => {
if (
[
@@ -50,7 +74,7 @@ export const goToPage = (newPage, data) => {
].includes(newPage)
) {
if (newPage === ADD_POSTS_PAGE) {
-
+
page = user ? ADD_POSTS_PAGE : AUTH_PAGE;
return renderApp();
}
@@ -101,7 +125,7 @@ export const goToPage = (newPage, data) => {
export const toggleLike = (postId, isLiked) => {
const token = getToken();
if (!token) {
- goToPage(AUTH_PAGE);
+ smoothPageTransition(() => goToPage(AUTH_PAGE));
return Promise.resolve();
}
@@ -115,7 +139,7 @@ export const toggleLike = (postId, isLiked) => {
posts[postIndex] = responseData.post;
}
-
+
renderApp();
})
.catch((error) => {
@@ -140,7 +164,7 @@ const renderApp = () => {
setUser: (newUser) => {
user = newUser;
saveUserToLocalStorage(user);
- goToPage(POSTS_PAGE);
+ smoothPageTransition(() => goToPage(POSTS_PAGE));
},
user,
goToPage,
@@ -153,11 +177,11 @@ const renderApp = () => {
onAddPostClick({ description, imageUrl }) {
const token = getToken();
if (!token) {
- goToPage(AUTH_PAGE);
+ smoothPageTransition(() => goToPage(AUTH_PAGE));
return;
}
-
+
if (!description.trim()) {
alert("Введите описание поста");
return;
@@ -168,11 +192,11 @@ const renderApp = () => {
return;
}
-
+
addPost({ token, description, imageUrl })
.then(() => {
-
- goToPage(POSTS_PAGE);
+
+ smoothPageTransition(() => goToPage(POSTS_PAGE));
})
.catch((error) => {
console.error("Ошибка при добавлении поста:", error);
From 6e59ac771bac245e7458bf5c9443e2451e80515b Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:54:50 +0500
Subject: [PATCH 016/202] Update README.md
---
README.md | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 34719e139..cfad9f3e6 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,18 @@
# Instapro
MVP социальной сети для обмена фотографиями
-
+Удалось реализовать :
+Анимации интерфейса
+Плавные переходы между страницами
+Анимация лайков при нажатии
+Анимация ошибок формы
+Индикаторы загрузки
+Спиннер загрузки с анимацией
+Визуальная обратная связь при действиях пользователя
## Первоначальная оценка
-ХХХХ часов
+18 часов
## Фактически затраченное время
-YYYY часов
+10 часов
From 6b830a69d1a28046b0b8569d9d9c779fc300dc27 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Thu, 31 Jul 2025 21:56:58 +0500
Subject: [PATCH 017/202] Update index.js
---
index.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/index.js b/index.js
index c03358d42..a42d9ac7e 100644
--- a/index.js
+++ b/index.js
@@ -38,7 +38,6 @@ export const logout = () => {
goToPage(POSTS_PAGE);
};
-
const smoothPageTransition = (callback) => {
const appEl = document.getElementById("app");
const container = appEl.querySelector('.page-container');
From 5344bfb8391ae43894be3b668e8cb474607d9cd9 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Sun, 3 Aug 2025 19:24:27 +0500
Subject: [PATCH 018/202] Add files via upload
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
добавлено изображение
---
assets/images/default-image.jpg | Bin 0 -> 4964 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 assets/images/default-image.jpg
diff --git a/assets/images/default-image.jpg b/assets/images/default-image.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..c2e6d817b8c8e134562fc8369b0e6eb407cb0cea
GIT binary patch
literal 4964
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
Date: Sun, 3 Aug 2025 19:26:53 +0500
Subject: [PATCH 019/202] Update posts-page-component.js
---
components/posts-page-component.js | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/components/posts-page-component.js b/components/posts-page-component.js
index 4c5e37ea5..ff98d9d28 100644
--- a/components/posts-page-component.js
+++ b/components/posts-page-component.js
@@ -96,7 +96,6 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
? "./assets/images/like-active.svg"
: "./assets/images/like-not-active.svg";
-
const imageSrc = post.imageUrl && post.imageUrl.trim() !== ''
? post.imageUrl
: "./assets/images/default-image.jpg";
@@ -148,9 +147,12 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
document.querySelectorAll(".post-header").forEach(userEl => {
userEl.addEventListener("click", () => {
- goToPage(USER_POSTS_PAGE, {
- userId: userEl.dataset.userId,
- });
+ const userId = userEl.dataset.userId;
+ if (userId) {
+ goToPage(USER_POSTS_PAGE, {
+ userId: userEl.dataset.userId,
+ });
+ }
});
});
@@ -159,7 +161,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
button.addEventListener('click', (event) => {
event.stopPropagation();
-
+
button.classList.add('liked');
setTimeout(() => {
button.classList.remove('liked');
@@ -176,7 +178,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
});
});
-
+
const container = document.querySelector('.page-container');
if (container) {
container.classList.add('page-transition');
From 1126d664af5573b00705911b6b292d6bfbfe2166 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Sun, 3 Aug 2025 19:28:51 +0500
Subject: [PATCH 020/202] Update styles.css
---
styles.css | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/styles.css b/styles.css
index 176241db7..f6c2b93b8 100644
--- a/styles.css
+++ b/styles.css
@@ -1,5 +1,7 @@
.page-container {
padding: 0 20px;
+ opacity: 1;
+ transition: opacity 0.3s ease;
}
.page-header {
@@ -382,6 +384,10 @@
transform: scale(1.2);
}
+.like-button.liked {
+ animation: pulse 0.3s ease;
+}
+
.user-name {
font-weight: 500;
}
From 3e192f33f7168f5a74aa0d3d343f1e49427a77df Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Sun, 3 Aug 2025 19:34:57 +0500
Subject: [PATCH 021/202] Update index.js
---
index.js | 50 +++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 41 insertions(+), 9 deletions(-)
diff --git a/index.js b/index.js
index a42d9ac7e..41e2f6a28 100644
--- a/index.js
+++ b/index.js
@@ -38,6 +38,7 @@ export const logout = () => {
goToPage(POSTS_PAGE);
};
+
const smoothPageTransition = (callback) => {
const appEl = document.getElementById("app");
const container = appEl.querySelector('.page-container');
@@ -73,7 +74,7 @@ export const goToPage = (newPage, data) => {
].includes(newPage)
) {
if (newPage === ADD_POSTS_PAGE) {
-
+
page = user ? ADD_POSTS_PAGE : AUTH_PAGE;
return renderApp();
}
@@ -124,7 +125,18 @@ export const goToPage = (newPage, data) => {
export const toggleLike = (postId, isLiked) => {
const token = getToken();
if (!token) {
- smoothPageTransition(() => goToPage(AUTH_PAGE));
+
+ const appEl = document.getElementById("app");
+ const container = appEl.querySelector('.page-container');
+
+ if (container) {
+ container.classList.remove('active');
+ setTimeout(() => {
+ goToPage(AUTH_PAGE);
+ }, 300);
+ } else {
+ goToPage(AUTH_PAGE);
+ }
return Promise.resolve();
}
@@ -132,14 +144,14 @@ export const toggleLike = (postId, isLiked) => {
return likePromise({ token, postId })
.then((responseData) => {
-
+
const postIndex = posts.findIndex(post => post.id === postId);
if (postIndex !== -1) {
posts[postIndex] = responseData.post;
- }
+
-
- renderApp();
+ updatePostInDOM(postId, responseData.post);
+ }
})
.catch((error) => {
console.error("Ошибка при работе с лайком:", error);
@@ -147,6 +159,27 @@ export const toggleLike = (postId, isLiked) => {
});
};
+
+function updatePostInDOM(postId, updatedPost) {
+ const postElement = document.querySelector(`.like-button[data-post-id="${postId}"]`);
+ if (postElement) {
+
+ const likeImage = postElement.querySelector('img');
+ const newLikeImage = updatedPost.isLiked
+ ? "./assets/images/like-active.svg"
+ : "./assets/images/like-not-active.svg";
+ likeImage.src = newLikeImage;
+
+ const likesCountElement = postElement.closest('.post-likes').querySelector('.post-likes-text strong');
+ if (likesCountElement) {
+ likesCountElement.textContent = updatedPost.likes.length;
+ }
+
+
+ postElement.dataset.isLiked = updatedPost.isLiked;
+ }
+}
+
const renderApp = () => {
const appEl = document.getElementById("app");
if (page === LOADING_PAGE) {
@@ -180,7 +213,7 @@ const renderApp = () => {
return;
}
-
+
if (!description.trim()) {
alert("Введите описание поста");
return;
@@ -191,10 +224,9 @@ const renderApp = () => {
return;
}
-
addPost({ token, description, imageUrl })
.then(() => {
-
+ // После успешного добавления переходим к списку постов
smoothPageTransition(() => goToPage(POSTS_PAGE));
})
.catch((error) => {
From 75a15c5f9c0495dc08b2e76dc04c3af35250be00 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Sun, 3 Aug 2025 19:36:06 +0500
Subject: [PATCH 022/202] Update posts-page-component.js
---
components/posts-page-component.js | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/components/posts-page-component.js b/components/posts-page-component.js
index ff98d9d28..e086ebd4f 100644
--- a/components/posts-page-component.js
+++ b/components/posts-page-component.js
@@ -78,7 +78,7 @@ function getDaysWord(days) {
}
export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleLike, isUserPostsPage = false }) {
-
+
let pageHeader = '';
if (isUserPostsPage && posts.length > 0) {
const firstPost = posts[0];
@@ -96,9 +96,10 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
? "./assets/images/like-active.svg"
: "./assets/images/like-not-active.svg";
+
const imageSrc = post.imageUrl && post.imageUrl.trim() !== ''
? post.imageUrl
- : "./assets/images/default-image.jpg";
+ : "https://via.placeholder.com/500x500?text=No+Image";
return `
@@ -107,7 +108,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
-
+
@@ -156,7 +157,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
});
});
-
+
document.querySelectorAll('.like-button').forEach(button => {
button.addEventListener('click', (event) => {
event.stopPropagation();
@@ -170,6 +171,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
const postId = button.dataset.postId;
const isLiked = button.dataset.isLiked === 'true';
+
button.dataset.isLiked = !isLiked;
if (typeof toggleLike === 'function') {
@@ -178,7 +180,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
});
});
-
+
const container = document.querySelector('.page-container');
if (container) {
container.classList.add('page-transition');
From dbf8610c83f16ee8743ef98922ca9012dd35e612 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Sun, 3 Aug 2025 19:37:11 +0500
Subject: [PATCH 023/202] Update styles.css
---
styles.css | 295 +----------------------------------------------------
1 file changed, 5 insertions(+), 290 deletions(-)
diff --git a/styles.css b/styles.css
index f6c2b93b8..cf17298c5 100644
--- a/styles.css
+++ b/styles.css
@@ -1,289 +1,3 @@
-.page-container {
- padding: 0 20px;
- opacity: 1;
- transition: opacity 0.3s ease;
-}
-
-.page-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid #e0e0e0;
- padding: 20px 16px;
- margin-left: -20px;
- margin-right: -20px;
-}
-
-.logo {
- margin: 0;
- font-size: 28px;
- cursor: pointer;
- width: 130px;
-}
-
-.header-container {
- margin-bottom: 8px;
-}
-
-.header-button {
- padding: 0;
- border: none;
- background-color: transparent;
- font-size: 14px;
- line-height: 18px;
- font-weight: 500;
- cursor: pointer;
-}
-
-.logout-button {
- width: 130px;
- text-align: right;
-}
-
-.posts {
- display: flex;
- flex-direction: column;
- list-style: none;
- padding: 0;
- margin: 0;
-}
-
-.post-header {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-bottom: 10px;
- cursor: pointer;
-}
-
-.post-header__user-image {
- width: 40px;
- height: 40px;
- object-fit: cover;
- border-radius: 40px;
- margin-right: 10px;
-}
-
-.post-image-container {
- margin-left: -20px;
- margin-right: -20px;
- height: 500px;
- display: flex;
- justify-content: center;
- background-color: #e0e0e0;
-}
-
-.post-image {
- width: 100%;
- height: 100%;
- max-width: 500px;
- object-fit: cover;
-}
-
-.post-likes {
- display: flex;
- flex-direction: row;
- align-items: center;
-}
-
-.like-button {
- border: none;
- background-color: transparent;
- padding: 8px;
- padding-left: 0px;
- padding-bottom: 5px;
- cursor: pointer;
-}
-
-.user-name {
- font-weight: 500;
-}
-
-.post-text {
- font-size: 14px;
- line-height: 18px;
- margin-top: 5px;
-}
-
-.post-date {
- color: #8a8a8a;
-}
-
-.post + .post {
- margin-top: 20px;
-}
-
-.posts-user-header {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-bottom: 12px;
-}
-
-.posts-user-header__user-image {
- width: 70px;
- height: 70px;
- object-fit: cover;
- border-radius: 40px;
- margin-right: 10px;
-}
-
-.posts-user-header__user-name {
- font-size: 28px;
- line-height: 35px;
-}
-
-.loading-page {
- display: flex;
- justify-content: center;
- margin-top: 100px;
-}
-
-.add-post-sign {
- background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 122.88 122.88'%3E%3Ctitle%3Eadd%3C/title%3E%3Cpath d='M61.44,0A61.46,61.46,0,1,1,18,18,61.25,61.25,0,0,1,61.44,0ZM88.6,56.82v9.24a4,4,0,0,1-4,4H70V84.62a4,4,0,0,1-4,4H56.82a4,4,0,0,1-4-4V70H38.26a4,4,0,0,1-4-4V56.82a4,4,0,0,1,4-4H52.84V38.26a4,4,0,0,1,4-4h9.24a4,4,0,0,1,4,4V52.84H84.62a4,4,0,0,1,4,4Zm8.83-31.37a50.92,50.92,0,1,0,14.9,36,50.78,50.78,0,0,0-14.9-36Z'/%3E%3C/svg%3E");
- height: 30px;
- width: 30px;
-}
-
-.form {
- display: flex;
- flex-direction: column;
- width: 100%;
-}
-
-.form-title {
- font-size: 28px;
- line-height: 35px;
- text-align: center;
-}
-
-.form-inputs {
- display: flex;
- flex-direction: column;
- gap: 10px;
-}
-
-.form-error {
- color: red;
-}
-
-.form-footer {
- margin-top: 50px;
- display: flex;
- flex-direction: column;
- gap: 10px;
-}
-
-.form-footer-title {
- text-align: center;
-}
-
-.file-upload-image-conrainer {
- display: flex;
- align-items: center;
-}
-
-.file-upload-image {
- width: 100px;
- height: 100px;
- object-fit: cover;
- margin-right: 10px;
- border: 1px solid gray;
- border-radius: 5px;
-}
-
-.file-upload-label {
- display: inline-block;
- box-sizing: border-box;
- width: 100%;
- text-align: center;
-}
-
-
-.form {
- display: flex;
- flex-direction: column;
- width: 100%;
- max-width: 500px;
- margin: 0 auto;
- padding: 20px;
-}
-
-.form-title {
- font-size: 28px;
- line-height: 35px;
- text-align: center;
- margin-bottom: 20px;
-}
-
-.form-inputs {
- display: flex;
- flex-direction: column;
- gap: 15px;
-}
-
-.form-textarea {
- width: 100%;
- min-height: 100px;
- padding: 10px;
- border: 1px solid #ccc;
- border-radius: 4px;
- font-family: inherit;
- resize: vertical;
-}
-
-.form-error {
- color: red;
- font-size: 14px;
- text-align: center;
-}
-
-.form-footer {
- margin-top: 20px;
- display: flex;
- justify-content: center;
-}
-
-.button {
- background-color: #0095f6;
- color: white;
- border: none;
- padding: 8px 16px;
- border-radius: 4px;
- cursor: pointer;
- font-weight: 500;
- font-size: 16px;
-}
-
-.button:disabled {
- background-color: #b2dffc;
- cursor: not-allowed;
-}
-
-.button:hover:not(:disabled) {
- background-color: #0081d1;
-}
-
-.upload-image-container {
- margin-bottom: 15px;
-}
-
-
-.uploaded-image-container {
- display: flex;
- align-items: center;
- margin-bottom: 15px;
-}
-
-.uploaded-image {
- width: 100px;
- height: 100px;
- object-fit: cover;
- margin-right: 10px;
- border: 1px solid gray;
- border-radius: 5px;
-}
-
.page-container {
padding: 0 20px;
}
@@ -384,10 +98,6 @@
transform: scale(1.2);
}
-.like-button.liked {
- animation: pulse 0.3s ease;
-}
-
.user-name {
font-weight: 500;
}
@@ -615,3 +325,8 @@
.upload-image-container {
margin-bottom: 15px;
}
+
+
+.post-likes-text strong {
+ transition: all 0.2s ease;
+}
From f403824b1ef8212657c0e99ad4d999258c0ae44d Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Sun, 3 Aug 2025 19:42:44 +0500
Subject: [PATCH 024/202] Update posts-page-component.js
---
components/posts-page-component.js | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/components/posts-page-component.js b/components/posts-page-component.js
index e086ebd4f..5698a13ec 100644
--- a/components/posts-page-component.js
+++ b/components/posts-page-component.js
@@ -25,7 +25,7 @@ function simpleFormatDate(dateString) {
}
}
-
+// Функции для правильного склонения слов
function getMinutesWord(minutes) {
const lastDigit = minutes % 10;
const lastTwoDigits = minutes % 100;
@@ -78,7 +78,7 @@ function getDaysWord(days) {
}
export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleLike, isUserPostsPage = false }) {
-
+
let pageHeader = '';
if (isUserPostsPage && posts.length > 0) {
const firstPost = posts[0];
@@ -96,10 +96,10 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
? "./assets/images/like-active.svg"
: "./assets/images/like-not-active.svg";
-
+
const imageSrc = post.imageUrl && post.imageUrl.trim() !== ''
? post.imageUrl
- : "https://via.placeholder.com/500x500?text=No+Image";
+ : "./assets/images/default-image.jpg";
return `
@@ -108,7 +108,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
-
+
@@ -145,7 +145,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
element: document.querySelector(".header-container"),
});
-
+
document.querySelectorAll(".post-header").forEach(userEl => {
userEl.addEventListener("click", () => {
const userId = userEl.dataset.userId;
@@ -157,12 +157,12 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
});
});
-
+
document.querySelectorAll('.like-button').forEach(button => {
button.addEventListener('click', (event) => {
event.stopPropagation();
-
+
button.classList.add('liked');
setTimeout(() => {
button.classList.remove('liked');
@@ -171,7 +171,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
const postId = button.dataset.postId;
const isLiked = button.dataset.isLiked === 'true';
-
+
button.dataset.isLiked = !isLiked;
if (typeof toggleLike === 'function') {
@@ -180,7 +180,7 @@ export function renderPostsPageComponent({ appEl, posts, user, goToPage, toggleL
});
});
-
+
const container = document.querySelector('.page-container');
if (container) {
container.classList.add('page-transition');
From 4e370d9573e9e4c96dd13482280d27632be255ee Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 17:42:29 +0500
Subject: [PATCH 025/202] Update upload-image-component.js
---
components/upload-image-component.js | 92 +++++++++++++++-------------
1 file changed, 49 insertions(+), 43 deletions(-)
diff --git a/components/upload-image-component.js b/components/upload-image-component.js
index e2cdf2018..2e25b0ca6 100644
--- a/components/upload-image-component.js
+++ b/components/upload-image-component.js
@@ -1,27 +1,11 @@
import { uploadImage } from "../api.js";
-/**
- * Компонент загрузки изображения.
- * Этот компонент позволяет пользователю загружать изображение и отображать его превью.
- * Если изображение уже загружено, пользователь может заменить его.
- *
- * @param {HTMLElement} params.element - HTML-элемент, в который будет рендериться компонент.
- * @param {Function} params.onImageUrlChange - Функция, вызываемая при изменении URL изображения.
- * Принимает один аргумент - новый URL изображения или пустую строку.
- */
+
export function renderUploadImageComponent({ element, onImageUrlChange }) {
- /**
- * URL текущего изображения.
- * Изначально пуст, пока пользователь не загрузит изображение.
- * @type {string}
- */
+
let imageUrl = "";
- /**
- * Функция рендеринга компонента.
- * Отображает интерфейс компонента в зависимости от состояния:
- * либо форма выбора файла, либо превью загруженного изображения с кнопкой замены.
- */
+
const render = () => {
element.innerHTML = `
@@ -47,34 +31,56 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
`;
- // Обработчик выбора файла
+
const fileInputElement = element.querySelector(".file-upload-input");
- fileInputElement?.addEventListener("change", () => {
- const file = fileInputElement.files[0];
- if (file) {
- const labelEl = document.querySelector(".file-upload-label");
- labelEl.setAttribute("disabled", true);
- labelEl.textContent = "Загружаю файл...";
-
- // Загружаем изображение с помощью API
- uploadImage({ file }).then(({ fileUrl }) => {
- imageUrl = fileUrl; // Сохраняем URL загруженного изображения
- onImageUrlChange(imageUrl); // Уведомляем о изменении URL изображения
- render(); // Перерисовываем компонент с новым состоянием
- });
- }
- });
+ if (fileInputElement) {
+ fileInputElement.addEventListener("change", () => {
+ const file = fileInputElement.files[0];
+ if (file) {
+ const labelEl = element.querySelector(".file-upload-label");
+ if (labelEl) {
+ labelEl.setAttribute("disabled", true);
+ labelEl.textContent = "Загружаю файл...";
+ }
+
+
+ uploadImage({ file })
+ .then(({ imageUrl: fileUrl }) => {
+ imageUrl = fileUrl;
+ if (typeof onImageUrlChange === 'function') {
+ onImageUrlChange(imageUrl);
+ }
+ render();
+ })
+ .catch((error) => {
+ console.error("Ошибка при загрузке изображения:", error);
+ if (typeof onImageUrlChange === 'function') {
+ onImageUrlChange("");
+ }
+
+ const labelEl = element.querySelector(".file-upload-label");
+ if (labelEl) {
+ labelEl.removeAttribute("disabled");
+ labelEl.textContent = "Выберите фото";
+ }
+ });
+ }
+ });
+ }
- // Обработчик удаления изображения
- element
- .querySelector(".file-upload-remove-button")
- ?.addEventListener("click", () => {
- imageUrl = ""; // Сбрасываем URL изображения
- onImageUrlChange(imageUrl); // Уведомляем об изменении URL изображения
- render(); // Перерисовываем компонент
+
+ const removeButton = element.querySelector(".file-upload-remove-button");
+ if (removeButton) {
+ removeButton.addEventListener("click", () => {
+ imageUrl = "";
+ if (typeof onImageUrlChange === 'function') {
+ onImageUrlChange(imageUrl);
+ }
+ render();
});
+ }
};
- // Инициализация компонента
+
render();
}
From 072b8edb6cb6f82254cb7d337413f37bf2eae418 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 17:50:57 +0500
Subject: [PATCH 026/202] Update api.js
---
api.js | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/api.js b/api.js
index d40c3506b..6d11ff4c2 100644
--- a/api.js
+++ b/api.js
@@ -1,4 +1,3 @@
-
const personalKey = "prod";
const baseHost = "https://webdev-hw-api.vercel.app";
const postsHost = `${baseHost}/api/v1/${personalKey}/instapro`;
@@ -25,6 +24,9 @@ export function getPosts({ token }) {
export function registerUser({ login, password, name, imageUrl }) {
return fetch(baseHost + "/api/user", {
method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
body: JSON.stringify({
login,
password,
@@ -42,6 +44,9 @@ export function registerUser({ login, password, name, imageUrl }) {
export function loginUser({ login, password }) {
return fetch(baseHost + "/api/user/login", {
method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
body: JSON.stringify({
login,
password,
@@ -62,12 +67,19 @@ export function uploadImage({ file }) {
return fetch(baseHost + "/api/upload/image", {
method: "POST",
body: data,
- }).then((response) => {
- return response.json();
- });
+ })
+ .then((response) => {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then((data) => {
+ console.log("Ответ от сервера uploadImage:", data); // Для отладки
+ return data;
+ });
}
-
export function addPost({ token, description, imageUrl }) {
return fetch(postsHost, {
method: "POST",
@@ -90,7 +102,6 @@ export function addPost({ token, description, imageUrl }) {
});
}
-
export function getUserPosts({ token, userId }) {
return fetch(`${postsHost}/user-posts/${userId}`, {
method: "GET",
@@ -109,7 +120,6 @@ export function getUserPosts({ token, userId }) {
});
}
-
export function likePost({ token, postId }) {
return fetch(`${postsHost}/${postId}/like`, {
method: "POST",
@@ -124,7 +134,6 @@ export function likePost({ token, postId }) {
});
}
-
export function dislikePost({ token, postId }) {
return fetch(`${postsHost}/${postId}/dislike`, {
method: "POST",
From bfc6f0360887130f4e656852906649268eefd0dc Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 17:54:00 +0500
Subject: [PATCH 027/202] Update upload-image-component.js
---
components/upload-image-component.js | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/components/upload-image-component.js b/components/upload-image-component.js
index 2e25b0ca6..6e9945b8e 100644
--- a/components/upload-image-component.js
+++ b/components/upload-image-component.js
@@ -5,7 +5,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
let imageUrl = "";
-
+
const render = () => {
element.innerHTML = `
@@ -31,7 +31,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
`;
-
+
const fileInputElement = element.querySelector(".file-upload-input");
if (fileInputElement) {
fileInputElement.addEventListener("change", () => {
@@ -42,11 +42,12 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
labelEl.setAttribute("disabled", true);
labelEl.textContent = "Загружаю файл...";
}
-
+
uploadImage({ file })
- .then(({ imageUrl: fileUrl }) => {
- imageUrl = fileUrl;
+ .then(({ imageUrl }) => {
+ console.log("Получен URL изображения:", imageUrl);
+ imageUrl = imageUrl;
if (typeof onImageUrlChange === 'function') {
onImageUrlChange(imageUrl);
}
@@ -72,15 +73,15 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
const removeButton = element.querySelector(".file-upload-remove-button");
if (removeButton) {
removeButton.addEventListener("click", () => {
- imageUrl = "";
+ imageUrl = "";
if (typeof onImageUrlChange === 'function') {
onImageUrlChange(imageUrl);
}
- render();
+ render();
});
}
};
-
+
render();
}
From b18e8a09e7fecc44de351cbbd31d72e4d38a921f Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 18:05:03 +0500
Subject: [PATCH 028/202] Update api.js
---
api.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/api.js b/api.js
index 6d11ff4c2..7c28a1282 100644
--- a/api.js
+++ b/api.js
@@ -1,5 +1,5 @@
const personalKey = "prod";
-const baseHost = "https://webdev-hw-api.vercel.app";
+const baseHost = "https://webdev-hw-api.vercel.app";
const postsHost = `${baseHost}/api/v1/${personalKey}/instapro`;
export function getPosts({ token }) {
@@ -75,8 +75,8 @@ export function uploadImage({ file }) {
return response.json();
})
.then((data) => {
- console.log("Ответ от сервера uploadImage:", data); // Для отладки
- return data;
+ console.log("Ответ от сервера uploadImage:", data);
+ return data.fileUrl;
});
}
From e0290bc6d29e07be8790731cf579dcafeb22b6a8 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 18:06:25 +0500
Subject: [PATCH 029/202] Update upload-image-component.js
---
components/upload-image-component.js | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/components/upload-image-component.js b/components/upload-image-component.js
index 6e9945b8e..7f68b5014 100644
--- a/components/upload-image-component.js
+++ b/components/upload-image-component.js
@@ -2,7 +2,7 @@ import { uploadImage } from "../api.js";
export function renderUploadImageComponent({ element, onImageUrlChange }) {
-
+
let imageUrl = "";
@@ -31,7 +31,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
`;
-
+
const fileInputElement = element.querySelector(".file-upload-input");
if (fileInputElement) {
fileInputElement.addEventListener("change", () => {
@@ -43,11 +43,11 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
labelEl.textContent = "Загружаю файл...";
}
-
+
uploadImage({ file })
- .then(({ imageUrl }) => {
- console.log("Получен URL изображения:", imageUrl);
- imageUrl = imageUrl;
+ .then((fileUrl) => {
+ console.log("Получен URL изображения:", fileUrl);
+ imageUrl = fileUrl;
if (typeof onImageUrlChange === 'function') {
onImageUrlChange(imageUrl);
}
@@ -58,7 +58,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
if (typeof onImageUrlChange === 'function') {
onImageUrlChange("");
}
-
+
const labelEl = element.querySelector(".file-upload-label");
if (labelEl) {
labelEl.removeAttribute("disabled");
@@ -73,11 +73,11 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
const removeButton = element.querySelector(".file-upload-remove-button");
if (removeButton) {
removeButton.addEventListener("click", () => {
- imageUrl = "";
+ imageUrl = "";
if (typeof onImageUrlChange === 'function') {
onImageUrlChange(imageUrl);
}
- render();
+ render();
});
}
};
From fc6873efd4947a9765cc87250e249957423a915b Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 18:13:33 +0500
Subject: [PATCH 030/202] Update upload-image-component.js
---
components/upload-image-component.js | 37 +++-------------------------
1 file changed, 3 insertions(+), 34 deletions(-)
diff --git a/components/upload-image-component.js b/components/upload-image-component.js
index 7f68b5014..d1cf43a96 100644
--- a/components/upload-image-component.js
+++ b/components/upload-image-component.js
@@ -31,7 +31,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
`;
-
+
const fileInputElement = element.querySelector(".file-upload-input");
if (fileInputElement) {
fileInputElement.addEventListener("change", () => {
@@ -43,7 +43,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
labelEl.textContent = "Загружаю файл...";
}
-
+
uploadImage({ file })
.then((fileUrl) => {
console.log("Получен URL изображения:", fileUrl);
@@ -53,35 +53,4 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
}
render();
})
- .catch((error) => {
- console.error("Ошибка при загрузке изображения:", error);
- if (typeof onImageUrlChange === 'function') {
- onImageUrlChange("");
- }
-
- const labelEl = element.querySelector(".file-upload-label");
- if (labelEl) {
- labelEl.removeAttribute("disabled");
- labelEl.textContent = "Выберите фото";
- }
- });
- }
- });
- }
-
-
- const removeButton = element.querySelector(".file-upload-remove-button");
- if (removeButton) {
- removeButton.addEventListener("click", () => {
- imageUrl = "";
- if (typeof onImageUrlChange === 'function') {
- onImageUrlChange(imageUrl);
- }
- render();
- });
- }
- };
-
-
- render();
-}
+ .catch((error) {
From f1cc48ca539b5e1b119d534ce4966fa2b3b1c87c Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 18:17:42 +0500
Subject: [PATCH 031/202] Update index.js
---
index.js | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/index.js b/index.js
index 41e2f6a28..966a57d18 100644
--- a/index.js
+++ b/index.js
@@ -125,7 +125,7 @@ export const goToPage = (newPage, data) => {
export const toggleLike = (postId, isLiked) => {
const token = getToken();
if (!token) {
-
+
const appEl = document.getElementById("app");
const container = appEl.querySelector('.page-container');
@@ -144,7 +144,7 @@ export const toggleLike = (postId, isLiked) => {
return likePromise({ token, postId })
.then((responseData) => {
-
+
const postIndex = posts.findIndex(post => post.id === postId);
if (postIndex !== -1) {
posts[postIndex] = responseData.post;
@@ -163,19 +163,20 @@ export const toggleLike = (postId, isLiked) => {
function updatePostInDOM(postId, updatedPost) {
const postElement = document.querySelector(`.like-button[data-post-id="${postId}"]`);
if (postElement) {
-
+
const likeImage = postElement.querySelector('img');
const newLikeImage = updatedPost.isLiked
? "./assets/images/like-active.svg"
: "./assets/images/like-not-active.svg";
likeImage.src = newLikeImage;
+
const likesCountElement = postElement.closest('.post-likes').querySelector('.post-likes-text strong');
if (likesCountElement) {
likesCountElement.textContent = updatedPost.likes.length;
}
-
+
postElement.dataset.isLiked = updatedPost.isLiked;
}
}
@@ -224,9 +225,10 @@ const renderApp = () => {
return;
}
+
addPost({ token, description, imageUrl })
.then(() => {
- // После успешного добавления переходим к списку постов
+
smoothPageTransition(() => goToPage(POSTS_PAGE));
})
.catch((error) => {
From 7ff094df554e3a6ad64574c76d1deb9e6255ed04 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 18:22:23 +0500
Subject: [PATCH 032/202] Update upload-image-component.js
---
components/upload-image-component.js | 41 ++++++++++++++++++++++++----
1 file changed, 36 insertions(+), 5 deletions(-)
diff --git a/components/upload-image-component.js b/components/upload-image-component.js
index d1cf43a96..7a046a112 100644
--- a/components/upload-image-component.js
+++ b/components/upload-image-component.js
@@ -2,10 +2,10 @@ import { uploadImage } from "../api.js";
export function renderUploadImageComponent({ element, onImageUrlChange }) {
-
+
let imageUrl = "";
-
+
const render = () => {
element.innerHTML = `
@@ -31,7 +31,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
`;
-
+
const fileInputElement = element.querySelector(".file-upload-input");
if (fileInputElement) {
fileInputElement.addEventListener("change", () => {
@@ -45,7 +45,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
uploadImage({ file })
- .then((fileUrl) => {
+ .then((fileUrl) => {
console.log("Получен URL изображения:", fileUrl);
imageUrl = fileUrl;
if (typeof onImageUrlChange === 'function') {
@@ -53,4 +53,35 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
}
render();
})
- .catch((error) {
+ .catch((error) => {
+ console.error("Ошибка при загрузке изображения:", error);
+ if (typeof onImageUrlChange === 'function') {
+ onImageUrlChange("");
+ }
+
+ const labelEl = element.querySelector(".file-upload-label");
+ if (labelEl) {
+ labelEl.removeAttribute("disabled");
+ labelEl.textContent = "Выберите фото";
+ }
+ });
+ }
+ });
+ }
+
+
+ const removeButton = element.querySelector(".file-upload-remove-button");
+ if (removeButton) {
+ removeButton.addEventListener("click", () => {
+ imageUrl = "";
+ if (typeof onImageUrlChange === 'function') {
+ onImageUrlChange(imageUrl);
+ }
+ render();
+ });
+ }
+ };
+
+
+ render();
+}
From 945e320255fe4e2f7e0e406a6a982361aa15b5a7 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 18:33:27 +0500
Subject: [PATCH 033/202] Update upload-image-component.js
---
components/upload-image-component.js | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/components/upload-image-component.js b/components/upload-image-component.js
index 7a046a112..481756cec 100644
--- a/components/upload-image-component.js
+++ b/components/upload-image-component.js
@@ -31,7 +31,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
`;
-
+
const fileInputElement = element.querySelector(".file-upload-input");
if (fileInputElement) {
fileInputElement.addEventListener("change", () => {
@@ -43,22 +43,22 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
labelEl.textContent = "Загружаю файл...";
}
-
+
uploadImage({ file })
.then((fileUrl) => {
- console.log("Получен URL изображения:", fileUrl);
- imageUrl = fileUrl;
- if (typeof onImageUrlChange === 'function') {
- onImageUrlChange(imageUrl);
+ console.log("Получен URL изображения:", fileUrl);
+ imageUrl = fileUrl;
+ if (typeof onImageUrlChange === 'function') {
+ onImageUrlChange(imageUrl);
}
- render();
+ render();
})
.catch((error) => {
console.error("Ошибка при загрузке изображения:", error);
if (typeof onImageUrlChange === 'function') {
- onImageUrlChange("");
+ onImageUrlChange("");
}
-
+
const labelEl = element.querySelector(".file-upload-label");
if (labelEl) {
labelEl.removeAttribute("disabled");
@@ -69,15 +69,15 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
});
}
-
+
const removeButton = element.querySelector(".file-upload-remove-button");
if (removeButton) {
removeButton.addEventListener("click", () => {
- imageUrl = "";
- if (typeof onImageUrlChange === 'function') {
- onImageUrlChange(imageUrl);
+ imageUrl = "";
+ if (typeof onImageUrlChange === 'function') {
+ onImageUrlChange(imageUrl);
}
- render();
+ render();
});
}
};
From 60371c1cf2a6c99bb0c449e488f8e232390bba3b Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 18:38:50 +0500
Subject: [PATCH 034/202] Update upload-image-component.js
---
components/upload-image-component.js | 27 +++++++++++++--------------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/components/upload-image-component.js b/components/upload-image-component.js
index 481756cec..3ea8014c9 100644
--- a/components/upload-image-component.js
+++ b/components/upload-image-component.js
@@ -5,7 +5,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
let imageUrl = "";
-
+
const render = () => {
element.innerHTML = `
@@ -31,7 +31,7 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
`;
-
+
const fileInputElement = element.querySelector(".file-upload-input");
if (fileInputElement) {
fileInputElement.addEventListener("change", () => {
@@ -43,22 +43,22 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
labelEl.textContent = "Загружаю файл...";
}
-
+
uploadImage({ file })
- .then((fileUrl) => {
- console.log("Получен URL изображения:", fileUrl);
- imageUrl = fileUrl;
+ .then((fileUrl) => {
+ console.log("Получен URL изображения:", fileUrl);
+ imageUrl = fileUrl; //
if (typeof onImageUrlChange === 'function') {
onImageUrlChange(imageUrl);
}
- render();
+ render();
})
.catch((error) => {
console.error("Ошибка при загрузке изображения:", error);
if (typeof onImageUrlChange === 'function') {
- onImageUrlChange("");
+ onImageUrlChange("");
}
-
+
const labelEl = element.querySelector(".file-upload-label");
if (labelEl) {
labelEl.removeAttribute("disabled");
@@ -69,19 +69,18 @@ export function renderUploadImageComponent({ element, onImageUrlChange }) {
});
}
-
+
const removeButton = element.querySelector(".file-upload-remove-button");
if (removeButton) {
removeButton.addEventListener("click", () => {
imageUrl = "";
- if (typeof onImageUrlChange === 'function') {
- onImageUrlChange(imageUrl);
+ if (typeof onImageUrlChange === 'function') {
+ onImageUrlChange(imageUrl);
}
- render();
+ render();
});
}
};
-
render();
}
From 468680e18c542b8f979e94ee427f0ed8f084f143 Mon Sep 17 00:00:00 2001
From: Alina-skypro2025
Date: Wed, 6 Aug 2025 18:43:02 +0500
Subject: [PATCH 035/202] Update add-post-page-component.js
---
components/add-post-page-component.js | 114 +++++++++++---------------
1 file changed, 47 insertions(+), 67 deletions(-)
diff --git a/components/add-post-page-component.js b/components/add-post-page-component.js
index 5fe8b0b82..92269f91a 100644
--- a/components/add-post-page-component.js
+++ b/components/add-post-page-component.js
@@ -1,81 +1,61 @@
import { uploadImage } from "../api.js";
import { renderUploadImageComponent } from "./upload-image-component.js";
-import { renderHeaderComponent } from "./header-component.js";
export function renderAddPostPageComponent({ appEl, onAddPostClick }) {
- let imageUrl = "";
+ let currentImageUrl = "";
- const render = () => {
- const appHtml = `
-