import { Component, inject } from '@angular/core'; import { ThemeService } from '../../services/theme.service'; import { SettingsService } from '../../services/settings.service'; import { CantiService } from '../../services/canti.service'; import { ConnectivityService } from '../../services/connectivity.service'; import { SwUpdate, VersionReadyEvent } from '@angular/service-worker'; import { ToastController, ModalController, AlertController } from '@ionic/angular'; import { VERSION } from '../../version'; import { PlaylistService } from '../../services/playlist.service'; import { Router } from '@angular/router'; import { filter, first } from 'rxjs/operators'; import { MyCantiService } from '../../services/my-canti.service'; import { CantiLettureService } from '../../services/canti-letture.service'; import { ComunitaService } from '../../services/comunita.service'; import { environment } from '../../../environments/environment'; import { showFullscreenUpdateOverlay } from '../../app.component'; @Component({ selector: 'app-settings', templateUrl: './settings.page.html', styleUrls: ['./settings.page.scss'], standalone: false }) export class SettingsPage { public myCantiService = inject(MyCantiService); public themeService = inject(ThemeService); public settingsService = inject(SettingsService); public cantiService = inject(CantiService); public connectivityService = inject(ConnectivityService); public playlistService = inject(PlaylistService); public cantiLettureService = inject(CantiLettureService); public comunitaService = inject(ComunitaService); private swUpdate = inject(SwUpdate); private toastCtrl = inject(ToastController); private modalCtrl = inject(ModalController); private alertCtrl = inject(AlertController); private router = inject(Router); public version = VERSION; public contactEmail = environment.contactEmail; public appName = environment.appName; 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'); } onMassChange(event: any) { this.cantiLettureService.setSelectedMass(event.detail.value); } /** * Performs a full data refresh + checks for app updates. * Uses both the Angular SW and the version.json fallback. */ async fullRefresh() { // 1. Refresh JSON data this.cantiService.refresh(); // 2. Refresh liturgical readings JSON try { await this.cantiLettureService.fetchData(); } catch (err) { console.error('Failed to refresh liturgical readings:', err); } // 3. Refresh community data if a code is active const comunitaCode = this.comunitaService.comunitaCode(); if (comunitaCode) { try { await this.comunitaService.setComunitaCode(comunitaCode); } catch (err) { console.error('Failed to refresh community data:', err); } } // 4. Check for app updates (SW + version.json fallback) const updateAvailable = await this.performUpdateCheck(); if (updateAvailable) { return; // updateAvailable already triggered the update/reload flow } const toast = await this.toastCtrl.create({ message: 'Dati aggiornati correttamente!', duration: 2000, color: 'success' }); await toast.present(); } async checkForAppUpdate() { if (!this.swUpdate.isEnabled) { const toast = await this.toastCtrl.create({ message: 'Aggiornamenti non supportati su questo browser.', duration: 3000, color: 'medium' }); await toast.present(); return; } const toastLoading = await this.toastCtrl.create({ message: 'Ricerca aggiornamenti in corso...', duration: 1500, color: 'secondary' }); await toastLoading.present(); const updateAvailable = await this.performUpdateCheck(); if (!updateAvailable) { const toast = await this.toastCtrl.create({ message: 'L\'applicazione è già aggiornata all\'ultima versione.', duration: 3000, color: 'success' }); await toast.present(); } } /** * Shared update check logic: tries Angular SW first, falls back to version.json. * Returns true if an update was found and the reload flow was initiated. */ private async performUpdateCheck(): Promise { try { // Layer 1: Force the browser to re-fetch the SW script if ('serviceWorker' in navigator) { const registration = await navigator.serviceWorker.ready; await registration.update(); } // Layer 2: Ask Angular SW to check if (this.swUpdate.isEnabled) { const swFoundUpdate = await this.swUpdate.checkForUpdate(); if (swFoundUpdate) { await this.applyUpdateAndReload(); return true; } } // Layer 3: Fallback — check version.json const versionMismatch = await this.checkVersionJson(); if (versionMismatch) { console.log('[PWA-Update] version.json mismatch detected from settings'); await this.applyUpdateAndReload(); return true; } return false; } catch (err) { console.error('[PWA-Update] Update check failed from settings:', err); const toast = await this.toastCtrl.create({ message: 'Errore durante la ricerca di aggiornamenti.', duration: 3000, color: 'danger' }); await toast.present(); return false; } } private async applyUpdateAndReload() { const overlay = showFullscreenUpdateOverlay(); let activated = false; const activateAndReload = async () => { if (activated) return; activated = true; try { if (this.swUpdate.isEnabled) { await this.swUpdate.activateUpdate(); } } catch (e) { console.warn('[PWA-Update] activateUpdate failed:', e); } overlay.finish(); setTimeout(() => { window.location.replace(window.location.origin + window.location.pathname + '?update=' + Date.now()); }, 600); }; // Listen for VERSION_READY + activate + reload if (this.swUpdate.isEnabled) { this.swUpdate.versionUpdates .pipe( filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY'), first() ) .subscribe(() => { console.log('[PWA-Update] Settings: version ready, activating...'); activateAndReload(); }); } // Safety timeout: reload after 6s regardless setTimeout(() => { console.log('[PWA-Update] Settings: safety timeout reached, activating...'); activateAndReload(); }, 6000); } private async checkVersionJson(): Promise { try { const response = await fetch(`/version.json?cb=${Date.now()}`, { cache: 'no-store' }); if (!response.ok) return false; const data = await response.json(); console.log(`[PWA-Update] Settings version check: local=${VERSION}, remote=${data.version}`); return data.version !== VERSION; } catch (err) { console.warn('[PWA-Update] version.json check failed:', err); return false; } } }