🐛 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
* **Advanced mutex framework** with 15-second timeout protection
@@ -254,7 +254,7 @@ open http://localhost:8000
## 🗺️ Development Roadmap
**Current:** v4.01.412 — PWA & File Transfer Edition ✅
**Current:** v4.01.413 — PWA & File Transfer Edition ✅
* Progressive Web App installation
* 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>

View File

@@ -1,6 +1,6 @@
# 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
@@ -203,6 +203,6 @@ This software is created to:
---
*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**

View File

@@ -161,7 +161,7 @@
icon: "fas fa-shield-halved",
color: "orange",
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",
@@ -511,7 +511,7 @@
Enhanced Security Edition Comparison
</h3>
<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>
<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>
@@ -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">
<h4 className="text-xl font-bold text-orange-400 mb-4 flex items-center">
<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>
<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.
@@ -3944,7 +3944,23 @@ if ('serviceWorker' in navigator) {
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
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();
} 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 => {
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) {
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) {
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() {
console.log('🆕 Showing update notification for PWA');
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.innerHTML = `
@@ -4058,6 +4121,30 @@ function checkPWASupport() {
document.addEventListener('DOMContentLoaded', () => {
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 = {
@@ -4087,6 +4174,38 @@ window.PWAUtils = {
} else {
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', {
key: 'subtitle',
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.dismissedCount = 0;
this.maxDismissals = 3;
this.installationChecked = false;
this.init();
}
@@ -18,39 +19,80 @@ class PWAInstallPrompt {
this.createInstallButton();
this.loadInstallPreferences();
if (this.isIOSSafari() && !this.isInstalled && this.shouldShowPrompt()) {
setTimeout(() => {
this.showIOSInstallInstructions();
}, 3000);
// Проверяем статус установки периодически для iOS
if (this.isIOSSafari()) {
this.startInstallationMonitoring();
}
console.log('✅ PWA Install Prompt initialized');
}
checkInstallationStatus() {
if (window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true) {
// Проверяем различные способы определения установки PWA
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;
console.log('📱 App is already installed as PWA');
document.body.classList.add('pwa-installed');
// Скрываем все промпты установки
this.hideInstallPrompts();
// Если это iOS, добавляем специальный класс
if (this.isIOSSafari()) {
document.body.classList.add('ios-pwa');
}
this.installationChecked = true;
return true;
}
if (this.isIOSSafari()) {
this.isInstalled = window.navigator.standalone === true;
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');
// Если не установлено, добавляем соответствующие классы
document.body.classList.add('pwa-browser');
if (this.isIOSSafari()) {
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() {
@@ -59,6 +101,7 @@ class PWAInstallPrompt {
event.preventDefault();
this.deferredPrompt = event;
// Показываем промпт только если приложение не установлено
if (!this.isInstalled && this.shouldShowPrompt()) {
this.showInstallOptions();
}
@@ -75,6 +118,7 @@ class PWAInstallPrompt {
document.body.classList.add('pwa-installed');
});
// Дополнительная проверка для iOS
if (this.isIOSSafari()) {
let wasStandalone = window.navigator.standalone;
@@ -91,6 +135,9 @@ class PWAInstallPrompt {
this.showInstallSuccess();
document.body.classList.remove('pwa-browser');
document.body.classList.add('pwa-installed', 'ios-pwa');
// Сохраняем предпочтение установки
this.saveInstallPreference('installed', true);
}
wasStandalone = isStandalone;
@@ -166,7 +213,15 @@ class PWAInstallPrompt {
}
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()) {
this.showInstallButton();
@@ -178,6 +233,11 @@ class PWAInstallPrompt {
}
showInstallButton() {
// Дополнительная проверка статуса установки
if (!this.installationChecked) {
this.checkInstallationStatus();
}
if (this.installButton && !this.isInstalled) {
this.installButton.classList.remove('hidden');
@@ -190,10 +250,22 @@ class PWAInstallPrompt {
}, 100);
console.log('💿 Install button shown');
} else {
console.log('💿 Install button not shown - app is installed or button not available');
}
}
showInstallBanner() {
// Дополнительная проверка статуса установки
if (!this.installationChecked) {
this.checkInstallationStatus();
}
if (this.isInstalled) {
console.log('💿 App is installed, not showing install banner');
return;
}
if (!this.installBanner) {
this.createInstallBanner();
}
@@ -205,18 +277,27 @@ class PWAInstallPrompt {
}, 1000);
console.log('💿 Install banner shown');
} else {
console.log('💿 Install banner not shown - app is installed or banner not available');
}
}
hideInstallPrompts() {
console.log('💿 Hiding all install prompts');
if (this.installButton) {
this.installButton.classList.add('hidden');
console.log('💿 Install button hidden');
}
if (this.installBanner) {
this.installBanner.classList.remove('show');
this.installBanner.style.transform = 'translateY(100%)';
console.log('💿 Install banner hidden');
}
// Устанавливаем флаг установки
this.isInstalled = true;
}
async handleInstallClick() {
@@ -358,6 +439,8 @@ class PWAInstallPrompt {
}
showInstallSuccess() {
console.log('✅ Showing installation success notification');
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';
@@ -387,12 +470,37 @@ class PWAInstallPrompt {
notification.classList.add('translate-x-full');
setTimeout(() => notification.remove(), 300);
}, 5000);
// Скрываем все промпты установки
this.hideInstallPrompts();
}
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();
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()) {
const lastShown = preferences.ios_instructions_shown;

View File

@@ -1,5 +1,5 @@
// 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
class PWAOfflineManager {

39
sw.js
View File

@@ -1,5 +1,5 @@
// SecureBit.chat Service Worker
// Enhanced Security Edition v4.01.412
// Enhanced Security Edition v4.01.413
const CACHE_NAME = 'securebit-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) => {
console.log('🚀 Service Worker activating...');
event.waitUntil(
caches.keys()
.then((cacheNames) => {
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== STATIC_CACHE &&
cacheName !== DYNAMIC_CACHE &&
cacheName !== CACHE_NAME) {
console.log('🗑️ Deleting old cache:', cacheName);
cacheNames.map(cacheName => {
// Remove old caches
if (cacheName !== STATIC_CACHE && cacheName !== DYNAMIC_CACHE && cacheName !== CACHE_NAME) {
console.log(`🗑️ Removing old cache: ${cacheName}`);
return caches.delete(cacheName);
}
})
);
})
.then(() => {
console.log('✅ Service Worker activated');
// Claim all clients immediately
return self.clients.claim();
}).then(() => {
console.log('✅ Service Worker activated and old caches cleaned');
// Notify all clients about the update
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
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
@@ -352,4 +361,4 @@ self.addEventListener('unhandledrejection', (event) => {
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');