From 8354e64ae7acac1d22126e1edbec7d0526e1bd59 Mon Sep 17 00:00:00 2001
From: Fovway
Date: Mon, 20 Oct 2025 23:14:38 +0700
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5?=
=?UTF-8?q?=D0=BD=D1=8B=20=D1=83=D1=81=D1=82=D0=B0=D1=80=D0=B5=D0=B2=D1=88?=
=?UTF-8?q?=D0=B8=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B8=20=D1=83?=
=?UTF-8?q?=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B0=20=D1=81=D1=82=D1=80?=
=?UTF-8?q?=D1=83=D0=BA=D1=82=D1=83=D1=80=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B5?=
=?UTF-8?q?=D0=BA=D1=82=D0=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Удалены файлы тестирования загрузки изображений и PWA, чтобы оптимизировать проект.
- Обновлены мета-теги и улучшены стили для поддержки мобильных устройств.
- Реализована функция ленивой загрузки изображений для повышения производительности.
- Добавлены новые функции для управления цветом акцента в профиле пользователя.
---
MOBILE-UPLOAD-TESTING.md | 92 -----
PWA-TESTING.md | 269 ---------------
public/app.js | 359 +++++++++++++-------
public/brave-pwa-test.html | 333 ------------------
public/index.html | 55 +--
public/mobile-install-test.html | 230 -------------
public/mobile-pwa-test.html | 415 -----------------------
public/notes.html | 110 ++++--
public/profile.html | 91 ++++-
public/profile.js | 118 ++++++-
public/pwa-debug.html | 411 -----------------------
public/pwa.js | 361 +-------------------
public/register.html | 71 +++-
public/style.css | 172 ++++++----
public/test-mobile-upload.html | 576 --------------------------------
public/test-pwa.html | 287 ----------------
server.js | 112 +++++--
test_images.html | 292 ----------------
18 files changed, 761 insertions(+), 3593 deletions(-)
delete mode 100644 MOBILE-UPLOAD-TESTING.md
delete mode 100644 PWA-TESTING.md
delete mode 100644 public/brave-pwa-test.html
delete mode 100644 public/mobile-install-test.html
delete mode 100644 public/mobile-pwa-test.html
delete mode 100644 public/pwa-debug.html
delete mode 100644 public/test-mobile-upload.html
delete mode 100644 public/test-pwa.html
delete mode 100644 test_images.html
diff --git a/MOBILE-UPLOAD-TESTING.md b/MOBILE-UPLOAD-TESTING.md
deleted file mode 100644
index 0f7d1fc..0000000
--- a/MOBILE-UPLOAD-TESTING.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# 📱 Тестирование загрузки изображений на мобильных устройствах
-
-## Проблема
-Пользователи не могли загружать картинки в заметки с мобильных телефонов.
-
-## Внесенные исправления
-
-### 1. Улучшения JavaScript (app.js)
-- ✅ Добавлена поддержка touch событий для кнопки загрузки изображений
-- ✅ Улучшена обработка выбора файлов с проверкой размера и типа
-- ✅ Добавлена защита от дублирования файлов
-- ✅ Улучшена функция обновления превью с обработкой ошибок
-- ✅ Добавлены индикаторы загрузки для мобильных устройств
-- ✅ Улучшена функция сохранения заметок с уведомлениями
-
-### 2. Улучшения CSS (style.css)
-- ✅ Добавлены стили для touch устройств (touch-action, -webkit-tap-highlight-color)
-- ✅ Увеличена минимальная высота кнопок для удобства touch (44px+)
-- ✅ Улучшены размеры кнопок удаления изображений
-- ✅ Добавлены специальные стили для мобильных устройств в медиа-запросах
-
-### 3. Создана тестовая страница
-- ✅ `/test-mobile-upload.html` - специальная страница для тестирования загрузки на мобильных
-
-## Как протестировать
-
-### На мобильном устройстве:
-1. Откройте приложение в мобильном браузере
-2. Перейдите на страницу заметок
-3. Нажмите на кнопку загрузки изображений (📷)
-4. Выберите одно или несколько изображений
-5. Проверьте превью изображений
-6. Нажмите "Сохранить"
-7. Убедитесь, что изображения загрузились и отображаются в заметке
-
-### Альтернативный способ тестирования:
-1. Откройте `/test-mobile-upload.html` на мобильном устройстве
-2. Эта страница содержит специальные тесты для проверки загрузки файлов
-3. Проверьте все функции загрузки и отображения
-
-## Основные улучшения для мобильных устройств
-
-### Touch Events
-- Добавлена поддержка `touchend` событий
-- Улучшена обработка touch для кнопок
-
-### Размеры элементов
-- Минимальная высота кнопок: 44px (рекомендация Apple/Google)
-- Увеличены размеры кнопок удаления изображений
-- Улучшены отступы и размеры для touch
-
-### Визуальная обратная связь
-- Индикаторы загрузки для мобильных устройств
-- Уведомления об успешном сохранении
-- Обработка ошибок с понятными сообщениями
-
-### Производительность
-- Проверка размера файлов (максимум 10MB)
-- Защита от дублирования файлов
-- Обработка ошибок чтения файлов
-
-## Поддерживаемые форматы изображений
-- JPEG (.jpg, .jpeg)
-- PNG (.png)
-- GIF (.gif)
-- WebP (.webp)
-
-## Ограничения
-- Максимальный размер файла: 10MB
-- Максимальное количество файлов за раз: 10
-- Поддерживаются только изображения
-
-## Браузеры
-Протестировано на:
-- ✅ Chrome Mobile (Android)
-- ✅ Safari Mobile (iOS)
-- ✅ Firefox Mobile
-- ✅ Samsung Internet
-
-## Если проблемы остаются
-
-1. **Проверьте консоль браузера** на наличие ошибок JavaScript
-2. **Убедитесь, что у вас стабильное интернет-соединение**
-3. **Попробуйте уменьшить размер изображений** (сжать перед загрузкой)
-4. **Проверьте, что браузер поддерживает File API** (современные браузеры поддерживают)
-5. **Попробуйте перезагрузить страницу** и повторить попытку
-
-## Отладка
-Для отладки используйте:
-- `/test-mobile-upload.html` - специальная тестовая страница
-- Консоль разработчика в мобильном браузере
-- Информация об устройстве и браузере на тестовой странице
diff --git a/PWA-TESTING.md b/PWA-TESTING.md
deleted file mode 100644
index db52886..0000000
--- a/PWA-TESTING.md
+++ /dev/null
@@ -1,269 +0,0 @@
-# 🚀 Тестирование PWA для NoteJS
-
-## Что было сделано
-
-✅ **Созданы файлы PWA:**
-
-- `manifest.json` - манифест приложения
-- `sw.js` - сервис-воркер для кэширования
-- `pwa.js` - JavaScript класс для управления PWA
-- `icon.svg` - SVG иконка приложения
-- `logo.svg` - логотип приложения
-- `icons/` - PNG иконки различных размеров
-- `browserconfig.xml` - конфигурация для Windows
-
-✅ **Обновлены HTML страницы:**
-
-- Добавлены PWA мета-теги
-- Подключены иконки и манифест
-- Добавлен скрипт регистрации Service Worker
-
-✅ **Настроен сервер:**
-
-- Правильные заголовки для PWA файлов
-- Поддержка кэширования
-
-## Как протестировать
-
-### 1. Откройте диагностическую страницу PWA
-
-```
-http://localhost:3000/pwa-debug.html
-```
-
-### 2. Откройте тестовую страницу для мобильных устройств
-
-```
-http://localhost:3000/mobile-pwa-test.html
-```
-
-### 3. Откройте тестовую страницу для Brave браузера
-
-```
-http://localhost:3000/brave-pwa-test.html
-```
-
-### 4. Откройте обычную тестовую страницу
-
-```
-http://localhost:3000/test-pwa.html
-```
-
-### 5. Проверьте требования PWA
-
-На мобильной тестовой странице автоматически проверяются все требования:
-
-- ✅ HTTPS или localhost
-- ✅ Service Worker
-- ✅ Manifest
-- ✅ Иконки
-- ❌ Уже установлено (должно быть красным, если не установлено)
-
-### 6. Установка приложения
-
-- Если все проверки пройдены, появится кнопка "Установить приложение"
-- Нажмите на неё для установки PWA
-- Следуйте инструкциям браузера или используйте инструкции на странице
-
-### 7. Проверка в разных браузерах
-
-#### Chrome/Edge:
-
-- Откройте DevTools (F12)
-- Перейдите в Application → Manifest
-- Проверьте, что манифест загружается без ошибок
-- В Application → Service Workers проверьте статус SW
-
-#### Firefox:
-
-- Откройте DevTools (F12)
-- Перейдите в Application → Manifest
-- Проверьте манифест
-
-#### Brave:
-
-- Откройте `http://localhost:3000/brave-pwa-test.html` для диагностики
-- Проверьте настройки PWA в браузере
-- Используйте меню браузера для установки
-
-#### Safari (iOS):
-
-- Откройте сайт в Safari
-- Нажмите кнопку "Поделиться"
-- Выберите "На экран Домой"
-- Приложение установится как PWA
-
-#### ПК/Десктоп (Chrome, Edge, Firefox):
-
-- Откройте сайт в браузере
-- Нажмите на иконку установки в адресной строке (если доступна)
-- Или используйте меню браузера → "Установить приложение"
-- Или используйте меню браузера → "Создать ярлык"
-
-## Новые улучшения для мобильных устройств
-
-✅ **Улучшенный manifest.json:**
-
-- Добавлены все необходимые размеры иконок (72x72, 96x96, 128x128, 144x144, 152x152, 192x192, 384x384, 512x512)
-- Добавлены maskable иконки для Android
-- Добавлены категории и скриншоты
-- Улучшена совместимость с мобильными устройствами
-
-✅ **Улучшенные мета-теги:**
-
-- Добавлены все необходимые apple-touch-icon размеры
-- Улучшена поддержка iOS Safari
-- Добавлены мета-теги для Windows
-- Настроен правильный статус-бар для iOS
-
-✅ **Улучшенный Service Worker:**
-
-- Кэширование всех иконок
-- Улучшенная обработка ошибок
-- Fallback для различных типов ресурсов
-- Лучшая поддержка мобильных устройств
-
-✅ **Улучшенный PWA Manager:**
-
-- Определение мобильного Safari
-- Разные инструкции для разных браузеров
-- Улучшенная проверка установки PWA
-- Поддержка различных режимов отображения
-- **Кнопка установки показывается только на мобильных устройствах**
-- Принудительная проверка возможности установки
-- Специальные инструкции для Android и iOS
-- Диагностическая страница для отладки PWA
-
-## Возможные проблемы и решения
-
-### 1. Кнопка установки не появляется
-
-**Причины:**
-
-- Приложение уже установлено
-- Браузер не поддерживает PWA
-- Не выполнены требования PWA
-- **Вы используете ПК/десктоп (кнопка скрыта для ПК)**
-- **Проблемы с Brave браузером**
-
-**Решение:**
-
-- Проверьте статус на мобильной тестовой странице
-- Убедитесь, что используете HTTPS или localhost
-- Проверьте консоль браузера на ошибки
-- Для iOS Safari используйте инструкции "Добавить на главный экран"
-- **На ПК используйте меню браузера для установки PWA**
-- **Для Brave: проверьте настройки PWA в браузере**
-
-### 1.1. Проблемы с Brave браузером
-
-**Причины:**
-
-- Brave может блокировать PWA по умолчанию
-- Неправильная конфигурация manifest.json
-- Проблемы с Service Worker в Brave
-
-**Решение:**
-
-- Откройте `http://localhost:3000/brave-pwa-test.html` для диагностики
-- В Brave перейдите в **Настройки → Дополнительно → Сайты и разрешения → PWA**
-- Убедитесь, что PWA включены
-- Попробуйте установить через меню браузера (три точки → "Установить приложение")
-- Обновите manifest.json с новыми полями для Brave совместимости
-
-### 2. Service Worker не регистрируется
-
-**Причины:**
-
-- Ошибки в коде SW
-- Проблемы с кэшированием файлов
-
-**Решение:**
-
-- Откройте DevTools → Application → Service Workers
-- Проверьте ошибки в консоли
-- Попробуйте очистить кэш
-
-### 3. Ошибка "Download error or resource isn't a valid image"
-
-**Причины:**
-
-- PNG иконки повреждены или имеют неправильный размер
-- Иконки не являются валидными PNG файлами
-
-**Решение:**
-
-- ✅ **ИСПРАВЛЕНО**: Созданы правильные PNG иконки с помощью pngjs
-- Проверьте, что иконки имеют правильные размеры (192x192, 512x512)
-- Убедитесь, что файлы иконок валидные PNG
-
-### 4. Предупреждение о deprecated meta tag
-
-**Причины:**
-
-- Использование устаревшего `apple-mobile-web-app-capable`
-
-**Решение:**
-
-- ✅ **ИСПРАВЛЕНО**: Добавлен современный `mobile-web-app-capable`
-- Оба тега теперь присутствуют для совместимости
-
-## Отладка
-
-### Консоль браузера
-
-Откройте DevTools (F12) и проверьте консоль на ошибки:
-
-```javascript
-// Проверить статус PWA
-window.debugPWA();
-
-// Проверить Service Worker
-navigator.serviceWorker.getRegistrations().then(console.log);
-
-// Проверить возможность установки
-window.checkInstallability();
-
-// Принудительная попытка установки
-window.forceInstall();
-```
-
-### Диагностическая страница
-
-Используйте `http://localhost:3000/pwa-debug.html` для:
-
-- Детальной диагностики всех требований PWA
-- Проверки загрузки манифеста и Service Worker
-- Отображения информации о браузере и устройстве
-- Просмотра ошибок консоли
-- Тестирования установки PWA
-
-### Lighthouse
-
-Запустите аудит Lighthouse в Chrome DevTools:
-
-1. Откройте DevTools (F12)
-2. Перейдите в Lighthouse
-3. Выберите "Progressive Web App"
-4. Нажмите "Generate report"
-
-## Файлы для проверки
-
-- ✅ `http://localhost:3000/manifest.json` - должен возвращать JSON
-- ✅ `http://localhost:3000/sw.js` - должен возвращать JavaScript
-- ✅ `http://localhost:3000/icons/icon-192x192.png` - должен возвращать PNG
-- ✅ `http://localhost:3000/icons/icon-512x512.png` - должен возвращать PNG
-- ✅ `http://localhost:3000/pwa-debug.html` - диагностическая страница PWA
-- ✅ `http://localhost:3000/mobile-pwa-test.html` - мобильная тестовая страница
-- ✅ `http://localhost:3000/brave-pwa-test.html` - тестовая страница для Brave браузера
-- ✅ `http://localhost:3000/test-pwa.html` - обычная тестовая страница
-
-## Следующие шаги
-
-После успешного тестирования:
-
-1. Удалите тестовую страницу `test-pwa.html`
-2. Настройте HTTPS для продакшена
-3. Добавьте реальные скриншоты в манифест
-4. Создайте качественные иконки с помощью дизайнера
-5. Настройте push-уведомления (опционально)
diff --git a/public/app.js b/public/app.js
index 4dbc509..b0cbf95 100644
--- a/public/app.js
+++ b/public/app.js
@@ -36,6 +36,47 @@ let searchResults = [];
let notesCache = null; // Кэш для заметок
let lastLoadTime = 0; // Время последней загрузки
+// 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", // Загружать изображения за 50px до появления в viewport
+ 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 getFormattedDateTime() {
let now = new Date();
@@ -151,7 +192,10 @@ function renderTags() {
// Добавляем обработчики кликов для тегов
tagsContainer.querySelectorAll(".tag").forEach((tagElement) => {
- tagElement.addEventListener("click", async (event) => await handleTagClick(event));
+ tagElement.addEventListener(
+ "click",
+ async (event) => await handleTagClick(event)
+ );
});
}
@@ -343,20 +387,21 @@ imageBtn.addEventListener("touchend", function (event) {
imageInput.addEventListener("change", function (event) {
const files = Array.from(event.target.files);
let addedCount = 0;
-
- files.forEach(file => {
- if (file.type.startsWith('image/')) {
+
+ files.forEach((file) => {
+ if (file.type.startsWith("image/")) {
// Проверяем размер файла (максимум 10MB)
if (file.size > 10 * 1024 * 1024) {
alert(`Файл "${file.name}" слишком большой. Максимальный размер: 10MB`);
return;
}
-
+
// Проверяем, не добавлен ли уже этот файл
- const isDuplicate = selectedImages.some(existingFile =>
- existingFile.name === file.name && existingFile.size === file.size
+ const isDuplicate = selectedImages.some(
+ (existingFile) =>
+ existingFile.name === file.name && existingFile.size === file.size
);
-
+
if (!isDuplicate) {
selectedImages.push(file);
addedCount++;
@@ -365,7 +410,7 @@ imageInput.addEventListener("change", function (event) {
alert(`Файл "${file.name}" не является изображением`);
}
});
-
+
if (addedCount > 0) {
updateImagePreview();
// Показываем уведомление о добавленных файлах
@@ -375,9 +420,9 @@ imageInput.addEventListener("change", function (event) {
console.log(`Добавлено ${addedCount} изображений`);
}
}
-
+
// Очищаем input для возможности повторного выбора тех же файлов
- event.target.value = '';
+ event.target.value = "";
});
// Обработчик очистки всех изображений
@@ -420,19 +465,20 @@ function updateImagePreview() {
reader.onload = function (e) {
const previewItem = document.createElement("div");
previewItem.className = "image-preview-item";
-
+
// Форматируем размер файла
const fileSize = (file.size / 1024 / 1024).toFixed(2);
- const fileName = file.name.length > 20 ? file.name.substring(0, 20) + '...' : file.name;
-
+ const fileName =
+ file.name.length > 20 ? file.name.substring(0, 20) + "..." : file.name;
+
previewItem.innerHTML = `
-
+
${fileName}
${fileSize} MB
`;
-
+
imagePreviewList.appendChild(previewItem);
-
+
// Обработчик удаления изображения
const removeBtn = previewItem.querySelector(".remove-image-btn");
removeBtn.addEventListener("click", function (event) {
@@ -441,7 +487,7 @@ function updateImagePreview() {
selectedImages.splice(index, 1);
updateImagePreview();
});
-
+
// Дополнительный обработчик для touch событий
removeBtn.addEventListener("touchend", function (event) {
event.preventDefault();
@@ -450,25 +496,25 @@ function updateImagePreview() {
updateImagePreview();
});
};
-
- reader.onerror = function() {
- console.error('Ошибка чтения файла:', file.name);
+
+ reader.onerror = function () {
+ console.error("Ошибка чтения файла:", file.name);
alert(`Ошибка чтения файла: ${file.name}`);
};
-
+
reader.readAsDataURL(file);
});
}
// Функция для отображения изображения в модальном окне
function showImageModal(imageSrc) {
- console.log('showImageModal called with:', imageSrc);
+ console.log("showImageModal called with:", imageSrc);
try {
modalImage.src = imageSrc;
imageModal.style.display = "block";
- console.log('Modal opened successfully');
+ console.log("Modal opened successfully");
} catch (error) {
- console.error('Error in showImageModal:', error);
+ console.error("Error in showImageModal:", error);
}
}
@@ -479,20 +525,23 @@ async function uploadImages(noteId) {
}
const formData = new FormData();
- selectedImages.forEach(file => {
+ selectedImages.forEach((file) => {
formData.append("images", file);
});
try {
// Показываем индикатор загрузки для мобильных устройств
- const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
- (navigator.maxTouchPoints && navigator.maxTouchPoints > 2) ||
- window.matchMedia('(max-width: 768px)').matches;
-
+ const isMobile =
+ /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
+ navigator.userAgent
+ ) ||
+ (navigator.maxTouchPoints && navigator.maxTouchPoints > 2) ||
+ window.matchMedia("(max-width: 768px)").matches;
+
if (isMobile) {
// Создаем простое уведомление о загрузке
- const loadingDiv = document.createElement('div');
- loadingDiv.id = 'mobile-upload-loading';
+ const loadingDiv = document.createElement("div");
+ loadingDiv.id = "mobile-upload-loading";
loadingDiv.style.cssText = `
position: fixed;
top: 50%;
@@ -523,23 +572,23 @@ async function uploadImages(noteId) {
}
const result = await response.json();
-
+
// Удаляем индикатор загрузки
- const loadingDiv = document.getElementById('mobile-upload-loading');
+ const loadingDiv = document.getElementById("mobile-upload-loading");
if (loadingDiv) {
loadingDiv.remove();
}
-
+
return result.images || [];
} catch (error) {
console.error("Ошибка загрузки изображений:", error);
-
+
// Удаляем индикатор загрузки в случае ошибки
- const loadingDiv = document.getElementById('mobile-upload-loading');
+ const loadingDiv = document.getElementById("mobile-upload-loading");
if (loadingDiv) {
loadingDiv.remove();
}
-
+
// Показываем ошибку пользователю
alert(`Ошибка загрузки изображений: ${error.message}`);
return [];
@@ -582,9 +631,9 @@ async function deleteNoteImage(noteId, imageId) {
async function loadNotes(forceReload = false) {
const now = Date.now();
const CACHE_DURATION = 30000; // 30 секунд кэширования
-
+
// Используем кэш, если он не устарел и не требуется принудительная перезагрузка
- if (!forceReload && notesCache && (now - lastLoadTime) < CACHE_DURATION) {
+ if (!forceReload && notesCache && now - lastLoadTime < CACHE_DURATION) {
allNotes = notesCache;
await renderNotes(notesCache);
renderCalendar();
@@ -772,18 +821,18 @@ async function renderNotes(notes) {
// Используем изображения, которые уже пришли с заметкой
const noteImages = Array.isArray(note.images) ? note.images : [];
let imagesHtml = "";
-
+
if (noteImages.length > 0) {
- imagesHtml = '';
- noteImages.forEach(image => {
- imagesHtml += `
+ imagesHtml = '
';
+ noteImages.forEach((image) => {
+ imagesHtml += `
-

+
`;
- });
- imagesHtml += '
';
+ });
+ imagesHtml += "
";
}
const noteHtml = `
@@ -818,36 +867,39 @@ async function renderNotes(notes) {
// Обрабатываем длинные заметки
handleLongNotes();
+
+ // Инициализируем lazy loading для новых изображений
+ initLazyLoading();
}
// Функция для обработки длинных заметок
function handleLongNotes() {
const MAX_HEIGHT = 300; // Максимальная высота в пикселях
-
+
document.querySelectorAll(".textNote").forEach((noteElement) => {
// Проверяем высоту контента
const contentHeight = noteElement.scrollHeight;
-
+
if (contentHeight > MAX_HEIGHT) {
// Добавляем класс для сворачивания
noteElement.classList.add("collapsed");
-
+
// Создаем кнопку "Показать все"
const showMoreBtn = document.createElement("button");
showMoreBtn.classList.add("show-more-btn");
showMoreBtn.textContent = "Показать полностью";
showMoreBtn.setAttribute("data-expanded", "false");
-
+
// Вставляем кнопку после заметки
noteElement.parentElement.insertBefore(
showMoreBtn,
noteElement.nextSibling
);
-
+
// Обработчик клика на кнопку
showMoreBtn.addEventListener("click", function () {
const isExpanded = this.getAttribute("data-expanded") === "true";
-
+
if (isExpanded) {
// Сворачиваем
noteElement.classList.add("collapsed");
@@ -899,7 +951,7 @@ function addNoteEventListeners() {
// Разворачиваем заметку при редактировании
noteContent.classList.remove("collapsed");
-
+
// Скрываем кнопку "Показать полностью" если она есть
const showMoreBtn = noteContainer.querySelector(".show-more-btn");
if (showMoreBtn) {
@@ -990,7 +1042,7 @@ function addNoteEventListeners() {
saveButtonContainer.appendChild(saveHint);
// Функция обновления превью изображений для режима редактирования
- const updateEditImagePreview = function() {
+ const updateEditImagePreview = function () {
if (editSelectedImages.length === 0) {
editImagePreviewContainer.style.display = "none";
return;
@@ -1004,15 +1056,15 @@ function addNoteEventListeners() {
reader.onload = function (e) {
const previewItem = document.createElement("div");
previewItem.className = "image-preview-item";
-
+
previewItem.innerHTML = `
-
+
${file.name}
`;
-
+
editImagePreviewList.appendChild(previewItem);
-
+
// Обработчик удаления изображения
const removeBtn = previewItem.querySelector(".remove-image-btn");
removeBtn.addEventListener("click", function () {
@@ -1025,13 +1077,13 @@ function addNoteEventListeners() {
};
// Функция загрузки изображений для режима редактирования
- const uploadEditImages = async function(noteId) {
+ const uploadEditImages = async function (noteId) {
if (editSelectedImages.length === 0) {
return [];
}
const formData = new FormData();
- editSelectedImages.forEach(file => {
+ editSelectedImages.forEach((file) => {
formData.append("images", file);
});
@@ -1099,8 +1151,8 @@ function addNoteEventListeners() {
// Обработчики для загрузки изображений в режиме редактирования
editImageInput.addEventListener("change", function (event) {
const files = Array.from(event.target.files);
- files.forEach(file => {
- if (file.type.startsWith('image/')) {
+ files.forEach((file) => {
+ if (file.type.startsWith("image/")) {
editSelectedImages.push(file);
}
});
@@ -1108,7 +1160,9 @@ function addNoteEventListeners() {
});
// Обработчик очистки всех изображений в режиме редактирования
- const editClearImagesBtn = editImagePreviewHeader.querySelector(`#editClearImagesBtn-${noteId}`);
+ const editClearImagesBtn = editImagePreviewHeader.querySelector(
+ `#editClearImagesBtn-${noteId}`
+ );
editClearImagesBtn.addEventListener("click", function () {
editSelectedImages.length = 0;
updateEditImagePreview();
@@ -1180,50 +1234,55 @@ function addImageEventListeners() {
if (imageElement._clickHandler) {
return; // Пропускаем, если обработчик уже добавлен
}
-
+
// Создаем новый обработчик
imageElement._clickHandler = function (event) {
event.preventDefault();
event.stopPropagation();
const imageSrc = this.dataset.imageSrc;
- console.log('Image clicked, src:', imageSrc); // Для отладки
+ console.log("Image clicked, src:", imageSrc); // Для отладки
if (imageSrc) {
showImageModal(imageSrc);
}
};
-
+
imageElement.addEventListener("click", imageElement._clickHandler);
});
// Обработчики для кнопок удаления изображений
- document.querySelectorAll(".remove-note-image-btn").forEach((buttonElement) => {
- // Удаляем старые обработчики, если они есть
- if (buttonElement._clickHandler) {
- buttonElement.removeEventListener("click", buttonElement._clickHandler);
- }
-
- // Создаем новый обработчик
- buttonElement._clickHandler = async function (event) {
- event.preventDefault();
- event.stopPropagation();
-
- const noteId = this.dataset.noteId;
- const imageId = this.dataset.imageId;
-
- if (noteId && imageId && confirm("Вы уверены, что хотите удалить это изображение?")) {
- const success = await deleteNoteImage(noteId, imageId);
- if (success) {
- await loadNotes(true); // Перезагружаем заметки
- } else {
- alert("Ошибка удаления изображения");
- }
+ document
+ .querySelectorAll(".remove-note-image-btn")
+ .forEach((buttonElement) => {
+ // Удаляем старые обработчики, если они есть
+ if (buttonElement._clickHandler) {
+ buttonElement.removeEventListener("click", buttonElement._clickHandler);
}
- };
-
- buttonElement.addEventListener("click", buttonElement._clickHandler);
- });
-}
+ // Создаем новый обработчик
+ buttonElement._clickHandler = async function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const noteId = this.dataset.noteId;
+ const imageId = this.dataset.imageId;
+
+ if (
+ noteId &&
+ imageId &&
+ confirm("Вы уверены, что хотите удалить это изображение?")
+ ) {
+ const success = await deleteNoteImage(noteId, imageId);
+ if (success) {
+ await loadNotes(true); // Перезагружаем заметки
+ } else {
+ alert("Ошибка удаления изображения");
+ }
+ }
+ };
+
+ buttonElement.addEventListener("click", buttonElement._clickHandler);
+ });
+}
// Функция сохранения заметки (вынесена отдельно для повторного использования)
async function saveNote() {
@@ -1232,14 +1291,17 @@ async function saveNote() {
const { date, time } = getFormattedDateTime();
// Показываем индикатор сохранения для мобильных устройств
- const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
- (navigator.maxTouchPoints && navigator.maxTouchPoints > 2) ||
- window.matchMedia('(max-width: 768px)').matches;
-
+ const isMobile =
+ /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
+ navigator.userAgent
+ ) ||
+ (navigator.maxTouchPoints && navigator.maxTouchPoints > 2) ||
+ window.matchMedia("(max-width: 768px)").matches;
+
let savingIndicator = null;
if (isMobile) {
- savingIndicator = document.createElement('div');
- savingIndicator.id = 'mobile-saving-indicator';
+ savingIndicator = document.createElement("div");
+ savingIndicator.id = "mobile-saving-indicator";
savingIndicator.style.cssText = `
position: fixed;
top: 50%;
@@ -1255,7 +1317,11 @@ async function saveNote() {
`;
savingIndicator.innerHTML = `
💾 Сохранение заметки...
- ${selectedImages.length > 0 ? `+ ${selectedImages.length} изображений
` : ''}
+ ${
+ selectedImages.length > 0
+ ? `+ ${selectedImages.length} изображений
`
+ : ""
+ }
`;
document.body.appendChild(savingIndicator);
}
@@ -1296,10 +1362,10 @@ async function saveNote() {
updateImagePreview();
imageInput.value = "";
await loadNotes(true);
-
+
// Показываем уведомление об успешном сохранении
if (isMobile) {
- const successDiv = document.createElement('div');
+ const successDiv = document.createElement("div");
successDiv.style.cssText = `
position: fixed;
top: 20px;
@@ -1312,23 +1378,24 @@ async function saveNote() {
z-index: 10000;
font-size: 14px;
`;
- successDiv.textContent = '✅ Заметка сохранена!';
+ successDiv.textContent = "✅ Заметка сохранена!";
document.body.appendChild(successDiv);
-
+
setTimeout(() => {
successDiv.remove();
}, 3000);
}
-
} catch (error) {
console.error("Ошибка:", error);
-
+
// Удаляем индикатор сохранения в случае ошибки
- const savingIndicator = document.getElementById('mobile-saving-indicator');
+ const savingIndicator = document.getElementById(
+ "mobile-saving-indicator"
+ );
if (savingIndicator) {
savingIndicator.remove();
}
-
+
alert("Ошибка сохранения заметки");
}
}
@@ -1352,7 +1419,10 @@ document.addEventListener("DOMContentLoaded", function () {
loadUserInfo();
loadNotes();
updateFilterIndicator();
-
+
+ // Инициализируем lazy loading для изображений
+ initLazyLoading();
+
// Добавляем обработчик для кнопки выхода
setupLogoutHandler();
});
@@ -1360,41 +1430,41 @@ document.addEventListener("DOMContentLoaded", function () {
// Функция для настройки обработчика выхода
function setupLogoutHandler() {
const logoutForms = document.querySelectorAll('form[action="/logout"]');
- logoutForms.forEach(form => {
- form.addEventListener('submit', function(e) {
+ logoutForms.forEach((form) => {
+ form.addEventListener("submit", function (e) {
// Очищаем localStorage перед выходом
- localStorage.removeItem('isAuthenticated');
- localStorage.removeItem('username');
+ localStorage.removeItem("isAuthenticated");
+ localStorage.removeItem("username");
});
});
}
// Функция для проверки аутентификации
async function checkAuthentication() {
- const isAuthenticated = localStorage.getItem('isAuthenticated');
-
- if (isAuthenticated !== 'true') {
+ 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');
+ localStorage.removeItem("isAuthenticated");
+ localStorage.removeItem("username");
window.location.href = "/";
return;
}
-
+
const authData = await response.json();
if (!authData.authenticated) {
// Если сервер говорит, что пользователь не аутентифицирован
- localStorage.removeItem('isAuthenticated');
- localStorage.removeItem('username');
+ localStorage.removeItem("isAuthenticated");
+ localStorage.removeItem("username");
window.location.href = "/";
return;
}
@@ -1432,6 +1502,16 @@ async function loadUserInfo() {
if (userAvatarContainer) {
userAvatarContainer.style.display = "none";
}
+
+ // Применяем цветовой акцент пользователя
+ if (user.accent_color) {
+ document.documentElement.style.setProperty(
+ "--accent-color",
+ user.accent_color
+ );
+ } else {
+ document.documentElement.style.setProperty("--accent-color", "#007bff");
+ }
}
} catch (error) {
console.error("Ошибка загрузки информации о пользователе:", error);
@@ -1517,7 +1597,10 @@ function renderCalendar() {
}
// Добавляем обработчик клика
- dayDiv.addEventListener("click", async (event) => await handleDayClick(event));
+ dayDiv.addEventListener(
+ "click",
+ async (event) => await handleDayClick(event)
+ );
calendarDays.appendChild(dayDiv);
}
@@ -1554,7 +1637,10 @@ function renderCalendar() {
}
// Добавляем обработчик клика
- dayDiv.addEventListener("click", async (event) => await handleDayClick(event));
+ dayDiv.addEventListener(
+ "click",
+ async (event) => await handleDayClick(event)
+ );
calendarDays.appendChild(dayDiv);
}
@@ -1586,7 +1672,10 @@ function renderCalendar() {
}
// Добавляем обработчик клика
- dayDiv.addEventListener("click", async (event) => await handleDayClick(event));
+ dayDiv.addEventListener(
+ "click",
+ async (event) => await handleDayClick(event)
+ );
calendarDays.appendChild(dayDiv);
}
@@ -1827,7 +1916,10 @@ function renderCalendarMobile() {
}
// Добавляем обработчик клика
- dayDiv.addEventListener("click", async (event) => await handleDayClickMobile(event));
+ dayDiv.addEventListener(
+ "click",
+ async (event) => await handleDayClickMobile(event)
+ );
calendarDays.appendChild(dayDiv);
}
@@ -1864,7 +1956,10 @@ function renderCalendarMobile() {
}
// Добавляем обработчик клика
- dayDiv.addEventListener("click", async (event) => await handleDayClickMobile(event));
+ dayDiv.addEventListener(
+ "click",
+ async (event) => await handleDayClickMobile(event)
+ );
calendarDays.appendChild(dayDiv);
}
@@ -1896,7 +1991,10 @@ function renderCalendarMobile() {
}
// Добавляем обработчик клика
- dayDiv.addEventListener("click", async (event) => await handleDayClickMobile(event));
+ dayDiv.addEventListener(
+ "click",
+ async (event) => await handleDayClickMobile(event)
+ );
calendarDays.appendChild(dayDiv);
}
@@ -1946,7 +2044,10 @@ function renderTagsMobile() {
// Добавляем обработчики кликов для тегов
tagsContainer.querySelectorAll(".tag").forEach((tagElement) => {
- tagElement.addEventListener("click", async (event) => await handleTagClickMobile(event));
+ tagElement.addEventListener(
+ "click",
+ async (event) => await handleTagClickMobile(event)
+ );
});
}
diff --git a/public/brave-pwa-test.html b/public/brave-pwa-test.html
deleted file mode 100644
index cfb2986..0000000
--- a/public/brave-pwa-test.html
+++ /dev/null
@@ -1,333 +0,0 @@
-
-
-
-
-
- Тест PWA для Brave - NoteJS
-
-
-
- 🔍 Диагностика PWA для Brave браузера
-
-
-
Информация о браузере:
-
-
-
-
-
📋 Проверка требований PWA
-
-
-
-
-
🔧 Тестирование установки
-
-
-
-
-
-
-
📱 Инструкции для Brave
-
-
-
-
-
🐛 Отладочная информация
-
-
-
-
-
-
-
diff --git a/public/index.html b/public/index.html
index e4de523..a1b2dd3 100644
--- a/public/index.html
+++ b/public/index.html
@@ -2,7 +2,10 @@
-
+
Вход в систему заметок
@@ -156,56 +159,6 @@
});
});
}
-
- // Обработка установки PWA
- let deferredPrompt;
- window.addEventListener("beforeinstallprompt", (e) => {
- console.log("beforeinstallprompt event fired");
-
- // Определяем браузер
- const isBrave = navigator.userAgent.includes('Brave') ||
- (navigator.brave && await navigator.brave.isBrave());
- const isMobile =
- /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
- navigator.userAgent
- ) ||
- (navigator.maxTouchPoints && navigator.maxTouchPoints > 2) ||
- window.matchMedia("(max-width: 768px)").matches;
-
- // Для Brave на мобильных устройствах не предотвращаем стандартное поведение
- if (!isMobile && !isBrave) {
- e.preventDefault();
- }
- deferredPrompt = e;
-
- // Показываем кнопку установки на мобильных устройствах или в Brave
- if (isMobile || isBrave) {
- const installButton = document.createElement("button");
- installButton.textContent = "Установить приложение";
- installButton.className = "btnSave";
- installButton.style.marginTop = "10px";
- installButton.style.width = "100%";
-
- installButton.addEventListener("click", async () => {
- if (deferredPrompt) {
- deferredPrompt.prompt();
- const choiceResult = await deferredPrompt.userChoice;
- if (choiceResult.outcome === "accepted") {
- console.log("Пользователь установил приложение");
- }
- deferredPrompt = null;
- installButton.remove();
- }
- });
-
- document.querySelector(".auth-link").appendChild(installButton);
- }
- });
-
- // Обработка успешной установки
- window.addEventListener("appinstalled", () => {
- console.log("PWA установлено успешно");
- });
diff --git a/public/mobile-install-test.html b/public/mobile-install-test.html
deleted file mode 100644
index d0b6361..0000000
--- a/public/mobile-install-test.html
+++ /dev/null
@@ -1,230 +0,0 @@
-
-
-
-
-
- Тест установки PWA на мобильном
-
-
-
-
-
-
-
📱 Тест установки PWA на мобильном
-
-
-
Инструкции:
-
1. Откройте эту страницу на мобильном устройстве
-
2. Должен появиться нативный баннер установки браузера
-
3. Если баннер не появился, используйте кнопки ниже
-
-
-
-
-
-
-
-
-
-
-
-
Отладочная информация:
-
-
-
-
-
-
-
diff --git a/public/mobile-pwa-test.html b/public/mobile-pwa-test.html
deleted file mode 100644
index a5b772b..0000000
--- a/public/mobile-pwa-test.html
+++ /dev/null
@@ -1,415 +0,0 @@
-
-
-
-
-
- Тест PWA для мобильных устройств - NoteJS
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
📱 Тест PWA для мобильных устройств
-
-
-
Проверка требований PWA
-
-
- HTTPS или localhost
- Проверка...
-
-
- Service Worker
- Проверка...
-
-
- Manifest
- Проверка...
-
-
- Иконки
- Проверка...
-
-
- Уже установлено
- Проверка...
-
-
-
-
-
-
Установка приложения
-
-
-
-
-
-
-
Инструкции по установке
-
-
Android Chrome/Edge:
-
- - Откройте сайт в Chrome или Edge
- - Нажмите кнопку "Установить приложение" выше
- - Или нажмите меню (⋮) → "Установить приложение"
- - Следуйте инструкциям браузера
-
-
-
-
-
iOS Safari:
-
- - Откройте сайт в Safari
- - Нажмите кнопку "Поделиться" (□↗)
- - Выберите "На экран Домой"
- - Нажмите "Добавить"
-
-
-
-
-
Другие браузеры:
-
- - Найдите опцию "Установить" в меню браузера
- - Или используйте "Добавить на главный экран"
-
-
-
-
-
-
Отладочная информация
-
-
-
-
-
-
Действия
-
-
-
-
-
-
-
-
-
diff --git a/public/notes.html b/public/notes.html
index 6ad650d..89353b7 100644
--- a/public/notes.html
+++ b/public/notes.html
@@ -2,15 +2,24 @@
-
+
Заметки - NoteJS
-
+
-
+
-
+
@@ -18,28 +27,58 @@
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
+
-
+
@@ -192,6 +231,7 @@
id="user-avatar"
src=""
alt="Аватар"
+ loading="lazy"
style="
width: 32px;
height: 32px;
@@ -233,7 +273,11 @@
-
-
-
🔍 Диагностика PWA
-
-
-
Основные требования PWA
-
-
- HTTPS или localhost
- Проверка...
-
-
- Service Worker
- Проверка...
-
-
- Manifest
- Проверка...
-
-
- Иконки
- Проверка...
-
-
-
-
-
-
Детальная диагностика
-
-
- Manifest загружается
- Проверка...
-
-
- Service Worker регистрируется
- Проверка...
-
-
- Иконки доступны
- Проверка...
-
-
- beforeinstallprompt событие
- Проверка...
-
-
-
-
-
-
Установка приложения
-
- Установить приложение
-
-
-
-
-
-
Информация о манифесте
-
Загрузка...
-
-
-
-
Информация о Service Worker
-
Загрузка...
-
-
-
-
Информация о браузере
-
Загрузка...
-
-
-
-
Ошибки консоли
-
Нет ошибок
-
-
-
-
-
diff --git a/public/profile.js b/public/profile.js
index 333b35b..33edbc5 100644
--- a/public/profile.js
+++ b/public/profile.js
@@ -11,6 +11,48 @@ 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") {
@@ -36,6 +78,14 @@ async function loadProfile() {
// Заполняем поля
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) {
@@ -54,6 +104,17 @@ async function loadProfile() {
}
}
+// Функция для обновления выбора цвета в цветовой палитре
+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];
@@ -139,6 +200,7 @@ deleteAvatarBtn.addEventListener("click", async function () {
updateProfileBtn.addEventListener("click", async function () {
const username = usernameInput.value.trim();
const email = emailInput.value.trim();
+ const accentColor = accentColorInput.value;
// Валидация
if (!username) {
@@ -165,6 +227,7 @@ updateProfileBtn.addEventListener("click", async function () {
body: JSON.stringify({
username,
email: email || null,
+ accent_color: accentColor,
}),
});
@@ -174,6 +237,10 @@ updateProfileBtn.addEventListener("click", async function () {
}
const result = await response.json();
+
+ // Применяем новый цветовой акцент
+ document.documentElement.style.setProperty("--accent-color", accentColor);
+
showMessage(result.message || "Профиль успешно обновлен", "success");
} catch (error) {
console.error("Ошибка обновления профиля:", error);
@@ -247,30 +314,30 @@ function isValidEmail(email) {
// Функция для проверки аутентификации
async function checkAuthentication() {
- const isAuthenticated = localStorage.getItem('isAuthenticated');
-
- if (isAuthenticated !== 'true') {
+ 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');
+ localStorage.removeItem("isAuthenticated");
+ localStorage.removeItem("username");
window.location.href = "/";
return;
}
-
+
const authData = await response.json();
if (!authData.authenticated) {
// Если сервер говорит, что пользователь не аутентифицирован
- localStorage.removeItem('isAuthenticated');
- localStorage.removeItem('username');
+ localStorage.removeItem("isAuthenticated");
+ localStorage.removeItem("username");
window.location.href = "/";
return;
}
@@ -285,21 +352,44 @@ async function checkAuthentication() {
// Функция для настройки обработчика выхода
function setupLogoutHandler() {
const logoutForms = document.querySelectorAll('form[action="/logout"]');
- logoutForms.forEach(form => {
- form.addEventListener('submit', function(e) {
+ logoutForms.forEach((form) => {
+ form.addEventListener("submit", function (e) {
// Очищаем localStorage перед выходом
- localStorage.removeItem('isAuthenticated');
- localStorage.removeItem('username');
+ 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();
});
diff --git a/public/pwa-debug.html b/public/pwa-debug.html
deleted file mode 100644
index 072d160..0000000
--- a/public/pwa-debug.html
+++ /dev/null
@@ -1,411 +0,0 @@
-
-
-