Storing imported tracks in local "forage".

This commit is contained in:
Eric van der Vlist 2022-09-23 22:34:59 +02:00
parent bb2130793d
commit d0bac1c509
9 changed files with 1276 additions and 53 deletions

1168
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
"@capacitor/status-bar": "4.0.1", "@capacitor/status-bar": "4.0.1",
"@ionic/react": "^6.2.6", "@ionic/react": "^6.2.6",
"@ionic/react-router": "^6.2.6", "@ionic/react-router": "^6.2.6",
"@ionic/storage": "^3.0.6", "@openpgp/asmcrypto.js": "^2.3.2",
"@reduxjs/toolkit": "^1.8.5", "@reduxjs/toolkit": "^1.8.5",
"@testing-library/jest-dom": "^5.11.9", "@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.7", "@testing-library/react": "^11.2.7",
@ -27,18 +27,25 @@
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"@types/react-router": "^5.1.11", "@types/react-router": "^5.1.11",
"@types/react-router-dom": "^5.1.7", "@types/react-router-dom": "^5.1.7",
"buffer": "^6.0.3",
"cordova-plugin-geolocation": "^4.1.0", "cordova-plugin-geolocation": "^4.1.0",
"cordova-sqlite-storage": "^6.0.0", "cordova-sqlite-storage": "^6.0.0",
"crypto-browserify": "^3.12.0",
"git": "^0.1.5",
"ionicons": "^6.0.3", "ionicons": "^6.0.3",
"isomorphic-xml2js": "^0.1.3", "isomorphic-xml2js": "^0.1.3",
"localforage": "^1.10.0",
"localforage-cordovasqlitedriver": "^1.8.0", "localforage-cordovasqlitedriver": "^1.8.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^18.2.0", "react": "^18.2.0",
"react-app-polyfill": "^3.0.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-redux": "^8.0.2", "react-redux": "^8.0.2",
"react-router": "^6.3.0", "react-router": "^6.3.0",
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",
"react-scripts": "^5.0.0", "react-scripts": "^5.0.0",
"status": "^0.0.13",
"stream-browserify": "^3.0.0",
"typescript": "^4.8.3", "typescript": "^4.8.3",
"web-vitals": "^0.2.4", "web-vitals": "^0.2.4",
"workbox-background-sync": "^5.1.4", "workbox-background-sync": "^5.1.4",

View File

@ -22,13 +22,16 @@ import '@ionic/react/css/display.css';
import './theme/variables.css'; import './theme/variables.css';
import Map from './components/map/map'; import Map from './components/map/map';
import DbContextProvider from './db/db-context-provider';
setupIonicReact(); setupIonicReact();
const App: React.FC = () => ( const App: React.FC = () => (
<IonApp> <IonApp>
<Provider store={store}> <Provider store={store}>
<Map /> <DbContextProvider>
<Map />
</DbContextProvider>
</Provider> </Provider>
</IonApp> </IonApp>
); );

View File

@ -14,7 +14,7 @@ const GpxImport: React.FC<{}> = () => {
const onChangeHandler = (event: any) => { const onChangeHandler = (event: any) => {
console.log('On change handler'); console.log('On change handler');
const file = event.target.files[0]; const file: File = event.target.files[0];
const fileReader = new FileReader(); const fileReader = new FileReader();
fileReader.readAsText(file); fileReader.readAsText(file);
@ -24,7 +24,18 @@ const GpxImport: React.FC<{}> = () => {
// this will then display a text file // this will then display a text file
console.log(fileReader.result); console.log(fileReader.result);
const track = GPX.parse(fileReader.result); const track = GPX.parse(fileReader.result);
dispatch(tracksActions.push(JSON.parse(JSON.stringify(track)))); dispatch(
tracksActions.push({
track: JSON.parse(JSON.stringify(track)),
metadata: {
lastModified: new Date(file.lastModified).toISOString(),
importDate: new Date().toISOString(),
name: file.name,
size: file.size,
type: file.type,
},
})
);
}, },
false false
); );

View File

@ -1,11 +1,31 @@
import React from 'react'; import React, { useContext, useEffect } from 'react';
import { DbContext } from '../../db/db-context-provider';
import { lat2tile, lon2tile } from '../../lib/geo'; import { lat2tile, lon2tile } from '../../lib/geo';
import { zoom0 } from '../../store/map'; import { zoom0 } from '../../store/map';
const Track: React.FC<{ track: any }> = (props) => { const Track: React.FC<{ track: any; id: string }> = (props) => {
const trkseg = props.track.trk[0].trkseg[0]; const dbContext = useContext(DbContext);
useEffect(() => {
const store = dbContext.tracksStore;
const checkDb = async () => {
store.getItem(props.id).then((value) => {
if (value === null) {
store.setItem(props.id, props.track).then((value) => {
console.log(
`Track ${props.id} saved in the ${store.driver()} Database.`
);
});
}
});
};
if (dbContext.ready) {
checkDb();
}
}, [dbContext, props]);
const trkseg = props.track.track.trk[0].trkseg[0];
const d = trkseg.trkpt.reduce( const d = trkseg.trkpt.reduce(
(previous: string, current: any, index: number) => { (previous: string, current: any, index: number) => {
const action = index === 0 ? 'M' : index === 1 ? 'L' : ''; const action = index === 0 ? 'M' : index === 1 ? 'L' : '';

View File

@ -1,33 +1,15 @@
import React, { Fragment, useEffect } from 'react'; import React, { Fragment, useContext } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { Drivers, Storage } from '@ionic/storage';
import * as CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';
import Track from './track'; import Track from './track';
var store: Storage;
const Tracks: React.FC<{}> = () => { const Tracks: React.FC<{}> = () => {
useEffect(() => {
store = new Storage({
driverOrder: [
CordovaSQLiteDriver._driver,
Drivers.IndexedDB,
Drivers.LocalStorage,
],
});
store
.defineDriver(CordovaSQLiteDriver)
.then(() => console.log('CordovaSQLiteDriver registered'));
}, []);
const tracks = useSelector((state: { tracks: any }) => state.tracks.tracks); const tracks = useSelector((state: { tracks: any }) => state.tracks.tracks);
return ( return (
<Fragment> <Fragment>
{Object.keys(tracks).map((key: any) => ( {Object.keys(tracks).map((key: any) => (
<Track key={key} track={tracks[key]} /> <Track key={key} id={key} track={tracks[key]} />
))} ))}
</Fragment> </Fragment>
); );

View File

@ -0,0 +1,40 @@
import react, { useEffect, useState } from 'react';
import * as CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';
import localforage from 'localforage';
const initialDbContext = { ready: false, tracksStore: localforage };
export const DbContext = react.createContext(initialDbContext);
const DbContextProvider: react.FC<{
children?: JSX.Element;
}> = (props: { children?: JSX.Element }) => {
const [store, setStore] = useState(localforage);
const [ready, setReady] = useState(false);
useEffect(() => {
const config = {
name: 'dyomedea',
driver: [
CordovaSQLiteDriver._driver,
localforage.INDEXEDDB,
localforage.WEBSQL,
localforage.LOCALSTORAGE,
],
};
const newStore = localforage.createInstance({
storeName: 'tracks',
...config,
});
setStore(newStore);
setReady(true);
}, []);
return (
<DbContext.Provider value={{ ready, tracksStore: store }}>
{props.children}
</DbContext.Provider>
);
};
export default DbContextProvider;

View File

@ -1,5 +1,12 @@
import { createSlice } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit';
import {
Sha256,
Sha512,
string_to_bytes,
bytes_to_base64,
} from '@openpgp/asmcrypto.js';
interface TracksState { interface TracksState {
tracks: { [index: string]: any }; tracks: { [index: string]: any };
} }
@ -13,10 +20,29 @@ const tracksSlice = createSlice({
initialState: initialTracks, initialState: initialTracks,
reducers: { reducers: {
add: (state, action) => { add: (state, action) => {
state.tracks[action.payload.id] = action.payload.track; const { id, ...data } = action.payload;
state.tracks[action.payload.id] = data;
}, },
push: (state, action) => { push: (state, action) => {
state.tracks[crypto.randomUUID()] = action.payload; const sha = new Sha512();
const result = sha
.process(string_to_bytes(JSON.stringify(action.payload.track)))
.finish().result;
var id;
if (result === null) {
console.log(`Can't hash`);
id = crypto.randomUUID();
} else {
id = bytes_to_base64(result);
console.log(`Digest: ${id}`);
}
if (state.tracks.hasOwnProperty(id)) {
console.log('Track had already been imported');
} else {
state.tracks[id] = {
...action.payload,
};
}
}, },
}, },
}); });

View File

@ -1,11 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": [ "lib": ["dom", "dom.iterable", "esnext"],
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
@ -21,6 +17,10 @@
"jsx": "react-jsx" "jsx": "react-jsx"
}, },
"include": [ "include": [
"src" "src",
] "node_modules/gpx-parser-builder/src/gpx-parser-builder.d.ts"
],
"paths": {
"crypto": ["node_modules/crypto-browserify"]
}
} }