aggiunto schermo nero durante il play audio in playlist
This commit is contained in:
+2
-2
@@ -57,8 +57,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "anyComponentStyle",
|
"type": "anyComponentStyle",
|
||||||
"maximumWarning": "16kb",
|
"maximumWarning": "32kb",
|
||||||
"maximumError": "25kb"
|
"maximumError": "48kb"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"fileReplacements": [
|
"fileReplacements": [
|
||||||
|
|||||||
@@ -389,15 +389,18 @@
|
|||||||
<ion-footer *ngIf="youtubePlayerService.isPlayerSupported() && youtubePlayerService.currentCantoId()" class="ion-no-border">
|
<ion-footer *ngIf="youtubePlayerService.isPlayerSupported() && youtubePlayerService.currentCantoId()" class="ion-no-border">
|
||||||
<ion-toolbar class="global-player-toolbar glass">
|
<ion-toolbar class="global-player-toolbar glass">
|
||||||
<div class="player-content">
|
<div class="player-content">
|
||||||
|
|
||||||
<div class="controls-row">
|
<div class="controls-row">
|
||||||
<ion-button fill="clear" color="secondary" (click)="playPrevPreview($event)" class="skip-btn">
|
<ion-button fill="clear" color="secondary" (click)="playPrevPreview($event)" class="skip-btn">
|
||||||
<ion-icon slot="icon-only" name="play-skip-back-sharp"></ion-icon>
|
<ion-icon slot="icon-only" name="chevron-back"></ion-icon>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
|
||||||
<ion-button fill="clear" color="secondary" (click)="youtubePlayerService.isPlaying() ? stopVideo($event) : playVideo($event, youtubePlayerService.currentCantoId()!)" class="play-btn">
|
<ion-button fill="clear" color="secondary" (click)="youtubePlayerService.togglePlayPause()" class="play-btn">
|
||||||
<ion-icon slot="icon-only" [name]="youtubePlayerService.isPlaying() ? 'pause-sharp' : 'play-sharp'"></ion-icon>
|
<ion-icon slot="icon-only" [name]="youtubePlayerService.isPlaying() ? 'pause-sharp' : 'play-sharp'"></ion-icon>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
|
||||||
|
<span class="player-time-label outfit-font" style="font-size: 0.85rem; color: var(--ion-color-secondary); opacity: 0.8; min-width: 38px; text-align: right; margin-left: 4px;">{{ formatSeconds(youtubePlayerService.videoProgress()) }}</span>
|
||||||
|
|
||||||
<ion-range
|
<ion-range
|
||||||
[min]="0"
|
[min]="0"
|
||||||
[max]="youtubePlayerService.videoDuration()"
|
[max]="youtubePlayerService.videoDuration()"
|
||||||
@@ -406,11 +409,14 @@
|
|||||||
(ionKnobMoveStart)="onSeekStart()"
|
(ionKnobMoveStart)="onSeekStart()"
|
||||||
(ionKnobMoveEnd)="onSeekEnd()"
|
(ionKnobMoveEnd)="onSeekEnd()"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
class="global-range">
|
class="global-range"
|
||||||
|
style="margin: 0; padding: 0 4px;">
|
||||||
</ion-range>
|
</ion-range>
|
||||||
|
|
||||||
|
<span class="player-time-label outfit-font" style="font-size: 0.85rem; color: var(--ion-color-secondary); opacity: 0.8; min-width: 38px; text-align: left; margin-right: 4px;">{{ formatSeconds(youtubePlayerService.videoDuration()) }}</span>
|
||||||
|
|
||||||
<ion-button fill="clear" color="secondary" (click)="playNextPreview($event)" class="skip-btn">
|
<ion-button fill="clear" color="secondary" (click)="playNextPreview($event)" class="skip-btn">
|
||||||
<ion-icon slot="icon-only" name="play-skip-forward-sharp"></ion-icon>
|
<ion-icon slot="icon-only" name="chevron-forward"></ion-icon>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
|
||||||
<ion-button fill="clear" color="medium" (click)="stopVideo($event)" class="close-btn">
|
<ion-button fill="clear" color="medium" (click)="stopVideo($event)" class="close-btn">
|
||||||
@@ -421,4 +427,36 @@
|
|||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-footer>
|
</ion-footer>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<div class="car-mode-canto-info" *ngIf="getPlayingCanto()">
|
||||||
|
<h2 class="car-mode-canto-title outfit-font">{{ getPlayingCanto()?.titolo }}</h2>
|
||||||
|
<p class="car-mode-canto-author outfit-font">{{ getPlayingCanto()?.autore }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="car-mode-progress outfit-font" *ngIf="youtubePlayerService.currentCantoId()">
|
||||||
|
<span class="car-mode-time">{{ formatSeconds(youtubePlayerService.videoProgress()) }} / {{ formatSeconds(youtubePlayerService.videoDuration()) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="car-mode-controls">
|
||||||
|
<ion-button fill="clear" (click)="playPrevPreview($event)" class="car-mode-btn">
|
||||||
|
<ion-icon slot="icon-only" name="play-skip-back-sharp"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
<ion-button fill="clear" (click)="youtubePlayerService.isPlaying() ? stopVideo($event) : playVideo($event, youtubePlayerService.currentCantoId()!)" class="car-mode-btn">
|
||||||
|
<ion-icon slot="icon-only" [name]="youtubePlayerService.isPlaying() ? 'pause-sharp' : 'play-sharp'"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
<ion-button fill="clear" (click)="playNextPreview($event)" class="car-mode-btn">
|
||||||
|
<ion-icon slot="icon-only" name="play-skip-forward-sharp"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="car-mode-subtext outfit-font" (click)="isBlackOverlayDismissed.set(true)">Tocca lo sfondo o l'icona per sbloccare</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -730,7 +730,6 @@ ion-title {
|
|||||||
&.glass {
|
&.glass {
|
||||||
background: rgba(26, 26, 46, 0.95) !important;
|
background: rgba(26, 26, 46, 0.95) !important;
|
||||||
backdrop-filter: blur(20px);
|
backdrop-filter: blur(20px);
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
@@ -1342,4 +1341,102 @@ ion-title {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* BLACK SCREEN OVERLAY FOR CAR MODE */
|
||||||
|
.black-screen-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #000000 !important;
|
||||||
|
z-index: 9999999 !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
|
||||||
|
|
||||||
|
.black-screen-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.car-mode-icon {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-text {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-subtext {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-canto-info {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
.car-mode-canto-title {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
margin: 0 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-canto-author {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: rgba(255, 255, 255, 0.2);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-progress {
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
.car-mode-time {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
|
.car-mode-btn {
|
||||||
|
--color: rgba(255, 255, 255, 0.25);
|
||||||
|
--padding-start: 8px;
|
||||||
|
--padding-end: 8px;
|
||||||
|
height: 48px;
|
||||||
|
width: 48px;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
ion-icon {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
--color: rgba(255, 255, 255, 0.55);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, computed, signal, inject, effect, OnDestroy } from '@angular/core';
|
import { Component, computed, signal, inject, effect, OnDestroy, HostListener } from '@angular/core';
|
||||||
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
||||||
import { CantiService } from '../services/canti.service';
|
import { CantiService } from '../services/canti.service';
|
||||||
import { Router, ActivatedRoute } from '@angular/router';
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
@@ -27,6 +27,28 @@ import { showFullscreenUpdateOverlay } from '../app.component';
|
|||||||
standalone: false,
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class HomePage implements OnDestroy {
|
export class HomePage implements OnDestroy {
|
||||||
|
@HostListener('window:keydown', ['$event'])
|
||||||
|
handleKeyDown(event: KeyboardEvent) {
|
||||||
|
// Only handle keys if a song is currently loaded in the player
|
||||||
|
if (!this.youtubePlayerService.currentCantoId()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === 'ArrowUp' || event.key === 'MediaTrackPrevious') {
|
||||||
|
this.playPrevPreview();
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.key === 'ArrowDown' || event.key === 'MediaTrackNext') {
|
||||||
|
this.playNextPreview();
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.key === 'MediaPlayPause') {
|
||||||
|
this.youtubePlayerService.togglePlayPause();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public searchQuery = signal<string>('');
|
public searchQuery = signal<string>('');
|
||||||
public selectedLiturgico = signal<number | null>(null);
|
public selectedLiturgico = signal<number | null>(null);
|
||||||
public selectedTematico = signal<number | null>(null);
|
public selectedTematico = signal<number | null>(null);
|
||||||
@@ -45,6 +67,13 @@ export class HomePage implements OnDestroy {
|
|||||||
public limit = signal<number>(30);
|
public limit = signal<number>(30);
|
||||||
public hasUpdateAvailable = signal<boolean>(false);
|
public hasUpdateAvailable = signal<boolean>(false);
|
||||||
|
|
||||||
|
public isBlackOverlayDismissed = signal<boolean>(false);
|
||||||
|
public isBlackOverlayActive = computed(() =>
|
||||||
|
this.settingsService.carModeBlackScreen() &&
|
||||||
|
this.youtubePlayerService.currentCantoId() !== null &&
|
||||||
|
!this.isBlackOverlayDismissed()
|
||||||
|
);
|
||||||
|
|
||||||
public fontSize = signal<number>(1.0);
|
public fontSize = signal<number>(1.0);
|
||||||
public youtubePlayerService = inject(YoutubePlayerService);
|
public youtubePlayerService = inject(YoutubePlayerService);
|
||||||
public comunitaService = inject(ComunitaService);
|
public comunitaService = inject(ComunitaService);
|
||||||
@@ -488,6 +517,14 @@ export class HomePage implements OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reset black screen dismissed state when play/pause changes
|
||||||
|
effect(() => {
|
||||||
|
const isPlaying = this.youtubePlayerService.isPlaying();
|
||||||
|
if (!isPlaying) {
|
||||||
|
this.isBlackOverlayDismissed.set(false);
|
||||||
|
}
|
||||||
|
}, { allowSignalWrites: true });
|
||||||
|
|
||||||
let queryParamsSubscribed = false;
|
let queryParamsSubscribed = false;
|
||||||
effect(() => {
|
effect(() => {
|
||||||
if (this.settingsService.isVersionCheckComplete() && !queryParamsSubscribed) {
|
if (this.settingsService.isVersionCheckComplete() && !queryParamsSubscribed) {
|
||||||
@@ -592,6 +629,16 @@ export class HomePage implements OnDestroy {
|
|||||||
this.isAddingSongs.set(false);
|
this.isAddingSongs.set(false);
|
||||||
}
|
}
|
||||||
}, { allowSignalWrites: true });
|
}, { allowSignalWrites: true });
|
||||||
|
|
||||||
|
// Fullscreen control when black screen overlay is active
|
||||||
|
effect(() => {
|
||||||
|
const isBlack = this.isBlackOverlayActive();
|
||||||
|
if (isBlack) {
|
||||||
|
this.enterFullscreen();
|
||||||
|
} else {
|
||||||
|
this.exitFullscreen();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1932,5 +1979,49 @@ export class HomePage implements OnDestroy {
|
|||||||
}
|
}
|
||||||
return dateStr;
|
return dateStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatSeconds(seconds: number | undefined | null): string {
|
||||||
|
if (seconds === undefined || seconds === null || isNaN(seconds)) {
|
||||||
|
return '0:00';
|
||||||
|
}
|
||||||
|
const mins = Math.floor(seconds / 60);
|
||||||
|
const secs = Math.floor(seconds % 60);
|
||||||
|
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async enterFullscreen() {
|
||||||
|
try {
|
||||||
|
const docEl = document.documentElement;
|
||||||
|
if (docEl.requestFullscreen) {
|
||||||
|
await docEl.requestFullscreen();
|
||||||
|
} else if ((docEl as any).webkitRequestFullscreen) {
|
||||||
|
await (docEl as any).webkitRequestFullscreen();
|
||||||
|
} else if ((docEl as any).msRequestFullscreen) {
|
||||||
|
await (docEl as any).msRequestFullscreen();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[HomePage] Failed to enter fullscreen:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async exitFullscreen() {
|
||||||
|
try {
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
if (document.fullscreenElement) {
|
||||||
|
await document.exitFullscreen();
|
||||||
|
}
|
||||||
|
} else if ((document as any).webkitExitFullscreen) {
|
||||||
|
if ((document as any).webkitFullscreenElement) {
|
||||||
|
await (document as any).webkitExitFullscreen();
|
||||||
|
}
|
||||||
|
} else if ((document as any).msExitFullscreen) {
|
||||||
|
if ((document as any).msFullscreenElement) {
|
||||||
|
await (document as any).msExitFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[HomePage] Failed to exit fullscreen:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,14 +154,19 @@
|
|||||||
<ion-icon slot="icon-only" [name]="youtubePlayerService.isPlaying() ? 'pause-sharp' : 'play-sharp'"></ion-icon>
|
<ion-icon slot="icon-only" [name]="youtubePlayerService.isPlaying() ? 'pause-sharp' : 'play-sharp'"></ion-icon>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
|
||||||
|
<span class="player-time-label outfit-font" style="font-size: 0.85rem; color: var(--ion-color-secondary); opacity: 0.8; min-width: 38px; text-align: right; margin-left: 4px;">{{ formatSeconds(youtubePlayerService.videoProgress()) }}</span>
|
||||||
|
|
||||||
<ion-range
|
<ion-range
|
||||||
[min]="0"
|
[min]="0"
|
||||||
[max]="youtubePlayerService.videoDuration()"
|
[max]="youtubePlayerService.videoDuration()"
|
||||||
[value]="youtubePlayerService.videoProgress()"
|
[value]="youtubePlayerService.videoProgress()"
|
||||||
(ionChange)="onSeek($event)"
|
(ionChange)="onSeek($event)"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
class="global-range">
|
class="global-range"
|
||||||
|
style="margin: 0; padding: 0 4px;">
|
||||||
</ion-range>
|
</ion-range>
|
||||||
|
|
||||||
|
<span class="player-time-label outfit-font" style="font-size: 0.85rem; color: var(--ion-color-secondary); opacity: 0.8; min-width: 38px; text-align: left; margin-right: 4px;">{{ formatSeconds(youtubePlayerService.videoDuration()) }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #noAudioMsg>
|
<ng-template #noAudioMsg>
|
||||||
<div class="no-audio-msg outfit-font" style="flex: 1; text-align: center; font-size: 0.85rem; opacity: 0.6; color: var(--ion-color-medium);">
|
<div class="no-audio-msg outfit-font" style="flex: 1; text-align: center; font-size: 0.85rem; opacity: 0.6; color: var(--ion-color-medium);">
|
||||||
@@ -258,7 +263,37 @@
|
|||||||
</ion-footer>
|
</ion-footer>
|
||||||
|
|
||||||
<!-- Black Screen Overlay -->
|
<!-- Black Screen Overlay -->
|
||||||
<div class="black-screen-overlay" *ngIf="isBlackScreen()" (click)="deactivateBlackScreen()"></div>
|
<div class="black-screen-overlay" *ngIf="isBlackScreen()" (click)="deactivateBlackScreen()">
|
||||||
|
<div class="black-screen-content" (click)="$event.stopPropagation()">
|
||||||
|
<div (click)="deactivateBlackScreen()" 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>
|
||||||
|
|
||||||
|
<div class="car-mode-canto-info" *ngIf="canto()">
|
||||||
|
<h2 class="car-mode-canto-title outfit-font">{{ canto()?.titolo }}</h2>
|
||||||
|
<p class="car-mode-canto-author outfit-font">{{ canto()?.autore }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="car-mode-progress outfit-font" *ngIf="youtubePlayerService.currentCantoId()">
|
||||||
|
<span class="car-mode-time">{{ formatSeconds(youtubePlayerService.videoProgress()) }} / {{ formatSeconds(youtubePlayerService.videoDuration()) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="car-mode-controls">
|
||||||
|
<ion-button fill="clear" (click)="prevSong()" class="car-mode-btn">
|
||||||
|
<ion-icon slot="icon-only" name="play-skip-back-sharp"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
<ion-button fill="clear" (click)="toggleAudio()" class="car-mode-btn">
|
||||||
|
<ion-icon slot="icon-only" [name]="youtubePlayerService.isPlaying() ? 'pause-sharp' : 'play-sharp'"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
<ion-button fill="clear" (click)="nextSong()" class="car-mode-btn">
|
||||||
|
<ion-icon slot="icon-only" name="play-skip-forward-sharp"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="car-mode-subtext outfit-font" (click)="deactivateBlackScreen()">Tocca lo sfondo o l'icona per sbloccare</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Hidden Camera Video for Head Navigation (needed for face detection API to run) -->
|
<!-- Hidden Camera Video for Head Navigation (needed for face detection API to run) -->
|
||||||
<video *ngIf="enableCameraNavigation()" id="face-preview-video" muted playsinline autoplay style="position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;"></video>
|
<video *ngIf="enableCameraNavigation()" id="face-preview-video" muted playsinline autoplay style="position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;"></video>
|
||||||
|
|||||||
@@ -891,11 +891,97 @@ ion-content.full-screen-content {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100vw;
|
right: 0;
|
||||||
height: 100vh;
|
bottom: 0;
|
||||||
background-color: #000000;
|
width: 100%;
|
||||||
z-index: 99999;
|
height: 100%;
|
||||||
|
background-color: #000000 !important;
|
||||||
|
z-index: 9999999 !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
|
||||||
|
|
||||||
|
.black-screen-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.car-mode-icon {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-text {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-subtext {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-canto-info {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
.car-mode-canto-title {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
margin: 0 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-canto-author {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: rgba(255, 255, 255, 0.2);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-progress {
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
.car-mode-time {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-mode-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
|
.car-mode-btn {
|
||||||
|
--color: rgba(255, 255, 255, 0.25);
|
||||||
|
--padding-start: 8px;
|
||||||
|
--padding-end: 8px;
|
||||||
|
height: 48px;
|
||||||
|
width: 48px;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
ion-icon {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
--color: rgba(255, 255, 255, 0.55);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.landscape-playlist-btn {
|
.landscape-playlist-btn {
|
||||||
|
|||||||
@@ -33,12 +33,15 @@ export class PlayerPage implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)) {
|
if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.key === 'ArrowUp') {
|
if (event.key === 'ArrowUp' || event.key === 'MediaTrackPrevious') {
|
||||||
this.prevSong();
|
this.prevSong();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
} else if (event.key === 'ArrowDown') {
|
} else if (event.key === 'ArrowDown' || event.key === 'MediaTrackNext') {
|
||||||
this.nextSong();
|
this.nextSong();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
} else if (event.key === 'MediaPlayPause') {
|
||||||
|
this.toggleAudio();
|
||||||
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,11 +325,12 @@ export class PlayerPage implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}, { allowSignalWrites: true });
|
}, { allowSignalWrites: true });
|
||||||
|
|
||||||
// Fullscreen control in landscape mode
|
// Fullscreen control in landscape mode or when black screen is active
|
||||||
effect(() => {
|
effect(() => {
|
||||||
const isLandscape = this.isLandscapeActive();
|
const isLandscape = this.isLandscapeActive();
|
||||||
|
const isBlack = this.isBlackScreen();
|
||||||
|
|
||||||
if (isLandscape) {
|
if (isLandscape || isBlack) {
|
||||||
this.enterFullscreen();
|
this.enterFullscreen();
|
||||||
} else {
|
} else {
|
||||||
this.exitFullscreen();
|
this.exitFullscreen();
|
||||||
@@ -1279,4 +1283,13 @@ export class PlayerPage implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.stopCameraNavigation();
|
this.stopCameraNavigation();
|
||||||
this.channel.close();
|
this.channel.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatSeconds(seconds: number | undefined | null): string {
|
||||||
|
if (seconds === undefined || seconds === null || isNaN(seconds)) {
|
||||||
|
return '0:00';
|
||||||
|
}
|
||||||
|
const mins = Math.floor(seconds / 60);
|
||||||
|
const secs = Math.floor(seconds % 60);
|
||||||
|
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,6 +150,14 @@
|
|||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-toggle slot="end" [checked]="settingsService.landscapeProjectionEnabled()" (ionChange)="settingsService.toggleLandscapeProjectionEnabled()" color="secondary"></ion-toggle>
|
<ion-toggle slot="end" [checked]="settingsService.landscapeProjectionEnabled()" (ionChange)="settingsService.toggleLandscapeProjectionEnabled()" color="secondary"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
<ion-item class="transparent-item" lines="none" style="border-top: 1px solid rgba(255,255,255,0.03);">
|
||||||
|
<ion-icon name="car-outline" slot="start" color="secondary"></ion-icon>
|
||||||
|
<ion-label class="outfit-font">
|
||||||
|
<h2 class="settings-item-title">Schermo nero durante l'ascolto</h2>
|
||||||
|
<p class="settings-item-subtitle">Se attiva la riproduzione in home, mostra uno schermo nero per risparmiare batteria. Tocca per disattivare.</p>
|
||||||
|
</ion-label>
|
||||||
|
<ion-toggle slot="end" [checked]="settingsService.carModeBlackScreen()" (ionChange)="settingsService.toggleCarModeBlackScreen()" color="secondary"></ion-toggle>
|
||||||
|
</ion-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Comunità -->
|
<!-- Comunità -->
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ export class SettingsService {
|
|||||||
/** Vista orizzontale per proiezione: true = attiva layout landscape per proiezione */
|
/** Vista orizzontale per proiezione: true = attiva layout landscape per proiezione */
|
||||||
public landscapeProjectionEnabled = signal<boolean>(false);
|
public landscapeProjectionEnabled = signal<boolean>(false);
|
||||||
|
|
||||||
|
/** Schermo nero per l'ascolto delle playlist in macchina: true = attivo */
|
||||||
|
public carModeBlackScreen = signal<boolean>(false);
|
||||||
|
|
||||||
/** Identificativo utente univoco per la gestione delle comunità */
|
/** Identificativo utente univoco per la gestione delle comunità */
|
||||||
public userUuid = signal<string>('');
|
public userUuid = signal<string>('');
|
||||||
|
|
||||||
@@ -254,6 +257,13 @@ export class SettingsService {
|
|||||||
this.landscapeProjectionEnabled.set(false);
|
this.landscapeProjectionEnabled.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const savedCarModeBlackScreen = localStorage.getItem('car-mode-black-screen');
|
||||||
|
if (savedCarModeBlackScreen !== null) {
|
||||||
|
this.carModeBlackScreen.set(savedCarModeBlackScreen === 'true');
|
||||||
|
} else {
|
||||||
|
this.carModeBlackScreen.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
// Sync browser fullscreen state with listeners (supporting vendor prefixes)
|
// Sync browser fullscreen state with listeners (supporting vendor prefixes)
|
||||||
const updateFullscreenState = () => {
|
const updateFullscreenState = () => {
|
||||||
const isFs = !!(
|
const isFs = !!(
|
||||||
@@ -311,6 +321,10 @@ export class SettingsService {
|
|||||||
localStorage.setItem('landscape-projection-enabled', this.landscapeProjectionEnabled().toString());
|
localStorage.setItem('landscape-projection-enabled', this.landscapeProjectionEnabled().toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
localStorage.setItem('car-mode-black-screen', this.carModeBlackScreen().toString());
|
||||||
|
});
|
||||||
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
const active = this.keepScreenOn();
|
const active = this.keepScreenOn();
|
||||||
localStorage.setItem('keep-screen-on', active.toString());
|
localStorage.setItem('keep-screen-on', active.toString());
|
||||||
@@ -446,6 +460,12 @@ export class SettingsService {
|
|||||||
localStorage.setItem('landscape-projection-enabled', newValue.toString());
|
localStorage.setItem('landscape-projection-enabled', newValue.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleCarModeBlackScreen() {
|
||||||
|
const newValue = !this.carModeBlackScreen();
|
||||||
|
this.carModeBlackScreen.set(newValue);
|
||||||
|
localStorage.setItem('car-mode-black-screen', newValue.toString());
|
||||||
|
}
|
||||||
|
|
||||||
setChordNotationPreference(val: 'diesis' | 'bemolle') {
|
setChordNotationPreference(val: 'diesis' | 'bemolle') {
|
||||||
this.chordNotationPreference.set(val);
|
this.chordNotationPreference.set(val);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
export const VERSION = '2026.06.18.0234';
|
export const VERSION = '2026.06.18.0959';
|
||||||
|
|||||||
Reference in New Issue
Block a user