import { CommonModule } from '@angular/common'; import { AfterViewInit, Component, Input, OnDestroy, OnInit, } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, } from '@angular/forms'; import { NavigationEnd, Router } from '@angular/router'; import { of, Subscription } from 'rxjs'; import { catchError, debounceTime, distinctUntilChanged, filter, switchMap, take, } from 'rxjs/operators'; import { Pin } from '../../model/Pin'; import { AutocompleteService } from '../../services/auto-complete/auto-complete.service'; import { ExifService } from '../../services/exif/exif.service'; import { MapReloadService } from '../../services/map-reload/map-reload.service'; import { ModalService } from '../../services/modal/modal.service'; import { PinService } from '../../services/pin/pin.service'; import { DragDropComponent } from '../drag-drop/drag-drop.component'; @Component({ selector: 'app-edit-pin-popup', standalone: true, imports: [ReactiveFormsModule, CommonModule, DragDropComponent], templateUrl: './edit-pin-popup.component.html', }) export class EditPinPopupComponent implements OnInit, AfterViewInit, OnDestroy { @Input() isHomePage: boolean = false; @Input() pin!: Pin; form!: FormGroup; suggestions: any[] = []; inputFocused: boolean = false; files: any[] = []; isPinModalOpen: boolean = false; @Input() modalId!: string; private modalOpenSubscription!: Subscription; private routerSubscription!: Subscription; private locationSubscription!: Subscription; constructor( private fb: FormBuilder, private autocompleteService: AutocompleteService, private pinService: PinService, private exifService: ExifService, private modalService: ModalService, private router: Router, private mapReloadService: MapReloadService ) { // Initialiser le formulaire avec des valeurs par défaut this.form = this.fb.group({ title: new FormControl(''), description: new FormControl(''), location: new FormControl(''), files: new FormControl(null), }); } onFocus(): void { this.inputFocused = true; } onBlur(): void { setTimeout(() => { this.inputFocused = false; // Désactiver le focus après un petit délai pour permettre un clic sur la liste }, 200); } ngOnInit(): void { // Initialiser le formulaire avec les valeurs de base this.form.patchValue({ title: this.pin?.title || '', description: this.pin?.description || '', location: "Chargement de l'adresse...", }); // Vérifier si nous avons des coordonnées valides dans pin.location if ( this.pin?.location && Array.isArray(this.pin.location) && this.pin.location.length >= 2 ) { const lat = this.pin.location[0]; const lon = this.pin.location[1]; if (lat !== undefined && lon !== undefined) { // Récupérer l'adresse à partir des coordonnées this.locationSubscription = this.autocompleteService .getAddressFromCoordinates(lat, lon) .pipe(take(1)) .subscribe( (address) => { if (address && address.display_name) { this.form.patchValue({ location: address.display_name }); } else { this.form.patchValue({ location: `${lat}, ${lon}` }); } }, (error) => { console.error( "Erreur lors de la récupération de l'adresse:", error ); this.form.patchValue({ location: `${lat}, ${lon}` }); } ); } } // S'abonner aux changements d'état du modal this.modalOpenSubscription = this.modalService .getModalState(this.modalId) .subscribe((state) => { this.isPinModalOpen = state; if (state) { setTimeout(() => this.moveModalToBody(), 0); } }); // S'abonner aux événements de navigation du router this.routerSubscription = this.router.events .pipe(filter((event) => event instanceof NavigationEnd)) .subscribe(() => { // Attendre que le DOM soit mis à jour après la navigation setTimeout(() => this.moveModalToBody(), 0); }); // Configuration de l'autocomplétion pour le champ d'adresse this.form .get('location') ?.valueChanges.pipe( debounceTime(300), // Attendre 300ms après la dernière frappe distinctUntilChanged(), // Ignorer si la nouvelle valeur est la même que la précédente switchMap((query) => { // Vérifier que query est une chaîne de caractères if (typeof query !== 'string') { return of([]); } const trimmedQuery = query.trim(); if (trimmedQuery.length > 2) { return this.autocompleteService.getAddressSuggestions(trimmedQuery); } return of([]); }), catchError((error) => { console.error('Error fetching suggestions:', error); return of([]); }) ) .subscribe((data) => { this.suggestions = data; }); } ngAfterViewInit() { // Appel initial pour déplacer le modal this.moveModalToBody(); } ngOnDestroy() { // Nettoyage des abonnements pour éviter les fuites de mémoire if (this.modalOpenSubscription) { this.modalOpenSubscription.unsubscribe(); } if (this.routerSubscription) { this.routerSubscription.unsubscribe(); } if (this.locationSubscription) { this.locationSubscription.unsubscribe(); } } // Méthode dédiée pour déplacer le modal vers le body private moveModalToBody(): void { const modal = document.getElementById('pin-modal'); if (modal && modal.parentElement !== document.body) { document.body.appendChild(modal); } const bg = document.getElementById('pin-modal-background'); if (bg && bg.parentElement !== document.body) { document.body.appendChild(bg); } } selectSuggestion(suggestion: any): void { const locationControl = this.form.get('location'); if (locationControl instanceof FormControl) { locationControl.setValue(suggestion.display_name); } this.suggestions = []; } async onFilesReceived(files: FileList): Promise { this.files = Array.from(files); for (let i = 0; i < this.files.length; i++) { try { const data = await this.exifService.getLocation(this.files[i]); if (data.latitude !== undefined && data.longitude !== undefined) { try { // Utiliser pipe(take(1)) pour s'assurer que l'observable se termine const address = await this.autocompleteService .getAddressFromCoordinates(data.latitude, data.longitude) .pipe(take(1)) .toPromise(); if (address) { this.form.get('location')?.setValue(address.display_name); break; } } catch (addressError) { console.error( "Erreur lors de la récupération de l'adresse:", addressError ); // Utiliser les coordonnées brutes en cas d'échec this.form .get('location') ?.setValue(`${data.latitude}, ${data.longitude}`); } } } catch (error) { console.error('Erreur :', error); } } } submitForm(): void { if (this.form.valid) { this.files = this.files.map((file) => { return file.name; //TODO: Mettre le hash du fichier }); const pinData = { ...this.form.value, files: this.files, user_id: this.pin.user_id, }; this.pinService.updatePin(this.pin.id, pinData).subscribe(() => { this.mapReloadService.requestReload(); // Demander le rechargement de la carte this.closePinModal(); }); } else { console.error('Le formulaire est invalide'); } } openPinModal() { this.modalService.openModal(this.modalId); } closePinModal() { this.modalService.closeModal(this.modalId); } }