78 lines
2.5 KiB
TypeScript
78 lines
2.5 KiB
TypeScript
import { useState, useRef, useCallback } from "react";
|
|
import * as Y from "yjs";
|
|
import { useTranslation } from "react-i18next";
|
|
import { parseGpxAsync, extractWaypoints } from "@trails-cool/gpx";
|
|
import type { YjsState } from "~/lib/use-yjs";
|
|
|
|
export function useGpxDrop(yjs: YjsState, onImportError?: (message: string) => void) {
|
|
const { t } = useTranslation("planner");
|
|
const [draggingOver, setDraggingOver] = useState(false);
|
|
const dragCounterRef = useRef(0);
|
|
|
|
const handleDragEnter = useCallback((e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
dragCounterRef.current++;
|
|
if (dragCounterRef.current === 1) setDraggingOver(true);
|
|
}, []);
|
|
|
|
const handleDragLeave = useCallback((e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
dragCounterRef.current--;
|
|
if (dragCounterRef.current === 0) setDraggingOver(false);
|
|
}, []);
|
|
|
|
const handleDragOver = useCallback((e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
}, []);
|
|
|
|
const handleDrop = useCallback(async (e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
dragCounterRef.current = 0;
|
|
setDraggingOver(false);
|
|
|
|
const file = e.dataTransfer.files[0];
|
|
if (!file) return;
|
|
if (!file.name.toLowerCase().endsWith(".gpx")) {
|
|
onImportError?.(t("importGpxError"));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const text = await file.text();
|
|
const gpxData = await parseGpxAsync(text);
|
|
const newWaypoints = extractWaypoints(gpxData);
|
|
if (newWaypoints.length < 2) return;
|
|
|
|
if (!window.confirm(t("replaceRouteConfirm"))) return;
|
|
|
|
yjs.doc.transact(() => {
|
|
yjs.waypoints.delete(0, yjs.waypoints.length);
|
|
for (const wp of newWaypoints) {
|
|
const yMap = new Y.Map();
|
|
yMap.set("lat", wp.lat);
|
|
yMap.set("lon", wp.lon);
|
|
if (wp.name) yMap.set("name", wp.name);
|
|
if (wp.isDayBreak) yMap.set("overnight", true);
|
|
if (wp.note) yMap.set("note", wp.note);
|
|
yjs.waypoints.push([yMap]);
|
|
}
|
|
|
|
yjs.noGoAreas.delete(0, yjs.noGoAreas.length);
|
|
for (const area of gpxData.noGoAreas) {
|
|
const yMap = new Y.Map();
|
|
yMap.set("points", area.points);
|
|
yjs.noGoAreas.push([yMap]);
|
|
}
|
|
|
|
if (gpxData.description) {
|
|
yjs.notes.delete(0, yjs.notes.length);
|
|
yjs.notes.insert(0, gpxData.description);
|
|
}
|
|
}, "local");
|
|
} catch {
|
|
onImportError?.(t("importGpxError"));
|
|
}
|
|
}, [yjs, t, onImportError]);
|
|
|
|
return { draggingOver, handleDragEnter, handleDragLeave, handleDragOver, handleDrop };
|
|
}
|