gestione comunità

This commit is contained in:
David Frassi
2026-05-18 16:07:37 +02:00
parent 961b7ba65c
commit af12a1f2da
15 changed files with 1021 additions and 61 deletions
+10 -1
View File
@@ -4,10 +4,19 @@
<div class="offline-badge-header" *ngIf="!connectivityService.isOnline()">
<ion-icon name="cloud-offline-outline"></ion-icon>
</div>
<ion-button fill="clear" (click)="toggleChords()" style="margin: 0; --padding-start: 6px; --padding-end: 6px;">
<ion-icon slot="icon-only"
[name]="showChords() ? 'musical-notes-outline' : 'text-outline'"
[color]="showChords() ? 'secondary' : 'medium'"
style="font-size: 1.3rem;">
</ion-icon>
</ion-button>
</ion-buttons>
<ion-title class="outfit-font wrapped-title">
<div class="title-main" [style.fontSize.rem]="fontSize() * 1.1">
<span class="canto-number" *ngIf="canto()?.id_canti">{{ canto()?.id_canti }}</span>
<span class="canto-number" *ngIf="canto()?.id_canti" [style.color]="getCommunitySongNumber(canto()) !== null ? 'var(--ion-color-secondary)' : ''">
{{ getCommunitySongNumber(canto()) || canto()?.id_canti }}
</span>
<span class="title-text">{{ canto()?.titolo || 'Player' }}</span>
</div>
</ion-title>
+49
View File
@@ -11,6 +11,8 @@ import { ConnectivityService } from '../../services/connectivity.service';
import { PlaylistService } from '../../services/playlist.service';
import { YoutubePlayerService } from '../../services/youtube-player.service';
import { MyCantiService } from '../../services/my-canti.service';
import { ComunitaService } from '../../services/comunita.service';
import { StatsService } from '../../services/stats.service';
@Component({
selector: 'app-player',
@@ -103,11 +105,15 @@ export class PlayerPage implements OnInit, AfterViewInit, OnDestroy {
public settingsService = inject(SettingsService);
public connectivityService = inject(ConnectivityService);
public playlistService = inject(PlaylistService);
public comunitaService = inject(ComunitaService);
private myCantiService = inject(MyCantiService);
private statsService = inject(StatsService);
private gestureCtrl = inject(GestureController);
private el = inject(ElementRef);
private sanitizer = inject(DomSanitizer);
private songStartTime: number = 0;
constructor() {
// Sync mode from global settings
effect(() => {
@@ -195,9 +201,24 @@ export class PlayerPage implements OnInit, AfterViewInit, OnDestroy {
}
if (found) {
this.logPreviousSongTime();
this.canto.set(found);
this.songStartTime = Date.now();
this.cantiService.getStorage()?.set('last_song_id', id);
this.channel.postMessage({ type: 'SYNC_CANTO', id: found.id });
// Set custom community transposition if active
if (this.comunitaService.comunitaCode() && this.comunitaService.isFilterActive()) {
const settings = this.comunitaService.comunitaCantiSettings();
const songSetting = settings.find(s => s.id_canti === found.id_canti || s.id_canti === Number(found.id));
if (songSetting && songSetting.tonalita !== undefined) {
this.transposeAmount.set(songSetting.tonalita);
} else {
this.transposeAmount.set(0);
}
} else {
this.transposeAmount.set(0);
}
}
}
});
@@ -243,11 +264,21 @@ export class PlayerPage implements OnInit, AfterViewInit, OnDestroy {
transposeUp() {
this.transposeAmount.update(v => v + 1);
this.channel.postMessage({ type: 'SYNC_TRANSPOSE', amount: this.transposeAmount() });
const c = this.canto();
if (c) {
this.statsService.updateSongSettings(c.id_canti, this.transposeAmount());
}
}
transposeDown() {
this.transposeAmount.update(v => v - 1);
this.channel.postMessage({ type: 'SYNC_TRANSPOSE', amount: this.transposeAmount() });
const c = this.canto();
if (c) {
this.statsService.updateSongSettings(c.id_canti, this.transposeAmount());
}
}
zoomIn() {
@@ -485,7 +516,25 @@ export class PlayerPage implements OnInit, AfterViewInit, OnDestroy {
}
}
getCommunitySongNumber(canto: any): string | null {
if (!canto) return null;
if (!this.comunitaService.comunitaCode() || !this.comunitaService.isFilterActive()) return null;
const cantiInfo = this.comunitaService.comunitaCantiInfo();
const info = cantiInfo.find(x => x.id_canti === canto.id_canti || x.id_canti === Number(canto.id));
return info && info.num_canto ? info.num_canto.toString() : null;
}
private logPreviousSongTime() {
const c = this.canto();
if (c && this.songStartTime > 0) {
const timeSpent = (Date.now() - this.songStartTime) / 1000;
this.statsService.logSongView(c.id_canti, timeSpent);
this.songStartTime = 0;
}
}
ngOnDestroy() {
this.logPreviousSongTime();
this.audioEngine.stopListening();
this.channel.close();
}
+3 -1
View File
@@ -25,7 +25,9 @@
</div>
<div class="song-info">
<span class="canto-number">{{ song.id_canti }}</span>
<span class="song-title">{{ song.titolo }}</span>
<span class="song-title">
<span *ngIf="getCommunitySongNumber(song)" style="color: var(--ion-color-secondary); font-weight: 600; margin-right: 6px;">{{ getCommunitySongNumber(song) }}</span>{{ song.titolo }}
</span>
</div>
<div class="example-custom-placeholder" *cdkDragPlaceholder></div>
</div>
+9
View File
@@ -1,6 +1,7 @@
import { Component, inject, computed } from '@angular/core';
import { PlaylistService } from '../../services/playlist.service';
import { CantiService } from '../../services/canti.service';
import { ComunitaService } from '../../services/comunita.service';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { AlertController, ToastController, ModalController } from '@ionic/angular';
import { Router } from '@angular/router';
@@ -14,6 +15,7 @@ import { Router } from '@angular/router';
export class PlaylistPage {
public playlistService = inject(PlaylistService);
public cantiService = inject(CantiService);
public comunitaService = inject(ComunitaService);
private alertCtrl = inject(AlertController);
private toastCtrl = inject(ToastController);
private router = inject(Router);
@@ -177,4 +179,11 @@ export class PlaylistPage {
});
await toast.present();
}
getCommunitySongNumber(song: any): string | null {
if (!this.comunitaService.comunitaCode() || !this.comunitaService.isFilterActive()) return null;
const cantiInfo = this.comunitaService.comunitaCantiInfo();
const info = cantiInfo.find(x => x.id_canti === song.id_canti || x.id_canti === Number(song.id));
return info && info.num_canto ? info.num_canto.toString() : null;
}
}
+41 -28
View File
@@ -61,37 +61,31 @@
</ion-item>
</div>
<!-- Messa di Riferimento Selection -->
<!-- Comunità -->
<div class="settings-group glass ion-margin-bottom">
<div class="group-header ion-padding-start ion-padding-top">
<h2 class="outfit-font settings-group-title">
Messa di Riferimento (Suggeriti)
</h2>
</div>
<div class="select-item-container ion-padding-horizontal ion-padding-bottom">
<div class="select-label-row">
<ion-icon name="calendar-outline" color="secondary" class="select-icon"></ion-icon>
<div class="select-text-group">
<h2 class="settings-item-title outfit-font">Seleziona Messa</h2>
<p class="settings-item-subtitle outfit-font">Scegli la messa per i canti consigliati</p>
</div>
</div>
<div class="select-box-wrapper">
<ion-select [value]="cantiLettureService.selectedMassDate()"
(ionChange)="onMassChange($event)"
interface="action-sheet"
class="custom-select-block"
placeholder="Seleziona una messa...">
<ion-select-option *ngFor="let mass of cantiLettureService.availableMasses()" [value]="mass.date">
{{ mass.formattedLabel }}
</ion-select-option>
</ion-select>
</div>
</div>
<ion-item class="transparent-item" lines="none">
<ion-icon name="people-outline" slot="start" color="secondary"></ion-icon>
<ion-label class="outfit-font">
<h2 class="settings-item-title">Comunità</h2>
<p class="settings-item-subtitle">Mostra il filtro Comunità nella home</p>
</ion-label>
<ion-toggle slot="end" [checked]="settingsService.comunitaEnabled()" (ionChange)="settingsService.toggleComunitaEnabled()" color="secondary"></ion-toggle>
</ion-item>
</div>
<!-- Invio Dati Statistici -->
<div class="settings-group glass ion-margin-bottom">
<ion-item class="transparent-item" lines="none">
<ion-icon name="analytics-outline" slot="start" color="secondary"></ion-icon>
<ion-label class="outfit-font">
<h2 class="settings-item-title">Invio dati statistici</h2>
<p class="settings-item-subtitle">Invia statistiche di utilizzo e tonalità</p>
</ion-label>
<ion-toggle slot="end" [checked]="settingsService.invioDatiStatistici()" (ionChange)="settingsService.toggleInvioDatiStatistici()" color="secondary"></ion-toggle>
</ion-item>
</div>
<!-- Display Mode Section -->
<div class="settings-group glass ion-margin-bottom">
<div class="group-header ion-padding-start ion-padding-top">
@@ -117,6 +111,8 @@
</div>
<div class="settings-group glass ion-margin-top" *ngIf="settingsService.showEditor()">
<ion-item class="transparent-item" lines="none" (click)="myCantiService.sendAllMyCanti()" detail="true" button *ngIf="myCantiService.myCanti().length > 0">
<ion-icon name="send-outline" slot="start" color="secondary"></ion-icon>
@@ -185,6 +181,23 @@
</div>
</div>
<div class="legend-item">
<div style="display: flex; flex-direction: column; gap: 4px; align-items: center;">
<div class="legend-icon-wrapper">
<ion-icon name="text-outline" color="medium"></ion-icon>
</div>
<div class="legend-icon-wrapper">
<ion-icon name="musical-notes-outline" color="secondary"></ion-icon>
</div>
</div>
<div class="legend-text" style="padding-top: 6px;">
<h4 class="outfit-font">Testo / Accordi (icona in alto a destra)</h4>
<p class="outfit-font">
Alterna la visualizzazione tra solo testo e testo con accordi. L'icona con la <strong>T</strong> (grigia) indica la modalità testo; l'icona con le note musicali (arancione) indica la modalità accordi. Un tocco cambia modalità all'istante.
</p>
</div>
</div>
<div class="legend-item">
<div class="legend-icon-wrapper">
<ion-icon name="arrow-up-circle" color="secondary"></ion-icon>
+9 -1
View File
@@ -11,6 +11,7 @@ import { Router } from '@angular/router';
import { MyCantiService } from '../../services/my-canti.service';
import { CantiLettureService } from '../../services/canti-letture.service';
import { ComunitaService } from '../../services/comunita.service';
@Component({
selector: 'app-settings',
@@ -26,6 +27,7 @@ export class SettingsPage {
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);
@@ -47,6 +49,13 @@ export class SettingsPage {
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);
}
// 2. Check for Service Worker updates
if (this.swUpdate.isEnabled) {
@@ -77,5 +86,4 @@ export class SettingsPage {
});
await toast.present();
}
}