All files / app/layout/picture-lightbox picture-lightbox.component.ts

87.5% Statements 35/40
65.62% Branches 21/32
66.66% Functions 8/12
87.17% Lines 34/39

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 933x 3x 3x 3x 3x 3x 3x 3x                 3x 41x 41x 41x 41x   41x     41x         3x 1x     2x 1x 1x 1x         1x 1x         1x 1x   1x 1x             1x 1x 1x       1x 1x               1x               41x                    
import { PictureLightboxService } from '../../services/pictures/picture-lightbox.service';
import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, HostListener, inject, viewChild } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { RouterLink } from '@angular/router';
import { LoggerService } from '@drevo-web/core';
import { IconButtonComponent, SpinnerComponent } from '@drevo-web/ui';
import { filter, fromEvent } from 'rxjs';
 
@Component({
    selector: 'app-picture-lightbox',
    imports: [IconButtonComponent, RouterLink, SpinnerComponent],
    templateUrl: './picture-lightbox.component.html',
    styleUrl: './picture-lightbox.component.scss',
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PictureLightboxComponent {
    protected readonly lightboxService = inject(PictureLightboxService);
    private readonly document = inject(DOCUMENT);
    private readonly destroyRef = inject(DestroyRef);
    private readonly logger = inject(LoggerService).withContext('PictureLightbox');
 
    private readonly backdropEl = viewChild<ElementRef<HTMLElement>>('backdropEl');
 
    constructor() {
        this.listenFullscreenChange();
    }
 
    @HostListener('document:keydown.escape')
    onEscape(): void {
        if (!this.lightboxService.isOpen()) {
            return;
        }
 
        if (this.lightboxService.isZoomed() && !this.document.fullscreenElement) {
            this.lightboxService.toggleZoom();
        } else Eif (!this.lightboxService.isZoomed()) {
            this.lightboxService.close();
        }
    }
 
    onBackdropClick(event: MouseEvent): void {
        Eif ((event.target as HTMLElement).classList.contains('lightbox__backdrop')) {
            this.lightboxService.close();
        }
    }
 
    onImageClick(): void {
        const willZoom = !this.lightboxService.isZoomed();
        this.lightboxService.toggleZoom();
 
        if (willZoom) {
            this.enterFullscreen();
        } else E{
            this.exitFullscreen();
        }
    }
 
    onDetailLinkClick(): void {
        this.logger.info('Navigating to picture detail', { id: this.lightboxService.currentPicture()?.id });
        this.exitFullscreen();
        this.lightboxService.closeWithoutNavigation();
    }
 
    private enterFullscreen(): void {
        const el = this.backdropEl()?.nativeElement;
        Iif (el?.requestFullscreen && !this.document.fullscreenElement) {
            el.requestFullscreen().catch(() => {
                // Fullscreen may be blocked by browser policy — zoom still works without it
            });
        }
    }
 
    private exitFullscreen(): void {
        Iif (this.document.fullscreenElement) {
            this.document.exitFullscreen().catch(() => {
                // Fullscreen exit may fail if already exited
            });
        }
    }
 
    private listenFullscreenChange(): void {
        fromEvent(this.document, 'fullscreenchange')
            .pipe(
                filter(() => !this.document.fullscreenElement && this.lightboxService.isZoomed()),
                takeUntilDestroyed(this.destroyRef)
            )
            .subscribe(() => {
                this.lightboxService.toggleZoom();
            });
    }
}