Ottimizzazione UI player, rotazione head tilt invertita, prefisso playlist in Home e implementazione Identity QR modal e interceptor API

This commit is contained in:
David Frassi
2026-06-12 17:13:46 +02:00
parent 4d1883a45d
commit d023bb7d3f
31 changed files with 1706 additions and 160 deletions
+118 -4
View File
@@ -1,5 +1,9 @@
import { Component, inject } from '@angular/core';
import { Component, inject, OnInit } from '@angular/core';
import { ThemeService } from './services/theme.service';
import { CantiService } from './services/canti.service';
import { VERSION } from './version';
import { Router, ActivatedRoute } from '@angular/router';
import { ToastController } from '@ionic/angular';
@Component({
selector: 'app-root',
@@ -7,14 +11,100 @@ import { ThemeService } from './services/theme.service';
styleUrls: ['app.component.scss'],
standalone: false,
})
export class AppComponent {
export class AppComponent implements OnInit {
private themeService = inject(ThemeService); // Ensures theme is initialized at boot
public cantiService = inject(CantiService);
public version = VERSION;
private router = inject(Router);
private route = inject(ActivatedRoute);
private toastCtrl = inject(ToastController);
constructor() {
// Gli aggiornamenti automatici e periodici sono stati rimossi.
// L'aggiornamento viene gestito esclusivamente in modo manuale
// tramite il pulsante "Verifica Aggiornamenti App" in SettingsPage.
}
ngOnInit() {
this.route.queryParams.subscribe(params => {
const protocolUrl = params['url'];
if (protocolUrl && protocolUrl.startsWith('web+canti:')) {
try {
const cleanUrl = protocolUrl.replace('web+canti://', 'http://localhost/');
const urlObj = new URL(cleanUrl);
let targetPath = urlObj.pathname;
if (targetPath === '/open' || targetPath === '//open') {
targetPath = '/';
} else if (targetPath.startsWith('/open/')) {
targetPath = targetPath.substring(5);
}
const queryParams: any = {};
urlObj.searchParams.forEach((value, key) => {
queryParams[key] = value;
});
this.router.navigate([targetPath], { queryParams, replaceUrl: true });
} catch (e) {
console.error('Failed to parse protocol url:', protocolUrl, e);
}
}
});
this.checkAndRedirectToPwa();
}
async checkAndRedirectToPwa() {
const isStandalone = window.matchMedia('(display-mode: standalone)').matches || (navigator as any).standalone;
if (isStandalone) {
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;
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();
}
}
}
}
export function showFullscreenUpdateOverlay() {
@@ -37,8 +127,13 @@ export function showFullscreenUpdateOverlay() {
overlay.innerHTML = `
<div style="text-align: center; padding: 20px; max-width: 400px; width: 100%;">
<h2 style="font-size: 1.8rem; font-weight: 600; margin-bottom: 10px; color: #ffffff; font-family: 'Outfit', sans-serif; -webkit-font-smoothing: antialiased;">Aggiornamento in corso</h2>
<p style="font-size: 1rem; color: rgba(255, 255, 255, 0.6); margin-bottom: 30px; font-family: 'Outfit', sans-serif; -webkit-font-smoothing: antialiased;">Installazione della nuova versione...</p>
<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>
@@ -47,6 +142,25 @@ export function showFullscreenUpdateOverlay() {
`;
document.body.appendChild(overlay);
// Fetch remote version to display the version being downloaded
fetch(`/version.json?cb=${Date.now()}`)
.then(res => {
if (res.ok) return res.json();
throw new Error('Fallback');
})
.then(data => {
const versionEl = document.getElementById('pwa-update-version');
if (data && data.version && versionEl) {
versionEl.textContent = 'Versione ' + data.version;
}
})
.catch(() => {
const versionEl = document.getElementById('pwa-update-version');
if (versionEl) {
versionEl.textContent = '';
}
});
let percent = 0;
const interval = setInterval(() => {
if (percent < 95) {