// Универсальная функция для модальных окон подтверждения function showConfirmModal(title, message, options = {}) { return new Promise((resolve) => { // Создаем модальное окно const modal = document.createElement("div"); modal.className = "modal"; modal.style.display = "block"; // Создаем содержимое модального окна const modalContent = document.createElement("div"); modalContent.className = "modal-content"; // Создаем заголовок const modalHeader = document.createElement("div"); modalHeader.className = "modal-header"; modalHeader.innerHTML = `
${message}
`; // Создаем футер с кнопками const modalFooter = document.createElement("div"); modalFooter.className = "modal-footer"; modalFooter.innerHTML = ` `; // Собираем модальное окно modalContent.appendChild(modalHeader); modalContent.appendChild(modalBody); modalContent.appendChild(modalFooter); modal.appendChild(modalContent); // Добавляем на страницу document.body.appendChild(modal); // Функция закрытия function closeModal() { modal.style.display = "none"; if (modal.parentNode) { modal.parentNode.removeChild(modal); } } // Обработчики событий const closeBtn = modalHeader.querySelector(".modal-close"); const cancelBtn = modalFooter.querySelector("#cancelBtn"); const confirmBtn = modalFooter.querySelector("#confirmBtn"); closeBtn.addEventListener("click", () => { closeModal(); resolve(false); }); cancelBtn.addEventListener("click", () => { closeModal(); resolve(false); }); confirmBtn.addEventListener("click", () => { closeModal(); resolve(true); }); // Закрытие при клике вне модального окна modal.addEventListener("click", (e) => { if (e.target === modal) { closeModal(); resolve(false); } }); // Закрытие по Escape const handleEscape = (e) => { if (e.key === "Escape") { closeModal(); resolve(false); document.removeEventListener("keydown", handleEscape); } }; document.addEventListener("keydown", handleEscape); }); } // PWA Service Worker Registration class PWAManager { constructor() { this.init(); } init() { console.log("PWA Manager инициализирован"); this.registerServiceWorker(); this.checkPWARequirements(); this.setupServiceWorkerMessages(); } // Проверка требований PWA checkPWARequirements() { console.log("Проверка требований PWA:"); console.log("- Service Worker:", "serviceWorker" in navigator); console.log( "- HTTPS:", location.protocol === "https:" || location.hostname === "localhost" ); console.log( "- Manifest:", document.querySelector('link[rel="manifest"]') !== null ); console.log( "- Icons:", document.querySelector('link[rel="icon"]') !== null ); } // Регистрация Service Worker registerServiceWorker() { if ("serviceWorker" in navigator) { window.addEventListener("load", () => { navigator.serviceWorker .register("/sw.js") .then((registration) => { console.log("SW зарегистрирован успешно:", registration.scope); // Проверяем обновления registration.addEventListener("updatefound", () => { const newWorker = registration.installing; newWorker.addEventListener("statechange", () => { if ( newWorker.state === "installed" && navigator.serviceWorker.controller ) { this.showUpdateNotification(); } }); }); }) .catch((error) => { console.log("Ошибка регистрации SW:", error); }); }); } else { console.log("Service Worker не поддерживается"); } } // Показ уведомления об обновлении async showUpdateNotification() { const confirmed = await showConfirmModal( "Обновление приложения", "Доступна новая версия приложения. Обновить?", { confirmText: "Обновить" } ); if (confirmed) { if (navigator.serviceWorker.controller) { navigator.serviceWorker.controller.postMessage({ type: "SKIP_WAITING", }); } window.location.reload(); } } // Проверка на мобильное устройство isMobileDevice() { const ua = navigator.userAgent; return ( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( ua ) || (navigator.maxTouchPoints && navigator.maxTouchPoints > 2) || window.matchMedia("(max-width: 768px)").matches || /Mobile|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua) ); } // Принудительное обновление кэша async forceUpdateCache() { console.log("Принудительное обновление кэша..."); if ("serviceWorker" in navigator && navigator.serviceWorker.controller) { try { // Отправляем сообщение Service Worker для обновления кэша navigator.serviceWorker.controller.postMessage({ type: "FORCE_UPDATE_CACHE", }); console.log("Запрос на обновление кэша отправлен"); return true; } catch (error) { console.error("Ошибка при обновлении кэша:", error); return false; } } else { console.log("Service Worker не доступен"); return false; } } // Полная очистка кэша async clearAllCache() { console.log("Полная очистка кэша..."); if ("serviceWorker" in navigator && navigator.serviceWorker.controller) { try { // Отправляем сообщение Service Worker для очистки кэша navigator.serviceWorker.controller.postMessage({ type: "CLEAR_ALL_CACHE", }); console.log("Запрос на очистку кэша отправлен"); return true; } catch (error) { console.error("Ошибка при очистке кэша:", error); return false; } } else { console.log("Service Worker не доступен"); return false; } } // Получение версии кэша async getCacheVersion() { return new Promise((resolve) => { if ("serviceWorker" in navigator && navigator.serviceWorker.controller) { const messageChannel = new MessageChannel(); messageChannel.port1.onmessage = (event) => { resolve(event.data.version || "Неизвестно"); }; navigator.serviceWorker.controller.postMessage( { type: "GET_VERSION" }, [messageChannel.port2] ); } else { resolve("Service Worker не доступен"); } }); } // Проверка обновлений и принудительное обновление async checkForUpdates() { console.log("Проверка обновлений..."); if ("serviceWorker" in navigator) { try { const registration = await navigator.serviceWorker.getRegistration(); if (registration) { await registration.update(); console.log("Проверка обновлений завершена"); return true; } } catch (error) { console.error("Ошибка при проверке обновлений:", error); return false; } } return false; } // Настройка обработки сообщений от Service Worker setupServiceWorkerMessages() { if ("serviceWorker" in navigator) { navigator.serviceWorker.addEventListener("message", (event) => { console.log("Получено сообщение от SW:", event.data); switch (event.data.type) { case "CACHE_UPDATED": console.log("Кэш обновлен до версии:", event.data.version); this.showNotification("Кэш успешно обновлен!", "success"); break; case "CACHE_CLEARED": console.log("Кэш полностью очищен"); this.showNotification("Кэш полностью очищен!", "info"); break; } }); } } // Показ уведомления showNotification(message, type = "info") { // Создаем уведомление const notification = document.createElement("div"); notification.className = `pwa-notification pwa-notification-${type}`; notification.textContent = message; // Стили для уведомления notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 12px 20px; border-radius: 8px; color: white; font-weight: bold; z-index: 10000; max-width: 300px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); transform: translateX(100%); transition: transform 0.3s ease; `; // Цвета для разных типов уведомлений switch (type) { case "success": notification.style.backgroundColor = "#28a745"; break; case "error": notification.style.backgroundColor = "#dc3545"; break; case "warning": notification.style.backgroundColor = "#ffc107"; notification.style.color = "#000"; break; default: notification.style.backgroundColor = "#007bff"; } // Добавляем на страницу document.body.appendChild(notification); // Анимация появления setTimeout(() => { notification.style.transform = "translateX(0)"; }, 100); // Автоматическое удаление через 3 секунды setTimeout(() => { notification.style.transform = "translateX(100%)"; setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, 3000); } } // Инициализация PWA Manager const pwaManager = new PWAManager(); // Экспорт для использования в других скриптах window.PWAManager = pwaManager; // Добавляем глобальные функции для управления кэшем window.debugPWA = () => { console.log("PWA Debug Info:", { isOnline: navigator.onLine, hasServiceWorker: "serviceWorker" in navigator, userAgent: navigator.userAgent, platform: navigator.platform, language: navigator.language, displayMode: window.matchMedia("(display-mode: standalone)").matches ? "standalone" : "browser", }); }; // Принудительное обновление кэша window.updateCache = () => { return pwaManager.forceUpdateCache(); }; // Полная очистка кэша window.clearCache = () => { return pwaManager.clearAllCache(); }; // Получение версии кэша window.getCacheVersion = () => { return pwaManager.getCacheVersion(); }; // Проверка обновлений window.checkUpdates = () => { return pwaManager.checkForUpdates(); }; // Комбинированная функция: проверка обновлений + принудительное обновление кэша window.forceUpdate = async () => { console.log("Принудительное обновление приложения..."); // Сначала проверяем обновления await pwaManager.checkForUpdates(); // Затем принудительно обновляем кэш await pwaManager.forceUpdateCache(); // Перезагружаем страницу setTimeout(() => { window.location.reload(); }, 1000); };