real incremental immer patches

This commit is contained in:
vcoppe
2024-07-04 02:17:50 +02:00
parent affc3ed946
commit 8643abfafa
16 changed files with 469 additions and 630 deletions

View File

@@ -1,4 +1,4 @@
import { dbUtils, getFile, getFileIds } from "$lib/db";
import { dbUtils, getFile } from "$lib/db";
import { castDraft, freeze } from "immer";
import { GPXFile, Track, TrackSegment, Waypoint } from "gpx";
import { selection } from "./Selection";
@@ -353,59 +353,41 @@ export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: L
let files = [fromParent.getFileId(), toParent.getFileId()];
let callbacks = [
(file, context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[]) => {
let newFile = file;
fromItems.forEach((item) => {
if (item instanceof ListTrackItem) {
let [result, removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex(), []);
newFile = castDraft(result);
context.push(...removed);
context.push(...file.replaceTracks(item.getTrackIndex(), item.getTrackIndex(), []));
} else if (item instanceof ListTrackSegmentItem) {
let [result, removed] = newFile.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex(), []);
newFile = castDraft(result);
context.push(...removed);
context.push(...file.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex(), []));
} else if (item instanceof ListWaypointsItem) {
let [result, removed] = newFile.replaceWaypoints(0, newFile.wpt.length - 1, []);
newFile = castDraft(result);
context.push(removed);
context.push(file.replaceWaypoints(0, newFile.wpt.length - 1, []));
} else if (item instanceof ListWaypointItem) {
let [result, removed] = newFile.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex(), []);
newFile = castDraft(result);
context.push(...removed);
context.push(...file.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex(), []));
}
});
context.reverse();
return newFile;
},
(file, context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[]) => {
let newFile = file;
toItems.forEach((item, i) => {
if (item instanceof ListTrackItem) {
if (context[i] instanceof Track) {
let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [context[i]]);
newFile = castDraft(result);
file.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [context[i]]);
} else if (context[i] instanceof TrackSegment) {
let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [new Track({
file.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [new Track({
trkseg: [context[i]]
})]);
newFile = castDraft(result);
}
} else if (item instanceof ListTrackSegmentItem && context[i] instanceof TrackSegment) {
let [result, _removed] = newFile.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex() - 1, [context[i]]);
newFile = castDraft(result);
file.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex() - 1, [context[i]]);
} else if (item instanceof ListWaypointsItem) {
if (Array.isArray(context[i]) && context[i].length > 0 && context[i][0] instanceof Waypoint) {
let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, context[i]);
newFile = castDraft(result);
file.replaceWaypoints(file.wpt.length, file.wpt.length - 1, context[i]);
} else if (context[i] instanceof Waypoint) {
let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, [context[i]]);
newFile = castDraft(result);
file.replaceWaypoints(file.wpt.length, file.wpt.length - 1, [context[i]]);
}
} else if (item instanceof ListWaypointItem && context[i] instanceof Waypoint) {
let [result, _removed] = newFile.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex() - 1, [context[i]]);
newFile = castDraft(result);
file.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex() - 1, [context[i]]);
}
});
return newFile;
}
];
@@ -433,12 +415,13 @@ export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: L
if (context[i].name) {
newFile.metadata.name = context[i].name;
}
newFile = newFile.replaceTracks(0, 0, [context[i]])[0];
console.log(context[i]);
newFile.replaceTracks(0, 0, [context[i]])[0];
files.set(item.getFileId(), freeze(newFile));
} else if (context[i] instanceof TrackSegment) {
let newFile = newGPXFile();
newFile._data.id = item.getFileId();
newFile = newFile.replaceTracks(0, 0, [new Track({
newFile.replaceTracks(0, 0, [new Track({
trkseg: [context[i]]
})])[0];
files.set(item.getFileId(), freeze(newFile));

View File

@@ -27,8 +27,8 @@
export let node:
| Map<string, Readable<GPXFileWithStatistics | undefined>>
| GPXTreeElement<AnyGPXTreeElement>
| ReadonlyArray<Readonly<Waypoint>>
| Readonly<Waypoint>;
| Waypoint[]
| Waypoint;
export let item: ListItem;
let recursive = getContext<boolean>('recursive');

View File

@@ -27,7 +27,7 @@
export let node:
| Map<string, Readable<GPXFileWithStatistics | undefined>>
| GPXTreeElement<AnyGPXTreeElement>
| Readonly<Waypoint>;
| Waypoint;
export let item: ListItem;
export let waypointRoot: boolean = false;

View File

@@ -51,10 +51,7 @@
import MetadataDialog from './MetadataDialog.svelte';
import StyleDialog from './StyleDialog.svelte';
export let node:
| GPXTreeElement<AnyGPXTreeElement>
| ReadonlyArray<Readonly<Waypoint>>
| Readonly<Waypoint>;
export let node: GPXTreeElement<AnyGPXTreeElement> | Waypoint[] | Waypoint;
export let item: ListItem;
export let label: string | undefined;
@@ -232,9 +229,8 @@
<ContextMenu.Item
disabled={!singleSelection}
on:click={() =>
dbUtils.applyToFile(
item.getFileId(),
(file) => file.replaceTracks(file.trk.length, file.trk.length, [new Track()])[0]
dbUtils.applyToFile(item.getFileId(), (file) =>
file.replaceTracks(file.trk.length, file.trk.length, [new Track()])
)}
>
<Plus size="16" class="mr-1" />
@@ -246,15 +242,13 @@
disabled={!singleSelection}
on:click={() => {
let trackIndex = item.getTrackIndex();
dbUtils.applyToFile(
item.getFileId(),
(file) =>
file.replaceTrackSegments(
trackIndex,
file.trk[trackIndex].trkseg.length,
file.trk[trackIndex].trkseg.length,
[new TrackSegment()]
)[0]
dbUtils.applyToFile(item.getFileId(), (file) =>
file.replaceTrackSegments(
trackIndex,
file.trk[trackIndex].trkseg.length,
file.trk[trackIndex].trkseg.length,
[new TrackSegment()]
)
);
}}
>

View File

@@ -11,10 +11,7 @@
import { _ } from 'svelte-i18n';
import { editMetadata } from '$lib/stores';
export let node:
| GPXTreeElement<AnyGPXTreeElement>
| ReadonlyArray<Readonly<Waypoint>>
| Readonly<Waypoint>;
export let node: GPXTreeElement<AnyGPXTreeElement> | Waypoint[] | Waypoint;
export let item: ListItem;
export let open = false;
@@ -54,7 +51,6 @@
file.trk[item.getTrackIndex()].name = name;
file.trk[item.getTrackIndex()].desc = description;
}
return file;
});
open = false;
}}

View File

@@ -6,7 +6,6 @@ import { currentPopupWaypoint, deleteWaypoint, waypointPopup } from "./WaypointP
import { addSelectItem, selectItem, selection } from "$lib/components/file-list/Selection";
import { ListTrackSegmentItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList";
import type { Waypoint } from "gpx";
import { produce } from "immer";
import { resetCursor, setCursor, setGrabbingCursor, setPointerCursor } from "$lib/utils";
import { font } from "$lib/assets/layers";
import { selectedWaypoint } from "$lib/components/toolbar/tools/Waypoint.svelte";
@@ -231,13 +230,13 @@ export class GPXLayer {
resetCursor();
marker.getElement().style.cursor = '';
dbUtils.applyToFile(this.fileId, (file) => {
return produce(file, (draft) => {
let latLng = marker.getLngLat();
draft.wpt[marker._waypoint._data.index].setCoordinates({
lat: latLng.lat,
lon: latLng.lng
});
let latLng = marker.getLngLat();
let wpt = file.wpt[marker._waypoint._data.index];
wpt.setCoordinates({
lat: latLng.lat,
lon: latLng.lng
});
wpt.ele = this.map.queryTerrainElevation([latLng.lng, latLng.lat], { exaggerated: false }) ?? 0;
});
dragEndTimestamp = Date.now()
});

View File

@@ -21,5 +21,5 @@ export const waypointPopup = new mapboxgl.Popup({
});
export function deleteWaypoint(fileId: string, waypointIndex: number) {
dbUtils.applyToFile(fileId, (file) => file.replaceWaypoints(waypointIndex, waypointIndex, [])[0]);
dbUtils.applyToFile(fileId, (file) => file.replaceWaypoints(waypointIndex, waypointIndex, []));
}

View File

@@ -303,16 +303,16 @@
let fileId = item.getFileId();
dbUtils.applyToFile(fileId, (file) => {
if (item instanceof ListFileItem) {
return file.changeTimestamps(getDate(startDate, startTime), effectiveSpeed, ratio);
file.changeTimestamps(getDate(startDate, startTime), effectiveSpeed, ratio);
} else if (item instanceof ListTrackItem) {
return file.changeTimestamps(
file.changeTimestamps(
getDate(startDate, startTime),
effectiveSpeed,
ratio,
item.getTrackIndex()
);
} else if (item instanceof ListTrackSegmentItem) {
return file.changeTimestamps(
file.changeTimestamps(
getDate(startDate, startTime),
effectiveSpeed,
ratio,

View File

@@ -104,19 +104,16 @@
longitude = parseFloat(longitude.toFixed(6));
if ($selectedWaypoint) {
dbUtils.applyToFile($selectedWaypoint[1], (file) => {
let waypoint = $selectedWaypoint[0].clone();
waypoint.name = name;
waypoint.desc = description;
waypoint.cmt = description;
waypoint.setCoordinates({
let wpt = file.wpt[$selectedWaypoint[0]._data.index];
wpt.name = name;
wpt.desc = description;
wpt.cmt = description;
wpt.setCoordinates({
lat: latitude,
lon: longitude
});
return file.replaceWaypoints(
$selectedWaypoint[0]._data.index,
$selectedWaypoint[0]._data.index,
[waypoint]
)[0];
wpt.ele =
get(map)?.queryTerrainElevation([longitude, latitude], { exaggerated: false }) ?? 0;
});
} else {
let fileIds = new Set<string>();
@@ -134,9 +131,8 @@
});
waypoint.ele =
get(map)?.queryTerrainElevation([longitude, latitude], { exaggerated: false }) ?? 0;
dbUtils.applyToFiles(
Array.from(fileIds),
(file) => file.replaceWaypoints(file.wpt.length, file.wpt.length, [waypoint])[0]
dbUtils.applyToFiles(Array.from(fileIds), (file) =>
file.replaceWaypoints(file.wpt.length, file.wpt.length, [waypoint])
);
}
selectedWaypoint.set(undefined);

View File

@@ -71,7 +71,7 @@
function createFileWithPoint(e: any) {
if ($selection.size === 0) {
let file = newGPXFile();
file = file.replaceTrackPoints(0, 0, 0, 0, [
file.replaceTrackPoints(0, 0, 0, 0, [
new TrackPoint({
attributes: {
lat: e.lngLat.lat,
@@ -79,9 +79,7 @@
}
})
]);
file = produce(file, (draft) => {
draft._data.id = getFileIds(1)[0];
});
file._data.id = getFileIds(1)[0];
dbUtils.add(file);
selectFileWhenLoaded(file._data.id);
}

View File

@@ -351,7 +351,7 @@ export class RoutingControls {
} else if (nextAnchor === null) { // Last point, remove trackpoints from previousAnchor
dbUtils.applyToFile(this.fileId, (file) => {
let segment = file.getSegment(anchor.trackIndex, anchor.segmentIndex);
return file.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, previousAnchor.point._data.index + 1, segment.trkpt.length - 1, []);
file.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, previousAnchor.point._data.index + 1, segment.trkpt.length - 1, []);
});
} else { // Route between previousAnchor and nextAnchor
this.routeBetweenAnchors([previousAnchor, nextAnchor], [previousAnchor.point.getCoordinates(), nextAnchor.point.getCoordinates()]);
@@ -374,8 +374,8 @@ export class RoutingControls {
let segment = anchor.segment;
dbUtils.applyToFile(this.fileId, (file) => {
let newFile = file.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, segment.trkpt.length, segment.trkpt.length - 1, segment.trkpt.slice(0, anchor.point._data.index), speed > 0 ? speed : undefined);
return newFile.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, 0, anchor.point._data.index - 1, []);
file.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, segment.trkpt.length, segment.trkpt.length - 1, segment.trkpt.slice(0, anchor.point._data.index), speed > 0 ? speed : undefined);
file.crop(anchor.point._data.index, anchor.point._data.index + segment.trkpt.length - 1, [anchor.trackIndex], [anchor.segmentIndex]);
});
}
@@ -412,14 +412,14 @@ export class RoutingControls {
}
if (file.trk.length === 0) {
let track = new Track();
track = track.replaceTrackPoints(0, 0, 0, [newPoint]);
return file.replaceTracks(0, 0, [track])[0];
track.replaceTrackPoints(0, 0, 0, [newPoint]);
file.replaceTracks(0, 0, [track])[0];
} else if (file.trk[trackIndex].trkseg.length === 0) {
let segment = new TrackSegment();
segment = segment.replaceTrackPoints(0, 0, [newPoint]);
return file.replaceTrackSegments(trackIndex, 0, 0, [segment])[0];
segment.replaceTrackPoints(0, 0, [newPoint]);
file.replaceTrackSegments(trackIndex, 0, 0, [segment]);
} else {
return file.replaceTrackPoints(trackIndex, segmentIndex, 0, 0, [newPoint]);
file.replaceTrackPoints(trackIndex, segmentIndex, 0, 0, [newPoint]);
}
});
return;
@@ -458,11 +458,11 @@ export class RoutingControls {
let lastAnchor = this.anchors[this.anchors.length - 1];
let segment = lastAnchor.segment;
dbUtils.applyToFile(this.fileId, (file) => {
let segment = original(file).getSegment(lastAnchor.trackIndex, lastAnchor.segmentIndex);
let newSegment = segment.clone();
newSegment = newSegment._reverse(segment.getEndTimestamp(), segment.getEndTimestamp());
return file.replaceTrackPoints(lastAnchor.trackIndex, lastAnchor.segmentIndex, segment.trkpt.length, segment.trkpt.length, newSegment.trkpt.map((point) => point));
newSegment._reverse(segment.getEndTimestamp(), segment.getEndTimestamp());
file.replaceTrackPoints(lastAnchor.trackIndex, lastAnchor.segmentIndex, segment.trkpt.length, segment.trkpt.length, newSegment.trkpt.map((point) => point));
});
}

View File

@@ -23,15 +23,11 @@ export function updateAnchorPoints(file: GPXFile) {
}
if (segment.trkpt.length > 0) {
if (!segment.trkpt[0]._data.anchor) { // First point is not an anchor, make it one
segment.trkpt[0]._data.anchor = true;
segment.trkpt[0]._data.zoom = 0;
}
if (!segment.trkpt[segment.trkpt.length - 1]._data.anchor) { // Last point is not an anchor, make it one
segment.trkpt[segment.trkpt.length - 1]._data.anchor = true;
segment.trkpt[segment.trkpt.length - 1]._data.zoom = 0;
}
// Ensure first and last points are anchors and always visible
segment.trkpt[0]._data.anchor = true;
segment.trkpt[0]._data.zoom = 0;
segment.trkpt[segment.trkpt.length - 1]._data.anchor = true;
segment.trkpt[segment.trkpt.length - 1]._data.zoom = 0;
}
}
}