import { Injectable, signal, inject, effect } from '@angular/core'; import { HttpClient, HttpRequest, HttpEventType, HttpResponse } from '@angular/common/http'; import { SettingsService } from './settings.service'; import { Canto } from './canti.service'; export interface ParrocchiaItem { id_parrocchia: number; nome: string; codice: string; mail: string; guid_parrocchia: string; } export interface ParrocchiaCantiItem { id_parrocchia: number; id_canti: number; num_canto: number; } export interface CantiSettingsItem { id_canti: number; speed: number; tonalita: number; } export interface GetAllAppTablesResponse { parrocchia?: { data: ParrocchiaItem[] }; parrocchia_canti?: { data: ParrocchiaCantiItem[] }; canti_settings?: { data: CantiSettingsItem[] }; } @Injectable({ providedIn: 'root' }) export class ComunitaService { private http = inject(HttpClient); private settingsService = inject(SettingsService); public comunitaCode = signal(''); public comunitaNome = signal(''); public comunitaMail = signal(''); public comunitaCantiIds = signal<(number | string)[]>([]); public comunitaCantiInfo = signal([]); public comunitaCantiSettings = signal([]); public comunitaCantiPersonali = signal([]); public isFilterActive = signal(false); public loading = signal(false); public loadingProgress = signal(0); constructor() { const savedCode = localStorage.getItem('comunita-code'); const savedNome = localStorage.getItem('comunita-nome'); const savedMail = localStorage.getItem('comunita-mail'); const savedCanti = localStorage.getItem('comunita-canti-ids'); const savedInfo = localStorage.getItem('comunita-canti-info'); const savedSettings = localStorage.getItem('comunita-canti-settings'); const savedCantiPersonali = localStorage.getItem('comunita-canti-personali'); const savedFilterActive = localStorage.getItem('comunita-filter-active') === 'true'; if (savedCode) { this.comunitaCode.set(savedCode); // Run background refresh 1 second after startup to fetch any new custom canti or updates // but only if the community feature is enabled in settings setTimeout(() => { if (!this.settingsService.comunitaEnabled()) { // Community is disabled: skip refresh to avoid re-enabling the filter return; } this.setComunitaCode(savedCode).catch(err => { console.warn('Failed background community refresh at startup:', err); }); }, 1000); } if (savedNome) this.comunitaNome.set(savedNome); if (savedMail) this.comunitaMail.set(savedMail); if (savedCanti) { try { this.comunitaCantiIds.set(JSON.parse(savedCanti)); } catch (e) {} } if (savedInfo) { try { this.comunitaCantiInfo.set(JSON.parse(savedInfo)); } catch (e) {} } if (savedSettings) { try { this.comunitaCantiSettings.set(JSON.parse(savedSettings)); } catch (e) {} } if (savedCantiPersonali) { try { this.comunitaCantiPersonali.set(JSON.parse(savedCantiPersonali)); } catch (e) {} } this.isFilterActive.set(savedFilterActive); effect(() => { localStorage.setItem('comunita-code', this.comunitaCode()); localStorage.setItem('comunita-nome', this.comunitaNome()); localStorage.setItem('comunita-mail', this.comunitaMail()); localStorage.setItem('comunita-canti-ids', JSON.stringify(this.comunitaCantiIds())); localStorage.setItem('comunita-canti-info', JSON.stringify(this.comunitaCantiInfo())); localStorage.setItem('comunita-canti-settings', JSON.stringify(this.comunitaCantiSettings())); localStorage.setItem('comunita-canti-personali', JSON.stringify(this.comunitaCantiPersonali())); localStorage.setItem('comunita-filter-active', this.isFilterActive().toString()); }); effect(() => { if (!this.settingsService.comunitaEnabled()) { this.isFilterActive.set(false); } }); } private fetchWithProgress(url: string): Promise { return new Promise((resolve, reject) => { const req = new HttpRequest('GET', url, { reportProgress: true, responseType: 'json' }); this.http.request(req).subscribe({ next: (event) => { if (event.type === HttpEventType.DownloadProgress) { if (event.total && event.total > 0) { const pct = Math.round((event.loaded / event.total) * 100); this.loadingProgress.set(pct); } else { // No Content-Length header — simulate gradual progress up to 85% const simulated = Math.min(85, this.loadingProgress() + 12); this.loadingProgress.set(simulated); } } else if (event instanceof HttpResponse) { this.loadingProgress.set(100); resolve(event.body as T); } }, error: (err) => reject(err) }); }); } async setComunitaCode(code: string): Promise { const trimmedCode = code.trim(); if (!trimmedCode) { this.comunitaCode.set(''); this.comunitaNome.set(''); this.comunitaMail.set(''); this.comunitaCantiIds.set([]); this.comunitaCantiInfo.set([]); this.comunitaCantiSettings.set([]); this.comunitaCantiPersonali.set([]); this.isFilterActive.set(false); return true; } this.loading.set(true); this.loadingProgress.set(0); // 1. Try to fetch from the actual production API try { const url = `https://libretto.mmcinet.eu/canti/api/v3/get_all_app_tables?uuid=pwa-cc-uuid&email=&platform=browser&version=1.0&gruppo=${trimmedCode}&all_song=false&t=${Date.now()}`; const data = await this.fetchWithProgress(url); if (data && data.parrocchia?.data && data.parrocchia.data.length > 0) { const parrocchia = data.parrocchia.data[0]; const cantiList = data.parrocchia_canti?.data || []; const settingsList = data.canti_settings?.data || []; // Parse canti_personali (custom unvalidated community canti) const cantiPersonaliList = (data as any).canti_personali?.data || []; const mappedCantiPersonali = cantiPersonaliList.map((cp: any) => ({ id: cp.id_canti.toString(), id_canti: cp.id_canti, titolo: cp.titolo, testo: cp.accordi || '', accordi: cp.accordi || '', autore: cp.autore || '', link_youtube: cp.link_youtube || '', id_momenti: [], data_update: cp.data_update || '', nonValidato: true })); this.comunitaCode.set(trimmedCode); this.comunitaNome.set(parrocchia.nome || `Comunità ${trimmedCode}`); this.comunitaMail.set(parrocchia.mail || ''); this.comunitaCantiInfo.set(cantiList); this.comunitaCantiIds.set(cantiList.map(c => c.id_canti)); this.comunitaCantiSettings.set(settingsList); this.comunitaCantiPersonali.set(mappedCantiPersonali); this.isFilterActive.set(true); this.loading.set(false); return true; } } catch (err) { console.warn('Failed to fetch from production API, trying fallback...', err); this.loadingProgress.set(0); } // 2. Fallback to static community JSON try { const isProduction = window.location.hostname.includes('canticristiani.it'); const origin = isProduction ? window.location.origin : 'https://www.canticristiani.it'; const fallbackUrl = `${origin}/api/comunita_${trimmedCode}.json?t=${Date.now()}`; interface StaticComunitaData { id_comunita: string; nome_comunita: string; canti: (number | string)[]; } const staticData = await this.fetchWithProgress(fallbackUrl); if (staticData && staticData.canti) { this.comunitaCode.set(trimmedCode); this.comunitaNome.set(staticData.nome_comunita || `Comunità ${trimmedCode}`); this.comunitaCantiIds.set(staticData.canti); const mockInfo = staticData.canti.map((id, index) => ({ id_parrocchia: 1, id_canti: Number(id), num_canto: index + 1 })); this.comunitaCantiInfo.set(mockInfo); this.comunitaCantiSettings.set([]); this.comunitaCantiPersonali.set([]); this.isFilterActive.set(true); this.loading.set(false); return true; } } catch (err) { console.error('All community fetch strategies failed:', err); } this.loading.set(false); return false; } }