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
162 changes: 145 additions & 17 deletions api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Замени на свой, чтобы получить независимый от других набор данных.
// "боевая" версия инстапро лежит в ключе prod
// api.js - ПОЛНОСТЬЮ ИСПРАВЛЕННАЯ ВЕРСИЯ

const personalKey = "prod";
const baseHost = "https://webdev-hw-api.vercel.app";
const postsHost = `${baseHost}/api/v1/${personalKey}/instapro`;
Expand All @@ -15,11 +15,113 @@ export function getPosts({ token }) {
if (response.status === 401) {
throw new Error("Нет авторизации");
}
if (!response.ok) {
throw new Error(`Ошибка ${response.status}: ${response.statusText}`);
}
return response.json();
})
.then((data) => {
console.log("getPosts response:", data);
return data.posts || [];
});
}

export function getUserPosts({ token, userId }) {
return fetch(postsHost, {
method: "GET",
headers: {
Authorization: token,
},
})
.then((response) => {
if (response.status === 401) {
throw new Error("Нет авторизации");
}
if (!response.ok) {
throw new Error(`Ошибка ${response.status}: ${response.statusText}`);
}
return response.json();
})
.then((data) => {
return data.posts;
console.log("getUserPosts response:", data);
const allPosts = data.posts || [];
return allPosts.filter(post => post.user?.id === userId);
});
}

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}: ${response.statusText}`);
}
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}: ${response.statusText}`);
}
return response.json();
});
}

export function addPost({ token, description, imageUrl }) {
if (!description || !imageUrl) {
return Promise.reject(new Error("Описание и изображение обязательны"));
}

console.log("Sending post data:", { description, imageUrl });

return fetch(postsHost, {
method: "POST",
headers: {
Authorization: token,
},
body: JSON.stringify({
description: description.trim(),
imageUrl: imageUrl.trim(),
}),
})
.then(async (response) => {
const responseText = await response.text();
console.log("Response status:", response.status);
console.log("Response text:", responseText);

if (response.status === 401) {
throw new Error("Нет авторизации");
}
if (response.status === 400) {
throw new Error("Неверные данные поста");
}
if (!response.ok) {
throw new Error(`Ошибка ${response.status}: ${responseText}`);
}

try {
return JSON.parse(responseText);
} catch (e) {
return { success: true };
}
});
}

Expand All @@ -32,12 +134,22 @@ export function registerUser({ login, password, name, imageUrl }) {
name,
imageUrl,
}),
}).then((response) => {
if (response.status === 400) {
throw new Error("Такой пользователь уже существует");
}
return response.json();
});
})
.then(async (response) => {
const responseText = await response.text();
console.log("Register response:", response.status, responseText);

if (response.status === 400) {
throw new Error("Такой пользователь уже существует");
}
if (!response.ok) {
throw new Error(`Ошибка ${response.status}: ${responseText}`);
}

const data = JSON.parse(responseText);
// Возвращаем пользователя в правильном формате
return { user: data.user };
});
}

export function loginUser({ login, password }) {
Expand All @@ -47,15 +159,28 @@ export function loginUser({ login, password }) {
login,
password,
}),
}).then((response) => {
if (response.status === 400) {
throw new Error("Неверный логин или пароль");
}
return response.json();
});
})
.then(async (response) => {
const responseText = await response.text();
console.log("Login response:", response.status, responseText);

if (response.status === 400) {
throw new Error("Неверный логин или пароль");
}
if (response.status === 401) {
throw new Error("Не авторизован");
}
if (!response.ok) {
throw new Error(`Ошибка ${response.status}: ${responseText}`);
}

const data = JSON.parse(responseText);
console.log("Parsed login data:", data);
// Возвращаем пользователя в правильном формате
return { user: data.user };
});
}

// Загружает картинку в облако, возвращает url загруженной картинки
export function uploadImage({ file }) {
const data = new FormData();
data.append("file", file);
Expand All @@ -64,6 +189,9 @@ export function uploadImage({ file }) {
method: "POST",
body: data,
}).then((response) => {
if (!response.ok) {
throw new Error(`Ошибка загрузки изображения: ${response.status}`);
}
return response.json();
});
}
}
112 changes: 99 additions & 13 deletions components/add-post-page-component.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,109 @@
import { renderHeaderComponent } from "./header-component.js";
import { renderUploadImageComponent } from "./upload-image-component.js";
import { goToPage } from "../index.js";
import { POSTS_PAGE } from "../routes.js";

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

const render = () => {
// @TODO: Реализовать страницу добавления поста
const appHtml = `
<div class="page-container">
<div class="header-container"></div>
Cтраница добавления поста
<button class="button" id="add-button">Добавить</button>
</div>
`;
<div class="page-container">
<div class="header-container"></div>
<div class="form">
<h3 class="form-title">Добавить новый пост</h3>
<div class="form-inputs">
<div class="upload-image-container"></div>
<textarea
id="description-input"
class="input textarea"
placeholder="Введите описание..."
rows="4"
></textarea>
<div class="form-error"></div>
<button class="button" id="add-button">Опубликовать</button>
<button class="secondary-button" id="cancel-button">Отмена</button>
</div>
</div>
</div>
`;

appEl.innerHTML = appHtml;

document.getElementById("add-button").addEventListener("click", () => {
onAddPostClick({
description: "Описание картинки",
imageUrl: "https://image.png",
});
renderHeaderComponent({
element: document.querySelector(".header-container"),
});

const uploadImageContainer = appEl.querySelector(".upload-image-container");
renderUploadImageComponent({
element: uploadImageContainer,
onImageUrlChange(newImageUrl) {
imageUrl = newImageUrl;
console.log("Image URL updated:", imageUrl);
const addButton = document.getElementById("add-button");
if (addButton) {
addButton.disabled = !imageUrl || !description;
}
},
});

const descriptionInput = document.getElementById("description-input");
const addButton = document.getElementById("add-button");
const cancelButton = document.getElementById("cancel-button");
const errorDiv = document.querySelector(".form-error");

descriptionInput.addEventListener("input", (e) => {
description = e.target.value;
console.log("Description updated:", description);
if (addButton) {
addButton.disabled = !imageUrl || !description.trim();
}
if (errorDiv) {
errorDiv.textContent = "";
}
});

addButton.addEventListener("click", async () => {
console.log("Add button clicked");
console.log("Current imageUrl:", imageUrl);
console.log("Current description:", description);

if (!imageUrl) {
errorDiv.textContent = "Пожалуйста, выберите изображение";
return;
}

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

addButton.disabled = true;
addButton.textContent = "Публикация...";

try {
const postData = {
description: description.trim(),
imageUrl: imageUrl,
};
console.log("Sending post data:", postData);

await onAddPostClick(postData);
console.log("Post added successfully");
goToPage(POSTS_PAGE);
} catch (error) {
console.error("Ошибка при добавлении поста:", error);
errorDiv.textContent = error.message || "Ошибка при публикации";
addButton.disabled = false;
addButton.textContent = "Опубликовать";
}
});

cancelButton.addEventListener("click", () => {
goToPage(POSTS_PAGE);
});
};

render();
}
}
45 changes: 45 additions & 0 deletions components/date-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export function formatDistanceToNow(date) {
const now = new Date();
const diffInSeconds = Math.floor((now - date) / 1000);

if (diffInSeconds < 0) return "только что";

const intervals = [
{ label: "год", seconds: 31536000, threshold: 5 },
{ label: "месяц", seconds: 2592000, threshold: 11 },
{ label: "день", seconds: 86400, threshold: 6 },
{ label: "час", seconds: 3600, threshold: 23 },
{ label: "минуту", seconds: 60, threshold: 59 }
];

for (const interval of intervals) {
const count = Math.floor(diffInSeconds / interval.seconds);
if (count >= 1) {
if (count === 1) {
return `${count} ${interval.label} назад`;
}
let label = interval.label;
if (label === "минуту") label = "минут";
if (label === "час") label = "часов";
if (label === "день") {
if (count % 10 === 1 && count % 100 !== 11) label = "день";
else if (count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 10 || count % 100 >= 20)) label = "дня";
else label = "дней";
}
if (label === "месяц") {
if (count % 10 === 1 && count % 100 !== 11) label = "месяц";
else if (count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 10 || count % 100 >= 20)) label = "месяца";
else label = "месяцев";
}
if (label === "год") {
if (count % 10 === 1 && count % 100 !== 11) label = "год";
else if (count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 10 || count % 100 >= 20)) label = "года";
else label = "лет";
}

return `${count} ${label} назад`;
}
}

return "только что";
}
Loading