diff --git a/public/pwa.js b/public/pwa.js index c212418..9684ad2 100644 --- a/public/pwa.js +++ b/public/pwa.js @@ -11,6 +11,7 @@ class PWAManager { this.setupInstallPrompt(); this.setupAppInstalled(); this.checkPWARequirements(); + this.setupServiceWorkerMessages(); } // Проверка требований PWA @@ -179,6 +180,171 @@ class PWAManager { hasDeferredPrompt: this.deferredPrompt !== null }; } + + // Принудительное обновление кэша + 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 @@ -187,7 +353,43 @@ const pwaManager = new PWAManager(); // Экспорт для использования в других скриптах window.PWAManager = pwaManager; -// Добавляем глобальную функцию для отладки +// Добавляем глобальные функции для управления кэшем window.debugPWA = () => { console.log('PWA Debug Info:', pwaManager.getPWAInfo()); +}; + +// Принудительное обновление кэша +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); }; \ No newline at end of file diff --git a/public/sw.js b/public/sw.js index 5e251a0..1fa7d63 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,6 +1,7 @@ // Service Worker для NoteJS -const CACHE_NAME = 'notejs-v1.0.1'; -const STATIC_CACHE_NAME = 'notejs-static-v1.0.1'; +const APP_VERSION = '1.0.2'; +const CACHE_NAME = `notejs-v${APP_VERSION}`; +const STATIC_CACHE_NAME = `notejs-static-v${APP_VERSION}`; // Файлы для кэширования при установке const STATIC_FILES = [ @@ -126,4 +127,74 @@ self.addEventListener('message', (event) => { if (event.data && event.data.type === 'GET_VERSION') { event.ports[0].postMessage({ version: CACHE_NAME }); } -}); \ No newline at end of file + + if (event.data && event.data.type === 'FORCE_UPDATE_CACHE') { + forceUpdateCache(); + } + + if (event.data && event.data.type === 'CLEAR_ALL_CACHE') { + clearAllCache(); + } +}); + +// Функция принудительного обновления кэша +async function forceUpdateCache() { + console.log('[SW] Принудительное обновление кэша...'); + + try { + // Удаляем все старые кэши + const cacheNames = await caches.keys(); + await Promise.all( + cacheNames.map(cacheName => { + console.log('[SW] Удаление кэша:', cacheName); + return caches.delete(cacheName); + }) + ); + + // Создаем новый кэш с актуальной версией + const newCache = await caches.open(STATIC_CACHE_NAME); + await newCache.addAll(STATIC_FILES); + + console.log('[SW] Кэш успешно обновлен до версии:', APP_VERSION); + + // Уведомляем клиентов об обновлении + const clients = await self.clients.matchAll(); + clients.forEach(client => { + client.postMessage({ + type: 'CACHE_UPDATED', + version: APP_VERSION + }); + }); + + } catch (error) { + console.error('[SW] Ошибка при обновлении кэша:', error); + } +} + +// Функция полной очистки кэша +async function clearAllCache() { + console.log('[SW] Полная очистка кэша...'); + + try { + const cacheNames = await caches.keys(); + await Promise.all( + cacheNames.map(cacheName => { + console.log('[SW] Удаление кэша:', cacheName); + return caches.delete(cacheName); + }) + ); + + console.log('[SW] Весь кэш очищен'); + + // Уведомляем клиентов об очистке + const clients = await self.clients.matchAll(); + clients.forEach(client => { + client.postMessage({ + type: 'CACHE_CLEARED' + }); + }); + + } catch (error) { + console.error('[SW] Ошибка при очистке кэша:', error); + } +} \ No newline at end of file