mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-03 09:12:30 +00:00
functional with immer
This commit is contained in:
10
gpx/package-lock.json
generated
10
gpx/package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-xml-parser": "^4.3.6",
|
"fast-xml-parser": "^4.3.6",
|
||||||
|
"immer": "^10.1.1",
|
||||||
"ts-node": "^10.9.2"
|
"ts-node": "^10.9.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -1958,6 +1959,15 @@
|
|||||||
"node": ">=10.17.0"
|
"node": ">=10.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immer": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/immer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/import-local": {
|
"node_modules/import-local": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-xml-parser": "^4.3.6",
|
"fast-xml-parser": "^4.3.6",
|
||||||
|
"immer": "^10.1.1",
|
||||||
"ts-node": "^10.9.2"
|
"ts-node": "^10.9.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -26,4 +27,4 @@
|
|||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.4.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { Coordinates, GPXFileAttributes, GPXFileType, Link, Metadata, TrackExtensions, TrackPointExtensions, TrackPointType, TrackSegmentType, TrackType, WaypointType } from "./types";
|
import { Coordinates, GPXFileAttributes, GPXFileType, Link, Metadata, TrackExtensions, TrackPointExtensions, TrackPointType, TrackSegmentType, TrackType, WaypointType } from "./types";
|
||||||
|
import { immerable } from "immer";
|
||||||
|
|
||||||
function cloneJSON<T>(obj: T): T {
|
function cloneJSON<T>(obj: T): T {
|
||||||
if (obj === null || typeof obj !== 'object') {
|
if (obj === null || typeof obj !== 'object') {
|
||||||
@@ -120,6 +121,8 @@ export class GPXFiles extends GPXTreeNode<GPXFile> {
|
|||||||
|
|
||||||
// A class that represents a GPX file
|
// A class that represents a GPX file
|
||||||
export class GPXFile extends GPXTreeNode<Track>{
|
export class GPXFile extends GPXTreeNode<Track>{
|
||||||
|
[immerable] = true;
|
||||||
|
|
||||||
attributes: GPXFileAttributes;
|
attributes: GPXFileAttributes;
|
||||||
metadata: Metadata;
|
metadata: Metadata;
|
||||||
wpt: Waypoint[];
|
wpt: Waypoint[];
|
||||||
@@ -176,6 +179,8 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
|
|
||||||
// A class that represents a Track in a GPX file
|
// A class that represents a Track in a GPX file
|
||||||
export class Track extends GPXTreeNode<TrackSegment> {
|
export class Track extends GPXTreeNode<TrackSegment> {
|
||||||
|
[immerable] = true;
|
||||||
|
|
||||||
name?: string;
|
name?: string;
|
||||||
cmt?: string;
|
cmt?: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
@@ -256,6 +261,8 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
|||||||
|
|
||||||
// A class that represents a TrackSegment in a GPX file
|
// A class that represents a TrackSegment in a GPX file
|
||||||
export class TrackSegment extends GPXTreeLeaf {
|
export class TrackSegment extends GPXTreeLeaf {
|
||||||
|
[immerable] = true;
|
||||||
|
|
||||||
trkpt: TrackPoint[];
|
trkpt: TrackPoint[];
|
||||||
|
|
||||||
constructor(segment?: TrackSegmentType | TrackSegment) {
|
constructor(segment?: TrackSegmentType | TrackSegment) {
|
||||||
@@ -432,6 +439,8 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class TrackPoint {
|
export class TrackPoint {
|
||||||
|
[immerable] = true;
|
||||||
|
|
||||||
attributes: Coordinates;
|
attributes: Coordinates;
|
||||||
ele?: number;
|
ele?: number;
|
||||||
time?: Date;
|
time?: Date;
|
||||||
|
16
website/package-lock.json
generated
16
website/package-lock.json
generated
@@ -14,11 +14,11 @@
|
|||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"dexie": "^4.0.4",
|
"dexie": "^4.0.4",
|
||||||
"gpx": "file:../gpx",
|
"gpx": "file:../gpx",
|
||||||
|
"immer": "^10.1.1",
|
||||||
"lucide-svelte": "^0.365.0",
|
"lucide-svelte": "^0.365.0",
|
||||||
"mapbox-gl": "^3.2.0",
|
"mapbox-gl": "^3.2.0",
|
||||||
"mode-watcher": "^0.3.0",
|
"mode-watcher": "^0.3.0",
|
||||||
"sortablejs": "^1.15.2",
|
"sortablejs": "^1.15.2",
|
||||||
"structurajs": "^0.12.0",
|
|
||||||
"svelte-i18n": "^4.0.0",
|
"svelte-i18n": "^4.0.0",
|
||||||
"svelte-sonner": "^0.3.22",
|
"svelte-sonner": "^0.3.22",
|
||||||
"tailwind-merge": "^2.2.2",
|
"tailwind-merge": "^2.2.2",
|
||||||
@@ -3363,6 +3363,15 @@
|
|||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immer": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/immer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@@ -5132,11 +5141,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/structurajs": {
|
|
||||||
"version": "0.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/structurajs/-/structurajs-0.12.0.tgz",
|
|
||||||
"integrity": "sha512-vadDl3zCv6OM2dXfzelUH3RLhNu9pmIEZU7zS0jIgMR3BDKIk44fDd/X9KK9LE27MMU+/aw3ibBTQQgBH0zfiw=="
|
|
||||||
},
|
|
||||||
"node_modules/subtag": {
|
"node_modules/subtag": {
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/subtag/-/subtag-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/subtag/-/subtag-0.5.0.tgz",
|
||||||
|
@@ -47,11 +47,11 @@
|
|||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"dexie": "^4.0.4",
|
"dexie": "^4.0.4",
|
||||||
"gpx": "file:../gpx",
|
"gpx": "file:../gpx",
|
||||||
|
"immer": "^10.1.1",
|
||||||
"lucide-svelte": "^0.365.0",
|
"lucide-svelte": "^0.365.0",
|
||||||
"mapbox-gl": "^3.2.0",
|
"mapbox-gl": "^3.2.0",
|
||||||
"mode-watcher": "^0.3.0",
|
"mode-watcher": "^0.3.0",
|
||||||
"sortablejs": "^1.15.2",
|
"sortablejs": "^1.15.2",
|
||||||
"structurajs": "^0.12.0",
|
|
||||||
"svelte-i18n": "^4.0.0",
|
"svelte-i18n": "^4.0.0",
|
||||||
"svelte-sonner": "^0.3.22",
|
"svelte-sonner": "^0.3.22",
|
||||||
"tailwind-merge": "^2.2.2",
|
"tailwind-merge": "^2.2.2",
|
||||||
|
@@ -1,15 +1,18 @@
|
|||||||
import Dexie, { liveQuery } from 'dexie';
|
import Dexie, { liveQuery } from 'dexie';
|
||||||
import { GPXFile } from 'gpx';
|
import { GPXFile } from 'gpx';
|
||||||
import { type FreezedObject, type Patch, produceWithPatches, applyPatches } from 'structurajs';
|
import { enableMapSet, enablePatches, produceWithPatches, applyPatches, type Patch } from 'immer';
|
||||||
import { writable, get, derived, type Readable, type Writable } from 'svelte/store';
|
import { writable, get, derived, type Readable, type Writable } from 'svelte/store';
|
||||||
import { fileOrder, selectedFiles } from './stores';
|
import { fileOrder, selectedFiles } from './stores';
|
||||||
import { mode } from 'mode-watcher';
|
import { mode } from 'mode-watcher';
|
||||||
import { defaultBasemap, defaultBasemapTree, defaultOverlayTree, defaultOverlays } from './assets/layers';
|
import { defaultBasemap, defaultBasemapTree, defaultOverlayTree, defaultOverlays } from './assets/layers';
|
||||||
|
|
||||||
|
enableMapSet();
|
||||||
|
enablePatches();
|
||||||
|
|
||||||
class Database extends Dexie {
|
class Database extends Dexie {
|
||||||
|
|
||||||
fileids!: Dexie.Table<string, string>;
|
fileids!: Dexie.Table<string, string>;
|
||||||
files!: Dexie.Table<FreezedObject<GPXFile>, string>;
|
files!: Dexie.Table<GPXFile, string>;
|
||||||
patches!: Dexie.Table<{ patch: Patch[], inversePatch: Patch[], index: number }, number>;
|
patches!: Dexie.Table<{ patch: Patch[], inversePatch: Patch[], index: number }, number>;
|
||||||
settings!: Dexie.Table<any, string>;
|
settings!: Dexie.Table<any, string>;
|
||||||
|
|
||||||
@@ -103,7 +106,7 @@ function dexieStore<T>(querier: () => T | Promise<T>, initial?: T): Readable<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrap Dexie live queries in a Svelte store to avoid triggering the query for every subscriber, also takes care of the conversion to a GPXFile object
|
// Wrap Dexie live queries in a Svelte store to avoid triggering the query for every subscriber, also takes care of the conversion to a GPXFile object
|
||||||
function dexieGPXFileStore(querier: () => FreezedObject<GPXFile> | undefined | Promise<FreezedObject<GPXFile> | undefined>): Readable<GPXFile> {
|
function dexieGPXFileStore(querier: () => GPXFile | undefined | Promise<GPXFile | undefined>): Readable<GPXFile> {
|
||||||
let store = writable<GPXFile>(undefined);
|
let store = writable<GPXFile>(undefined);
|
||||||
liveQuery(querier).subscribe(value => {
|
liveQuery(querier).subscribe(value => {
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
@@ -118,8 +121,8 @@ function dexieGPXFileStore(querier: () => FreezedObject<GPXFile> | undefined | P
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add/update the files to the database
|
// Add/update the files to the database
|
||||||
function updateDbFiles(files: (FreezedObject<GPXFile> | undefined)[], add: boolean = false) {
|
function updateDbFiles(files: (GPXFile | undefined)[], add: boolean = false) {
|
||||||
let filteredFiles = files.filter(file => file !== undefined) as FreezedObject<GPXFile>[];
|
let filteredFiles = files.filter(file => file !== undefined) as GPXFile[];
|
||||||
let fileIds = filteredFiles.map(file => file._data.id);
|
let fileIds = filteredFiles.map(file => file._data.id);
|
||||||
if (add) {
|
if (add) {
|
||||||
return db.transaction('rw', db.fileids, db.files, async () => {
|
return db.transaction('rw', db.fileids, db.files, async () => {
|
||||||
@@ -140,7 +143,7 @@ function deleteDbFiles(fileIds: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Commit the changes to the file state to the database
|
// Commit the changes to the file state to the database
|
||||||
function commitFileStateChange(newFileState: ReadonlyMap<string, FreezedObject<GPXFile>>, patch: Patch[]) {
|
function commitFileStateChange(newFileState: ReadonlyMap<string, GPXFile>, patch: Patch[]) {
|
||||||
if (newFileState.size > fileState.size) {
|
if (newFileState.size > fileState.size) {
|
||||||
return updateDbFiles(getChangedFileIds(patch).map((fileId) => newFileState.get(fileId)), true);
|
return updateDbFiles(getChangedFileIds(patch).map((fileId) => newFileState.get(fileId)), true);
|
||||||
} else if (newFileState.size === fileState.size) {
|
} else if (newFileState.size === fileState.size) {
|
||||||
@@ -238,15 +241,12 @@ function applyPatch(patch: Patch[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the file ids of the files that have changed in the patch
|
// Get the file ids of the files that have changed in the patch
|
||||||
function getChangedFileIds(patch: Patch[]) {
|
function getChangedFileIds(patch: Patch[]): string[] {
|
||||||
let changedFileIds = [];
|
let changedFileIds = new Set();
|
||||||
for (let p of patch) {
|
for (let p of patch) {
|
||||||
let fileId = p.p?.toString();
|
changedFileIds.add(p.path[0]);
|
||||||
if (fileId) {
|
|
||||||
changedFileIds.push(fileId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return changedFileIds;
|
return Array.from(changedFileIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate unique file ids, different from the ones in the database
|
// Generate unique file ids, different from the ones in the database
|
||||||
|
Reference in New Issue
Block a user