fix: correzione installabilità PWA e integrazione prompt installazione nativo

- Cambiate le proprietà 'scope' e 'start_url' in manifest.webmanifest a valori relativi ('./') al posto di un valore hardcoded '/ionic/'. Questo risolve il problema di installazione quando l'app viene pubblicata in directory diverse (es. root '/' vs sotto-cartella '/ionic/').
- Aggiunto il tracciamento del prompt di installazione nativo tramite l'evento 'beforeinstallprompt' nel SettingsService.
- Aggiunta la rilevazione dello stato standalone (app già installata) e del sistema operativo iOS.
- Inserita una sezione premium dedicata all'installazione nelle Impostazioni:
  * Su Android/Chrome: mostra un pulsante 'Installa' quando il browser conferma l'installabilità.
  * Su iOS (iPhone/iPad): mostra una guida collassabile con le istruzioni per installare l'app manualmente da Safari ('Condividi' -> 'Aggiungi alla schermata Home').
This commit is contained in:
David Frassi
2026-05-20 16:51:24 +02:00
parent e18c1c120b
commit d82d56a3cc
5 changed files with 104 additions and 3 deletions
+2 -2
View File
@@ -2,8 +2,8 @@
"name": "CantiCristiani",
"short_name": "CantiCristiani",
"display": "standalone",
"scope": "/ionic/",
"start_url": "/ionic/",
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "icons/icon-72x72.png",
+43
View File
@@ -10,6 +10,49 @@
<ion-content [fullscreen]="true" class="bg-gradient">
<div class="settings-container ion-padding">
<!-- PWA Install Group (Android / Chrome custom prompt) -->
<div class="settings-group glass ion-margin-bottom" *ngIf="settingsService.showInstallButton() && !settingsService.isStandalone()">
<ion-item class="transparent-item" lines="none" button (click)="installApp()">
<ion-icon name="download-outline" slot="start" color="secondary"></ion-icon>
<ion-label class="outfit-font">
<h2 class="settings-item-title" style="color: var(--ion-color-secondary); font-weight: bold;">Installa Applicazione</h2>
<p class="settings-item-subtitle">Aggiungi CantiCristiani alla schermata home</p>
</ion-label>
<ion-button fill="solid" size="small" color="secondary" slot="end" class="outfit-font" style="--border-radius: 12px; font-weight: bold; text-transform: none; margin: 0;">
Installa
</ion-button>
</ion-item>
</div>
<!-- PWA Install Group (iOS Safari Instructions) -->
<div class="settings-group glass ion-margin-bottom" *ngIf="settingsService.isIos() && !settingsService.isStandalone()">
<ion-item class="transparent-item" lines="none" button (click)="toggleIosInstructions()">
<ion-icon name="logo-apple" slot="start" color="secondary"></ion-icon>
<ion-label class="outfit-font">
<h2 class="settings-item-title">Installazione su iPhone / iPad</h2>
<p class="settings-item-subtitle">Scopri come aggiungere l'app sulla home</p>
</ion-label>
<ion-icon slot="end" [name]="showIosInstructions ? 'chevron-up' : 'chevron-down'" color="medium" style="font-size: 1.2rem;"></ion-icon>
</ion-item>
<div class="ios-instructions-content ion-padding" *ngIf="showIosInstructions" style="border-top: 1px solid rgba(255,255,255,0.08); background: rgba(255,255,255,0.02);">
<p class="outfit-font" style="font-size: 0.85rem; color: rgba(255,255,255,0.8); margin: 0 0 12px 0; line-height: 1.4;">
Apple non consente l'installazione automatica dei siti web. Segui questi semplici passi da <strong>Safari</strong>:
</p>
<ol class="outfit-font" style="font-size: 0.85rem; color: rgba(255,255,255,0.8); margin: 0; padding-left: 20px; line-height: 1.6;">
<li style="margin-bottom: 8px;">
Tocca il pulsante di <strong>Condivisione</strong> <ion-icon name="share-outline" style="font-size: 1.1rem; vertical-align: middle; margin: 0 2px; color: var(--ion-color-secondary);"></ion-icon> nella barra di navigazione inferiore di Safari.
</li>
<li style="margin-bottom: 8px;">
Scorri il menu verso il basso e seleziona <strong>Aggiungi alla schermata Home</strong> <ion-icon name="add-circle-outline" style="font-size: 1.1rem; vertical-align: middle; margin: 0 2px; color: var(--ion-color-secondary);"></ion-icon>.
</li>
<li>
Conferma toccando <strong>Aggiungi</strong> in alto a destra. L'icona dell'app apparirà sul tuo schermo!
</li>
</ol>
</div>
</div>
<div class="settings-group glass ion-margin-bottom">
<ion-item class="transparent-item" lines="none">
<ion-icon name="scan-outline" slot="start" color="secondary"></ion-icon>
+9
View File
@@ -37,9 +37,18 @@ export class SettingsPage {
public version = VERSION;
public contactEmail = environment.contactEmail;
public showIosInstructions = false;
constructor() {}
async installApp() {
await this.settingsService.installPwa();
}
toggleIosInstructions() {
this.showIosInstructions = !this.showIosInstructions;
}
onModeChange(event: any) {
this.settingsService.setShowChordsDefault(event.detail.value === 'chords');
}
+49
View File
@@ -49,7 +49,41 @@ export class SettingsService {
private wakeLock: any = null;
// PWA installation signals
public deferredPrompt = signal<any>(null);
public showInstallButton = signal<boolean>(false);
public isStandalone = signal<boolean>(false);
public isIos = signal<boolean>(false);
constructor() {
// Detect PWA status
try {
this.isStandalone.set(
window.matchMedia('(display-mode: standalone)').matches ||
(window.navigator as any).standalone === true
);
this.isIos.set(
/iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream
);
} catch (e) {
console.warn('Browser environment does not support display-mode media query');
}
window.addEventListener('beforeinstallprompt', (e: any) => {
// Prevent the mini-infobar from appearing on mobile
e.preventDefault();
// Stash the event so it can be triggered later.
this.deferredPrompt.set(e);
// Update UI notify the user they can install the PWA
this.showInstallButton.set(true);
});
window.addEventListener('appinstalled', () => {
this.deferredPrompt.set(null);
this.showInstallButton.set(false);
this.isStandalone.set(true);
console.log('PWA was installed');
});
const savedChords = localStorage.getItem('show-chords-default');
if (savedChords !== null) {
this.showChordsDefault.set(savedChords === 'true');
@@ -298,4 +332,19 @@ export class SettingsService {
this.enableAcousticAutoscroll.set(newValue);
localStorage.setItem('enable-acoustic-autoscroll', newValue.toString());
}
async installPwa() {
const promptEvent = this.deferredPrompt();
if (!promptEvent) {
return;
}
// Show the install prompt
promptEvent.prompt();
// Wait for the user to respond to the prompt
const { outcome } = await promptEvent.userChoice;
console.log(`User response to the install prompt: ${outcome}`);
// We've used the prompt, and can't use it again, discard it
this.deferredPrompt.set(null);
this.showInstallButton.set(false);
}
}
+1 -1
View File
@@ -1 +1 @@
export const VERSION = '2026.05.20.1602';
export const VERSION = '2026.05.20.1624';