class PwaInstallBanner extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.deferredPrompt = null; this.STORAGE_KEY = 'PwaInstallBanner'; // Визначаємо ключ localStorage this.isInStandaloneMode = () => ('standalone' in window.navigator && window.navigator.standalone === true); this.os = this.detectOS(); } connectedCallback() { // Додаємо стилі та розмітку до Shadow DOM this.shadowRoot.innerHTML = this.getStyles() + this.getTemplate(); this.elements = { backdrop: this.shadowRoot.getElementById('blur-backdrop'), installOverlay: this.shadowRoot.getElementById('pwa-install-overlay'), iosOverlay: this.shadowRoot.getElementById('pwa-ios-overlay'), installButton: this.shadowRoot.getElementById('pwa-install-button'), closeButton: this.shadowRoot.getElementById('pwa-close-button'), iosCloseButton: this.shadowRoot.getElementById('pwa-ios-close-button'), }; this.setupListeners(); this.checkInitialDisplay(); } // --- Утиліти --- detectOS() { const userAgent = navigator.userAgent || navigator.vendor || window.opera; if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { return 'iOS'; } // ... (можна додати Android, Windows, але для PWA нас цікавить в першу чергу iOS) return 'Other'; } shouldShowBanner() { return localStorage.getItem(this.STORAGE_KEY) !== 'false'; } checkInitialDisplay() { if (!this.shouldShowBanner()) { return; // Не показуємо, якщо localStorage = 'false' } // Логіка для iOS if (this.os === 'iOS' && !this.isInStandaloneMode()) { // Затримка відображення, як у вихідному коді setTimeout(() => { this.openPopup(this.elements.iosOverlay); }, 1000); } } openPopup(overlayElement) { this.elements.backdrop.classList.remove('pwa-hidden'); overlayElement.classList.remove('pwa-hidden'); document.body.classList.add('modal-open'); } closePopup = () => { this.elements.installOverlay.classList.add('pwa-hidden'); this.elements.iosOverlay.classList.add('pwa-hidden'); this.elements.backdrop.classList.add('pwa-hidden'); document.body.classList.remove('modal-open'); this.deferredPrompt = null; } // --- Обробники подій --- setupListeners() { window.addEventListener("beforeinstallprompt", this.handleBeforeInstallPrompt); // Обробники кнопок this.elements.installButton.addEventListener("click", this.handleInstallClick); this.elements.closeButton.addEventListener("click", this.closePopup); this.elements.iosCloseButton.addEventListener('click', this.closePopup); } handleBeforeInstallPrompt = (e) => { // Вихідний код перевіряв localStorage, але для простоти прикладу я її пропускаю. if (!this.shouldShowBanner()) { return; // Не показуємо, якщо localStorage = 'false' } e.preventDefault(); this.deferredPrompt = e; // Показуємо стандартний банер, якщо доступно і не в режимі iOS if (this.os !== 'iOS') { this.openPopup(this.elements.installOverlay); } } handleInstallClick = async () => { if (!this.deferredPrompt) return; this.deferredPrompt.prompt(); const { outcome } = await this.deferredPrompt.userChoice; console.log(`[APP] Результат встановлення PWA: ${outcome}`); this.closePopup(); } // --- Шаблон (Template) та Стилі (Styles) --- getTemplate() { // HTML розмітка з вихідного коду return `
`; } getStyles() { // CSS стилі, які були у вихідному коді, але адаптовані для Shadow DOM // Примітки: // 1. Змінні CSS (наприклад, --ColorThemes0) мають бути визначені в основному документі // або передані через властивості, інакше вони не працюватимуть в Shadow DOM. // Я залишаю їх як є, припускаючи, що вони глобально доступні. // 2. Стилі для body.modal-open потрібно додати в основний CSS. return ` `; } } // Реєстрація веб-компонента customElements.define('pwa-install-banner', PwaInstallBanner);