NoteJS/public/profile.js
Fovway 8354e64ae7 Удалены устаревшие файлы и улучшена структура проекта
- Удалены файлы тестирования загрузки изображений и PWA, чтобы оптимизировать проект.
- Обновлены мета-теги и улучшены стили для поддержки мобильных устройств.
- Реализована функция ленивой загрузки изображений для повышения производительности.
- Добавлены новые функции для управления цветом акцента в профиле пользователя.
2025-10-20 23:14:38 +07:00

396 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// DOM элементы
const avatarImage = document.getElementById("avatarImage");
const avatarPlaceholder = document.getElementById("avatarPlaceholder");
const avatarInput = document.getElementById("avatarInput");
const deleteAvatarBtn = document.getElementById("deleteAvatarBtn");
const usernameInput = document.getElementById("username");
const emailInput = document.getElementById("email");
const updateProfileBtn = document.getElementById("updateProfileBtn");
const currentPasswordInput = document.getElementById("currentPassword");
const newPasswordInput = document.getElementById("newPassword");
const confirmPasswordInput = document.getElementById("confirmPassword");
const changePasswordBtn = document.getElementById("changePasswordBtn");
const messageContainer = document.getElementById("messageContainer");
const accentColorInput = document.getElementById("accentColor");
// Lazy loading для изображений
function initLazyLoading() {
// Проверяем поддержку Intersection Observer API
if ("IntersectionObserver" in window) {
const imageObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
// Если у изображения есть data-src, загружаем его
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute("data-src");
}
img.classList.remove("lazy");
observer.unobserve(img);
}
});
},
{
rootMargin: "50px 0px",
threshold: 0.01,
}
);
// Наблюдаем за всеми изображениями с классом lazy
document.querySelectorAll("img.lazy").forEach((img) => {
imageObserver.observe(img);
});
} else {
// Fallback для старых браузеров
document.querySelectorAll("img.lazy").forEach((img) => {
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute("data-src");
}
img.classList.remove("lazy");
});
}
}
// Функция для показа сообщений
function showMessage(message, type = "success") {
messageContainer.textContent = message;
messageContainer.className = `message-container ${type}`;
messageContainer.style.display = "block";
setTimeout(() => {
messageContainer.style.display = "none";
}, 5000);
}
// Загрузка данных профиля
async function loadProfile() {
try {
const response = await fetch("/api/user");
if (!response.ok) {
throw new Error("Ошибка загрузки профиля");
}
const user = await response.json();
// Заполняем поля
usernameInput.value = user.username || "";
emailInput.value = user.email || "";
accentColorInput.value = user.accent_color || "#007bff";
// Обновляем выбранный цвет в цветовых опциях
updateColorPickerSelection(user.accent_color || "#007bff");
// Применяем цветовой акцент пользователя
const accentColor = user.accent_color || "#007bff";
document.documentElement.style.setProperty("--accent-color", accentColor);
// Обрабатываем аватарку
if (user.avatar) {
avatarImage.src = user.avatar;
avatarImage.style.display = "block";
avatarPlaceholder.style.display = "none";
deleteAvatarBtn.style.display = "inline-block";
} else {
avatarImage.style.display = "none";
avatarPlaceholder.style.display = "inline-flex";
deleteAvatarBtn.style.display = "none";
}
} catch (error) {
console.error("Ошибка загрузки профиля:", error);
showMessage("Ошибка загрузки данных профиля", "error");
}
}
// Функция для обновления выбора цвета в цветовой палитре
function updateColorPickerSelection(selectedColor) {
const colorOptions = document.querySelectorAll(".color-option");
colorOptions.forEach((option) => {
option.classList.remove("selected");
if (option.dataset.color === selectedColor) {
option.classList.add("selected");
}
});
}
// Обработчик загрузки аватарки
avatarInput.addEventListener("change", async function (event) {
const file = event.target.files[0];
if (!file) return;
// Проверка размера файла (5MB)
if (file.size > 5 * 1024 * 1024) {
showMessage("Файл слишком большой. Максимальный размер: 5 МБ", "error");
return;
}
// Проверка типа файла
const allowedTypes = ["image/jpeg", "image/jpg", "image/png", "image/gif"];
if (!allowedTypes.includes(file.type)) {
showMessage(
"Недопустимый формат файла. Используйте JPG, PNG или GIF",
"error"
);
return;
}
try {
const formData = new FormData();
formData.append("avatar", file);
const response = await fetch("/api/user/avatar", {
method: "POST",
body: formData,
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Ошибка загрузки аватарки");
}
const result = await response.json();
// Обновляем отображение аватарки
avatarImage.src = result.avatar + "?t=" + Date.now(); // Добавляем timestamp для обновления кэша
avatarImage.style.display = "block";
avatarPlaceholder.style.display = "none";
deleteAvatarBtn.style.display = "inline-block";
showMessage("Аватарка успешно загружена", "success");
} catch (error) {
console.error("Ошибка загрузки аватарки:", error);
showMessage(error.message, "error");
}
// Сбрасываем input для возможности повторной загрузки того же файла
event.target.value = "";
});
// Обработчик удаления аватарки
deleteAvatarBtn.addEventListener("click", async function () {
if (!confirm("Вы уверены, что хотите удалить аватарку?")) {
return;
}
try {
const response = await fetch("/api/user/avatar", {
method: "DELETE",
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Ошибка удаления аватарки");
}
// Обновляем отображение
avatarImage.style.display = "none";
avatarPlaceholder.style.display = "inline-flex";
deleteAvatarBtn.style.display = "none";
showMessage("Аватарка успешно удалена", "success");
} catch (error) {
console.error("Ошибка удаления аватарки:", error);
showMessage(error.message, "error");
}
});
// Обработчик обновления профиля
updateProfileBtn.addEventListener("click", async function () {
const username = usernameInput.value.trim();
const email = emailInput.value.trim();
const accentColor = accentColorInput.value;
// Валидация
if (!username) {
showMessage("Логин не может быть пустым", "error");
return;
}
if (username.length < 3) {
showMessage("Логин должен быть не менее 3 символов", "error");
return;
}
if (email && !isValidEmail(email)) {
showMessage("Некорректный email адрес", "error");
return;
}
try {
const response = await fetch("/api/user/profile", {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
username,
email: email || null,
accent_color: accentColor,
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Ошибка обновления профиля");
}
const result = await response.json();
// Применяем новый цветовой акцент
document.documentElement.style.setProperty("--accent-color", accentColor);
showMessage(result.message || "Профиль успешно обновлен", "success");
} catch (error) {
console.error("Ошибка обновления профиля:", error);
showMessage(error.message, "error");
}
});
// Обработчик изменения пароля
changePasswordBtn.addEventListener("click", async function () {
const currentPassword = currentPasswordInput.value;
const newPassword = newPasswordInput.value;
const confirmPassword = confirmPasswordInput.value;
// Валидация
if (!currentPassword) {
showMessage("Введите текущий пароль", "error");
return;
}
if (!newPassword) {
showMessage("Введите новый пароль", "error");
return;
}
if (newPassword.length < 6) {
showMessage("Новый пароль должен быть не менее 6 символов", "error");
return;
}
if (newPassword !== confirmPassword) {
showMessage("Новый пароль и подтверждение не совпадают", "error");
return;
}
try {
const response = await fetch("/api/user/profile", {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
currentPassword,
newPassword,
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Ошибка изменения пароля");
}
const result = await response.json();
// Очищаем поля паролей
currentPasswordInput.value = "";
newPasswordInput.value = "";
confirmPasswordInput.value = "";
showMessage(result.message || "Пароль успешно изменен", "success");
} catch (error) {
console.error("Ошибка изменения пароля:", error);
showMessage(error.message, "error");
}
});
// Функция валидации email
function isValidEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
// Функция для проверки аутентификации
async function checkAuthentication() {
const isAuthenticated = localStorage.getItem("isAuthenticated");
if (isAuthenticated !== "true") {
// Если пользователь не аутентифицирован, перенаправляем на страницу входа
window.location.href = "/";
return;
}
// Проверяем, что сессия на сервере еще действительна
try {
const response = await fetch("/api/auth/status");
if (!response.ok) {
// Если сессия недействительна, очищаем localStorage и перенаправляем
localStorage.removeItem("isAuthenticated");
localStorage.removeItem("username");
window.location.href = "/";
return;
}
const authData = await response.json();
if (!authData.authenticated) {
// Если сервер говорит, что пользователь не аутентифицирован
localStorage.removeItem("isAuthenticated");
localStorage.removeItem("username");
window.location.href = "/";
return;
}
} catch (error) {
console.error("Ошибка проверки аутентификации:", error);
// В случае ошибки сети, оставляем пользователя на странице
// но показываем предупреждение
console.warn("Не удалось проверить статус аутентификации");
}
}
// Функция для настройки обработчика выхода
function setupLogoutHandler() {
const logoutForms = document.querySelectorAll('form[action="/logout"]');
logoutForms.forEach((form) => {
form.addEventListener("submit", function (e) {
// Очищаем localStorage перед выходом
localStorage.removeItem("isAuthenticated");
localStorage.removeItem("username");
});
});
}
// Обработчики для цветовой палитры
function setupColorPicker() {
const colorOptions = document.querySelectorAll(".color-option");
colorOptions.forEach((option) => {
option.addEventListener("click", function () {
const selectedColor = this.dataset.color;
accentColorInput.value = selectedColor;
updateColorPickerSelection(selectedColor);
});
});
// Обработчик для input color
accentColorInput.addEventListener("input", function () {
updateColorPickerSelection(this.value);
});
}
// Загружаем профиль при загрузке страницы
document.addEventListener("DOMContentLoaded", function () {
// Проверяем аутентификацию при загрузке страницы
checkAuthentication();
loadProfile();
// Инициализируем lazy loading для изображений
initLazyLoading();
// Настраиваем цветовую палитру
setupColorPicker();
// Добавляем обработчик для кнопки выхода
setupLogoutHandler();
});