From f3e3e599181d3e1aa5c6dc1427efad3ff459604b Mon Sep 17 00:00:00 2001 From: DmitriiD89 Date: Sat, 29 Nov 2025 15:18:14 +0500 Subject: [PATCH 1/5] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=20post-page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/posts-page-component.js | 106 +++++++++++------------------ 1 file changed, 39 insertions(+), 67 deletions(-) diff --git a/components/posts-page-component.js b/components/posts-page-component.js index 662ccdbdc..59f9b143e 100644 --- a/components/posts-page-component.js +++ b/components/posts-page-component.js @@ -1,6 +1,8 @@ import { USER_POSTS_PAGE } from "../routes.js"; import { renderHeaderComponent } from "./header-component.js"; import { posts, goToPage } from "../index.js"; +import { formatDistanceToNow } from "https://cdn.skypack.dev/date-fns"; +import { ru } from "https://cdn.skypack.dev/date-fns/locale"; export function renderPostsPageComponent({ appEl }) { // @TODO: реализовать рендер постов из api @@ -10,88 +12,58 @@ export function renderPostsPageComponent({ appEl }) { * @TODO: чтобы отформатировать дату создания поста в виде "19 минут назад" * можно использовать https://date-fns.org/v2.29.3/docs/formatDistanceToNow */ - const appHtml = ` + const appHtml = posts + .map((post, index) => { + return `
-
`; + `; + }) + .join(""); appEl.innerHTML = appHtml; From 054bb5513920431c1b0b91147b9bb53db899f528 Mon Sep 17 00:00:00 2001 From: DmitriiD89 Date: Sat, 29 Nov 2025 16:10:39 +0500 Subject: [PATCH 2/5] =?UTF-8?q?=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=D0=B8=20add-post-page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.js | 22 +++++++++++++- components/add-post-page-component.js | 42 +++++++++++++++++++++------ index.js | 15 ++++++++-- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/api.js b/api.js index 123a9e3b6..5ade2735b 100644 --- a/api.js +++ b/api.js @@ -4,6 +4,25 @@ const personalKey = "prod"; const baseHost = "https://webdev-hw-api.vercel.app"; const postsHost = `${baseHost}/api/v1/${personalKey}/instapro`; +export async function addNewPost({token, description, imageUrl}){ + return fetch(postsHost,{ + method: "POST", + headers:{ + Authorization: token, + },body:JSON.stringify({ + description, + imageUrl + }) + }) + .then((response)=>{ + if (response.status === 400){ + throw new Error('Не удалось добавить пост') + } + return response.json(); + }) + +} + export function getPosts({ token }) { return fetch(postsHost, { method: "GET", @@ -23,7 +42,8 @@ export function getPosts({ token }) { }); } -export function registerUser({ login, password, name, imageUrl }) { + +export async function registerUser({ login, password, name, imageUrl }) { return fetch(baseHost + "/api/user", { method: "POST", body: JSON.stringify({ diff --git a/components/add-post-page-component.js b/components/add-post-page-component.js index 8277f093b..2fdd02ecf 100644 --- a/components/add-post-page-component.js +++ b/components/add-post-page-component.js @@ -1,23 +1,47 @@ +import { renderHeaderComponent } from "./header-component.js"; +import { renderUploadImageComponent } from './upload-image-component.js' export function renderAddPostPageComponent({ appEl, onAddPostClick }) { + const render = () => { // @TODO: Реализовать страницу добавления поста - const appHtml = ` + let appHtml = `
-
- Cтраница добавления поста - -
+
+
+

Добавить пост

+
+
+
+ + +
+
+ `; - + let imageUrl = ''; appEl.innerHTML = appHtml; - + renderHeaderComponent({element: document.querySelector('.header-container')}) + renderUploadImageComponent({element: document.querySelector('.upload-image-container'), + onImageUrlChange:(url)=> imageUrl = url}) + let textAreaElement = document.getElementById('post-description') + document.getElementById("add-button").addEventListener("click", () => { + if (!textAreaElement.value || !imageUrl){ + alert ('Заполните все поля') + return + } + onAddPostClick({ - description: "Описание картинки", - imageUrl: "https://image.png", + description: textAreaElement.value, + imageUrl: imageUrl, }); + }); }; render(); + } diff --git a/index.js b/index.js index 7f1817c75..78f5f2547 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -import { getPosts } from "./api.js"; +import { getPosts, addNewPost } from "./api.js"; import { renderAddPostPageComponent } from "./components/add-post-page-component.js"; import { renderAuthPageComponent } from "./components/auth-page-component.js"; import { @@ -20,7 +20,7 @@ export let user = getUserFromLocalStorage(); export let page = null; export let posts = []; -const getToken = () => { +export const getToken = () => { const token = user ? `Bearer ${user.token}` : undefined; return token; }; @@ -34,6 +34,13 @@ export const logout = () => { /** * Включает страницу приложения */ + +export const updatePosts = (newPosts) => { + posts = newPosts +} + + + export const goToPage = (newPage, data) => { if ( [ @@ -110,11 +117,13 @@ const renderApp = () => { return renderAddPostPageComponent({ appEl, onAddPostClick({ description, imageUrl }) { + addNewPost({ token:getToken(), description, imageUrl}) // @TODO: реализовать добавление поста в API console.log("Добавляю пост...", { description, imageUrl }); goToPage(POSTS_PAGE); }, - }); + }) + } if (page === POSTS_PAGE) { From 0185e1ba5781726db8c1486b0507c118db202ef9 Mon Sep 17 00:00:00 2001 From: DmitriiD89 Date: Wed, 3 Dec 2025 13:26:39 +0500 Subject: [PATCH 3/5] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D1=82=D0=BE=D0=B2=20=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.js | 18 +++++++ components/posts-page-component.js | 21 +++++--- components/user-post-page-component.js | 70 ++++++++++++++++++++++++++ index.js | 22 +++++--- 4 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 components/user-post-page-component.js diff --git a/api.js b/api.js index 5ade2735b..95136e409 100644 --- a/api.js +++ b/api.js @@ -87,3 +87,21 @@ export function uploadImage({ file }) { return response.json(); }); } + +export async function getUserPosts({token,id}){ + try { + const response = await fetch(postsHost+'/user-posts/'+id,{ + method: 'GET', + headers:{ + Authorization: token, + } + }) + if (!response.ok){ + throw new Error('Ошибка запроса') + } + return await response.json() + } catch (error) { + console.log(error); + } +}//Сетевой запрос постов пользователя + diff --git a/components/posts-page-component.js b/components/posts-page-component.js index 59f9b143e..fd9377175 100644 --- a/components/posts-page-component.js +++ b/components/posts-page-component.js @@ -12,13 +12,14 @@ export function renderPostsPageComponent({ appEl }) { * @TODO: чтобы отформатировать дату создания поста в виде "19 минут назад" * можно использовать https://date-fns.org/v2.29.3/docs/formatDistanceToNow */ - const appHtml = posts - .map((post, index) => { - return ` + const appHtml = + + `
    -
  • + ${posts.map((post) => ( + `
  • +
    ` + + + appEl.innerHTML = appHtml; diff --git a/components/user-post-page-component.js b/components/user-post-page-component.js new file mode 100644 index 000000000..c959ea0c8 --- /dev/null +++ b/components/user-post-page-component.js @@ -0,0 +1,70 @@ +import { posts } from "../index.js"; +import { formatDistanceToNow } from "https://cdn.skypack.dev/date-fns"; +import { ru } from "https://cdn.skypack.dev/date-fns/locale"; +import { renderHeaderComponent } from "./header-component.js"; + +export function renderUserPostsPageComponent({ appEl }) { + let userPosts = posts.posts; + + console.log(userPosts); + const appHtml = + ` +
    +
    +
      + ${userPosts.map((post) => ( + `
    • +
      + +

      ${post.user.name}

      +
      +
      + +
      +
      + +

      + Нравится: ${ + post.likes.length + ? post.likes.length > 1 + ? `${ + post.likes[ + Math.floor( + Math.random() * post.likes.length + ) + ].name + } и еще ${post.likes.length - 1}` + : post.likes[0].name + : "0" + } +

      +
      +

      + ${post.user.name} + ${post.description} +

      + +
    • ` + ) + +).join('')} +
    +
    ` + + appEl.innerHTML = appHtml; + renderHeaderComponent({ + element: document.querySelector(".header-container"), + }); +} diff --git a/index.js b/index.js index 78f5f2547..60d4ccd48 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -import { getPosts, addNewPost } from "./api.js"; +import { getPosts, addNewPost, getUserPosts } from "./api.js"; import { renderAddPostPageComponent } from "./components/add-post-page-component.js"; import { renderAuthPageComponent } from "./components/auth-page-component.js"; import { @@ -15,6 +15,7 @@ import { removeUserFromLocalStorage, saveUserToLocalStorage, } from "./helpers.js"; +import { renderUserPostsPageComponent } from "./components/user-post-page-component.js"; export let user = getUserFromLocalStorage(); export let page = null; @@ -74,11 +75,20 @@ export const goToPage = (newPage, data) => { } if (newPage === USER_POSTS_PAGE) { + page = LOADING_PAGE; + renderApp(); // @@TODO: реализовать получение постов юзера из API console.log("Открываю страницу пользователя: ", data.userId); - page = USER_POSTS_PAGE; - posts = []; - return renderApp(); + return getUserPosts({token: getToken(),id: data.userId}) + .then ((userPosts)=>{ + posts = userPosts; + page = USER_POSTS_PAGE; + renderApp(); + } + ).catch((error)=>{ + console.log(error); + goToPage(POSTS_PAGE) + }) } page = newPage; @@ -134,8 +144,8 @@ const renderApp = () => { if (page === USER_POSTS_PAGE) { // @TODO: реализовать страницу с фотографиями отдельного пользвателя - appEl.innerHTML = "Здесь будет страница фотографий пользователя"; - return; + + return renderUserPostsPageComponent({appEl}); } }; From c6a4ff62b82819b7dbceccf752dd2c40df6e84a7 Mon Sep 17 00:00:00 2001 From: DmitriiD89 Date: Fri, 5 Dec 2025 13:59:34 +0500 Subject: [PATCH 4/5] =?UTF-8?q?=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BB=D0=B0=D0=B9=D0=BA=20=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.js | 20 +++++++++++++++++--- components/posts-page-component.js | 17 ++++++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/api.js b/api.js index 95136e409..19470487e 100644 --- a/api.js +++ b/api.js @@ -89,7 +89,7 @@ export function uploadImage({ file }) { } export async function getUserPosts({token,id}){ - try { + const response = await fetch(postsHost+'/user-posts/'+id,{ method: 'GET', headers:{ @@ -100,8 +100,22 @@ export async function getUserPosts({token,id}){ throw new Error('Ошибка запроса') } return await response.json() - } catch (error) { +}//Сетевой запрос постов пользователя + +export async function setLike({token,id}){ + try{ + const response = await fetch (postsHost+`/${id}/like`,{ + method: 'POST', + headers:{ + Authorization:token, + } + }) + if (!response.ok){ + throw new Error('сетевая ошибка') + } + return await response.json() + } catch (error){ console.log(error); } -}//Сетевой запрос постов пользователя +} diff --git a/components/posts-page-component.js b/components/posts-page-component.js index fd9377175..e2519422e 100644 --- a/components/posts-page-component.js +++ b/components/posts-page-component.js @@ -31,7 +31,7 @@ export function renderPostsPageComponent({ appEl }) {
` - + appEl.innerHTML = appHtml; renderHeaderComponent({ element: document.querySelector(".header-container"), - }); + } + ); + toggleLike(); } diff --git a/helpers.js b/helpers.js index b47d3b777..95c52446c 100644 --- a/helpers.js +++ b/helpers.js @@ -1,15 +1,55 @@ +import { setLike, unsetLike } from "../api.js"; +import { posts, getToken } from "../index.js"; + export function saveUserToLocalStorage(user) { window.localStorage.setItem("user", JSON.stringify(user)); } -export function getUserFromLocalStorage(user) { +export function getUserFromLocalStorage() { try { return JSON.parse(window.localStorage.getItem("user")); } catch (error) { return null; } -} +}; export function removeUserFromLocalStorage(user) { window.localStorage.removeItem("user"); +}; + +export function toggleLike(){ + for (let buttonLike of document.querySelectorAll(".like-button")) { + buttonLike.addEventListener("click", async (event) => { + if (!getUserFromLocalStorage()){ + alert('Сначала авторизуйтесь') + return + } + const postId = event.target.dataset.postId; + const post = posts.find((post) => post.id === postId); + const postLikes = event.target.closest(".post-likes"); + const isLiked = !post.isLiked; + const likeImageElement = postLikes.querySelector("img"); + const postLikeTextElement = postLikes.querySelector(".post-likes-text"); + + likeImageElement.src = isLiked + ? "./assets/images/like-active.svg" + : "./assets/images/like-not-active.svg"; + let response; + if (post.isLiked) { + response = await unsetLike({ token: getToken(), id: postId }); + } else { + response = await setLike({ token: getToken(), id: postId }); + } + post.likes = response.post.likes; + post.isLiked = response.post.isLiked; + postLikeTextElement.innerHTML = ` Нравится: ${ + post.likes.length + ? post.likes.length > 1 + ? `${ post.likes[Math.floor(Math.random() * post.likes.length)].name } и еще ${post.likes.length - 1}` + : post.likes[0].name + : "0" + }`; + console.log(post.likes, post.isLiked,post); + }); + } }