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.
318 lines
10 KiB
318 lines
10 KiB
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 { forkJoin, 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 { ImageService } from '../../services/image/image.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,
|
|
private imageService: ImageService
|
|
) {
|
|
// Initialiser le formulaire avec des valeurs par défaut
|
|
this.form = this.fb.group({
|
|
title: new FormControl(''),
|
|
description: new FormControl(''),
|
|
location: new FormControl(''),
|
|
complete_address: new FormControl(''),
|
|
coordinates: new FormControl<number[]>([]),
|
|
files: new FormControl(null),
|
|
date: new FormControl(''),
|
|
});
|
|
}
|
|
|
|
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: this.pin?.complete_address || '',
|
|
complete_address: this.pin?.complete_address || '',
|
|
coordinates: this.pin?.location || [],
|
|
files: this.pin?.files || [],
|
|
date: this.pin?.date
|
|
? new Date(this.pin.date).toISOString().split('T')[0]
|
|
: '',
|
|
});
|
|
|
|
// Initialiser les fichiers existants
|
|
this.files = this.pin?.files || [];
|
|
|
|
// 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.form.get('complete_address')?.setValue(suggestion.display_name);
|
|
this.form.get('coordinates')?.setValue([suggestion.lat, suggestion.lon]);
|
|
}
|
|
this.suggestions = [];
|
|
}
|
|
|
|
async onFilesReceived(files: FileList): Promise<void> {
|
|
// Ajouter les nouveaux fichiers à la liste existante
|
|
this.files = [...this.files, ...Array.from(files)];
|
|
|
|
for (let i = 0; i < files.length; i++) {
|
|
try {
|
|
const data = await this.exifService.getLocation(files[i]);
|
|
if (data.latitude !== undefined && data.longitude !== undefined) {
|
|
try {
|
|
const address = await this.autocompleteService
|
|
.getAddressFromCoordinates(data.latitude, data.longitude)
|
|
.pipe(take(1))
|
|
.toPromise();
|
|
|
|
if (address) {
|
|
this.form.get('location')?.setValue(address.display_name);
|
|
this.form.get('complete_address')?.setValue(address.display_name);
|
|
this.form.get('coordinates')?.setValue([data.latitude, data.longitude]);
|
|
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}`);
|
|
this.form.get('complete_address')?.setValue(`${data.latitude}, ${data.longitude}`);
|
|
this.form.get('coordinates')?.setValue([data.latitude, data.longitude]);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Erreur :', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
submitForm(): void {
|
|
if (this.form.valid) {
|
|
const coordinates = this.form.get('coordinates')?.value;
|
|
const pinData = {
|
|
...this.form.value,
|
|
files: this.files,
|
|
date: this.form.get('date')?.value || null,
|
|
location: coordinates || this.pin.location, // Utiliser les coordonnées pour location
|
|
complete_address: this.form.get('complete_address')?.value || this.form.get('location')?.value,
|
|
};
|
|
|
|
// Supprimer le champ coordinates qui n'est pas dans le modèle Pin
|
|
delete pinData.coordinates;
|
|
// Filtrer les fichiers qui sont déjà des IDs (images existantes)
|
|
const existingFiles = this.files.filter(
|
|
(file) => typeof file === 'string'
|
|
);
|
|
const newFiles = this.files.filter((file) => file instanceof File);
|
|
|
|
// Récupérer la date et la formater correctement
|
|
const dateValue = this.form.get('date')?.value;
|
|
const formattedDate = dateValue
|
|
? new Date(dateValue).toISOString()
|
|
: null;
|
|
|
|
if (newFiles.length > 0) {
|
|
// Upload des nouveaux fichiers
|
|
const uploadObservables = newFiles.map((file) =>
|
|
this.imageService.postImage(file)
|
|
);
|
|
|
|
forkJoin(uploadObservables).subscribe((responses) => {
|
|
// Combiner les IDs des nouvelles images avec les IDs existants
|
|
const allFileIds = [
|
|
...existingFiles,
|
|
...responses.map((res: any) => res.id),
|
|
];
|
|
|
|
const pinData = {
|
|
...this.form.getRawValue(),
|
|
files: allFileIds,
|
|
user_id: this.pin.user_id,
|
|
date: formattedDate,
|
|
location: coordinates || this.pin.location, // Utiliser les coordonnées pour location
|
|
complete_address: this.form.get('complete_address')?.value || this.form.get('location')?.value,
|
|
};
|
|
|
|
this.pinService.updatePin(this.pin.id, pinData).subscribe(() => {
|
|
this.mapReloadService.requestReload();
|
|
this.closePinModal();
|
|
});
|
|
});
|
|
} else {
|
|
// Si pas de nouveaux fichiers, mettre à jour directement avec les fichiers existants
|
|
const pinData = {
|
|
...this.form.getRawValue(),
|
|
files: existingFiles,
|
|
user_id: this.pin.user_id,
|
|
date: formattedDate,
|
|
location: coordinates || this.pin.location, // Utiliser les coordonnées pour location
|
|
complete_address: this.form.get('complete_address')?.value || this.form.get('location')?.value,
|
|
};
|
|
|
|
this.pinService.updatePin(this.pin.id, pinData).subscribe(() => {
|
|
this.mapReloadService.requestReload();
|
|
this.closePinModal();
|
|
});
|
|
}
|
|
}
|
|
|
|
openPinModal() {
|
|
this.modalService.openModal(this.modalId);
|
|
}
|
|
|
|
closePinModal() {
|
|
this.modalService.closeModal(this.modalId);
|
|
}
|
|
|
|
removeFile(fileName: string): void {
|
|
const index = this.files.findIndex((file) => {
|
|
if (typeof file === 'string') {
|
|
return file === fileName;
|
|
}
|
|
return file.name === fileName;
|
|
});
|
|
|
|
if (index > -1) {
|
|
this.files.splice(index, 1);
|
|
this.form.patchValue({ files: this.files });
|
|
}
|
|
}
|
|
}
|