fix: PWA install button UX improvements and CSP compliance
Changes: - Add user feedback when PWA installation unavailable - Remove all inline event handlers (onclick=) for CSP compliance - Show helpful messages: "Already Installed" vs "Browser Not Supported" - Auto-dismiss unavailable message after 8 seconds - All buttons now use addEventListener (CSP compliant) Fixes: Non-responsive install button when prompt unavailable Security: Full CSP compliance - no inline event handlers
This commit is contained in:
parent
a15e67fb36
commit
72251385cb
1 changed files with 115 additions and 5 deletions
|
|
@ -134,14 +134,14 @@ class VersionManager {
|
|||
</div>
|
||||
<div class="flex gap-3">
|
||||
${versionInfo.forceUpdate ? `
|
||||
<button onclick="window.versionManager.applyUpdate()" class="bg-white text-blue-600 px-6 py-2 rounded-lg font-semibold hover:bg-blue-50 transition">
|
||||
<button id="update-now-btn" class="bg-white text-blue-600 px-6 py-2 rounded-lg font-semibold hover:bg-blue-50 transition">
|
||||
Update Now
|
||||
</button>
|
||||
` : `
|
||||
<button onclick="window.versionManager.dismissUpdate()" class="text-white hover:text-blue-100 transition px-3">
|
||||
<button id="update-later-btn" class="text-white hover:text-blue-100 transition px-3">
|
||||
Later
|
||||
</button>
|
||||
<button onclick="window.versionManager.applyUpdate()" class="bg-white text-blue-600 px-6 py-2 rounded-lg font-semibold hover:bg-blue-50 transition">
|
||||
<button id="update-reload-btn" class="bg-white text-blue-600 px-6 py-2 rounded-lg font-semibold hover:bg-blue-50 transition">
|
||||
Reload
|
||||
</button>
|
||||
`}
|
||||
|
|
@ -151,6 +151,21 @@ class VersionManager {
|
|||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Add event listeners (CSP compliant)
|
||||
const updateNowBtn = document.getElementById('update-now-btn');
|
||||
const updateLaterBtn = document.getElementById('update-later-btn');
|
||||
const updateReloadBtn = document.getElementById('update-reload-btn');
|
||||
|
||||
if (updateNowBtn) {
|
||||
updateNowBtn.addEventListener('click', () => this.applyUpdate());
|
||||
}
|
||||
if (updateLaterBtn) {
|
||||
updateLaterBtn.addEventListener('click', () => this.dismissUpdate());
|
||||
}
|
||||
if (updateReloadBtn) {
|
||||
updateReloadBtn.addEventListener('click', () => this.applyUpdate());
|
||||
}
|
||||
|
||||
// Animate in
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('translate-y-full');
|
||||
|
|
@ -250,10 +265,10 @@ class VersionManager {
|
|||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button onclick="window.versionManager.dismissInstallPrompt()" class="text-white hover:text-purple-100 transition px-3">
|
||||
<button id="dismiss-install-btn" class="text-white hover:text-purple-100 transition px-3">
|
||||
Not Now
|
||||
</button>
|
||||
<button onclick="window.versionManager.installApp()" class="bg-white text-purple-600 px-6 py-2 rounded-lg font-semibold hover:bg-purple-50 transition">
|
||||
<button id="install-app-btn" class="bg-white text-purple-600 px-6 py-2 rounded-lg font-semibold hover:bg-purple-50 transition">
|
||||
Install
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -262,6 +277,17 @@ class VersionManager {
|
|||
|
||||
document.body.appendChild(prompt);
|
||||
|
||||
// Add event listeners (CSP compliant)
|
||||
const dismissBtn = document.getElementById('dismiss-install-btn');
|
||||
const installBtn = document.getElementById('install-app-btn');
|
||||
|
||||
if (dismissBtn) {
|
||||
dismissBtn.addEventListener('click', () => this.dismissInstallPrompt());
|
||||
}
|
||||
if (installBtn) {
|
||||
installBtn.addEventListener('click', () => this.installApp());
|
||||
}
|
||||
|
||||
// Animate in
|
||||
setTimeout(() => {
|
||||
prompt.classList.remove('translate-y-full');
|
||||
|
|
@ -270,6 +296,8 @@ class VersionManager {
|
|||
|
||||
async installApp() {
|
||||
if (!this.deferredInstallPrompt) {
|
||||
// Show helpful feedback if installation isn't available
|
||||
this.showInstallUnavailableMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -287,6 +315,88 @@ class VersionManager {
|
|||
this.dismissInstallPrompt();
|
||||
}
|
||||
|
||||
showInstallUnavailableMessage() {
|
||||
// Check if app is already installed
|
||||
const isInstalled = window.matchMedia('(display-mode: standalone)').matches;
|
||||
|
||||
// Don't show message if it already exists
|
||||
if (document.getElementById('tractatus-install-unavailable')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = document.createElement('div');
|
||||
message.id = 'tractatus-install-unavailable';
|
||||
message.className = 'fixed bottom-0 left-0 right-0 bg-gray-800 text-white px-4 py-3 shadow-lg z-50 transform transition-transform duration-300 translate-y-full';
|
||||
|
||||
if (isInstalled) {
|
||||
message.innerHTML = `
|
||||
<div class="max-w-7xl mx-auto flex items-center justify-between flex-wrap gap-4">
|
||||
<div class="flex items-start flex-1">
|
||||
<svg class="w-6 h-6 mr-3 flex-shrink-0 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<div>
|
||||
<p class="font-semibold">Already Installed</p>
|
||||
<p class="text-sm text-gray-300">
|
||||
Tractatus is already installed on your device. You're using it right now!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button id="dismiss-unavailable-btn" class="text-white hover:text-gray-300 transition px-3">
|
||||
Okay
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
message.innerHTML = `
|
||||
<div class="max-w-7xl mx-auto flex items-center justify-between flex-wrap gap-4">
|
||||
<div class="flex items-start flex-1">
|
||||
<svg class="w-6 h-6 mr-3 flex-shrink-0 text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<div>
|
||||
<p class="font-semibold">Installation Not Available</p>
|
||||
<p class="text-sm text-gray-300">
|
||||
Your browser doesn't currently support app installation. Try using Chrome, Edge, or Safari on a supported device.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button id="dismiss-unavailable-btn" class="text-white hover:text-gray-300 transition px-3">
|
||||
Okay
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
document.body.appendChild(message);
|
||||
|
||||
// Add event listener for dismiss button
|
||||
const dismissBtn = document.getElementById('dismiss-unavailable-btn');
|
||||
if (dismissBtn) {
|
||||
dismissBtn.addEventListener('click', () => {
|
||||
message.classList.add('translate-y-full');
|
||||
setTimeout(() => {
|
||||
message.remove();
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
|
||||
// Animate in
|
||||
setTimeout(() => {
|
||||
message.classList.remove('translate-y-full');
|
||||
}, 100);
|
||||
|
||||
// Auto-dismiss after 8 seconds
|
||||
setTimeout(() => {
|
||||
if (message.parentElement) {
|
||||
message.classList.add('translate-y-full');
|
||||
setTimeout(() => {
|
||||
message.remove();
|
||||
}, 300);
|
||||
}
|
||||
}, 8000);
|
||||
}
|
||||
|
||||
dismissInstallPrompt() {
|
||||
const prompt = document.getElementById('tractatus-install-prompt');
|
||||
if (prompt) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue