refactor(react/collections): reintroduce gpx tracks

This commit is contained in:
Elian Doran 2025-09-04 22:48:00 +03:00
parent ec378a8fc5
commit b25f3094b7
No known key found for this signature in database
4 changed files with 59 additions and 55 deletions

View File

@ -1,11 +1,11 @@
import Map from "./map";
import "./index.css";
import { ViewModeProps } from "../interface";
import { useNoteLabel, useNoteLabelBoolean, useNoteProperty, useSpacedUpdate, useTriliumEvent } from "../../react/hooks";
import { useNoteBlob, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useSpacedUpdate, useTriliumEvent } from "../../react/hooks";
import { DEFAULT_MAP_LAYER_NAME } from "./map_layer";
import { divIcon, LatLng, LeafletMouseEvent } from "leaflet";
import { divIcon, GPXOptions, LatLng, LeafletMouseEvent } from "leaflet";
import { useCallback, useEffect, useMemo, useState } from "preact/hooks";
import Marker from "./marker";
import Marker, { GpxTrack } from "./marker";
import froca from "../../../services/froca";
import FNote from "../../../entities/fnote";
import markerIcon from "leaflet/dist/images/marker-icon.png";
@ -15,6 +15,7 @@ import { createNewNote, moveMarker } from "./api";
import openContextMenu, { openMapContextMenu } from "./context_menu";
import toast from "../../../services/toast";
import { t } from "../../../services/i18n";
import server from "../../../services/server";
const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659];
const DEFAULT_ZOOM = 2;
@ -99,7 +100,11 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
onContextMenu={onContextMenu}
scale={hasScale}
>
{notes.map(note => <NoteMarker note={note} editable={!isReadOnly} />)}
{notes.map(note => (
note.mime !== "application/gpx+xml"
? <NoteMarker note={note} editable={!isReadOnly} />
: <NoteGpxTrack note={note} />
))}
</Map>
</div>
);
@ -148,6 +153,39 @@ function NoteMarker({ note, editable }: { note: FNote, editable: boolean }) {
/>
}
function NoteGpxTrack({ note }: { note: FNote }) {
const [ xmlString, setXmlString ] = useState<string>();
const blob = useNoteBlob(note);
useEffect(() => {
server.get<string | Uint8Array>(`notes/${note.noteId}/open`, undefined, true).then(xmlResponse => {
if (xmlResponse instanceof Uint8Array) {
setXmlString(new TextDecoder().decode(xmlResponse));
} else {
setXmlString(xmlResponse);
}
});
}, [ blob ]);
// React to changes
const color = useNoteLabel(note, "color");
const iconClass = useNoteLabel(note, "iconClass");
const options = useMemo<GPXOptions>(() => ({
markers: {
startIcon: buildIcon(note.getIcon(), note.getColorClass(), note.title),
endIcon: buildIcon("bxs-flag-checkered"),
wptIcons: {
"": buildIcon("bx bx-pin")
}
},
polyline_options: {
color: note.getLabelValue("color") ?? "blue"
}
}), [ color, iconClass ]);
return xmlString && <GpxTrack gpxXmlString={xmlString} options={options} />
}
function buildIcon(bxIconClass: string, colorClass?: string, title?: string, noteIdLink?: string) {
let html = /*html*/`\
<img class="icon" src="${markerIcon}" />

View File

@ -1,6 +1,7 @@
import { useContext, useEffect } from "preact/hooks";
import { ParentMap } from "./map";
import { DivIcon, Icon, LatLng, Marker as LeafletMarker, LeafletMouseEvent, marker, MarkerOptions } from "leaflet";
import { DivIcon, GPX, GPXOptions, Icon, LatLng, Marker as LeafletMarker, LeafletMouseEvent, marker, MarkerOptions } from "leaflet";
import "leaflet-gpx";
export interface MarkerProps {
coordinates: [ number, number ];
@ -53,3 +54,18 @@ export default function Marker({ coordinates, icon, draggable, onClick, onDragge
return (<div />)
}
export function GpxTrack({ gpxXmlString, options }: { gpxXmlString: string, options: GPXOptions }) {
const parentMap = useContext(ParentMap);
useEffect(() => {
if (!parentMap) return;
const track = new GPX(gpxXmlString, options);
track.addTo(parentMap);
return () => track.removeFrom(parentMap);
}, [ parentMap, gpxXmlString, options ]);
return <div />;
}

View File

@ -83,18 +83,6 @@ export default class GeoView extends ViewMode<MapData> {
this.currentMarkerData = {};
const notes = await this.parentNote.getSubtreeNotes();
const draggable = !this.isReadOnly;
for (const childNote of notes) {
if (childNote.mime === "application/gpx+xml") {
const track = await processNoteWithGpxTrack(this.map, childNote);
this.currentTrackData[childNote.noteId] = track;
continue;
}
if (latLng) {
const marker = processNoteWithMarker(this.map, childNote, latLng, draggable);
this.currentMarkerData[childNote.noteId] = marker;
}
}
}
#changeState(newState: State) {

View File

@ -1,38 +0,0 @@
import { marker, latLng, divIcon, Map, type Marker } from "leaflet";
import type FNote from "../../../entities/fnote.js";
import openContextMenu from "./context_menu.js";
import server from "../../../services/server.js";
import { moveMarker } from "./editing.js";
import L from "leaflet";
let gpxLoaded = false;
export async function processNoteWithGpxTrack(map: Map, note: FNote) {
if (!gpxLoaded) {
const GPX = await import("leaflet-gpx");
gpxLoaded = true;
}
const xmlResponse = await server.get<string | Uint8Array>(`notes/${note.noteId}/open`, undefined, true);
let stringResponse: string;
if (xmlResponse instanceof Uint8Array) {
stringResponse = new TextDecoder().decode(xmlResponse);
} else {
stringResponse = xmlResponse;
}
const track = new L.GPX(stringResponse, {
markers: {
startIcon: buildIcon(note.getIcon(), note.getColorClass(), note.title),
endIcon: buildIcon("bxs-flag-checkered"),
wptIcons: {
"": buildIcon("bx bx-pin")
}
},
polyline_options: {
color: note.getLabelValue("color") ?? "blue"
}
});
track.addTo(map);
return track;
}