From 30534df9aeb848a307a6351743af79c6164b038b Mon Sep 17 00:00:00 2001 From: "mathis.framit" Date: Wed, 11 Jun 2025 11:16:19 +0200 Subject: [PATCH] :boom: pin detail page --- src/app/app.component.ts | 1 + src/app/app.routes.ts | 2 + .../pin-detail/pin-detail.component.html | 150 ++++++++++++++++++ .../pin-detail/pin-detail.component.spec.ts | 23 +++ .../pin-detail/pin-detail.component.ts | 141 ++++++++++++++++ 5 files changed, 317 insertions(+) create mode 100644 src/app/components/pin-detail/pin-detail.component.html create mode 100644 src/app/components/pin-detail/pin-detail.component.spec.ts create mode 100644 src/app/components/pin-detail/pin-detail.component.ts diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 07dcc6b..f588bf6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -5,6 +5,7 @@ import { HomeNavbarComponent } from './components/home-navbar/home-navbar.compon import { NavbarComponent } from './components/navbar/navbar.component'; import { AdminFooterComponent } from './components/admin-footer/admin-footer.component'; import { AuthService } from './services/auth/auth.service'; +import { PinDetailComponent } from './components/pin-detail/pin-detail.component'; @Component({ selector: 'app-root', diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 3e4cb1c..cf32711 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -4,10 +4,12 @@ import { HomePageComponent } from './components/home-page/home-page.component'; import { LeafletMapComponent } from './components/leaflet-map/leaflet-map.component'; import { NotFoundComponent } from './components/not-found/not-found.component'; import { TimelineComponent } from './components/timeline/timeline.component'; +import { PinDetailComponent } from './components/pin-detail/pin-detail.component'; export const routes: Routes = [ { path: '', component: HomePageComponent }, { path: 'map', component: LeafletMapComponent, canActivate: [AuthGuard] }, { path: 'timeline', component: TimelineComponent, canActivate: [AuthGuard] }, + { path: 'pin/:id', component: PinDetailComponent, canActivate: [AuthGuard] }, { path: '**', component: NotFoundComponent }, ]; diff --git a/src/app/components/pin-detail/pin-detail.component.html b/src/app/components/pin-detail/pin-detail.component.html new file mode 100644 index 0000000..300b859 --- /dev/null +++ b/src/app/components/pin-detail/pin-detail.component.html @@ -0,0 +1,150 @@ + +
+ +
+ +

+ {{ pin.title }} +

+ + +
+ +
+
+ +
+ Loading image... +
+
+
+ + + + + + + + + +
+ + + +
+ No images available +
+
+ + +

📍 {{ pin.complete_address }}

+ + +

+ 📅 {{ pin.date | date : "longDate" }} +

+ + +
+ {{ pin.description || "Aucune description" }} +
+ + +
+ +
+ + +
Utilisateur : {{ username }}
+
+ +
diff --git a/src/app/components/pin-detail/pin-detail.component.spec.ts b/src/app/components/pin-detail/pin-detail.component.spec.ts new file mode 100644 index 0000000..1253818 --- /dev/null +++ b/src/app/components/pin-detail/pin-detail.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PinDetailComponent } from './pin-detail.component'; + +describe('PinDetailComponent', () => { + let component: PinDetailComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PinDetailComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(PinDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/pin-detail/pin-detail.component.ts b/src/app/components/pin-detail/pin-detail.component.ts new file mode 100644 index 0000000..fc2b342 --- /dev/null +++ b/src/app/components/pin-detail/pin-detail.component.ts @@ -0,0 +1,141 @@ +import { CommonModule } from '@angular/common'; +import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; +import { Pin } from '../../model/Pin'; +import { PinService } from '../../services/pin/pin.service'; +import { RouterModule } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; +import { AuthService } from '../../services/auth/auth.service'; +import { UserService } from '../../services/user/user.service'; +import { ImageService } from '../../services/image/image.service'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { Router } from '@angular/router'; +import { Renderer2 } from '@angular/core'; + +@Component({ + selector: 'app-pin-detail', + templateUrl: './pin-detail.component.html', + standalone: true, + imports: [CommonModule, RouterModule], +}) +export class PinDetailComponent { + pin!: Pin; + username: string = ''; + imageUrls: SafeUrl[] = []; + imagesLoaded = false; + sharedUsers: { + user_id: string; + username: string; + can_edit: boolean; + can_delete: boolean; + }[] = []; + expandedDescription = false; + currentIndex: number = 0; + + @ViewChild('desc') descriptionDiv!: ElementRef; + showToggleButton = false; + + constructor( + private pinService: PinService, + private route: ActivatedRoute, + private authService: AuthService, + private userService: UserService, + private imageService: ImageService, + private sanitizer: DomSanitizer, + private router: Router, + private renderer: Renderer2 + ) {} + + ngOnInit(): void { + this.renderer.addClass(document.body, 'no-scroll-body'); + + const pinId = this.route.snapshot.paramMap.get('id'); + if (pinId) { + this.pinService.getPinById(pinId).subscribe({ + next: (data) => { + this.pin = data; + this.loadUsername(data.user_id); + this.loadImages(); + }, + error: (err) => + console.error('Erreur lors du chargement du pin :', err), + }); + } + } + + loadUsername(userId: string) { + this.userService.getUserById(userId).subscribe({ + next: (user) => { + this.username = user.username; // ou user.name selon ton backend + }, + error: (err) => { + console.error('Erreur lors de la récupération du pseudo :', err); + this.username = 'Utilisateur inconnu'; + }, + }); + } + + loadImages() { + this.pin.files.forEach((imageId) => { + this.imageService.getImage(imageId).subscribe((blob) => { + const objectUrl = URL.createObjectURL(blob); + const safeUrl = this.sanitizer.bypassSecurityTrustUrl(objectUrl); + this.imageUrls.push(safeUrl); + if (this.imageUrls.length === this.pin.files.length) { + this.imagesLoaded = true; + } + }); + }); + } + + loadSharedUsers(pinId: string) { + this.pinService.getSharedUsersForPin(pinId).subscribe({ + next: (shares) => { + this.sharedUsers = shares; + }, + error: (err) => { + console.error('Erreur récupération utilisateurs partagés:', err); + this.sharedUsers = []; + }, + }); + } + + // toggleDescription(): void { + // this.expandedDescription = !this.expandedDescription; + // } + + ngAfterViewInit(): void { + this.checkIfDescriptionIsTruncated(); + } + + toggleDescription() { + this.expandedDescription = !this.expandedDescription; + if (!this.expandedDescription) { + // re-check quand on replie + setTimeout(() => this.checkIfDescriptionIsTruncated(), 0); + } + } + + checkIfDescriptionIsTruncated() { + if (!this.descriptionDiv) return; + + const el = this.descriptionDiv.nativeElement; + this.showToggleButton = el.scrollHeight > el.clientHeight; + } + + prevSlide(): void { + this.currentIndex = + (this.currentIndex - 1 + this.imageUrls.length) % this.imageUrls.length; + } + + nextSlide(): void { + this.currentIndex = (this.currentIndex + 1) % this.imageUrls.length; + } + + ngOnDestroy() { + this.renderer.removeClass(document.body, 'no-scroll-body'); + } + + goBack() { + this.router.navigate(['/map'], { queryParams: { pin: this.pin.id } }); + } +}