fix(pwa): progress bar setup loading, PWA install detection, Miei filter chip & UI cleanup
This commit is contained in:
+311
-159
@@ -6,12 +6,15 @@
|
||||
<div class="header-text-group">
|
||||
<span class="app-name">{{ appName }}</span>
|
||||
<span class="version-badge" style="cursor: default; display: inline-flex; align-items: center; gap: 4px;">
|
||||
v{{ version }}<span *ngIf="settingsService.userName()"> - {{ settingsService.userName() }}</span>
|
||||
v{{ version }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button routerLink="/propose-canto" class="add-btn" title="Crea un nuovo canto">
|
||||
<ion-icon slot="icon-only" name="add-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ng-container *ngIf="hasUpdateAvailable(); else showQrBtn">
|
||||
<ion-button (click)="checkForAppUpdate($event)" class="add-btn" title="Aggiornamento disponibile">
|
||||
<ion-icon slot="icon-only" name="refresh-outline" color="secondary"></ion-icon>
|
||||
@@ -35,117 +38,328 @@
|
||||
placeholder="Cerca un canto..."
|
||||
[value]="searchQuery()"
|
||||
(ionInput)="onSearch($event)"
|
||||
[disabled]="isAdvancedSearchOpen()"
|
||||
class="custom-searchbar">
|
||||
</ion-searchbar>
|
||||
<ion-button fill="clear" (click)="toggleVoiceSearch()" class="voice-search-btn">
|
||||
<ion-icon slot="icon-only" [name]="audioEngine.isSearching() ? 'mic' : 'mic-outline'" [color]="audioEngine.isSearching() ? 'danger' : 'secondary'"></ion-icon>
|
||||
<ion-button fill="clear" (click)="toggleAdvancedSearch()" class="adv-search-btn" [class.active]="isAdvancedSearchOpen()" title="Ricerca avanzata">
|
||||
<ion-icon slot="icon-only" name="options-outline" [color]="isAdvancedSearchOpen() ? 'secondary' : 'medium'"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" (click)="toggleVoiceSearch('global')" class="voice-search-btn" [disabled]="isAdvancedSearchOpen()">
|
||||
<ion-icon slot="icon-only" [name]="(audioEngine.isSearching() && activeVoiceField() === 'global') ? 'mic' : 'mic-outline'" [color]="(audioEngine.isSearching() && activeVoiceField() === 'global') ? 'danger' : 'secondary'"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-actions-row">
|
||||
<div class="song-count-card outfit-font">
|
||||
{{ filteredCanti().length }}
|
||||
</div>
|
||||
<div *ngIf="playlistService.activeListName() !== null && totalPlaylistDuration()" class="song-count-card outfit-font">
|
||||
<ion-icon name="time-outline" style="margin-right: 4px; font-size: 1rem; color: var(--ion-color-secondary);"></ion-icon>
|
||||
<span>{{ totalPlaylistDuration() }}</span>
|
||||
</div>
|
||||
<div class="filter-buttons">
|
||||
<ion-button
|
||||
[fill]="activeFilterType() === 'playlist' || playlistService.activeListName() !== null ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleFilterType('playlist')"
|
||||
class="filter-chip">
|
||||
{{ playlistService.activeListName() !== null ? 'Playlist: ' + playlistService.activeListName() : 'Playlist' }}
|
||||
<span *ngIf="isRemotePlaylist() && playlistService.hasRemotePlaylistUpdate()" (click)="refreshActiveRemotePlaylist(); $event.stopPropagation();" style="display: inline-flex; align-items: center; justify-content: center; padding: 2px; margin-left: 6px; background: rgba(var(--ion-color-secondary-rgb), 0.25); border-radius: 50%; border: 1px solid var(--ion-color-secondary); width: 20px; height: 20px; box-shadow: 0 0 8px rgba(var(--ion-color-secondary-rgb), 0.3); vertical-align: middle;">
|
||||
<ion-icon name="refresh-outline" style="font-size: 0.95rem; color: var(--ion-color-secondary); font-weight: bold;"></ion-icon>
|
||||
</span>
|
||||
<ion-icon slot="end" name="chevron-down-outline" *ngIf="playlistService.activeListName() === null"></ion-icon>
|
||||
<span class="close-icon-wrapper" *ngIf="playlistService.activeListName() !== null" (click)="clearSpecialList($event)">
|
||||
<ion-icon slot="end" name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</ion-button>
|
||||
|
||||
<div class="comunita-filter-wrapper" style="position: relative; display: inline-flex; align-items: center; margin-right: 8px; z-index: 999;" *ngIf="settingsService.comunitaEnabled()">
|
||||
<ion-button
|
||||
[fill]="comunitaService.isFilterActive() ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleComunitaFilter()"
|
||||
[color]="comunitaService.isFilterActive() ? 'secondary' : 'medium'"
|
||||
class="filter-chip"
|
||||
style="margin: 0;">
|
||||
<ion-icon slot="start" name="people-outline" style="font-size: 1.1rem; margin-right: 4px;"></ion-icon>
|
||||
{{ comunitaService.comunitaCode() ? comunitaService.comunitaNome() : 'Comunità' }}
|
||||
</ion-button>
|
||||
<!-- Advanced Search & Filters Toggle Row -->
|
||||
<div class="advanced-search-toggle-row">
|
||||
<div class="toggle-row-left">
|
||||
<div class="song-count-card outfit-font">
|
||||
{{ filteredCanti().length }}
|
||||
</div>
|
||||
<button type="button" class="advanced-search-link outfit-font" (click)="toggleAdvancedSearch()">
|
||||
<ion-icon [name]="isAdvancedSearchOpen() ? 'chevron-up-outline' : 'options-outline'"></ion-icon>
|
||||
<span>Ricerche</span>
|
||||
<span class="inline-clear-btn" *ngIf="hasAdvancedSearchParams()" (click)="clearAdvancedSearch($event)" title="Reset ricerche">
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="advanced-search-link outfit-font" (click)="toggleFilterCard()">
|
||||
<ion-icon [name]="isFilterCardOpen() ? 'chevron-up-outline' : 'filter-outline'"></ion-icon>
|
||||
<span>Filtri</span>
|
||||
<span class="inline-clear-btn" *ngIf="hasFilterCardParams()" (click)="clearFilterCard($event)" title="Reset filtri">
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="advanced-search-link outfit-font" (click)="togglePlaylistCard()">
|
||||
<ion-icon [name]="isPlaylistCardOpen() ? 'chevron-up-outline' : 'list-outline'"></ion-icon>
|
||||
<span>Liste</span>
|
||||
<span class="inline-clear-btn" *ngIf="hasPlaylistCardParams()" (click)="clearPlaylistCard($event)" title="Reset liste">
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Advanced Search Panel -->
|
||||
<div class="advanced-search-panel glass outfit-font"
|
||||
*ngIf="isAdvancedSearchOpen()"
|
||||
(touchstart)="onPanelTouchStart($event)"
|
||||
(touchend)="onPanelTouchEnd($event, 'advancedSearch')">
|
||||
<div class="adv-search-grid">
|
||||
<div class="adv-input-group">
|
||||
<label class="adv-label">Ricerca per titolo</label>
|
||||
<div class="adv-input-wrapper">
|
||||
<ion-icon name="text-outline" class="adv-input-icon"></ion-icon>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Titolo canto..."
|
||||
[value]="searchTitle()"
|
||||
(input)="onSearchTitleInput($event)"
|
||||
class="adv-input" />
|
||||
<ion-icon name="close-circle" *ngIf="searchTitle()" (click)="searchTitle.set('')" class="adv-clear-icon"></ion-icon>
|
||||
<ion-button fill="clear" size="small" (click)="toggleVoiceSearch('title')" class="adv-mic-btn">
|
||||
<ion-icon slot="icon-only" [name]="(audioEngine.isSearching() && activeVoiceField() === 'title') ? 'mic' : 'mic-outline'" [color]="(audioEngine.isSearching() && activeVoiceField() === 'title') ? 'danger' : 'secondary'"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ion-button
|
||||
[fill]="activeFilterType() === 'lista_completa' || showValidati() || showNonValidati() ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleFilterType('lista_completa')"
|
||||
class="filter-chip">
|
||||
{{ showValidati() ? 'Lista: Validati' : (showNonValidati() ? 'Lista: Non Validati' : 'Lista completa') }}
|
||||
<ion-icon slot="end" name="chevron-down-outline" *ngIf="!showValidati() && !showNonValidati()"></ion-icon>
|
||||
<span class="close-icon-wrapper" *ngIf="showValidati() || showNonValidati()" (click)="clearListaCompleta($event)">
|
||||
<ion-icon slot="end" name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</ion-button>
|
||||
|
||||
<ion-button
|
||||
[fill]="activeFilterType() === 'liturgico' || selectedLiturgico() !== null ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleFilterType('liturgico')"
|
||||
class="filter-chip">
|
||||
{{ selectedLiturgico() !== null ? 'Liturgia: ' + getSelectedLiturgicoLabel() : 'Liturgia' }}
|
||||
<ion-icon slot="end" name="chevron-down-outline" *ngIf="selectedLiturgico() === null"></ion-icon>
|
||||
<span class="close-icon-wrapper" *ngIf="selectedLiturgico() !== null" (click)="clearLiturgico($event)">
|
||||
<ion-icon slot="end" name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</ion-button>
|
||||
|
||||
<ion-button
|
||||
[fill]="activeFilterType() === 'tematico' || selectedTematico() !== null ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleFilterType('tematico')"
|
||||
class="filter-chip">
|
||||
{{ selectedTematico() !== null ? 'Periodo: ' + getSelectedTematicoLabel() : 'Periodo' }}
|
||||
<ion-icon slot="end" name="chevron-down-outline" *ngIf="selectedTematico() === null"></ion-icon>
|
||||
<span class="close-icon-wrapper" *ngIf="selectedTematico() !== null" (click)="clearTematico($event)">
|
||||
<ion-icon slot="end" name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</ion-button>
|
||||
|
||||
<ion-button
|
||||
[fill]="showSuggeriti() ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleSuggeriti()"
|
||||
class="filter-chip">
|
||||
Suggeriti
|
||||
<span class="close-icon-wrapper" *ngIf="showSuggeriti()" (click)="clearSuggeriti($event)">
|
||||
<ion-icon slot="end" name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</ion-button>
|
||||
<div class="adv-input-group">
|
||||
<label class="adv-label">Ricerca per autore</label>
|
||||
<div class="adv-input-wrapper">
|
||||
<ion-icon name="person-outline" class="adv-input-icon"></ion-icon>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Nome autore..."
|
||||
[value]="searchAuthor()"
|
||||
(input)="onSearchAuthorInput($event)"
|
||||
class="adv-input" />
|
||||
<ion-icon name="close-circle" *ngIf="searchAuthor()" (click)="searchAuthor.set('')" class="adv-clear-icon"></ion-icon>
|
||||
<ion-button fill="clear" size="small" (click)="toggleVoiceSearch('author')" class="adv-mic-btn">
|
||||
<ion-icon slot="icon-only" [name]="(audioEngine.isSearching() && activeVoiceField() === 'author') ? 'mic' : 'mic-outline'" [color]="(audioEngine.isSearching() && activeVoiceField() === 'author') ? 'danger' : 'secondary'"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ion-button
|
||||
[fill]="showTopTen() ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleTopTen()"
|
||||
class="filter-chip">
|
||||
Top Ten
|
||||
<span class="close-icon-wrapper" *ngIf="showTopTen()" (click)="clearTopTen($event)">
|
||||
<ion-icon slot="end" name="close-circle"></ion-icon>
|
||||
<div class="adv-input-group">
|
||||
<label class="adv-label">Ricerca per testo</label>
|
||||
<div class="adv-input-wrapper">
|
||||
<ion-icon name="document-text-outline" class="adv-input-icon"></ion-icon>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Parole nel testo..."
|
||||
[value]="searchText()"
|
||||
(input)="onSearchTextInput($event)"
|
||||
class="adv-input" />
|
||||
<ion-icon name="close-circle" *ngIf="searchText()" (click)="searchText.set('')" class="adv-clear-icon"></ion-icon>
|
||||
<ion-button fill="clear" size="small" (click)="toggleVoiceSearch('text')" class="adv-mic-btn">
|
||||
<ion-icon slot="icon-only" [name]="(audioEngine.isSearching() && activeVoiceField() === 'text') ? 'mic' : 'mic-outline'" [color]="(audioEngine.isSearching() && activeVoiceField() === 'text') ? 'danger' : 'secondary'"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="adv-input-group">
|
||||
<label class="adv-label">Ricerca per nr canto</label>
|
||||
<div class="adv-input-wrapper">
|
||||
<ion-icon name="pricetag-outline" class="adv-input-icon"></ion-icon>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Numero canto..."
|
||||
[value]="searchNumber()"
|
||||
(input)="onSearchNumberInput($event)"
|
||||
class="adv-input" />
|
||||
<ion-icon name="close-circle" *ngIf="searchNumber()" (click)="searchNumber.set('')" class="adv-clear-icon"></ion-icon>
|
||||
<ion-button fill="clear" size="small" (click)="toggleVoiceSearch('number')" class="adv-mic-btn">
|
||||
<ion-icon slot="icon-only" [name]="(audioEngine.isSearching() && activeVoiceField() === 'number') ? 'mic' : 'mic-outline'" [color]="(audioEngine.isSearching() && activeVoiceField() === 'number') ? 'danger' : 'secondary'"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Playlist Card (default closed) -->
|
||||
<div class="filter-card glass outfit-font"
|
||||
*ngIf="isPlaylistCardOpen()"
|
||||
(touchstart)="onPanelTouchStart($event)"
|
||||
(touchend)="onPanelTouchEnd($event, 'playlistCard')">
|
||||
<div class="filter-card-row">
|
||||
<span class="filter-row-label">Playlist</span>
|
||||
<div class="filter-row-items">
|
||||
<!-- Comunità filter button if enabled -->
|
||||
<div class="comunita-filter-wrapper" style="position: relative; display: inline-flex; align-items: center; z-index: 999;" *ngIf="settingsService.comunitaEnabled()">
|
||||
<ion-button
|
||||
[fill]="comunitaService.isFilterActive() ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleComunitaFilter()"
|
||||
[color]="comunitaService.isFilterActive() ? 'secondary' : 'medium'"
|
||||
class="filter-chip"
|
||||
style="margin: 0;">
|
||||
<ion-icon slot="start" name="people-outline" style="font-size: 1.1rem; margin-right: 4px;"></ion-icon>
|
||||
{{ comunitaService.comunitaCode() ? comunitaService.comunitaNome() : 'Comunità' }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<!-- Playlist Chips -->
|
||||
<div
|
||||
*ngFor="let item of playlistService.allPlaylists()"
|
||||
class="momento-chip glass"
|
||||
[class.active]="playlistService.activePlaylistId() === item.id"
|
||||
[class.comunita-chip]="item.isComunita"
|
||||
(click)="playlistService.activePlaylistId() === item.id ? clearSpecialList($event) : selectPlaylist(item)">
|
||||
<ion-icon *ngIf="item.isComunita" name="people-outline" style="font-size: 0.85rem; margin-right: 4px; vertical-align: middle;"></ion-icon>
|
||||
{{ item.name }}
|
||||
<span *ngIf="playlistService.activePlaylistId() === item.id && isRemotePlaylist() && playlistService.hasRemotePlaylistUpdate()" (click)="refreshActiveRemotePlaylist(); $event.stopPropagation();" style="display: inline-flex; align-items: center; justify-content: center; padding: 2px; margin-left: 6px; background: rgba(var(--ion-color-secondary-rgb), 0.25); border-radius: 50%; border: 1px solid var(--ion-color-secondary); width: 18px; height: 18px; vertical-align: middle;">
|
||||
<ion-icon name="refresh-outline" style="font-size: 0.85rem; color: var(--ion-color-secondary); font-weight: bold;"></ion-icon>
|
||||
</span>
|
||||
<span class="close-icon-wrapper" *ngIf="playlistService.activePlaylistId() === item.id" (click)="clearSpecialList($event)">
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span *ngIf="playlistService.allPlaylists().length === 0 && !playlistService.selectionMode()" class="empty-playlist-text">
|
||||
Nessuna playlist creata
|
||||
</span>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-card-footer" *ngIf="hasPlaylistCardParams()">
|
||||
<div></div>
|
||||
<button type="button" class="clear-advanced-link outfit-font" (click)="clearPlaylistCard()">
|
||||
<ion-icon name="close-circle-outline"></ion-icon>
|
||||
<span>Reset liste</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Card (default closed) -->
|
||||
<div class="filter-card glass outfit-font"
|
||||
*ngIf="isFilterCardOpen()"
|
||||
(touchstart)="onPanelTouchStart($event)"
|
||||
(touchend)="onPanelTouchEnd($event, 'filterCard')">
|
||||
|
||||
<!-- Row 2: Tipologia -->
|
||||
<div class="filter-card-row">
|
||||
<span class="filter-row-label">Tipologia</span>
|
||||
<div class="filter-row-items">
|
||||
<div
|
||||
class="momento-chip glass"
|
||||
[class.active]="showValidati()"
|
||||
(click)="toggleValidati()">
|
||||
Validati
|
||||
<span class="close-icon-wrapper" *ngIf="showValidati()" (click)="clearListaCompleta($event)">
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="momento-chip glass"
|
||||
[class.active]="showNonValidati()"
|
||||
(click)="toggleNonValidati()"
|
||||
style="display: inline-flex; align-items: center; gap: 6px;">
|
||||
<span>Non Validati</span>
|
||||
<ion-icon
|
||||
*ngIf="showNonValidati() && !playlistService.selectionMode() && settingsService.showEditor()"
|
||||
name="add-outline"
|
||||
(click)="$event.stopPropagation()"
|
||||
routerLink="/propose-canto"
|
||||
class="non-validati-add-icon">
|
||||
</ion-icon>
|
||||
<span class="close-icon-wrapper" *ngIf="showNonValidati()" (click)="clearListaCompleta($event)">
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 3: Liturgia -->
|
||||
<div class="filter-card-row">
|
||||
<span class="filter-row-label">Liturgia</span>
|
||||
<div class="filter-row-items">
|
||||
<div
|
||||
*ngFor="let item of cantiService.indiceLiturgico()"
|
||||
class="momento-chip glass"
|
||||
[class.active]="selectedLiturgico() === item.id"
|
||||
(click)="toggleIndex(item.id, 'liturgico')">
|
||||
{{ item.tag_name }}
|
||||
<span class="close-icon-wrapper" *ngIf="selectedLiturgico() === item.id" (click)="clearLiturgico($event)">
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 4: Periodo -->
|
||||
<div class="filter-card-row">
|
||||
<span class="filter-row-label">Periodo</span>
|
||||
<div class="filter-row-items">
|
||||
<div
|
||||
*ngFor="let item of cantiService.indiceTematico()"
|
||||
class="momento-chip glass"
|
||||
[class.active]="selectedTematico() === item.id"
|
||||
(click)="toggleIndex(item.id, 'tematico')">
|
||||
{{ item.tag_name }}
|
||||
<span class="close-icon-wrapper" *ngIf="selectedTematico() === item.id" (click)="clearTematico($event)">
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 5 (Footer): Suggeriti, Top Ten & Reset filtri -->
|
||||
<div class="filter-card-footer">
|
||||
<div class="footer-actions">
|
||||
<ion-button
|
||||
[fill]="showSuggeriti() ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleSuggeriti()"
|
||||
class="filter-chip">
|
||||
Suggeriti
|
||||
<span class="close-icon-wrapper" *ngIf="showSuggeriti()" (click)="clearSuggeriti($event)">
|
||||
<ion-icon slot="end" name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</ion-button>
|
||||
|
||||
<ion-button
|
||||
[fill]="showTopTen() ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleTopTen()"
|
||||
class="filter-chip">
|
||||
Top Ten
|
||||
<span class="close-icon-wrapper" *ngIf="showTopTen()" (click)="clearTopTen($event)">
|
||||
<ion-icon slot="end" name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</ion-button>
|
||||
|
||||
<ion-button
|
||||
[fill]="showOnlyMine() ? 'solid' : 'outline'"
|
||||
size="small"
|
||||
(click)="toggleOnlyMine()"
|
||||
class="filter-chip">
|
||||
Miei
|
||||
<span class="close-icon-wrapper" *ngIf="showOnlyMine()" (click)="clearOnlyMine($event)">
|
||||
<ion-icon slot="end" name="close-circle"></ion-icon>
|
||||
</span>
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-toolbar>
|
||||
|
||||
<!-- Fixed Actions Bar in header for active playlist or selection mode -->
|
||||
<ion-toolbar class="bg-gradient playlist-toolbar" *ngIf="(playlistService.activeListName() !== null && !playlistService.selectionMode()) || (playlistService.selectionMode() && reorderList().length > 0)">
|
||||
<div class="playlist-actions-bar">
|
||||
<!-- Duration Badge on the left -->
|
||||
<div class="playlist-duration-pill outfit-font" *ngIf="totalPlaylistDuration()">
|
||||
<ion-icon name="time-outline"></ion-icon>
|
||||
<span>{{ totalPlaylistDuration() }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Playlist Name in middle -->
|
||||
<div class="playlist-title-badge outfit-font" *ngIf="playlistService.activeListName() && !playlistService.selectionMode()">
|
||||
<span class="playlist-title-text">{{ playlistService.activeListName() }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Active Playlist Actions -->
|
||||
<div class="selection-pill glass" *ngIf="playlistService.activeListName() !== null && !playlistService.selectionMode()">
|
||||
<ion-button fill="clear" color="secondary" (click)="refreshActiveRemotePlaylist()" class="mini-action-btn" title="Aggiorna Playlist" *ngIf="isRemotePlaylist()">
|
||||
<ion-icon slot="icon-only" name="refresh-outline" [style.color]="playlistService.hasRemotePlaylistUpdate() ? 'var(--ion-color-warning)' : 'inherit'"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="secondary" (click)="editPlaylist()" class="mini-action-btn" *ngIf="!isComunitaPlaylist() && isActivePlaylistSaved()">
|
||||
<ion-icon slot="icon-only" name="create-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="secondary" (click)="cloneActivePlaylist()" class="mini-action-btn" title="Clona Playlist">
|
||||
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="secondary" (click)="printActivePlaylist()" class="mini-action-btn" title="Esporta PDF">
|
||||
<ion-icon slot="icon-only" name="print-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="secondary" (click)="shareActivePlaylist()" class="mini-action-btn">
|
||||
<ion-icon slot="icon-only" name="share-social-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="danger" (click)="deleteActivePlaylist()" class="mini-action-btn" *ngIf="playlistService.activeListName() !== null && !isComunitaPlaylist()">
|
||||
<ion-icon name="trash-outline" slot="icon-only"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<!-- Category Scroll (Dropdown style) -->
|
||||
<ion-toolbar class="bg-gradient momentos-toolbar" *ngIf="shouldShowSubSectionToolbar()">
|
||||
<div class="momento-scroll" *ngIf="activeFilterType() !== 'playlist' || playlistService.allPlaylists().length > 0 || playlistService.selectionMode()">
|
||||
<!-- Selection Mode Actions -->
|
||||
<div class="selection-pill glass" *ngIf="playlistService.selectionMode() && reorderList().length > 0">
|
||||
<span class="selection-count">{{ reorderList().length }}</span>
|
||||
@@ -166,68 +380,6 @@
|
||||
<ion-icon slot="icon-only" name="close-circle-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<!-- Active Playlist Actions -->
|
||||
<div class="selection-pill glass" *ngIf="activeFilterType() === 'playlist' && playlistService.activePlaylistId() && !playlistService.selectionMode()">
|
||||
<ion-button fill="clear" color="secondary" (click)="refreshActiveRemotePlaylist()" class="mini-action-btn" title="Aggiorna Playlist" *ngIf="isRemotePlaylist()">
|
||||
<ion-icon slot="icon-only" name="refresh-outline" [style.color]="playlistService.hasRemotePlaylistUpdate() ? 'var(--ion-color-warning)' : 'inherit'"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="secondary" (click)="editPlaylist()" class="mini-action-btn" *ngIf="!isComunitaPlaylist() && isActivePlaylistSaved()">
|
||||
<ion-icon slot="icon-only" name="create-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="secondary" (click)="cloneActivePlaylist()" class="mini-action-btn" title="Clona Playlist">
|
||||
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="secondary" (click)="shareActivePlaylist()" class="mini-action-btn">
|
||||
<ion-icon slot="icon-only" name="share-social-outline"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" color="danger" (click)="deleteActivePlaylist()" class="mini-action-btn" *ngIf="playlistService.activePlaylistId() && !isComunitaPlaylist()">
|
||||
<ion-icon name="trash-outline" slot="icon-only"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<!-- Filter chips -->
|
||||
<ng-container *ngIf="activeFilterType() === 'lista_completa'">
|
||||
<div
|
||||
class="momento-chip glass"
|
||||
[class.active]="showValidati()"
|
||||
(click)="toggleValidati()">
|
||||
Validati
|
||||
</div>
|
||||
<div
|
||||
class="momento-chip glass"
|
||||
[class.active]="showNonValidati()"
|
||||
(click)="toggleNonValidati()"
|
||||
style="display: inline-flex; align-items: center; gap: 6px;">
|
||||
<span>Non Validati</span>
|
||||
<ion-icon
|
||||
*ngIf="showNonValidati() && !playlistService.selectionMode() && settingsService.showEditor()"
|
||||
name="add-outline"
|
||||
(click)="$event.stopPropagation()"
|
||||
routerLink="/propose-canto"
|
||||
class="non-validati-add-icon">
|
||||
</ion-icon>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="activeFilterType() !== 'lista_completa'">
|
||||
<div
|
||||
*ngFor="let item of (activeFilterType() === 'playlist' ? playlistService.allPlaylists() : (activeFilterType() === 'liturgico' ? cantiService.indiceLiturgico() : (activeFilterType() === 'tematico' ? cantiService.indiceTematico() : [])))"
|
||||
class="momento-chip glass"
|
||||
[class.active]="activeFilterType() === 'playlist' ? playlistService.activePlaylistId() === item.id : isIndexSelected(item.id)"
|
||||
[class.comunita-chip]="activeFilterType() === 'playlist' && item.isComunita"
|
||||
(click)="activeFilterType() === 'playlist' ? selectPlaylist(item) : toggleIndex(item.id, activeFilterType()!)">
|
||||
<ion-icon *ngIf="activeFilterType() === 'playlist' && item.isComunita" name="people-outline" style="font-size: 0.85rem; margin-right: 4px; vertical-align: middle;"></ion-icon>
|
||||
{{ activeFilterType() === 'playlist' ? item.name : item.tag_name }}
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<!-- Instructions when playlist is empty -->
|
||||
<div class="empty-playlist-container" *ngIf="activeFilterType() === 'playlist' && playlistService.allPlaylists().length === 0 && !playlistService.selectionMode()">
|
||||
<p class="empty-playlist-instruction outfit-font">
|
||||
Per creare una playlist, seleziona il nr del canto che vuoi inserire nella playlist, riordinali e salvala con nome
|
||||
</p>
|
||||
</div>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
@@ -244,6 +396,7 @@
|
||||
<div class="loading-wrapper">
|
||||
<ion-spinner name="crescent" color="secondary"></ion-spinner>
|
||||
<div class="percentage-label outfit-font">{{ cantiService.progress() }}%</div>
|
||||
<ion-progress-bar [value]="cantiService.progress() / 100" color="secondary" style="width: 200px; border-radius: 10px; height: 8px; margin-top: 8px;"></ion-progress-bar>
|
||||
<p class="ion-margin-top" style="color: var(--ion-color-secondary)">Caricamento canti...</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -473,7 +626,6 @@
|
||||
<div class="black-screen-overlay" *ngIf="isBlackOverlayActive()" (click)="isBlackOverlayDismissed.set(true)">
|
||||
<div class="black-screen-content" (click)="$event.stopPropagation()">
|
||||
<div (click)="isBlackOverlayDismissed.set(true)" style="display: flex; flex-direction: column; align-items: center; gap: 8px; cursor: pointer; width: 100%;">
|
||||
<ion-icon name="car-outline" class="car-mode-icon"></ion-icon>
|
||||
<p class="car-mode-text outfit-font">Schermo nero attivo</p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user