Storing imported tracks in local "forage".
This commit is contained in:
parent
bb2130793d
commit
d0bac1c509
File diff suppressed because it is too large
Load Diff
|
@ -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",
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
@ -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' : '';
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -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"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue