/** * Register a Service Worker and add PWA features like installing prompts, * but only if the HTML tag has data-pwa="true". Unregister service worker if not. */ export default (() => { const htmlElement = document.documentElement // Check the 'data-pwa' attribute of the HTML element if (htmlElement.getAttribute('data-pwa') !== 'true') { // Unregister the service worker if it's registered and 'data-pwa' is not 'true' if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistrations().then((registrations) => { for (let registration of registrations) { registration.unregister() } }) } return // Stop further execution to prevent PWA setup when not specified } // Settings const SETTINGS = { appName: 'Cartzilla', remindAfterHours: 24, // Number of hours to wait before showing the prompt again serviceWorkerFile: '/service-worker.js', // Service worker file path and name serviceWorkerScope: '/', // Scope of the service worker diagnostics: false, // Set to true to enable diagnostic logs } /** * Helper function for logging messages to the console based on the message type. * @param {string} message - The message to log. * @param {string} type - The type of message ('info' or 'error'). */ const logMessage = (message, type = 'info') => { if (SETTINGS.diagnostics) { if (type === 'error') { console.error(message) } else { console.log(message) } } } // Helper functions for detecting user's operating system and browser const userAgent = window.navigator.userAgent.toLowerCase() const detectOS = () => { if (userAgent.includes('android')) return 'Android' if (/iphone|ipad|ipod/.test(userAgent)) return 'iOS' if (userAgent.includes('mac')) return 'macOS' if (userAgent.includes('win')) return 'Windows' if (userAgent.includes('cros')) return 'ChromeOS' if (userAgent.includes('linux')) return 'Linux' return 'Unknown' } const detectBrowser = () => { if (userAgent.includes('chrome') && !userAgent.includes('edg')) return 'Chrome' if (userAgent.includes('safari') && !userAgent.includes('chrome')) return 'Safari' if (userAgent.includes('firefox')) return 'Firefox' if (userAgent.includes('edg')) return 'Edge' if (userAgent.includes('opera') || userAgent.includes('opr')) return 'Opera' return 'Unknown' } // Register service worker if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register(SETTINGS.serviceWorkerFile, { scope: SETTINGS.serviceWorkerScope, }) .then((registration) => { // Registration was successful logMessage( 'Service Worker registration successful with scope: ' + registration.scope ) }) .catch((err) => { // Registration failed logMessage('Service Worker registration failed: ' + err, 'error') }) }) } // Store variables for future use across application const promptId = 'installPWAPrompt' const timeoutKey = `${SETTINGS.appName}-Prompt-Timeout` const foreverKey = `${SETTINGS.appName}-Prompt-Dismiss-Forever` const installedKey = `${SETTINGS.appName}-App-Installed` // Initialize deferredPrompt for use later to show the install prompt let deferredPrompt // Function to create and show an installation prompt const installationPrompt = () => { // Detecting user's browser const browser = detectBrowser() // Check if we should show the prompt const now = Date.now() const setupTime = localStorage.getItem(timeoutKey) const dismissForever = localStorage.getItem(foreverKey) const appInstalled = localStorage.getItem(installedKey) // Check if dismiss forever is set to true or app installed, or not enough time has passed if ( dismissForever === 'true' || appInstalled === 'true' || (setupTime && now - setupTime < SETTINGS.remindAfterHours * 60 * 60 * 1000) ) { return } // HTML content for the prompt const promptHTML = `
` // Append the prompt HTML to the body document.body.insertAdjacentHTML('beforeend', promptHTML) // Get prompt instance const promptElement = document.getElementById(promptId) /* eslint-disable no-undef */ const promptInstance = new bootstrap.Modal(promptElement, { backdrop: 'static', // Optional: makes prompt not close when clicking outside keyboard: false, // Optional: makes prompt not close when pressing escape key }) /* eslint-enable no-undef */ // Show the prompt promptInstance.show() // Log the message logMessage('PWA installation prompt has been displayed.') // Event listeners to remove the prompt from the DOM when dismissed document .getElementById('timeoutPWAButton') .addEventListener('click', () => { promptInstance.hide() localStorage.setItem(timeoutKey, Date.now()) // Set the new timeout value when dismissed }) document .getElementById('dismissPWAButton') .addEventListener('click', () => { promptInstance.hide() localStorage.setItem(foreverKey, true) // Set the foreverKey value to true }) promptElement.addEventListener('hidden.bs.modal', () => { promptInstance.dispose() // Ensure the prompt is cleaned up correctly promptElement.remove() // Remove the prompt from the DOM }) } // Handling appinstalled event for cases when PWA installed from the address bar or other browser components window.addEventListener('appinstalled', () => { localStorage.setItem(installedKey, true) // Set the installedKey value to true deferredPrompt = null // Clear the deferredPrompt so it can be garbage collected logMessage('PWA was installed') // Log message }) // Funtion for initialization and configuration of Progressive Web App (PWA) installation prompts based on the user's operating system and browser const setupPWAInstallation = () => { const os = detectOS() const browser = detectBrowser() // Check if the PWA is already installed const isInStandaloneMode = () => ('standalone' in navigator && navigator.standalone) || window.matchMedia('(display-mode: standalone)').matches if (os === 'iOS' && browser === 'Safari') { // Specific instructions for Safari on iOS setTimeout(() => { if (!isInStandaloneMode()) { installationPrompt() logMessage('PWA installation prompt has been displayed.') } }, 3500) } else if ( os !== 'iOS' && (browser === 'Chrome' || browser === 'Edge' || browser === 'Opera') ) { // Setup for Chrome, Edge, and Opera on non-iOS devices if (!isInStandaloneMode()) { window.addEventListener('beforeinstallprompt', (e) => { // Log message logMessage(`'beforeinstallprompt' event was fired.`) // Prevent the mini-infobar from appearing on mobile e.preventDefault() // Stash the event so it can be triggered later deferredPrompt = e // Show the installation prompt to the user setTimeout(() => { installationPrompt() }, 3500) }) // Handle "Install" button click event document.body.addEventListener('click', (e) => { const target = e.target // Check if the clicked element is an "Install" button if (target.id === 'installPWAButton') { const promptElement = document.getElementById(promptId) /* eslint-disable no-undef */ const promptInstance = bootstrap.Modal.getInstance(promptElement) /* eslint-enable no-undef */ if (promptInstance) { promptInstance.hide() // Hide the prompt } deferredPrompt.prompt() // Show the installation prompt deferredPrompt.userChoice.then((choiceResult) => { if (choiceResult.outcome === 'accepted') { logMessage('User accepted the A2HS prompt. PWA was installed') localStorage.setItem(installedKey, true) // Set the installedKey value to true } else { logMessage('User dismissed the A2HS prompt') localStorage.setItem(timeoutKey, Date.now()) // Set the new timeout value } deferredPrompt = null // We've used the prompt and can't use it again, throw it away }) } }) } } else { logMessage('PWA installation is not supported on your device or browser.') } } // Call the setup function setupPWAInstallation() })()