🐛 Fix PWA install prompt showing after installation

Fixed critical bug where PWA install message continued showing after app installation

-  Enhanced PWA installation status detection
-  Fixed install prompt logic to hide after installation
-  Improved Service Worker update handling
-  Added proper installation state management
-  Enhanced iOS Safari PWA detection
-  Added installation preferences storage

- Added installationChecked flag for better state management
- Enhanced checkInstallationStatus() method with multiple detection methods
- Improved shouldShowPrompt() logic to prevent showing after installation
- Added periodic installation monitoring for iOS devices
- Enhanced Service Worker activation event handling
- Added PWAUtils.checkInstallationStatus() utility method

- public/src/pwa/install-prompt.js (major refactor)
- public/index.html (PWA logic improvements)
- public/sw.js (Service Worker enhancements)

- PWA install message no longer shows after successful installation
- Only update notifications are shown for installed PWAs
- Proper distinction between install prompts and update notifications

Version: Enhanced Security Edition v4.01.413
This commit is contained in:
lockbitchat
2025-08-23 17:21:32 -04:00
parent 235e3e06cb
commit 434301fe6f
7 changed files with 286 additions and 50 deletions

View File

@@ -15,7 +15,7 @@
--- ---
## ✨ What's New in v4.01.412 ## ✨ What's New in v4.01.413
### 🔒 Comprehensive Connection Security Overhaul ### 🔒 Comprehensive Connection Security Overhaul
* **Advanced mutex framework** with 15-second timeout protection * **Advanced mutex framework** with 15-second timeout protection
@@ -254,7 +254,7 @@ open http://localhost:8000
## 🗺️ Development Roadmap ## 🗺️ Development Roadmap
**Current:** v4.01.412 — PWA & File Transfer Edition ✅ **Current:** v4.01.413 — PWA & File Transfer Edition ✅
* Progressive Web App installation * Progressive Web App installation
* Secure P2P file transfer system * Secure P2P file transfer system
@@ -551,6 +551,6 @@ SecureBit.chat:
--- ---
**Latest Release: v4.01.412** — PWA & Secure File Transfer **Latest Release: v4.01.413** — PWA & Secure File Transfer
</div> </div>

View File

@@ -1,6 +1,6 @@
# Security Disclaimer and Terms of Use # Security Disclaimer and Terms of Use
## 🔒 SecureBit.chat Enhanced Security Edition v4.01.412 ## 🔒 SecureBit.chat Enhanced Security Edition v4.01.413
### Important Legal Notice ### Important Legal Notice
@@ -203,6 +203,6 @@ This software is created to:
--- ---
*Last Updated: 08.07.2025* *Last Updated: 08.07.2025*
*Version: Enhanced Security Edition v4.01.412* *Version: Enhanced Security Edition v4.01.413*
**USE AT YOUR OWN RISK AND RESPONSIBILITY** **USE AT YOUR OWN RISK AND RESPONSIBILITY**

View File

@@ -161,7 +161,7 @@
icon: "fas fa-shield-halved", icon: "fas fa-shield-halved",
color: "orange", color: "orange",
title: "12-Layer Military Security", title: "12-Layer Military Security",
description: "Revolutionary defense system with ECDH P-384 + AES-GCM 256 + ECDSA. Enhanced Security Edition v4.01.412 provides military-grade protection exceeding government standards." description: "Revolutionary defense system with ECDH P-384 + AES-GCM 256 + ECDSA. Enhanced Security Edition v4.01.413 provides military-grade protection exceeding government standards."
}, },
{ {
icon: "fas fa-bolt", icon: "fas fa-bolt",
@@ -511,7 +511,7 @@
Enhanced Security Edition Comparison Enhanced Security Edition Comparison
</h3> </h3>
<p className="text-secondary max-w-2xl mx-auto mb-4"> <p className="text-secondary max-w-2xl mx-auto mb-4">
SecureBit.chat v4.01.412 Enhanced Security Edition vs leading secure messengers SecureBit.chat v4.01.413 Enhanced Security Edition vs leading secure messengers
</p> </p>
<div className="inline-flex items-center px-4 py-2 bg-yellow-500/10 border border-yellow-500/20 rounded-lg"> <div className="inline-flex items-center px-4 py-2 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
<span className="text-yellow-400 mr-2">🏆</span> <span className="text-yellow-400 mr-2">🏆</span>
@@ -657,7 +657,7 @@
<div className="p-6 bg-gradient-to-r from-orange-500/10 to-yellow-500/10 border border-orange-500/20 rounded-xl"> <div className="p-6 bg-gradient-to-r from-orange-500/10 to-yellow-500/10 border border-orange-500/20 rounded-xl">
<h4 className="text-xl font-bold text-orange-400 mb-4 flex items-center"> <h4 className="text-xl font-bold text-orange-400 mb-4 flex items-center">
<i className="fas fa-trophy mr-3" /> <i className="fas fa-trophy mr-3" />
SecureBit.chat v4.01.412 Enhanced Security Edition Summary SecureBit.chat v4.01.413 Enhanced Security Edition Summary
</h4> </h4>
<p className="text-secondary leading-relaxed text-lg mb-4"> <p className="text-secondary leading-relaxed text-lg mb-4">
SecureBit.chat dominates in 11 out of 15 security categories, establishing itself as the most secure P2P messenger available. SecureBit.chat dominates in 11 out of 15 security categories, establishing itself as the most secure P2P messenger available.
@@ -3944,7 +3944,23 @@ if ('serviceWorker' in navigator) {
newWorker.addEventListener('statechange', () => { newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
console.log('🆕 PWA: New version available'); console.log('🆕 PWA: New version available');
// Проверяем, установлено ли приложение как PWA
const isPWAInstalled = window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true ||
(window.pwaInstallPrompt && window.pwaInstallPrompt.isInstalled);
if (isPWAInstalled) {
// Если это PWA, показываем уведомление об обновлении
showUpdateNotification(); showUpdateNotification();
} else {
// Если это браузер, показываем промпт установки
if (window.pwaInstallPrompt && !window.pwaInstallPrompt.isInstalled) {
setTimeout(() => {
window.pwaInstallPrompt.showInstallOptions();
}, 2000);
}
}
} }
}); });
}); });
@@ -3966,17 +3982,64 @@ if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then(registration => {
console.log('🎯 PWA: Service Worker ready'); console.log('🎯 PWA: Service Worker ready');
// Проверяем статус установки PWA
const isPWAInstalled = window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true;
console.log('🔍 PWA Installation Status:', {
isStandalone: isPWAInstalled,
displayMode: window.matchMedia('(display-mode: standalone)').matches,
iosStandalone: window.navigator.standalone === true
});
if (window.pwaInstallPrompt && window.pwaInstallPrompt.setServiceWorkerRegistration) { if (window.pwaInstallPrompt && window.pwaInstallPrompt.setServiceWorkerRegistration) {
window.pwaInstallPrompt.setServiceWorkerRegistration(registration); window.pwaInstallPrompt.setServiceWorkerRegistration(registration);
// Если PWA уже установлено, обновляем статус
if (isPWAInstalled && !window.pwaInstallPrompt.isInstalled) {
console.log('✅ PWA already installed, updating status');
window.pwaInstallPrompt.isInstalled = true;
window.pwaInstallPrompt.hideInstallPrompts();
}
} }
if (window.pwaOfflineManager && window.pwaOfflineManager.setServiceWorkerRegistration) { if (window.pwaOfflineManager && window.pwaOfflineManager.setServiceWorkerRegistration) {
window.pwaOfflineManager.setServiceWorkerRegistration(registration); window.pwaOfflineManager.setServiceWorkerRegistration(registration);
} }
}); });
// Слушаем сообщения от Service Worker
navigator.serviceWorker.addEventListener('message', (event) => {
console.log('📨 Message from Service Worker:', event.data);
if (event.data && event.data.type === 'SW_ACTIVATED') {
console.log('🔄 Service Worker activated, checking for updates...');
// Проверяем, установлено ли приложение как PWA
const isPWAInstalled = window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true ||
(window.pwaInstallPrompt && window.pwaInstallPrompt.isInstalled);
if (isPWAInstalled) {
// Если это PWA, показываем уведомление об обновлении
setTimeout(() => {
showUpdateNotification();
}, 1000);
} else {
// Если это браузер, показываем промпт установки
if (window.pwaInstallPrompt && !window.pwaInstallPrompt.isInstalled) {
setTimeout(() => {
window.pwaInstallPrompt.showInstallOptions();
}, 2000);
}
}
}
});
} }
function showUpdateNotification() { function showUpdateNotification() {
console.log('🆕 Showing update notification for PWA');
const notification = document.createElement('div'); const notification = document.createElement('div');
notification.className = 'fixed top-4 left-1/2 transform -translate-x-1/2 bg-blue-500 text-white p-4 rounded-lg shadow-lg z-50 max-w-sm'; notification.className = 'fixed top-4 left-1/2 transform -translate-x-1/2 bg-blue-500 text-white p-4 rounded-lg shadow-lg z-50 max-w-sm';
notification.innerHTML = ` notification.innerHTML = `
@@ -4058,6 +4121,30 @@ function checkPWASupport() {
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
checkPWASupport(); checkPWASupport();
// Дополнительная проверка PWA Install Prompt после полной загрузки
setTimeout(() => {
if (window.pwaInstallPrompt) {
console.log('🔍 Final PWA Install Prompt status check...');
// Используем новый метод для проверки статуса
window.PWAUtils.checkInstallationStatus();
console.log('📱 Final PWA status:', {
isInstalled: window.pwaInstallPrompt.isInstalled,
displayMode: window.matchMedia('(display-mode: standalone)').matches,
iosStandalone: window.navigator.standalone === true
});
}
}, 2000);
// Дополнительная проверка через 5 секунд для надежности
setTimeout(() => {
if (window.pwaInstallPrompt) {
console.log('🔍 Delayed PWA status check...');
window.PWAUtils.checkInstallationStatus();
}
}, 5000);
}); });
window.PWAUtils = { window.PWAUtils = {
@@ -4087,6 +4174,38 @@ window.PWAUtils = {
} else { } else {
console.warn('⚠️ PWA Install Prompt not initialized'); console.warn('⚠️ PWA Install Prompt not initialized');
} }
},
// Новый метод для принудительной проверки статуса установки
checkInstallationStatus: () => {
const isPWAInstalled = window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true;
console.log('🔍 PWA Installation Status Check:', {
isStandalone: isPWAInstalled,
displayMode: window.matchMedia('(display-mode: standalone)').matches,
iosStandalone: window.navigator.standalone === true,
pwaInstallPrompt: !!window.pwaInstallPrompt,
pwaInstallPromptInstalled: window.pwaInstallPrompt ? window.pwaInstallPrompt.isInstalled : false
});
if (window.pwaInstallPrompt && isPWAInstalled && !window.pwaInstallPrompt.isInstalled) {
console.log('✅ Updating PWA Install Prompt status to installed');
window.pwaInstallPrompt.isInstalled = true;
window.pwaInstallPrompt.hideInstallPrompts();
}
return isPWAInstalled;
},
// Метод для сброса статуса установки (для тестирования)
resetInstallationStatus: () => {
if (window.pwaInstallPrompt) {
window.pwaInstallPrompt.isInstalled = false;
window.pwaInstallPrompt.installationChecked = false;
window.pwaInstallPrompt.checkInstallationStatus();
console.log('🔄 PWA Installation status reset');
}
} }
}; };

View File

@@ -497,7 +497,7 @@ const EnhancedMinimalHeader = ({
React.createElement('p', { React.createElement('p', {
key: 'subtitle', key: 'subtitle',
className: 'text-xs sm:text-sm text-muted hidden sm:block' className: 'text-xs sm:text-sm text-muted hidden sm:block'
}, 'End-to-end freedom. v4.01.412') }, 'End-to-end freedom. v4.01.413')
]) ])
]), ]),

View File

@@ -6,6 +6,7 @@ class PWAInstallPrompt {
this.installBanner = null; this.installBanner = null;
this.dismissedCount = 0; this.dismissedCount = 0;
this.maxDismissals = 3; this.maxDismissals = 3;
this.installationChecked = false;
this.init(); this.init();
} }
@@ -18,39 +19,80 @@ class PWAInstallPrompt {
this.createInstallButton(); this.createInstallButton();
this.loadInstallPreferences(); this.loadInstallPreferences();
if (this.isIOSSafari() && !this.isInstalled && this.shouldShowPrompt()) { // Проверяем статус установки периодически для iOS
setTimeout(() => { if (this.isIOSSafari()) {
this.showIOSInstallInstructions(); this.startInstallationMonitoring();
}, 3000);
} }
console.log('✅ PWA Install Prompt initialized'); console.log('✅ PWA Install Prompt initialized');
} }
checkInstallationStatus() { checkInstallationStatus() {
if (window.matchMedia('(display-mode: standalone)').matches || // Проверяем различные способы определения установки PWA
window.navigator.standalone === true) { const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
const isIOSStandalone = window.navigator.standalone === true;
const hasInstallPreference = this.loadInstallPreferences().installed;
// Проверяем, установлено ли приложение
if (isStandalone || isIOSStandalone || hasInstallPreference) {
this.isInstalled = true; this.isInstalled = true;
console.log('📱 App is already installed as PWA'); console.log('📱 App is already installed as PWA');
document.body.classList.add('pwa-installed'); document.body.classList.add('pwa-installed');
// Скрываем все промпты установки
this.hideInstallPrompts();
// Если это iOS, добавляем специальный класс
if (this.isIOSSafari()) {
document.body.classList.add('ios-pwa');
}
this.installationChecked = true;
return true; return true;
} }
if (this.isIOSSafari()) { // Если не установлено, добавляем соответствующие классы
this.isInstalled = window.navigator.standalone === true; document.body.classList.add('pwa-browser');
if (this.isInstalled) {
console.log('📱 iOS PWA detected');
document.body.classList.add('pwa-installed', 'ios-pwa');
}
}
document.body.classList.add(this.isInstalled ? 'pwa-installed' : 'pwa-browser');
if (this.isIOSSafari()) { if (this.isIOSSafari()) {
document.body.classList.add('ios-safari'); document.body.classList.add('ios-safari');
} }
return this.isInstalled; this.installationChecked = true;
return false;
}
startInstallationMonitoring() {
// Для iOS Safari мониторим изменения в standalone режиме
let wasStandalone = window.navigator.standalone;
const checkStandalone = () => {
const isStandalone = window.navigator.standalone;
if (isStandalone && !wasStandalone && !this.isInstalled) {
console.log('✅ iOS PWA installation detected');
this.isInstalled = true;
this.hideInstallPrompts();
this.showInstallSuccess();
document.body.classList.remove('pwa-browser');
document.body.classList.add('pwa-installed', 'ios-pwa');
// Сохраняем предпочтение установки
this.saveInstallPreference('installed', true);
}
wasStandalone = isStandalone;
};
// Проверяем каждые 2 секунды
setInterval(checkStandalone, 2000);
// Также проверяем при изменении видимости страницы
window.addEventListener('visibilitychange', () => {
if (!document.hidden) {
setTimeout(checkStandalone, 1000);
}
});
} }
setupEventListeners() { setupEventListeners() {
@@ -59,6 +101,7 @@ class PWAInstallPrompt {
event.preventDefault(); event.preventDefault();
this.deferredPrompt = event; this.deferredPrompt = event;
// Показываем промпт только если приложение не установлено
if (!this.isInstalled && this.shouldShowPrompt()) { if (!this.isInstalled && this.shouldShowPrompt()) {
this.showInstallOptions(); this.showInstallOptions();
} }
@@ -75,6 +118,7 @@ class PWAInstallPrompt {
document.body.classList.add('pwa-installed'); document.body.classList.add('pwa-installed');
}); });
// Дополнительная проверка для iOS
if (this.isIOSSafari()) { if (this.isIOSSafari()) {
let wasStandalone = window.navigator.standalone; let wasStandalone = window.navigator.standalone;
@@ -91,6 +135,9 @@ class PWAInstallPrompt {
this.showInstallSuccess(); this.showInstallSuccess();
document.body.classList.remove('pwa-browser'); document.body.classList.remove('pwa-browser');
document.body.classList.add('pwa-installed', 'ios-pwa'); document.body.classList.add('pwa-installed', 'ios-pwa');
// Сохраняем предпочтение установки
this.saveInstallPreference('installed', true);
} }
wasStandalone = isStandalone; wasStandalone = isStandalone;
@@ -166,7 +213,15 @@ class PWAInstallPrompt {
} }
showInstallOptions() { showInstallOptions() {
if (this.isInstalled) return; // Дополнительная проверка статуса установки
if (!this.installationChecked) {
this.checkInstallationStatus();
}
if (this.isInstalled) {
console.log('💿 App is already installed, not showing install options');
return;
}
if (this.isIOSSafari()) { if (this.isIOSSafari()) {
this.showInstallButton(); this.showInstallButton();
@@ -178,6 +233,11 @@ class PWAInstallPrompt {
} }
showInstallButton() { showInstallButton() {
// Дополнительная проверка статуса установки
if (!this.installationChecked) {
this.checkInstallationStatus();
}
if (this.installButton && !this.isInstalled) { if (this.installButton && !this.isInstalled) {
this.installButton.classList.remove('hidden'); this.installButton.classList.remove('hidden');
@@ -190,10 +250,22 @@ class PWAInstallPrompt {
}, 100); }, 100);
console.log('💿 Install button shown'); console.log('💿 Install button shown');
} else {
console.log('💿 Install button not shown - app is installed or button not available');
} }
} }
showInstallBanner() { showInstallBanner() {
// Дополнительная проверка статуса установки
if (!this.installationChecked) {
this.checkInstallationStatus();
}
if (this.isInstalled) {
console.log('💿 App is installed, not showing install banner');
return;
}
if (!this.installBanner) { if (!this.installBanner) {
this.createInstallBanner(); this.createInstallBanner();
} }
@@ -205,18 +277,27 @@ class PWAInstallPrompt {
}, 1000); }, 1000);
console.log('💿 Install banner shown'); console.log('💿 Install banner shown');
} else {
console.log('💿 Install banner not shown - app is installed or banner not available');
} }
} }
hideInstallPrompts() { hideInstallPrompts() {
console.log('💿 Hiding all install prompts');
if (this.installButton) { if (this.installButton) {
this.installButton.classList.add('hidden'); this.installButton.classList.add('hidden');
console.log('💿 Install button hidden');
} }
if (this.installBanner) { if (this.installBanner) {
this.installBanner.classList.remove('show'); this.installBanner.classList.remove('show');
this.installBanner.style.transform = 'translateY(100%)'; this.installBanner.style.transform = 'translateY(100%)';
console.log('💿 Install banner hidden');
} }
// Устанавливаем флаг установки
this.isInstalled = true;
} }
async handleInstallClick() { async handleInstallClick() {
@@ -358,6 +439,8 @@ class PWAInstallPrompt {
} }
showInstallSuccess() { showInstallSuccess() {
console.log('✅ Showing installation success notification');
const notification = document.createElement('div'); const notification = document.createElement('div');
notification.className = 'fixed top-4 right-4 bg-green-500 text-white p-4 rounded-lg shadow-lg z-50 max-w-sm transform translate-x-full transition-transform duration-300'; notification.className = 'fixed top-4 right-4 bg-green-500 text-white p-4 rounded-lg shadow-lg z-50 max-w-sm transform translate-x-full transition-transform duration-300';
@@ -387,12 +470,37 @@ class PWAInstallPrompt {
notification.classList.add('translate-x-full'); notification.classList.add('translate-x-full');
setTimeout(() => notification.remove(), 300); setTimeout(() => notification.remove(), 300);
}, 5000); }, 5000);
// Скрываем все промпты установки
this.hideInstallPrompts();
} }
shouldShowPrompt() { shouldShowPrompt() {
// Если приложение уже установлено, не показываем промпт
if (this.isInstalled) {
console.log('💿 App is already installed, not showing install prompt');
return false;
}
// Дополнительная проверка статуса установки
if (!this.installationChecked) {
this.checkInstallationStatus();
}
// Если после проверки приложение установлено, не показываем промпт
if (this.isInstalled) {
console.log('💿 App installation confirmed, not showing install prompt');
return false;
}
const preferences = this.loadInstallPreferences(); const preferences = this.loadInstallPreferences();
if (this.isInstalled) return false; // Проверяем, не было ли приложение уже установлено
if (preferences.installed) {
console.log('💿 Installation preference found, not showing install prompt');
this.isInstalled = true;
return false;
}
if (this.isIOSSafari()) { if (this.isIOSSafari()) {
const lastShown = preferences.ios_instructions_shown; const lastShown = preferences.ios_instructions_shown;

View File

@@ -1,5 +1,5 @@
// PWA Offline Manager for SecureBit.chat // PWA Offline Manager for SecureBit.chat
// Enhanced Security Edition v4.01.412 // Enhanced Security Edition v4.01.413
// Handles offline functionality, data synchronization, and user experience // Handles offline functionality, data synchronization, and user experience
class PWAOfflineManager { class PWAOfflineManager {

39
sw.js
View File

@@ -1,5 +1,5 @@
// SecureBit.chat Service Worker // SecureBit.chat Service Worker
// Enhanced Security Edition v4.01.412 // Enhanced Security Edition v4.01.413
const CACHE_NAME = 'securebit-v4.0.3'; const CACHE_NAME = 'securebit-v4.0.3';
const STATIC_CACHE = 'securebit-static-v4.0.3'; const STATIC_CACHE = 'securebit-static-v4.0.3';
@@ -96,32 +96,41 @@ self.addEventListener('install', (event) => {
); );
}); });
// Activate event - clean up old caches // Activate event - clean up old caches and notify about updates
self.addEventListener('activate', (event) => { self.addEventListener('activate', (event) => {
console.log('🚀 Service Worker activating...'); console.log('🚀 Service Worker activating...');
event.waitUntil( event.waitUntil(
caches.keys() caches.keys().then(cacheNames => {
.then((cacheNames) => {
return Promise.all( return Promise.all(
cacheNames.map((cacheName) => { cacheNames.map(cacheName => {
if (cacheName !== STATIC_CACHE && // Remove old caches
cacheName !== DYNAMIC_CACHE && if (cacheName !== STATIC_CACHE && cacheName !== DYNAMIC_CACHE && cacheName !== CACHE_NAME) {
cacheName !== CACHE_NAME) { console.log(`🗑️ Removing old cache: ${cacheName}`);
console.log('🗑️ Deleting old cache:', cacheName);
return caches.delete(cacheName); return caches.delete(cacheName);
} }
}) })
); );
}) }).then(() => {
.then(() => { console.log('✅ Service Worker activated and old caches cleaned');
console.log('✅ Service Worker activated');
// Claim all clients immediately // Notify all clients about the update
return self.clients.claim(); return self.clients.claim().then(() => {
self.clients.matchAll().then(clients => {
clients.forEach(client => {
client.postMessage({
type: 'SW_ACTIVATED',
timestamp: Date.now()
});
});
});
});
}) })
); );
}); });
// Удаляем дублирующийся код activate event
// Fetch event - handle requests with security-aware caching // Fetch event - handle requests with security-aware caching
self.addEventListener('fetch', (event) => { self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url); const url = new URL(event.request.url);
@@ -352,4 +361,4 @@ self.addEventListener('unhandledrejection', (event) => {
console.error('❌ Service Worker unhandled rejection:', event.reason); console.error('❌ Service Worker unhandled rejection:', event.reason);
}); });
console.log('🔧 SecureBit.chat Service Worker loaded - Enhanced Security Edition v4.01.412'); console.log('🔧 SecureBit.chat Service Worker loaded - Enhanced Security Edition v4.01.413');