From 1caac9d7de8299ef558caf1d5bc327217e688847 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Mon, 10 Jun 2024 21:36:01 +0200 Subject: [PATCH 1/3] style(layout): fix formatting --- src/components/merge/UploadForm.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/merge/UploadForm.tsx b/src/components/merge/UploadForm.tsx index a6dcc56..27fe146 100644 --- a/src/components/merge/UploadForm.tsx +++ b/src/components/merge/UploadForm.tsx @@ -67,7 +67,10 @@ const UploadForm: React.FC = () => { {mergedFile === null && (
-
+
{isDragActive ? ( <> From 31c163b18f774a3fafe2f0afd7be09e0c034ee49 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Mon, 10 Jun 2024 21:44:14 +0200 Subject: [PATCH 2/3] feat(backend): persist waypoints to the backend --- src/components/track/TrackHeader.tsx | 4 ++-- src/pages/TrackScreen.tsx | 17 ++++++++++++++- src/utils/tools.test.ts | 10 +++++---- src/utils/tools.ts | 32 +++++++++++++++------------- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/components/track/TrackHeader.tsx b/src/components/track/TrackHeader.tsx index 010e094..952972f 100644 --- a/src/components/track/TrackHeader.tsx +++ b/src/components/track/TrackHeader.tsx @@ -3,7 +3,7 @@ import { FaCircleInfo, FaEye, FaEyeSlash, FaPenToSquare } from 'react-icons/fa6' import ContentEditable, { ContentEditableEvent } from 'react-contenteditable' import React, { useMemo, useRef, useState } from 'react' import { WayPoint } from '../../@types/gps' -import { generateGeoJson, sanitizeFilename } from '../../utils/tools' +import { generateFeatureCollection, sanitizeFilename } from '../../utils/tools' import useLanguage from '../../hooks/useLanguage' type TrackHeaderProps = { @@ -27,7 +27,7 @@ const TrackHeader: React.FC = ({ const tracknameInputFieldRef: React.RefObject = useRef(null) const tracknameRef = useRef('') const { getMessage } = useLanguage() - const markerGeoJson = useMemo(() => generateGeoJson(markerPositions), [markerPositions]) + const markerGeoJson = useMemo(() => generateFeatureCollection(markerPositions), [markerPositions]) useMemo(() => { tracknameRef.current = trackname diff --git a/src/pages/TrackScreen.tsx b/src/pages/TrackScreen.tsx index a686442..b6d8077 100644 --- a/src/pages/TrackScreen.tsx +++ b/src/pages/TrackScreen.tsx @@ -5,12 +5,13 @@ import { FeatureCollection, LineString, Point } from 'geojson' import { PoiType, WayPoint } from '../@types/gps' import { v4 as uuidv4 } from 'uuid' import { LatLngBoundsExpression, LatLngExpression, LatLngTuple } from 'leaflet' -import { sanitizeFilename } from '../utils/tools' +import { generateFeatureCollection, sanitizeFilename } from '../utils/tools' import VisualizeTrack from '../components/track/VisualizeTrack' import TrackHeader from '../components/track/TrackHeader' import ResetButton from '../components/track/ResetButton' import { useFeedbackContext } from '../hooks/useFeedbackContext' import useLanguage from '../hooks/useLanguage' +import { useThrottle } from '@uidotdev/usehooks' const TrackScreen = () => { const { id: trackId } = useParams() @@ -133,6 +134,20 @@ const TrackScreen = () => { }) }, [trackId]) + const throttledMarkerPositions = useThrottle(markerPositions, 0) + useEffect(() => { + if (throttledMarkerPositions !== undefined && !isLoading) { + const featureCollection = generateFeatureCollection(markerPositions) + API.put(`/tracks/${trackId}/points`, featureCollection, { + headers: { + 'Content-Type': 'application/geo+json', + }, + }).catch((error) => { + console.error('Failed to update waypoint', error) + }) + } + }, [throttledMarkerPositions]) + return ( <> {isLoading ? ( diff --git a/src/utils/tools.test.ts b/src/utils/tools.test.ts index 24e5221..c1bd511 100644 --- a/src/utils/tools.test.ts +++ b/src/utils/tools.test.ts @@ -3,7 +3,7 @@ import { convertOsmToPoiType, decodeFromBase64, encodeToBase64, - generateGeoJson, + generateFeatureCollection, sanitizeFilename, } from './tools' import { WayPoint } from '../@types/gps' @@ -75,7 +75,7 @@ describe('generate GeoJSON', () => { const given: WayPoint[] = [ { id: id1, - position: [11, 12], + position: [11, 21], name: 'way point 1', type: 'GENERIC', }, @@ -86,18 +86,19 @@ describe('generate GeoJSON', () => { type: 'FOOD', }, ] - const actual = generateGeoJson(given) + const actual = generateFeatureCollection(given) expect(actual).toEqual({ features: [ { geometry: { - coordinates: [12, 11], + coordinates: [21, 11], type: 'Point', }, properties: { name: 'way point 1', type: 'GENERIC', + uuid: id1, }, type: 'Feature', }, @@ -109,6 +110,7 @@ describe('generate GeoJSON', () => { properties: { name: 'way point 2', type: 'FOOD', + uuid: id2, }, type: 'Feature', }, diff --git a/src/utils/tools.ts b/src/utils/tools.ts index fb78cfa..4abacdf 100644 --- a/src/utils/tools.ts +++ b/src/utils/tools.ts @@ -35,23 +35,25 @@ export const encodeToBase64 = (input: string) => { return btoa(binString) } -export const generateGeoJson = (markerPositions: WayPoint[]): FeatureCollection => { +export const generateFeature = (waypoint: WayPoint): Feature => { + return { + type: 'Feature', + properties: { + name: waypoint.name, + type: waypoint.type, + uuid: waypoint.id, + }, + geometry: { + type: 'Point', + coordinates: [waypoint.position[1], waypoint.position[0]], + }, + } as Feature +} + +export const generateFeatureCollection = (markerPositions: WayPoint[]): FeatureCollection => { return { type: 'FeatureCollection', - features: markerPositions.map( - (waypoint) => - ({ - type: 'Feature', - properties: { - name: waypoint.name, - type: waypoint.type, - }, - geometry: { - type: 'Point', - coordinates: [waypoint.position[1], waypoint.position[0]], - }, - }) as Feature, - ), + features: markerPositions.map((waypoint) => generateFeature(waypoint)), } } From 6db532933820bb22f65844860d491712d9e87054 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Tue, 11 Jun 2024 18:28:52 +0200 Subject: [PATCH 3/3] feat(backend): persist trackname to the backend --- src/components/track/DownloadLink.tsx | 16 ++-------------- src/components/track/MarkerSearch.tsx | 2 +- src/components/track/TrackHeader.tsx | 22 +++------------------- src/pages/TrackScreen.tsx | 26 ++++++++++++++++++++++++-- 4 files changed, 30 insertions(+), 36 deletions(-) diff --git a/src/components/track/DownloadLink.tsx b/src/components/track/DownloadLink.tsx index 50f40c4..8729f09 100644 --- a/src/components/track/DownloadLink.tsx +++ b/src/components/track/DownloadLink.tsx @@ -1,25 +1,15 @@ import React from 'react' import { FiDownload } from 'react-icons/fi' -import { GeoJsonObject } from 'geojson' -import { encodeToBase64 } from '../../utils/tools' import useLanguage from '../../hooks/useLanguage' import { Link } from 'react-router-dom' type DownloadLinkProps = { trackId: string type: string - trackname: string optimizeWaypoints: boolean - geoJson?: GeoJsonObject | null } -const DownloadLink: React.FC = ({ - trackId, - type, - trackname, - optimizeWaypoints, - geoJson, -}) => { +const DownloadLink: React.FC = ({ trackId, type, optimizeWaypoints }) => { const { getMessage } = useLanguage() const baseUrl = import.meta.env.VITE_BACKEND_BASE_URL const linkTo: string = @@ -28,9 +18,7 @@ const DownloadLink: React.FC = ({ trackId + '?mode=dl&type=' + type + - (optimizeWaypoints ? '&mode=opt' : '') + - (trackname.length > 0 ? `&name=${encodeToBase64(trackname)}` : '') + - (geoJson != null ? `&wp=${encodeToBase64(JSON.stringify(geoJson))}` : '') + (optimizeWaypoints ? '&mode=opt' : '') return ( <> diff --git a/src/components/track/MarkerSearch.tsx b/src/components/track/MarkerSearch.tsx index e559fa4..dce3acc 100644 --- a/src/components/track/MarkerSearch.tsx +++ b/src/components/track/MarkerSearch.tsx @@ -14,7 +14,7 @@ type MarkerSearchProps = { const MarkerSearch: React.FC = ({ setMarkerPositions }) => { const [searchTerm, setSearchTerm] = useState('') - const debouncedSearchTerm = useDebounce(searchTerm, 1000) + const debouncedSearchTerm = useDebounce(searchTerm, 500) const [results, setResults] = useState([]) const dropdownDivRef = useRef(null) const inputRef = useRef(null) diff --git a/src/components/track/TrackHeader.tsx b/src/components/track/TrackHeader.tsx index 952972f..59d36a0 100644 --- a/src/components/track/TrackHeader.tsx +++ b/src/components/track/TrackHeader.tsx @@ -2,13 +2,11 @@ import DownloadLink from './DownloadLink' import { FaCircleInfo, FaEye, FaEyeSlash, FaPenToSquare } from 'react-icons/fa6' import ContentEditable, { ContentEditableEvent } from 'react-contenteditable' import React, { useMemo, useRef, useState } from 'react' -import { WayPoint } from '../../@types/gps' -import { generateFeatureCollection, sanitizeFilename } from '../../utils/tools' +import { sanitizeFilename } from '../../utils/tools' import useLanguage from '../../hooks/useLanguage' type TrackHeaderProps = { trackId: string - markerPositions: WayPoint[] trackname: string setTrackname: React.Dispatch> showPolyline: boolean @@ -17,7 +15,6 @@ type TrackHeaderProps = { const TrackHeader: React.FC = ({ trackId, - markerPositions, trackname, setTrackname, showPolyline, @@ -27,7 +24,6 @@ const TrackHeader: React.FC = ({ const tracknameInputFieldRef: React.RefObject = useRef(null) const tracknameRef = useRef('') const { getMessage } = useLanguage() - const markerGeoJson = useMemo(() => generateFeatureCollection(markerPositions), [markerPositions]) useMemo(() => { tracknameRef.current = trackname @@ -73,20 +69,8 @@ const TrackHeader: React.FC = ({
- - + +
{ }) }, [trackId]) - const throttledMarkerPositions = useThrottle(markerPositions, 0) + const throttledMarkerPositions = useThrottle(markerPositions, 500) useEffect(() => { if (throttledMarkerPositions !== undefined && !isLoading) { const featureCollection = generateFeatureCollection(markerPositions) @@ -148,6 +148,29 @@ const TrackScreen = () => { } }, [throttledMarkerPositions]) + const throttledTrackname = useThrottle(trackname, 500) + useEffect(() => { + if (throttledTrackname !== undefined && !isLoading) { + const feature = { + type: 'Feature', + properties: { + name: throttledTrackname, + }, + geometry: { + type: 'LineString', + coordinates: [], + }, + } + API.patch(`/tracks/${trackId}`, feature, { + headers: { + 'Content-Type': 'application/geo+json', + }, + }).catch((error) => { + console.error('Failed to update waypoint', error) + }) + } + }, [throttledTrackname]) + return ( <> {isLoading ? ( @@ -156,7 +179,6 @@ const TrackScreen = () => {