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>
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
${versionInfo.forceUpdate ? `
|
${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
|
Update Now
|
||||||
</button>
|
</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
|
Later
|
||||||
</button>
|
</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
|
Reload
|
||||||
</button>
|
</button>
|
||||||
`}
|
`}
|
||||||
|
|
@ -151,6 +151,21 @@ class VersionManager {
|
||||||
|
|
||||||
document.body.appendChild(notification);
|
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
|
// Animate in
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
notification.classList.remove('translate-y-full');
|
notification.classList.remove('translate-y-full');
|
||||||
|
|
@ -250,10 +265,10 @@ class VersionManager {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-3">
|
<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
|
Not Now
|
||||||
</button>
|
</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
|
Install
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -262,6 +277,17 @@ class VersionManager {
|
||||||
|
|
||||||
document.body.appendChild(prompt);
|
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
|
// Animate in
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
prompt.classList.remove('translate-y-full');
|
prompt.classList.remove('translate-y-full');
|
||||||
|
|
@ -270,6 +296,8 @@ class VersionManager {
|
||||||
|
|
||||||
async installApp() {
|
async installApp() {
|
||||||
if (!this.deferredInstallPrompt) {
|
if (!this.deferredInstallPrompt) {
|
||||||
|
// Show helpful feedback if installation isn't available
|
||||||
|
this.showInstallUnavailableMessage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,6 +315,88 @@ class VersionManager {
|
||||||
this.dismissInstallPrompt();
|
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() {
|
dismissInstallPrompt() {
|
||||||
const prompt = document.getElementById('tractatus-install-prompt');
|
const prompt = document.getElementById('tractatus-install-prompt');
|
||||||
if (prompt) {
|
if (prompt) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue