From 9eae8f9b53da6a4a5fe942f4fddfe7c7dd03a5ad Mon Sep 17 00:00:00 2001 From: itskovacs Date: Sat, 23 Aug 2025 16:04:56 +0200 Subject: [PATCH] :sparkles: Map: handle GPX click --- .../dashboard/dashboard.component.html | 5 +++ .../dashboard/dashboard.component.ts | 32 +++++++++++++++- .../shared/place-gpx/place-gpx.component.html | 22 +++++++++++ .../shared/place-gpx/place-gpx.component.scss | 25 ++++++++++++ .../shared/place-gpx/place-gpx.component.ts | 38 +++++++++++++++++++ 5 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 src/src/app/shared/place-gpx/place-gpx.component.html create mode 100644 src/src/app/shared/place-gpx/place-gpx.component.scss create mode 100644 src/src/app/shared/place-gpx/place-gpx.component.ts diff --git a/src/src/app/components/dashboard/dashboard.component.html b/src/src/app/components/dashboard/dashboard.component.html index 29b27a1..a8f513c 100644 --- a/src/src/app/components/dashboard/dashboard.component.html +++ b/src/src/app/components/dashboard/dashboard.component.html @@ -6,6 +6,11 @@ (closeEmitter)="closePlaceBox()"> } +@if (selectedGPX) { + +} +
diff --git a/src/src/app/components/dashboard/dashboard.component.ts b/src/src/app/components/dashboard/dashboard.component.ts index 2e07042..1718c35 100644 --- a/src/src/app/components/dashboard/dashboard.component.ts +++ b/src/src/app/components/dashboard/dashboard.component.ts @@ -41,6 +41,7 @@ import { YesNoModalComponent } from "../../modals/yes-no-modal/yes-no-modal.comp import { CategoryCreateModalComponent } from "../../modals/category-create-modal/category-create-modal.component"; import { AuthService } from "../../services/auth.service"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { PlaceGPXComponent } from "../../shared/place-gpx/place-gpx.component"; export interface ContextMenuItem { text: string; @@ -62,6 +63,7 @@ export interface MarkerOptions extends L.MarkerOptions { standalone: true, imports: [ PlaceBoxComponent, + PlaceGPXComponent, FormsModule, SkeletonModule, ToggleSwitchModule, @@ -102,6 +104,7 @@ export class DashboardComponent implements OnInit, AfterViewInit { visiblePlaces: Place[] = []; selectedPlace?: Place; categories: Category[] = []; + selectedGPX?: Place; filter_display_visited = false; filter_display_favorite_only = false; @@ -531,21 +534,24 @@ export class DashboardComponent implements OnInit, AfterViewInit { } displayGPXOnMap(gpx: string) { - if (!this.map) return; + if (!this.map || !this.selectedPlace) return; if (!this.gpxLayerGroup) this.gpxLayerGroup = L.layerGroup().addTo(this.map); this.gpxLayerGroup.clearLayers(); try { const gpxPolyline = gpxToPolyline(gpx); + const selectedPlaceWithGPX = { ...this.selectedPlace, gpx }; + gpxPolyline.on("click", () => { - this.gpxLayerGroup?.removeLayer(gpxPolyline); + this.selectedGPX = selectedPlaceWithGPX; }); this.gpxLayerGroup?.addLayer(gpxPolyline); this.map.fitBounds(gpxPolyline.getBounds(), { padding: [20, 20] }); } catch { this.utilsService.toast("error", "Error", "Couldn't parse GPX data"); } + this.closePlaceBox(); } getPlaceGPX() { @@ -816,6 +822,28 @@ export class DashboardComponent implements OnInit, AfterViewInit { this.selectedPlace = undefined; } + closePlaceGPX() { + this.selectedGPX = undefined; + } + + downloadGPX() { + if (!this.selectedGPX?.gpx) return; + const dataBlob = new Blob([this.selectedGPX.gpx]); + const downloadURL = URL.createObjectURL(dataBlob); + const link = document.createElement("a"); + link.href = downloadURL; + link.download = `TRIP_${this.selectedGPX.name}.gpx`; + link.click(); + link.remove(); + URL.revokeObjectURL(downloadURL); + } + + removeGPX() { + if (!this.gpxLayerGroup) return; + this.gpxLayerGroup.clearLayers(); + this.closePlaceGPX(); + } + toGithub() { this.utilsService.toGithubTRIP(); } diff --git a/src/src/app/shared/place-gpx/place-gpx.component.html b/src/src/app/shared/place-gpx/place-gpx.component.html new file mode 100644 index 0000000..db9b596 --- /dev/null +++ b/src/src/app/shared/place-gpx/place-gpx.component.html @@ -0,0 +1,22 @@ +@if (selectedPlace) { +
+
+
+
+ +
+

Trace of {{ selectedPlace.name }}

+
+ +
+ + +
+ +
+
+
+
+
+} \ No newline at end of file diff --git a/src/src/app/shared/place-gpx/place-gpx.component.scss b/src/src/app/shared/place-gpx/place-gpx.component.scss new file mode 100644 index 0000000..0c5147a --- /dev/null +++ b/src/src/app/shared/place-gpx/place-gpx.component.scss @@ -0,0 +1,25 @@ +.place-box-dialog { + animation: slideYcenteredX 0.3s both; + z-index: 999; + min-height: 100px; + max-height: 800px; + width: 95%; + max-width: 1200px; + background-color: #fff; + position: fixed; + bottom: 10px; + left: 50%; + transform: translateX(-50%); + transition: none; + box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.1); + border-radius: 12px; + display: flex; + align-items: flex-end; + + &-content { + position: relative; + padding: 2rem; + border-radius: 8px 8px 0 0; + width: 100%; + } +} diff --git a/src/src/app/shared/place-gpx/place-gpx.component.ts b/src/src/app/shared/place-gpx/place-gpx.component.ts new file mode 100644 index 0000000..6a49da8 --- /dev/null +++ b/src/src/app/shared/place-gpx/place-gpx.component.ts @@ -0,0 +1,38 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from "@angular/core"; +import { ButtonModule } from "primeng/button"; +import { Place } from "../../types/poi"; + +@Component({ + selector: "app-place-gpx", + standalone: true, + imports: [ButtonModule], + templateUrl: "./place-gpx.component.html", + styleUrls: ["./place-gpx.component.scss"], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PlaceGPXComponent { + @Input() selectedPlace: Place | undefined = undefined; + @Output() closeEmitter = new EventEmitter(); + @Output() removeEmitter = new EventEmitter(); + @Output() downloadEmitter = new EventEmitter(); + + constructor() {} + + close() { + this.closeEmitter.emit(); + } + + removeTrace() { + this.removeEmitter.emit(); + } + + downloadTrace() { + this.downloadEmitter.emit() + } +}