227 lines
7.2 KiB
TypeScript
227 lines
7.2 KiB
TypeScript
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<boolean> {
|
|
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<boolean> {
|
|
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;
|
|
}
|
|
}
|
|
}
|