From c0015a7cfa5f41aa3974cd1c32898edfff2f8c76 Mon Sep 17 00:00:00 2001 From: evlist Date: Mon, 3 Oct 2022 21:08:27 +0200 Subject: [PATCH] Creating a mechanism to store settings both in the database and in redux. --- src/components/map/GpxRecord.tsx | 16 ++- src/components/map/Settings.tsx | 177 ++++++++++++++++++++++++++++++ src/components/map/map.tsx | 2 + src/db/index.ts | 26 +++-- src/lib/background-geolocation.ts | 10 +- src/lib/ids.test.ts | 11 ++ src/lib/ids.ts | 3 +- src/store/index.ts | 3 +- src/store/settings.ts | 37 +++++++ src/theme/map.css | 5 + 10 files changed, 272 insertions(+), 18 deletions(-) create mode 100644 src/components/map/Settings.tsx create mode 100644 src/store/settings.ts diff --git a/src/components/map/GpxRecord.tsx b/src/components/map/GpxRecord.tsx index 9953705..63a7005 100644 --- a/src/components/map/GpxRecord.tsx +++ b/src/components/map/GpxRecord.tsx @@ -25,10 +25,9 @@ import { } from '../../lib/background-geolocation'; import { appendTrkpt, deleteCurrent, saveCurrent } from '../../db/gpx'; import { enterAnimation, leaveAnimation } from '../../lib/animation'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { mapActions } from '../../store/map'; - -const MIN_TIME_BETWEEN_LOCATIONS = 15 * 1000; +import { SettingsState } from '../../store/settings'; declare global { var $lastValidLocationTime: number; @@ -41,6 +40,10 @@ const GpxRecord: React.FC<{}> = () => { const [watcher_id, setWatcher_id] = useState(); + const geolocationsSettingsState = useSelector( + (state: { settings: SettingsState }) => state.settings.geolocation + ); + const gpxes = useFind({ selector: { type: 'gpx', @@ -72,7 +75,7 @@ const GpxRecord: React.FC<{}> = () => { ); if ( location.time - globalThis.$lastValidLocationTime > - MIN_TIME_BETWEEN_LOCATIONS + geolocationsSettingsState.minTimeInterval ) { globalThis.$lastValidLocationTime = location.time; appendTrkpt(db, { @@ -98,7 +101,10 @@ const GpxRecord: React.FC<{}> = () => { const startRecording = () => { globalThis.$lastValidLocationTime = 0; - startBackgroundGeolocation(newLocationHandler).then((result) => { + startBackgroundGeolocation( + newLocationHandler, + geolocationsSettingsState.minDistance + ).then((result) => { setWatcher_id(result); }); setIsRecording(true); diff --git a/src/components/map/Settings.tsx b/src/components/map/Settings.tsx new file mode 100644 index 0000000..8fe42f8 --- /dev/null +++ b/src/components/map/Settings.tsx @@ -0,0 +1,177 @@ +import React, { Fragment, useEffect, useRef, useState } from 'react'; + +import { useDB } from 'react-pouchdb'; + +import { + IonButton, + IonButtons, + IonContent, + IonIcon, + IonInput, + IonItem, + IonLabel, + IonList, + IonListHeader, + IonModal, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { options, text } from 'ionicons/icons'; +import { enterAnimation, leaveAnimation } from '../../lib/animation'; +import { useDispatch, useSelector } from 'react-redux'; +import { settingsActions, SettingsState } from '../../store/settings'; +import uri from '../../lib/ids'; +import _ from 'lodash'; + +const Settings: React.FC<{}> = () => { + const dispatch = useDispatch(); + const db = useDB(); + + useEffect(() => { + const initFromDb = async () => { + try { + const settingsFromDb = await db.get(uri('settings', {})); + console.log( + `settingsFromDb: ${JSON.stringify(settingsFromDb.settings)}` + ); + dispatch(settingsActions.saveSettings(settingsFromDb.settings)); + } catch {} + }; + initFromDb(); + }, []); + + const settingsState = useSelector( + (state: { settings: SettingsState }) => state.settings + ); + + useEffect(() => { + if (!settingsState.default) { + const settingsFromRedux = async () => { + const id = uri('settings', {}); + var settingsFromDb; + try { + settingsFromDb = await db.get(id); + } catch (error) { + console.log( + `Error getting settings from db: ${JSON.stringify(error)}` + ); + settingsFromDb = { _id: id, settings: {} }; + } + if (!_.isEqual(settingsState, settingsFromDb.settings)) { + console.log(`settingsFromRedux: ${JSON.stringify(settingsState)}`); + settingsFromDb.settings = settingsState; + db.put(settingsFromDb); + } + }; + settingsFromRedux(); + } + }, [settingsState]); + + const pseudo = useRef(null); + const minTimeInterval = useRef(null); + const minDistance = useRef(null); + + const modal = useRef(null); + + const dismiss = () => { + modal.current?.dismiss(); + }; + + const save = () => { + dispatch( + settingsActions.saveSettings({ + user: { + pseudo: pseudo.current?.value, + }, + geolocation: { + minTimeInterval: minTimeInterval.current?.value, + minDistance: minDistance.current?.value, + }, + }) + ); + dismiss(); + }; + + return ( + + + + + + + Settings + + dismiss()}>Close + + + + + + User + + + Pseudo: + + + + + + Geolocation + + + Minimal time interval (ms): + + + + Minimal distance (m): + + + + + + + save()} + > + Save + + dismiss()} + > + Cancel + + + + + + + + ); +}; + +export default Settings; diff --git a/src/components/map/map.tsx b/src/components/map/map.tsx index 29f130f..84e1e4c 100644 --- a/src/components/map/map.tsx +++ b/src/components/map/map.tsx @@ -25,6 +25,7 @@ import { initDb } from '../../db'; import TileServerChooserButton from './TileServerChooserButton'; import TileServerChooserDialog from './TileServerChooserDialog'; import TrackBrowser from './TracksBrowser'; +import Settings from './Settings'; const Map: react.FC<{}> = (props: {}) => { const dispatch = useDispatch(); @@ -68,6 +69,7 @@ const Map: react.FC<{}> = (props: {}) => { + diff --git a/src/db/index.ts b/src/db/index.ts index 137db6b..e3a678a 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,17 +1,22 @@ -import _ from 'lodash'; +import _, { constant } from 'lodash'; +import uri from '../lib/ids'; -const DBDEFINITION = '--db-definition--'; +const dbDefinitionId = uri('dbdef', {}); -const dbDefinition = { - _id: DBDEFINITION, - type: DBDEFINITION, - version: '0.000001', +const currentDbDefinition = { + _id: dbDefinitionId, + type: dbDefinitionId, + def: { version: '0.000001' }, }; export const initDb = async (db: any, setDbReady: any) => { + var previousDbDefinition = { + _id: dbDefinitionId, + type: dbDefinitionId, + def: { version: '0' }, + }; try { - await db.get(DBDEFINITION); - // TODO: support migrations + previousDbDefinition = await db.get(dbDefinitionId); } catch (error: any) { if (error.status !== 404) { console.log( @@ -21,6 +26,11 @@ export const initDb = async (db: any, setDbReady: any) => { } } + if (previousDbDefinition.def.version < currentDbDefinition.def.version) { + previousDbDefinition.def = currentDbDefinition.def; + db.put(previousDbDefinition); + // TODO: support migrations + } await await db.compact(); await db.viewCleanup(); diff --git a/src/lib/background-geolocation.ts b/src/lib/background-geolocation.ts index 052ccb5..0e2a1c0 100644 --- a/src/lib/background-geolocation.ts +++ b/src/lib/background-geolocation.ts @@ -34,7 +34,11 @@ const backgroundGeolocationConfig = { distanceFilter: 10, }; -export const startBackgroundGeolocation = async (newLocationHandler: any) => { +export const startBackgroundGeolocation = async ( + newLocationHandler: any, + distanceFilter: number +) => { + backgroundGeolocationConfig.distanceFilter = distanceFilter; const locationHandler = (location: any, error: any) => { console.log('com.dyomedea.dyomedea LOG', ' - Callback'); if (error) { @@ -57,8 +61,8 @@ export const startBackgroundGeolocation = async (newLocationHandler: any) => { } console.log(location); if (location !== undefined) { - newLocationHandler(location); - } + newLocationHandler(location); + } return console.log('com.dyomedea.dyomedea LOG', ' - location: ', location); }; diff --git a/src/lib/ids.test.ts b/src/lib/ids.test.ts index afeae81..770d3da 100644 --- a/src/lib/ids.test.ts +++ b/src/lib/ids.test.ts @@ -103,3 +103,14 @@ describe('Checking trkpt ids', () => { }); }); }); + +describe('Checking settings id', () => { + test(', vice', () => { + const rte = uri('settings', {}); + expect(rte).toBe('settings'); + }); + test(', and versa', () => { + const rte = uri('settings', 'settings'); + expect(rte).toMatchObject({}); + }); +}); diff --git a/src/lib/ids.ts b/src/lib/ids.ts index a24ca34..57aca42 100644 --- a/src/lib/ids.ts +++ b/src/lib/ids.ts @@ -1,7 +1,8 @@ import { route } from 'docuri'; const routes = { - config: route('config'), + dbdef: route('dbdef'), + settings: route('settings'), gpx: route('gpx/:gpx'), trk: route('gpx/:gpx/trk/:trk'), trkseg: route('gpx/:gpx/trk/:trk/:trkseg'), diff --git a/src/store/index.ts b/src/store/index.ts index 5e67564..e845907 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,9 +1,10 @@ import { configureStore } from '@reduxjs/toolkit'; import mapReducer from './map'; +import settingsReducer from './settings'; const store = configureStore({ - reducer: { map: mapReducer }, + reducer: { map: mapReducer, settings: settingsReducer }, }); export default store; diff --git a/src/store/settings.ts b/src/store/settings.ts new file mode 100644 index 0000000..3b17dba --- /dev/null +++ b/src/store/settings.ts @@ -0,0 +1,37 @@ +import { createSlice } from '@reduxjs/toolkit'; + +export interface SettingsState { + default?: boolean; + user: { + pseudo: string; + }; + geolocation: { + minTimeInterval: number; + minDistance: number; + }; +} + +export const initialSettingsState: SettingsState = { + default: true, + user: { + pseudo: 'user', + }, + geolocation: { + minTimeInterval: 15000, + minDistance: 10, + }, +}; + +const settingsSlice = createSlice({ + name: 'settings', + initialState: initialSettingsState, + reducers: { + saveSettings: (state, action) => { + return action.payload; + }, + }, +}); + +export const settingsActions = settingsSlice.actions; + +export default settingsSlice.reducer; diff --git a/src/theme/map.css b/src/theme/map.css index 227863f..737f689 100644 --- a/src/theme/map.css +++ b/src/theme/map.css @@ -64,6 +64,11 @@ ion-modal ion-toolbar { --color: white; } +ion-modal ion-toolbar.secondary { + --background: inherit; + --color: inherit; +} + ion-modal ion-content { background-color: rgba(255,255,255,.7) ;