You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
front/src/app/components/timeline/timeline.component.ts

141 lines
4.4 KiB

import { CommonModule, ViewportScroller } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { NavigationEnd, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { Pin } from '../../model/Pin';
import { ImageService } from '../../services/image/image.service';
import { ModalService } from '../../services/modal/modal.service';
import { PinService } from '../../services/pin/pin.service';
@Component({
selector: 'app-timeline',
standalone: true,
imports: [CommonModule],
templateUrl: './timeline.component.html',
})
export class TimelineComponent implements OnInit, OnDestroy {
pins: Pin[] = [];
imageUrls: SafeUrl[][] = [];
loading = true;
groupedPins: { [year: string]: Pin[] } = {};
sortedYears: string[] = [];
carouselIndexes: number[] = [];
expandedDescriptions: { [index: number]: boolean } = {};
private navigationSubscription: Subscription;
constructor(
private pinService: PinService,
private imageService: ImageService,
private sanitizer: DomSanitizer,
private modalService: ModalService,
private router: Router,
private viewportScroller: ViewportScroller
) {
// Écouter les événements de navigation
this.navigationSubscription = this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe(() => {
// Attendre que le contenu soit chargé
if (!this.loading) {
this.restoreScrollPosition();
}
});
}
ngOnDestroy() {
// Nettoyer la souscription lors de la destruction du composant
if (this.navigationSubscription) {
this.navigationSubscription.unsubscribe();
}
}
private restoreScrollPosition() {
const scrollPosition = sessionStorage.getItem('timelineScrollPosition');
if (scrollPosition) {
window.scrollTo({
top: parseInt(scrollPosition),
behavior: 'smooth',
});
sessionStorage.removeItem('timelineScrollPosition');
}
}
openPinModal() {
this.modalService.openModal('add-pin-modal');
}
navigateToPinOnMap(pinId: string) {
const scrollPosition = window.scrollY;
sessionStorage.setItem('timelineScrollPosition', scrollPosition.toString());
this.router.navigate(['/map'], { queryParams: { pin: pinId } });
}
ngOnInit(): void {
this.pinService.getPins().subscribe((pins: Pin[]) => {
this.pins = pins
.filter((pin) => !!pin.date)
.sort((a, b) => (a.date! > b.date! ? 1 : -1));
this.imageUrls = this.pins.map(() => []); // initialise le tableau d'images
this.pins.forEach((pin, index) => {
if (pin.files && pin.files.length > 0) {
pin.files.forEach((imageId) => {
this.imageService.getImage(imageId).subscribe((blob) => {
const objectUrl = URL.createObjectURL(blob);
const safeUrl = this.sanitizer.bypassSecurityTrustUrl(objectUrl);
this.imageUrls[index].push(safeUrl);
});
});
}
});
this.carouselIndexes = this.pins.map(() => 0);
this.loading = false;
this.groupPinsByYear();
// Attendre que le DOM soit mis à jour avant de restaurer la position
setTimeout(() => {
this.restoreScrollPosition();
}, 100);
});
}
private groupPinsByYear(): void {
this.groupedPins = {};
for (const pin of this.pins) {
const year = new Date(pin.date!).getFullYear().toString();
if (!this.groupedPins[year]) {
this.groupedPins[year] = [];
}
this.groupedPins[year].push(pin);
}
// Trie les pins dans chaque groupe (au cas où)
for (const year in this.groupedPins) {
this.groupedPins[year].sort((a, b) => a.date!.localeCompare(b.date!));
}
// Trie les années dans l'ordre croissant (utilisé dans le template)
this.sortedYears = Object.keys(this.groupedPins).sort((a, b) => +a - +b);
}
nextImage(index: number) {
const images = this.imageUrls[index];
this.carouselIndexes[index] =
(this.carouselIndexes[index] + 1) % images.length;
}
prevImage(index: number) {
const images = this.imageUrls[index];
this.carouselIndexes[index] =
(this.carouselIndexes[index] - 1 + images.length) % images.length;
}
toggleDescription(index: number): void {
this.expandedDescriptions[index] = !this.expandedDescriptions[index];
}
}