From 5fdd63103ef0a15365f8f581f6232488bc4cabe9 Mon Sep 17 00:00:00 2001 From: evlist Date: Mon, 14 Nov 2022 18:35:04 +0100 Subject: [PATCH] Reimplementing GPX imports. --- src/App.tsx | 1 - src/components/dialogs/GpxImport.tsx | 8 +++++ src/db/gpx.test.ts | 26 +++++++++++++- src/db/gpx.ts | 54 +++++++++++++++++++++++++++- src/db/lib.ts | 8 +++++ src/lib/gpx.test.ts | 33 +++++++++++++++++ src/lib/gpx.ts | 19 ++++++++++ src/workers/dispatcher-worker.ts | 4 +-- 8 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 src/lib/gpx.test.ts create mode 100644 src/lib/gpx.ts diff --git a/src/App.tsx b/src/App.tsx index 65b20b0..b2b1331 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -96,7 +96,6 @@ export const setCenterAtom = atom(null, (get, set, center: geoPoint) => { // initDb(db); dispatch({ action: 'initDb' }); -dispatch({ action: 'putNewTrk' }); /** * diff --git a/src/components/dialogs/GpxImport.tsx b/src/components/dialogs/GpxImport.tsx index 5a7ca3a..7f497d4 100644 --- a/src/components/dialogs/GpxImport.tsx +++ b/src/components/dialogs/GpxImport.tsx @@ -5,6 +5,9 @@ import GPX from '../../lib/gpx-parser-builder/src/gpx'; import css from './GpxImport.module.css'; import { IonIcon, IonItem } from '@ionic/react'; import { cloudUpload } from 'ionicons/icons'; +import { findStartTime } from '../../lib/gpx'; +import dispatch from '../../workers/dispatcher-main'; +import { intToGpxId } from '../../lib/ids'; const GpxImport: React.FC<{}> = () => { const onChangeHandler = (event: any) => { @@ -20,6 +23,11 @@ const GpxImport: React.FC<{}> = () => { console.log(fileReader.result); const gpx = GPX.parse(fileReader.result); console.log(`gpx: ${JSON.stringify(gpx)}`); + const startTime = new Date(findStartTime(gpx)!); + dispatch({ + action: 'pruneAndSaveImportedGpx', + params: { id: { gpx: intToGpxId(startTime.valueOf()) }, gpx: gpx }, + }); // pushGpx(db, { // gpx, // metadata: { diff --git a/src/db/gpx.test.ts b/src/db/gpx.test.ts index 0d72c1c..d74211d 100644 --- a/src/db/gpx.test.ts +++ b/src/db/gpx.test.ts @@ -1,4 +1,5 @@ -import { putNewGpx } from './gpx'; +import { initDb } from '.'; +import { existsGpx, putNewGpx } from './gpx'; declare global { var db: any; var dbReady: boolean; @@ -60,3 +61,26 @@ describe('The gpx module', () => { expect(id).toEqual({ gpx: 4320000000000000 }); }); }); + +describe('The gpx module with a real db', () => { + beforeEach(async () => { + await initDb({}); + globalThis.Date.now = () => 0; + }); + afterEach(async () => { + await db.destroy(); + db = undefined; + globalThis.Date.now = originalDateNow; + }); + + test("existsGpx returns false if the GPX doesn't exist", async () => { + const exists = await existsGpx({ gpx: 1 }); + expect(exists).toBeFalsy(); + }); + test('existsGpx returns false if the GPX exists', async () => { + const id = { gpx: 1 }; + await putNewGpx(id); + const exists = await existsGpx(id); + expect(exists).toBeTruthy(); + }); +}); diff --git a/src/db/gpx.ts b/src/db/gpx.ts index 5bc7a17..cba91e1 100644 --- a/src/db/gpx.ts +++ b/src/db/gpx.ts @@ -1,5 +1,6 @@ +import { PureComponent } from 'react'; import getUri, { intToGpxId } from '../lib/ids'; -import { put } from './lib'; +import { get, put, putAll } from './lib'; const emptyGpx: Gpx = { $: { @@ -46,3 +47,54 @@ export const putNewGpx = async ( ); return id; }; + +export const existsGpx = async (id: IdGpx) => { + const uri = getUri('gpx', id); + try { + await get(uri); + return true; + } catch { + return false; + } +}; + +const prune = (id: any, object: any, docs: any[]) => { + if (typeof object === 'object') { + for (const key in object) { + if ( + key === 'wpt' || + key === 'rte' || + key === 'rtept' || + key === 'trk' || + key === 'trkseg' || + key === 'trkpt' + ) { + const subObjects = object[key]; + for (const index in subObjects) { + const subId = { ...id }; + subId[key] = index; + docs.push({ + _id: getUri(key, subId), + type: key, + doc: subObjects[index], + }); + prune(subId, subObjects[index], docs); + } + object[key] = undefined; + } else prune(id, object[key], docs); + } + } +}; + +export const pruneAndSaveImportedGpx = async (params: any) => { + const { id, gpx } = params; + let docs: any[] = [{ _id: getUri('gpx', id), type: 'gpx', doc: gpx }]; + prune(id, gpx, docs); + console.log(JSON.stringify(docs)); + try { + const result = await putAll(docs); + console.log(JSON.stringify(result)); + } catch (err) { + console.error(`error: ${err}`); + } +}; diff --git a/src/db/lib.ts b/src/db/lib.ts index 3594940..3309276 100644 --- a/src/db/lib.ts +++ b/src/db/lib.ts @@ -38,3 +38,11 @@ export const getFamily = async (key: string, options: any = {}) => { ...options, }); }; + +export const get = async (id: string) => { + await db.get(id); +}; + +export const putAll = async (docs: any[]) => { + return await db.bulkDocs(docs); +}; diff --git a/src/lib/gpx.test.ts b/src/lib/gpx.test.ts new file mode 100644 index 0000000..ca8ce20 --- /dev/null +++ b/src/lib/gpx.test.ts @@ -0,0 +1,33 @@ +import { assert } from 'console'; +import { findStartTime } from './gpx'; + +describe('findStartTime', () => { + test('to be undefined for a string', () => { + const start = findStartTime(''); + expect(start).toBeUndefined(); + }); + test('to be undefined for an object without time key', () => { + const start = findStartTime({ foo: 'foo', bar: 'bar' }); + expect(start).toBeUndefined(); + }); + test('to be the time value for an object with a time key', () => { + const start = findStartTime({ foo: 'foo', time: 'bar' }); + expect(start).toEqual('bar'); + }); + test('to be the lowest time value for an object with several time keys', () => { + const start = findStartTime({ + foo: { time: 'foo' }, + time: 'bar', + bar: { time: 'a' }, + }); + expect(start).toEqual('a'); + }); + test('to be the lowest time value for an array with several objects with time keys', () => { + const start = findStartTime({ + foos: [{ time: 'foo' }, { time: '0' }], + time: 'bar', + bar: { time: 'a' }, + }); + expect(start).toEqual('0'); + }); +}); diff --git a/src/lib/gpx.ts b/src/lib/gpx.ts new file mode 100644 index 0000000..f3f32e8 --- /dev/null +++ b/src/lib/gpx.ts @@ -0,0 +1,19 @@ +const min = (s1?: string, s2?: string) => { + return s1! < s2! ? s1 : s2; +}; + +export const findStartTime = (x: any, startTime?: string) => { + if (typeof x === 'object') { + let newStartTime = startTime; + + for (const key in x) { + if (key === 'time') { + newStartTime = min(newStartTime, x[key]); + } else { + newStartTime = findStartTime(x[key], newStartTime); + } + } + return newStartTime; + } + else return startTime; +}; diff --git a/src/workers/dispatcher-worker.ts b/src/workers/dispatcher-worker.ts index 523129c..71b335d 100644 --- a/src/workers/dispatcher-worker.ts +++ b/src/workers/dispatcher-worker.ts @@ -1,10 +1,10 @@ import { initDb } from '../db'; -import { putNewGpx } from '../db/gpx'; +import { putNewGpx, existsGpx, pruneAndSaveImportedGpx } from '../db/gpx'; import { putNewTrk } from '../db/trk'; const self = globalThis as unknown as SharedWorkerGlobalScope; -const actions = { initDb, putNewGpx, putNewTrk }; +const actions = { initDb, putNewGpx, putNewTrk, existsGpx, pruneAndSaveImportedGpx }; self.onconnect = function (e) { var port = e.ports[0];