🐛 Fix timeline
continuous-integration/drone/push Build is passing Details

master
Alexis Feron 22 hours ago
parent a8576a2501
commit 7d510e86b1

@ -2,6 +2,7 @@ import { provideHttpClient } from '@angular/common/http';
import { import {
ApplicationConfig, ApplicationConfig,
isDevMode, isDevMode,
LOCALE_ID,
provideZoneChangeDetection, provideZoneChangeDetection,
} from '@angular/core'; } from '@angular/core';
import { provideRouter } from '@angular/router'; import { provideRouter } from '@angular/router';
@ -13,6 +14,7 @@ export const appConfig: ApplicationConfig = {
provideZoneChangeDetection({ eventCoalescing: true }), provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes), provideRouter(routes),
provideHttpClient(), provideHttpClient(),
{ provide: LOCALE_ID, useValue: 'fr-FR' },
provideServiceWorker('ngsw-worker.js', { provideServiceWorker('ngsw-worker.js', {
enabled: !isDevMode(), enabled: !isDevMode(),
registrationStrategy: 'registerWhenStable:30000', registrationStrategy: 'registerWhenStable:30000',

@ -23,7 +23,7 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
stroke-width="2" stroke-width="2"
d="M9 5l7 7-7 7" d="m15 19-7-7 7-7"
/> />
</svg> </svg>
</button> </button>
@ -56,7 +56,7 @@
<button <button
*ngIf="!isDesktop()" *ngIf="!isDesktop()"
(click)="toggleFilters()" (click)="toggleFilters()"
class="text-xl text-gray-700 dark:text-white focus:outline-none ml-2 p-0 pl-4 bg-transparent border-0" class="text-xl text-gray-700 dark:text-white focus:outline-none p-0 ml-4"
aria-label="Fermer les filtres" aria-label="Fermer les filtres"
> >
<svg <svg

@ -23,7 +23,7 @@
class="absolute left-1/2 transform -translate-x-1/2 h-16 w-8 bg-blue-500 z-0" class="absolute left-1/2 transform -translate-x-1/2 h-16 w-8 bg-blue-500 z-0"
></div> ></div>
<div <div
class="bg-blue-600 text-white text-4xl font-bold px-10 py-5 rounded-full shadow-2xl z-10 border-4 border-white" class="bg-blue-600 text-white text-2xl font-bold px-10 py-5 rounded-full shadow-2xl z-10 border-4 border-white"
> >
{{ year }} {{ year }}
</div> </div>
@ -42,33 +42,43 @@
<!-- Bulle centrale avec la date --> <!-- Bulle centrale avec la date -->
<div <div
class="z-20 flex items-center justify-center bg-white border-[6px] border-blue-600 text-blue-700 font-bold text-lg shadow-2xl w-32 h-32 rounded-full text-center px-4 leading-tight" class="z-20 flex items-center justify-center bg-white border-[6px] md:border-blue-600 border-gray-800 md:text-blue-700 text-gray-800 font-bold text-base sm:text-lg shadow-2xl md:rounded-full rounded-t-3xl sm:w-32 sm:h-32 text-center leading-tight px-4 py-2 sm:px-10 sm:py-5 date-bubble"
[ngClass]="{ [ngClass]="{
'sm:order-2': i % 2 === 0, 'sm:order-2': i % 2 === 0,
'sm:order-1': i % 2 !== 0 'sm:order-1': i % 2 !== 0
}" }"
> >
<span>{{ pin.date | date : "dd/MM/yyyy" }}</span> <span>{{ pin.date | date : "d MMMM yyyy" }}</span>
</div> </div>
<!-- Ligne de liaison (desktop uniquement) -->
<div
class="hidden -z-10 sm:block absolute top-1/2 transform -translate-y-1/2 h-2 w-[calc(50%-8rem)] bg-blue-500"
[ngClass]="{
'left-1/2': i % 2 === 0,
'right-1/2': i % 2 !== 0
}"
></div>
<!-- Carte de contenu --> <!-- Carte de contenu -->
<div <div
class="bg-white dark:bg-gray-800 rounded-3xl shadow-2xl px-10 py-8 w-full sm:w-5/12 transition-all duration-300 hover:scale-[1.02]" class="bg-white dark:bg-gray-800 rounded-3xl shadow-2xl px-10 py-8 w-full sm:w-5/12 transition-all duration-300 hover:scale-[1.02] cursor-pointer"
[ngClass]="{ [ngClass]="{
'sm:order-3 sm:text-left text-center': i % 2 === 0, 'sm:order-3 sm:text-left text-center': i % 2 === 0,
'sm:order-0 sm:text-right text-left': i % 2 !== 0 'sm:order-0 sm:text-right text-left': i % 2 !== 0
}" }"
(click)="navigateToPinOnMap(pin.id)"
> >
<!-- Titre centré --> <!-- Titre centré -->
<h3 <h3
class="text-3xl font-extrabold text-gray-900 dark:text-white mb-4 text-center" class="text-2xl font-extrabold text-gray-900 dark:text-white mb-4 text-center"
> >
{{ pin.title || "Titre inconnu" }} {{ pin.title || "Titre inconnu" }}
</h3> </h3>
<!-- Description justifiée tronquée --> <!-- Description justifiée tronquée -->
<div <div
class="text-lg text-gray-700 dark:text-gray-300 mb-4 text-justify transition-all duration-300" class="text-md text-gray-700 dark:text-gray-300 mb-4 text-justify transition-all duration-300"
[ngClass]="{ [ngClass]="{
'line-clamp-5 overflow-hidden': 'line-clamp-5 overflow-hidden':
!expandedDescriptions[pins.indexOf(pin)] !expandedDescriptions[pins.indexOf(pin)]
@ -209,3 +219,18 @@
></ng-container ></ng-container
> >
</div> </div>
<div
*ngIf="!loading && pins.length === 0"
class="flex flex-col items-center justify-center h-64 space-y-6"
>
<p class="text-xl text-gray-800 text-center">
Commencez à créer votre histoire en ajoutant des souvenirs sur la carte !
</p>
<button
(click)="openPinModal()"
class="px-6 py-3 bg-gray-800 text-white rounded-lg hover:bg-gray-700 transition-colors duration-200 shadow-lg"
>
Ajouter un souvenir
</button>
</div>

@ -1,8 +1,12 @@
import { CommonModule } from '@angular/common'; import { CommonModule, ViewportScroller } from '@angular/common';
import { Component, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; 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 { Pin } from '../../model/Pin';
import { ImageService } from '../../services/image/image.service'; import { ImageService } from '../../services/image/image.service';
import { ModalService } from '../../services/modal/modal.service';
import { PinService } from '../../services/pin/pin.service'; import { PinService } from '../../services/pin/pin.service';
@Component({ @Component({
@ -11,7 +15,7 @@ import { PinService } from '../../services/pin/pin.service';
imports: [CommonModule], imports: [CommonModule],
templateUrl: './timeline.component.html', templateUrl: './timeline.component.html',
}) })
export class TimelineComponent implements OnInit { export class TimelineComponent implements OnInit, OnDestroy {
pins: Pin[] = []; pins: Pin[] = [];
imageUrls: SafeUrl[][] = []; imageUrls: SafeUrl[][] = [];
loading = true; loading = true;
@ -19,12 +23,52 @@ export class TimelineComponent implements OnInit {
sortedYears: string[] = []; sortedYears: string[] = [];
carouselIndexes: number[] = []; carouselIndexes: number[] = [];
expandedDescriptions: { [index: number]: boolean } = {}; expandedDescriptions: { [index: number]: boolean } = {};
private navigationSubscription: Subscription;
constructor( constructor(
private pinService: PinService, private pinService: PinService,
private imageService: ImageService, private imageService: ImageService,
private sanitizer: DomSanitizer 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) {
requestAnimationFrame(() => {
this.viewportScroller.scrollToPosition([0, parseInt(scrollPosition)]);
});
}
}
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 { ngOnInit(): void {
this.pinService.getPins().subscribe((pins: Pin[]) => { this.pinService.getPins().subscribe((pins: Pin[]) => {
@ -46,10 +90,11 @@ export class TimelineComponent implements OnInit {
}); });
this.carouselIndexes = this.pins.map(() => 0); this.carouselIndexes = this.pins.map(() => 0);
this.loading = false; this.loading = false;
this.groupPinsByYear(); this.groupPinsByYear();
// Restaurer la position de défilement après le chargement initial
this.restoreScrollPosition();
}); });
} }
@ -69,7 +114,7 @@ export class TimelineComponent implements OnInit {
this.groupedPins[year].sort((a, b) => a.date!.localeCompare(b.date!)); this.groupedPins[year].sort((a, b) => a.date!.localeCompare(b.date!));
} }
// Trie les années dans lordre croissant (utilisé dans le template) // Trie les années dans l'ordre croissant (utilisé dans le template)
this.sortedYears = Object.keys(this.groupedPins).sort((a, b) => +a - +b); this.sortedYears = Object.keys(this.groupedPins).sort((a, b) => +a - +b);
} }

@ -1,6 +1,11 @@
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import { bootstrapApplication } from '@angular/platform-browser'; import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component'; import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
registerLocaleData(localeFr, 'fr');
bootstrapApplication(AppComponent, appConfig) bootstrapApplication(AppComponent, appConfig).catch((err) =>
.catch((err) => console.error(err)); console.error(err)
);

Loading…
Cancel
Save