Files
Sheep-Service/web/lib/customElements/pwaInstallBanner.js

301 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 `
<div id="blur-backdrop" class="pwa-hidden"></div>
<div id="pwa-install-overlay" class="pwa-overlay pwa-hidden">
<div class="popup">
<h2>Встановити застосунок?</h2>
<p>Додайте його на головний екран для швидкого доступу.</p>
<div>
<button id="pwa-install-button">Встановити</button>
<button id="pwa-close-button">Пізніше</button>
</div>
</div>
</div>
<div id="pwa-ios-overlay" class="pwa-overlay pwa-hidden">
<div class="popup">
<h2>Встановлення застосунку</h2>
<p>Щоб встановити застосунок, виконайте наступні кроки:</p>
<ol>
<li>1. Відкрийте посилання в браузері Safari.</li>
<li>
2. Натисніть кнопку
<span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
<path
d="M 14.984375 1 A 1.0001 1.0001 0 0 0 14.292969 1.2929688 L 10.292969 5.2929688 A 1.0001 1.0001 0 1 0 11.707031 6.7070312 L 14 4.4140625 L 14 17 A 1.0001 1.0001 0 1 0 16 17 L 16 4.4140625 L 18.292969 6.7070312 A 1.0001 1.0001 0 1 0 19.707031 5.2929688 L 15.707031 1.2929688 A 1.0001 1.0001 0 0 0 14.984375 1 z M 9 9 C 7.3550302 9 6 10.35503 6 12 L 6 24 C 6 25.64497 7.3550302 27 9 27 L 21 27 C 22.64497 27 24 25.64497 24 24 L 24 12 C 24 10.35503 22.64497 9 21 9 L 19 9 L 19 11 L 21 11 C 21.56503 11 22 11.43497 22 12 L 22 24 C 22 24.56503 21.56503 25 21 25 L 9 25 C 8.4349698 25 8 24.56503 8 24 L 8 12 C 8 11.43497 8.4349698 11 9 11 L 11 11 L 11 9 L 9 9 z"
/>
</svg>
</span>
в нижній частині екрана Safari.
</li>
<li>
3. У меню, що з’явиться, виберіть
<span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M 6 3 C 4.3550302 3 3 4.3550302 3 6 L 3 18 C 3 19.64497 4.3550302 21 6 21 L 18 21 C 19.64497 21 21 19.64497 21 18 L 21 6 C 21 4.3550302 19.64497 3 18 3 L 6 3 z M 6 5 L 18 5 C 18.56503 5 19 5.4349698 19 6 L 19 18 C 19 18.56503 18.56503 19 18 19 L 6 19 C 5.4349698 19 5 18.56503 5 18 L 5 6 C 5 5.4349698 5.4349698 5 6 5 z M 11.984375 6.9863281 A 1.0001 1.0001 0 0 0 11 8 L 11 11 L 8 11 A 1.0001 1.0001 0 1 0 8 13 L 11 13 L 11 16 A 1.0001 1.0001 0 1 0 13 16 L 13 13 L 16 13 A 1.0001 1.0001 0 1 0 16 11 L 13 11 L 13 8 A 1.0001 1.0001 0 0 0 11.984375 6.9863281 z"
/>
</svg>
</span>
«На Початковий екран».
</li>
</ol>
<div>
<button id="pwa-ios-close-button">Зрозуміло</button>
</div>
</div>
</div>
`;
}
getStyles() {
// CSS стилі, які були у вихідному коді, але адаптовані для Shadow DOM
// Примітки:
// 1. Змінні CSS (наприклад, --ColorThemes0) мають бути визначені в основному документі
// або передані через властивості, інакше вони не працюватимуть в Shadow DOM.
// Я залишаю їх як є, припускаючи, що вони глобально доступні.
// 2. Стилі для body.modal-open потрібно додати в основний CSS.
return `
<style>
#blur-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(8px);
z-index: 9998;
}
.pwa-overlay {
position: fixed;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.pwa-overlay>.popup {
background: var(--ColorThemes0, #ffffff); /* Fallback */
padding: 24px 32px;
border-radius: var(--border-radius, 15px);
max-width: 90%;
width: 320px;
text-align: center;
font-family: sans-serif;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
animation: fadeIn 0.3s ease-out;
display: flex;
flex-direction: column;
align-items: center;
}
.pwa-overlay>.popup h2 {
margin-bottom: 12px;
color: var(--ColorThemes3, #333);
opacity: 0.8;
}
.pwa-overlay>.popup p {
margin-bottom: 10px;
color: var(--ColorThemes3, #333);
opacity: 0.6;
}
.pwa-overlay>.popup ol {
text-align: justify;
font-size: var(--FontSize4, 15px);
margin-bottom: 10px;
max-width: 290px;
padding-left: 0; /* Виправлення відступу списку */
}
.pwa-overlay>.popup li {
list-style-type: none;
font-size: var(--FontSize3, 14px);
margin-bottom: 8px;
}
.pwa-overlay>.popup li span {
vertical-align: middle;
display: inline-block;
width: 22px;
height: 22px;
}
.pwa-overlay>.popup li span svg {
fill: var(--PrimaryColor, #007bff);
}
.pwa-overlay>.popup>div {
margin-top: 10px;
display: flex;
justify-content: center;
gap: 10px;
}
.pwa-overlay>.popup>div>button {
padding: 8px 16px;
border: none;
border-radius: calc(var(--border-radius, 15px) - 8px);
cursor: pointer;
font-size: var(--FontSize3, 14px);
}
#pwa-install-button {
background-color: var(--PrimaryColor, #007bff);
color: var(--PrimaryColorText, #ffffff);
}
#pwa-close-button,
#pwa-ios-close-button {
background-color: #ccc;
color: #333;
}
.pwa-hidden {
display: none !important; /* Важливо для скриптів */
}
@media (max-width: 450px) {
.pwa-overlay>.popup {
padding: 17px 10px;
}
.pwa-overlay>.popup h2 {
font-size: 22px;
}
.pwa-overlay>.popup p {
font-size: var(--FontSize4, 15px);
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
</style>
`;
}
}
// Реєстрація веб-компонента
customElements.define('pwa-install-banner', PwaInstallBanner);