Miglioramenti generali layout player landscape/portrait, traduzione pulsanti navigazione, gestione aggiornamenti PWA e modifiche al posizionamento del pulsante aggiungi canto
This commit is contained in:
+258
-93
@@ -1,9 +1,12 @@
|
||||
import { Component, inject, OnInit } from '@angular/core';
|
||||
import { Component, inject, OnInit, signal } from '@angular/core';
|
||||
import { ThemeService } from './services/theme.service';
|
||||
import { CantiService } from './services/canti.service';
|
||||
import { SettingsService } from './services/settings.service';
|
||||
import { VERSION } from './version';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { ToastController } from '@ionic/angular';
|
||||
import { SwUpdate, VersionReadyEvent } from '@angular/service-worker';
|
||||
import { filter, first } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -14,10 +17,16 @@ import { ToastController } from '@ionic/angular';
|
||||
export class AppComponent implements OnInit {
|
||||
private themeService = inject(ThemeService); // Ensures theme is initialized at boot
|
||||
public cantiService = inject(CantiService);
|
||||
public settingsService = inject(SettingsService);
|
||||
public version = VERSION;
|
||||
private router = inject(Router);
|
||||
private route = inject(ActivatedRoute);
|
||||
private toastCtrl = inject(ToastController);
|
||||
private swUpdate = inject(SwUpdate);
|
||||
|
||||
public showRedirectOverlay = signal<boolean>(false);
|
||||
public showInstallOverlay = signal<boolean>(false);
|
||||
public protocolLink = '';
|
||||
|
||||
constructor() {
|
||||
// Gli aggiornamenti automatici e periodici sono stati rimossi.
|
||||
@@ -25,7 +34,28 @@ export class AppComponent implements OnInit {
|
||||
// tramite il pulsante "Verifica Aggiornamenti App" in SettingsPage.
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
async ngOnInit() {
|
||||
// 1. Allineamento istantaneo alla versione remota
|
||||
if ((window as any).PwaLoader) {
|
||||
(window as any).PwaLoader.update({
|
||||
phase: 'Fase: Verifica Versione',
|
||||
desc: 'Verifica della versione più recente in corso...'
|
||||
});
|
||||
}
|
||||
|
||||
const updated = await this.checkVersionSync();
|
||||
if (updated) {
|
||||
return; // Reloading, skip further setup
|
||||
}
|
||||
|
||||
// Set signal indicating startup version check is complete
|
||||
this.settingsService.isVersionCheckComplete.set(true);
|
||||
|
||||
// Hide loader if canti are also already loaded
|
||||
if (this.cantiService.firstLoadCompleted() && (window as any).PwaLoader) {
|
||||
(window as any).PwaLoader.hide();
|
||||
}
|
||||
|
||||
this.route.queryParams.subscribe(params => {
|
||||
const protocolUrl = params['url'];
|
||||
if (protocolUrl && protocolUrl.startsWith('web+canti:')) {
|
||||
@@ -55,92 +85,234 @@ export class AppComponent implements OnInit {
|
||||
this.checkAndRedirectToPwa();
|
||||
}
|
||||
|
||||
async checkVersionSync(): Promise<boolean> {
|
||||
// Controlla SEMPRE version.json per primo — è il modo più affidabile per
|
||||
// rilevare un disallineamento di versione, indipendentemente dallo stato del SW.
|
||||
// Su mobile, checkForUpdate() può essere lento o inaffidabile.
|
||||
try {
|
||||
console.log(`[PWA-Update] Verifica version.json all'avvio (locale=${VERSION})...`);
|
||||
const response = await Promise.race([
|
||||
fetch(`/version.json?cb=${Date.now()}`, { cache: 'no-store' }),
|
||||
new Promise<Response | null>((resolve) => setTimeout(() => resolve(null), 5000))
|
||||
]);
|
||||
if (response && response.ok) {
|
||||
const data = await response.json();
|
||||
if (data && data.version && data.version !== VERSION) {
|
||||
console.log(`[PWA-Update] Mismatch rilevato: locale=${VERSION}, remota=${data.version}. Forza aggiornamento...`);
|
||||
const overlay = showFullscreenUpdateOverlay();
|
||||
|
||||
// Prova ad attivare tramite SwUpdate se abilitato (scarica il nuovo bundle SW)
|
||||
if (this.swUpdate.isEnabled) {
|
||||
try {
|
||||
const hasSwUpdate = await Promise.race([
|
||||
this.swUpdate.checkForUpdate(),
|
||||
new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 8000))
|
||||
]);
|
||||
if (hasSwUpdate) {
|
||||
console.log('[PWA-Update] SW aggiornamento disponibile, attivazione...');
|
||||
await Promise.race([
|
||||
this.swUpdate.activateUpdate(),
|
||||
new Promise<void>((resolve) => setTimeout(resolve, 5000))
|
||||
]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[PWA-Update] SwUpdate durante mismatch fallito:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Aggiorna anche la registrazione SW direttamente (doppia sicurezza)
|
||||
if ('serviceWorker' in navigator) {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
await registration.update();
|
||||
} catch (e) {
|
||||
console.warn('[PWA-Update] SW registration.update fallito:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Disattiva service worker attivi per forzare il refresh completo
|
||||
if ('serviceWorker' in navigator) {
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
for (const registration of registrations) {
|
||||
await registration.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
// Cancella le cache del browser
|
||||
if ('caches' in window) {
|
||||
const keys = await caches.keys();
|
||||
for (const key of keys) {
|
||||
await caches.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
overlay.finish();
|
||||
|
||||
// Ricarica con parametro cache-busting per forzare l'allineamento remoto
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('update_cb', Date.now().toString());
|
||||
window.location.replace(url.toString());
|
||||
return true;
|
||||
} else {
|
||||
console.log('[PWA-Update] Versione allineata, nessun aggiornamento necessario.');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[PWA-Update] version.json check fallito:', e);
|
||||
}
|
||||
|
||||
// 2. Fallback: Prova SwUpdate nel caso in cui il controllo version.json sia fallito o sia stato servito dalla cache
|
||||
if (this.swUpdate.isEnabled) {
|
||||
try {
|
||||
console.log('[PWA-Update] Verifica aggiornamenti via SwUpdate all\'avvio...');
|
||||
|
||||
let activated = false;
|
||||
let overlay: any = null;
|
||||
|
||||
const activateAndReload = async () => {
|
||||
if (activated) return;
|
||||
activated = true;
|
||||
try {
|
||||
await this.swUpdate.activateUpdate();
|
||||
} catch (e) {
|
||||
console.warn('[PWA-Update] activateUpdate fallito all\'avvio:', e);
|
||||
}
|
||||
|
||||
// Deregistra i vecchi SW e cancella le cache per un ricaricamento pulito
|
||||
if ('serviceWorker' in navigator) {
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
for (const reg of registrations) {
|
||||
await reg.unregister();
|
||||
}
|
||||
}
|
||||
if ('caches' in window) {
|
||||
const keys = await caches.keys();
|
||||
for (const key of keys) {
|
||||
await caches.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (overlay) overlay.finish();
|
||||
setTimeout(() => {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('update_cb', Date.now().toString());
|
||||
window.location.replace(url.toString());
|
||||
}, 600);
|
||||
};
|
||||
|
||||
// Sottoscrivi PRIMA di verificare l'aggiornamento per evitare race condition
|
||||
const sub = this.swUpdate.versionUpdates
|
||||
.pipe(
|
||||
filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY'),
|
||||
first()
|
||||
)
|
||||
.subscribe(() => {
|
||||
console.log('[PWA-Update] VERSION_READY ricevuto all\'avvio');
|
||||
activateAndReload();
|
||||
});
|
||||
|
||||
// Concedi fino a 8 secondi al controllo SW — le connessioni mobili possono essere lente
|
||||
const hasUpdate = await Promise.race([
|
||||
this.swUpdate.checkForUpdate(),
|
||||
new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 8000))
|
||||
]);
|
||||
|
||||
if (hasUpdate) {
|
||||
console.log('[PWA-Update] Aggiornamento rilevato via SwUpdate. Avvio download...');
|
||||
overlay = showFullscreenUpdateOverlay();
|
||||
|
||||
// Timeout di sicurezza di 25 secondi: se VERSION_READY non arriva, attiva comunque
|
||||
setTimeout(() => {
|
||||
console.log('[PWA-Update] Safety timeout raggiunto all\'avvio, procedo...');
|
||||
sub.unsubscribe();
|
||||
activateAndReload();
|
||||
}, 25000);
|
||||
|
||||
return true; // Attendi il ricaricamento
|
||||
} else {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[PWA-Update] Controllo SwUpdate fallito all\'avvio:', err);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async checkAndRedirectToPwa() {
|
||||
const isStandalone = window.matchMedia('(display-mode: standalone)').matches || (navigator as any).standalone;
|
||||
if (isStandalone) {
|
||||
localStorage.setItem('pwa-installed', 'true');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have sharing/navigation query parameters
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const hasSharing = urlParams.has('import') || urlParams.has('playlist-uid') || urlParams.has('restore-uid') || urlParams.has('id');
|
||||
|
||||
if (hasSharing) {
|
||||
let isInstalled = false;
|
||||
if ('getInstalledRelatedApps' in navigator) {
|
||||
try {
|
||||
const relatedApps = await (navigator as any).getInstalledRelatedApps();
|
||||
isInstalled = relatedApps.length > 0;
|
||||
} catch (e) {
|
||||
console.warn('Failed to check installed apps:', e);
|
||||
const search = window.location.search;
|
||||
const path = window.location.pathname;
|
||||
this.protocolLink = `web+canti://open${path}${search}`;
|
||||
|
||||
// Controlliamo se abbiamo già salvato che l'app è installata o se possiamo verificarlo
|
||||
let isInstalled = localStorage.getItem('pwa-installed') === 'true';
|
||||
if (!isInstalled && 'getInstalledRelatedApps' in navigator) {
|
||||
try {
|
||||
const relatedApps = await (navigator as any).getInstalledRelatedApps();
|
||||
isInstalled = relatedApps.length > 0;
|
||||
if (isInstalled) {
|
||||
localStorage.setItem('pwa-installed', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
const search = window.location.search;
|
||||
const path = window.location.pathname;
|
||||
const protocolLink = `web+canti://open${path}${search}`;
|
||||
|
||||
if (isInstalled) {
|
||||
window.location.href = protocolLink;
|
||||
} else {
|
||||
const toast = await this.toastCtrl.create({
|
||||
header: 'Apri nell\'App CantiCristiani',
|
||||
message: 'Usa la PWA installata per visualizzare questo contenuto ed evitare la cache del browser.',
|
||||
position: 'top',
|
||||
color: 'warning',
|
||||
buttons: [
|
||||
{
|
||||
text: 'APRI APP',
|
||||
handler: () => {
|
||||
window.location.href = protocolLink;
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Nascondi',
|
||||
role: 'cancel'
|
||||
}
|
||||
]
|
||||
});
|
||||
await toast.present();
|
||||
} catch (e) {
|
||||
console.warn('Failed to check installed apps:', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (isInstalled) {
|
||||
const skipRedirect = sessionStorage.getItem('skip-pwa-redirect') === 'true';
|
||||
if (!skipRedirect) {
|
||||
this.showRedirectOverlay.set(true);
|
||||
// Tentiamo il reindirizzamento automatico
|
||||
setTimeout(() => {
|
||||
window.location.href = this.protocolLink;
|
||||
}, 800);
|
||||
}
|
||||
} else {
|
||||
// Se non è installata, proponiamo l'installazione immediata per evitare la cache del browser e avere un'esperienza ottimale
|
||||
const skipInstall = sessionStorage.getItem('skip-pwa-install') === 'true';
|
||||
if (!skipInstall && (this.settingsService.isAndroid() || this.settingsService.isIos())) {
|
||||
this.showInstallOverlay.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
openPwaManual() {
|
||||
window.location.href = this.protocolLink;
|
||||
}
|
||||
|
||||
stayInBrowser() {
|
||||
sessionStorage.setItem('skip-pwa-redirect', 'true');
|
||||
this.showRedirectOverlay.set(false);
|
||||
}
|
||||
|
||||
closeInstallOverlay() {
|
||||
sessionStorage.setItem('skip-pwa-install', 'true');
|
||||
this.showInstallOverlay.set(false);
|
||||
}
|
||||
|
||||
async triggerInstall() {
|
||||
await this.settingsService.installPwa();
|
||||
this.closeInstallOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
export function showFullscreenUpdateOverlay() {
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = 'pwa-update-overlay';
|
||||
overlay.style.position = 'fixed';
|
||||
overlay.style.top = '0';
|
||||
overlay.style.left = '0';
|
||||
overlay.style.width = '100vw';
|
||||
overlay.style.height = '100vh';
|
||||
overlay.style.backgroundColor = '#121212';
|
||||
overlay.style.color = '#ffffff';
|
||||
overlay.style.display = 'flex';
|
||||
overlay.style.flexDirection = 'column';
|
||||
overlay.style.justifyContent = 'center';
|
||||
overlay.style.alignItems = 'center';
|
||||
overlay.style.zIndex = '99999';
|
||||
overlay.style.fontFamily = "'Outfit', sans-serif";
|
||||
overlay.style.transition = 'opacity 0.5s ease';
|
||||
|
||||
overlay.innerHTML = `
|
||||
<div style="text-align: center; padding: 20px; max-width: 400px; width: 100%;">
|
||||
<div style="margin-bottom: 25px; display: inline-block;">
|
||||
<img src="assets/icon/favicon.png" alt="CantiCristiani" style="width: 80px; height: 80px; border-radius: 20px; box-shadow: 0 8px 24px rgba(230, 126, 34, 0.3); border: 2px solid rgba(230, 126, 34, 0.2);">
|
||||
</div>
|
||||
<h2 style="font-size: 1.8rem; font-weight: 600; margin-bottom: 5px; color: #ffffff; font-family: 'Outfit', sans-serif; -webkit-font-smoothing: antialiased;">Download aggiornamento</h2>
|
||||
<p style="font-size: 1rem; color: rgba(255, 255, 255, 0.6); margin-bottom: 15px; font-family: 'Outfit', sans-serif; -webkit-font-smoothing: antialiased;">Scaricamento della nuova versione...</p>
|
||||
<div id="pwa-update-version" style="font-size: 0.85rem; color: rgba(255, 255, 255, 0.45); margin-bottom: 20px; font-family: 'Outfit', sans-serif; -webkit-font-smoothing: antialiased; font-weight: 500;">Ricerca versione...</div>
|
||||
<div style="font-size: 0.95rem; font-weight: 600; color: #fdcb6e; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 1.5px; font-family: 'Outfit', sans-serif;">Fase: Download</div>
|
||||
<div style="background: rgba(255, 255, 255, 0.1); border-radius: 10px; height: 8px; width: 100%; overflow: hidden; margin-bottom: 15px;">
|
||||
<div id="pwa-update-bar" style="background: #e67e22; height: 100%; width: 0%; transition: width 0.1s ease; border-radius: 10px;"></div>
|
||||
</div>
|
||||
<div id="pwa-update-percent" style="font-size: 1.2rem; font-weight: 700; color: #e67e22; font-family: 'Outfit', sans-serif; -webkit-font-smoothing: antialiased;">0%</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(overlay);
|
||||
if ((window as any).PwaLoader) {
|
||||
(window as any).PwaLoader.show();
|
||||
(window as any).PwaLoader.update({
|
||||
title: 'Download aggiornamento',
|
||||
phase: 'Fase: Download',
|
||||
desc: 'Scaricamento della nuova versione...',
|
||||
percent: 0
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch remote version to display the version being downloaded
|
||||
fetch(`/version.json?cb=${Date.now()}`)
|
||||
@@ -149,38 +321,31 @@ export function showFullscreenUpdateOverlay() {
|
||||
throw new Error('Fallback');
|
||||
})
|
||||
.then(data => {
|
||||
const versionEl = document.getElementById('pwa-update-version');
|
||||
if (data && data.version && versionEl) {
|
||||
versionEl.textContent = 'Versione ' + data.version;
|
||||
if (data && data.version && (window as any).PwaLoader) {
|
||||
(window as any).PwaLoader.update({
|
||||
version: 'Versione ' + data.version
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
const versionEl = document.getElementById('pwa-update-version');
|
||||
if (versionEl) {
|
||||
versionEl.textContent = '';
|
||||
}
|
||||
});
|
||||
.catch(() => {});
|
||||
|
||||
let percent = 0;
|
||||
const interval = setInterval(() => {
|
||||
if (percent < 95) {
|
||||
percent += Math.floor(Math.random() * 5) + 2;
|
||||
if (percent > 95) percent = 95;
|
||||
updatePercent(percent);
|
||||
if ((window as any).PwaLoader) {
|
||||
(window as any).PwaLoader.update({ percent });
|
||||
}
|
||||
}
|
||||
}, 150);
|
||||
|
||||
function updatePercent(val: number) {
|
||||
const bar = document.getElementById('pwa-update-bar');
|
||||
const txt = document.getElementById('pwa-update-percent');
|
||||
if (bar) bar.style.width = val + '%';
|
||||
if (txt) txt.textContent = val + '%';
|
||||
}
|
||||
|
||||
return {
|
||||
finish: () => {
|
||||
clearInterval(interval);
|
||||
updatePercent(100);
|
||||
if ((window as any).PwaLoader) {
|
||||
(window as any).PwaLoader.update({ percent: 100 });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user