littleshop/LittleShop/wwwroot/js/pwa.js
2025-09-01 04:49:05 +01:00

205 lines
6.9 KiB
JavaScript

// Progressive Web App functionality
// Handles service worker registration and PWA features
class PWAManager {
constructor() {
this.swRegistration = null;
this.init();
}
async init() {
console.log('PWA: Initializing PWA Manager...');
if ('serviceWorker' in navigator) {
try {
this.swRegistration = await navigator.serviceWorker.register('/sw.js');
console.log('SW: Service Worker registered successfully');
// Listen for updates
this.swRegistration.addEventListener('updatefound', () => {
console.log('SW: New version available');
this.showUpdateNotification();
});
} catch (error) {
console.log('SW: Service Worker registration failed:', error);
}
}
// Setup PWA install prompt
this.setupInstallPrompt();
// Setup notifications (if enabled)
this.setupNotifications();
// Show manual install option after 3 seconds if no prompt appeared
setTimeout(() => {
if (!document.getElementById('pwa-install-btn')) {
this.showManualInstallButton();
}
}, 3000);
}
setupInstallPrompt() {
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
console.log('PWA: beforeinstallprompt event fired');
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
deferredPrompt = e;
// Show custom install button
this.showInstallButton(deferredPrompt);
});
window.addEventListener('appinstalled', () => {
console.log('PWA: App was installed');
this.hideInstallButton();
});
// Debug: Check if app is already installed
if (this.isInstalled()) {
console.log('PWA: App is already installed (standalone mode)');
} else {
console.log('PWA: App is not installed, waiting for install prompt...');
}
}
showInstallButton(deferredPrompt) {
// Create install button
const installBtn = document.createElement('button');
installBtn.id = 'pwa-install-btn';
installBtn.className = 'btn btn-primary btn-sm';
installBtn.innerHTML = '<i class="fas fa-download"></i> Install App';
installBtn.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
`;
installBtn.addEventListener('click', async () => {
if (deferredPrompt) {
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
console.log('PWA: User response to install prompt:', outcome);
deferredPrompt = null;
this.hideInstallButton();
}
});
document.body.appendChild(installBtn);
}
hideInstallButton() {
const installBtn = document.getElementById('pwa-install-btn');
if (installBtn) {
installBtn.remove();
}
}
showUpdateNotification() {
// Show update available notification
const notification = document.createElement('div');
notification.className = 'alert alert-info alert-dismissible';
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 1050;
max-width: 300px;
`;
notification.innerHTML = `
<strong>Update Available!</strong><br>
A new version of the app is ready.
<button type="button" class="btn btn-sm btn-outline-info ms-2" id="update-btn">
Update Now
</button>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(notification);
// Handle update
document.getElementById('update-btn').addEventListener('click', () => {
if (this.swRegistration && this.swRegistration.waiting) {
this.swRegistration.waiting.postMessage({ type: 'SKIP_WAITING' });
window.location.reload();
}
});
}
async setupNotifications() {
// Check if notifications are supported and get permission
if ('Notification' in window) {
const permission = await this.requestNotificationPermission();
console.log('Notifications permission:', permission);
}
}
async requestNotificationPermission() {
if (Notification.permission === 'default') {
// Only request permission when user interacts with a relevant feature
// For now, just return the current status
return Notification.permission;
}
return Notification.permission;
}
// Show notification (if permission granted)
showNotification(title, options = {}) {
if (Notification.permission === 'granted') {
const notification = new Notification(title, {
icon: '/icons/icon-192x192.png',
badge: '/icons/icon-72x72.png',
tag: 'littleshop-admin',
...options
});
// Auto-close after 5 seconds
setTimeout(() => {
notification.close();
}, 5000);
return notification;
}
}
// Show manual install button for browsers that don't auto-prompt
showManualInstallButton() {
console.log('PWA: Showing manual install button');
const installBtn = document.createElement('button');
installBtn.id = 'pwa-install-btn';
installBtn.className = 'btn btn-primary btn-sm';
installBtn.innerHTML = '<i class="fas fa-mobile-alt"></i> Install as App';
installBtn.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
`;
installBtn.addEventListener('click', () => {
alert('To install this app:\\n\\n1. Click the browser menu (⋮)\\n2. Select "Install LittleShop Admin"\\n\\nOr look for the install icon in the address bar!');
});
document.body.appendChild(installBtn);
}
// Check if app is installed
isInstalled() {
return window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true;
}
}
// Initialize PWA Manager
const pwaManager = new PWAManager();
window.pwaManager = pwaManager;
// Expose notification function globally
window.showNotification = (title, options) => pwaManager.showNotification(title, options);