import { LitElement, html } from 'lit'; import { localized } from '@lit/localize'; import { property, customElement } from 'lit/decorators.js'; import { WebAppManifest } from 'web-app-manifest'; import { changeLocale } from './localization'; import { IRelatedApp, Manifest, IWindow, PWAInstallAttributes } from './types/types'; import PWAGalleryElement from './gallery'; import PWABottomSheetElement from './templates/chrome/bottom-sheet'; import Utils from './utils'; declare const window: IWindow; import styles from './templates/chrome/styles.scss'; import stylesApple from './templates/apple/styles-apple.scss'; import template from './templates/chrome/template'; import templateApple from './templates/apple/template-apple'; @localized() @customElement('pwa-install') export class PWAInstallElement extends LitElement { private manifest: WebAppManifest = new Manifest(); @property({attribute: 'manifest-url'}) manifestUrl = '/manifest.json'; @property() icon = ''; @property() name = ''; @property() description = ''; @property({attribute: 'install-description'}) installDescription = ''; @property({attribute: 'disable-install-description', type: Boolean}) disableDescription = false; @property({attribute: 'manual-apple', type: Boolean}) manualApple = false; @property({attribute: 'manual-chrome', type: Boolean}) manualChrome = false; @property({attribute: 'disable-chrome', type: Boolean}) disableChrome = false; static get styles() { return [ styles, stylesApple ]; } public platforms: BeforeInstallPromptEvent['platforms'] = []; public userChoiceResult = ''; public isDialogHidden: boolean = JSON.parse(window.sessionStorage.getItem('pwa-hide-install') || 'false'); public isInstallAvailable = false; public isAppleMobilePlatform = false; public isUnderStandaloneMode = false; public isRelatedAppsInstalled = false; /** @internal */ private _howToRequested = false; /** @internal */ private _galleryRequested = false; /** @internal */ private _install = { handleEvent: () => { if (window.deferredEvent) { this.hideDialog(); window.deferredEvent.prompt(); window.deferredEvent.userChoice .then((choiceResult: PromptResponseObject) => { this.userChoiceResult = choiceResult.outcome; Utils.eventUserChoiceResult(this, this.userChoiceResult); }) .catch((error) => { Utils.eventInstalledFail(this); }); window.deferredEvent = null; } }, passive: true } public install = () => { if (this.isAppleMobilePlatform) { this._howToRequested = true; this.requestUpdate(); } else this._install.handleEvent(); } /** @internal */ private _hideDialog = { handleEvent: () => { this.isDialogHidden = true; window.sessionStorage.setItem('pwa-hide-install', 'true'); this.requestUpdate(); }, passive: true } /** @internal */ private _hideDialogUser = () => { Utils.eventUserChoiceResult(this, 'dismissed'); this.hideDialog(); } public hideDialog = () => { this._hideDialog.handleEvent(); } public showDialog = (forced = false) => { this.isDialogHidden = false; if (forced) this.isInstallAvailable = true; window.sessionStorage.setItem('pwa-hide-install', 'false'); this.requestUpdate(); } public getInstalledRelatedApps = async (): Promise => { return await Utils.getInstalledRelatedApps(); } /** @internal */ private _howToForApple = { handleEvent: () => { this._howToRequested = !this._howToRequested; if (this._howToRequested && this._galleryRequested) this._galleryRequested = false; this.requestUpdate(); }, passive: true } /** @internal */ private _toggleGallery = { handleEvent: () => { this._galleryRequested = !this._galleryRequested; if (this._howToRequested && this._galleryRequested) this._howToRequested = false; this.requestUpdate(); }, passive: true } /** @internal */ private async _checkInstalled() { this.isUnderStandaloneMode = Utils.isStandalone(); this.isRelatedAppsInstalled = await Utils.isRelatedAppsInstalled(); this.isAppleMobilePlatform = Utils.isAppleMobile(); if (this.isAppleMobilePlatform) { if (!this.isUnderStandaloneMode) { this.manualApple && this.hideDialog(); setTimeout( () => { this.isInstallAvailable = true; this.requestUpdate() Utils.eventInstallAvailable(this); }, 300 ); } } else { this.manualChrome && this.hideDialog(); } } /** @internal */ private _init = () => { window.deferredEvent = null; this._checkInstalled(); if (!this.disableChrome) window.addEventListener('beforeinstallprompt', (e: BeforeInstallPromptEvent) => { window.deferredEvent = e; e.preventDefault(); this.platforms = e.platforms; if (this.isRelatedAppsInstalled || this.isUnderStandaloneMode) { this.isInstallAvailable = false; } else { this.isInstallAvailable = true; Utils.eventInstallAvailable(this); } if (this.userChoiceResult === 'accepted'){ this.isDialogHidden = true; Utils.eventInstalledSuccess(this); } this.requestUpdate(); }); window.addEventListener('appinstalled', (e) => { window.deferredEvent = null; this.isInstallAvailable = false; this.requestUpdate(); Utils.eventInstalledSuccess(this); }); fetch(this.manifestUrl).then((response: Response) => { if (response.ok) response.json().then((_json) => { this.icon = this.icon || _json.icons[0].src; this.name = this.name || _json['short_name'] || _json.name; this.description = this.description || _json.description; this.manifest = _json; }); else { this.icon = this.icon || this.manifest.icons?.[0].src || ''; this.name = this.name || this.manifest['short_name'] || ''; this.description = this.description || this.manifest.description || ''; } }); }; connectedCallback() { changeLocale(navigator.language); this._init(); PWAGalleryElement.finalized; PWABottomSheetElement.finalized; super.connectedCallback(); } // firstUpdated() { // return; // } render() { if (this.isAppleMobilePlatform) return html`${templateApple( this.name, this.description, this.installDescription, this.disableDescription, this.icon, this.manifest, this.isInstallAvailable && !this.isDialogHidden, this._hideDialogUser, this._howToForApple, this._howToRequested, this._toggleGallery, this._galleryRequested )}`; else return html`${template( this.name, this.description, this.installDescription, this.disableDescription, this.icon, this.manifest, this.isInstallAvailable && !this.isDialogHidden, this._hideDialogUser, this._install, this._toggleGallery, this._galleryRequested )}`; } } export { PWAInstallAttributes };