First version of notes, also allowing to delete wpts

This commit is contained in:
Eric van der Vlist 2023-03-06 16:45:20 +01:00
parent bf179a4b6d
commit adbbb3e356
7 changed files with 262 additions and 53 deletions

View File

@ -5,7 +5,7 @@ import { Geolocation } from '@awesome-cordova-plugins/geolocation';
import NoteIcon from '@suid/icons-material/Note'; import NoteIcon from '@suid/icons-material/Note';
import style from './Note.module.css'; import style from './Note.module.css';
import WptEditDialog from '../wpt/WptEditDialog'; import WptEditDialog, { Category } from '../wpt/WptEditDialog';
import { Coordinate } from 'ol/coordinate'; import { Coordinate } from 'ol/coordinate';
interface Props {} interface Props {}
@ -20,6 +20,38 @@ const Note: Component<Props> = (props) => {
setOpen(true); setOpen(true);
}; };
const initialWpt = () => {
const time = new Date().toISOString();
return {
$: { lat: 0, lon: 0 },
ele: undefined,
time: undefined,
magvar: undefined,
geoidheight: undefined,
name: undefined,
cmt: undefined,
desc: undefined,
src: undefined,
link: undefined,
sym: undefined,
type: undefined,
fix: undefined,
sat: undefined,
hdop: undefined,
vdop: undefined,
pdop: undefined,
ageofdgpsdata: undefined,
dgpsid: undefined,
extensions: {
address: undefined,
startTime: time,
endTime: time,
category: Category.NOTE,
subCategory: undefined,
},
};
};
return ( return (
<> <>
<div <div
@ -38,6 +70,7 @@ const Note: Component<Props> = (props) => {
open={open} open={open}
closeHandler={() => setOpen(false)} closeHandler={() => setOpen(false)}
coordinate={coordinate} coordinate={coordinate}
initialWpt={initialWpt}
/> />
</> </>
); );

View File

@ -78,11 +78,19 @@ export const Wpt: Component<Props> = ({
const features = new GeoJSON().readFeatures(geo); const features = new GeoJSON().readFeatures(geo);
console.log({ caller: 'Wpt', features }); console.log({ caller: 'Wpt', features });
const feature = vectorSources[0].getFeatureById(wptId); const feature0 = vectorSources[0].getFeatureById(wptId);
if (feature) { if (feature0) {
vectorSources[0].removeFeature(feature); vectorSources[0].removeFeature(feature0);
}
const feature1 = vectorSources[1].getFeatureById(wptId);
if (feature1) {
vectorSources[1].removeFeature(feature1);
}
if (wpt()?.extensions?.category === 'note') {
vectorSources[1].addFeatures(features);
} else {
vectorSources[0].addFeatures(features);
} }
vectorSources[0].addFeatures(features);
} }
}); });

View File

@ -1,4 +1,12 @@
import { Box, Button, Stack, TextField } from '@suid/material'; import {
Box,
Button,
FormControl,
InputLabel,
NativeSelect,
Stack,
TextField,
} from '@suid/material';
import { Component, createEffect, createSignal, Show } from 'solid-js'; import { Component, createEffect, createSignal, Show } from 'solid-js';
import { useI18n } from '@solid-primitives/i18n'; import { useI18n } from '@solid-primitives/i18n';
@ -15,21 +23,38 @@ import { emptyGpx } from '../../db/gpx';
import getUri from '../../lib/ids'; import getUri from '../../lib/ids';
import { Coordinate } from 'ol/coordinate'; import { Coordinate } from 'ol/coordinate';
export enum Category {
NOTE = 'note',
POI = 'poi',
UNKNOWN = '',
}
export enum SubCategory {
ACCOMMODATION = 'accommodation',
UNKNOWN = '',
}
interface Props { interface Props {
wptId?: string; wptId?: string;
coordinate?: () => Coordinate | undefined; coordinate?: () => Coordinate | undefined;
type?: string;
open: () => boolean; open: () => boolean;
closeHandler?: () => void; closeHandler?: () => void;
initialWpt: () => Wpt;
} }
const WptEditDialog: Component<Props> = (props) => { const WptEditDialog: Component<Props> = (props) => {
const { wptId, closeHandler, open, coordinate, type = 'poi' } = props; const {
wptId,
closeHandler,
open,
coordinate,
initialWpt = () => emptyWpt,
} = props;
const [t, { add, locale, dict }] = useI18n(); const [t, { add, locale, dict }] = useI18n();
const wpt = !!wptId const wpt = !!wptId
? peekCachedSignal({ id: wptId, method: 'getWpt' }) ? peekCachedSignal({ id: wptId, method: 'getWpt' })
: () => emptyWpt; : initialWpt;
const [gpxId, setGpxId] = !wptId const [gpxId, setGpxId] = !wptId
? createSignal<string>(currentGpxId()) ? createSignal<string>(currentGpxId())
@ -108,7 +133,7 @@ const WptEditDialog: Component<Props> = (props) => {
action: 'putWpt', action: 'putWpt',
params: { id: newWptId, wpt: editedWpt() }, params: { id: newWptId, wpt: editedWpt() },
}); });
setEditedWpt(undefined);
_closeHandler(); _closeHandler();
}; };
@ -119,6 +144,25 @@ const WptEditDialog: Component<Props> = (props) => {
return t('wpt'); return t('wpt');
}; };
// <TextField
// label={t('sym')}
// defaultValue={wpt()?.sym}
// onChange={(event: any, value: string) => {
// setEditedWpt({ ...editedWpt(), sym: value });
// }}
// />
// <TextField
// label={t('minZoom')}
// defaultValue={wpt()?.extensions?.['dyo:minZoom']?.toString()}
// onChange={(event: any, value: string) => {
// const extensions = {
// ...editedWpt()?.extensions,
// 'dyo:minZoom': parseFloat(value),
// };
// setEditedWpt({ ...editedWpt(), extensions });
// }}
// />
return ( return (
<Dialog <Dialog
open={open() && !!wpt()} open={open() && !!wpt()}
@ -148,24 +192,42 @@ const WptEditDialog: Component<Props> = (props) => {
setEditedWpt({ ...editedWpt(), name: value }); setEditedWpt({ ...editedWpt(), name: value });
}} }}
/> />
<TextField <FormControl>
label={t('sym')} <InputLabel variant='normal' htmlFor='category'>
defaultValue={wpt()?.sym} Type
onChange={(event: any, value: string) => { </InputLabel>
setEditedWpt({ ...editedWpt(), sym: value }); <NativeSelect
}} inputProps={{
/> name: 'category',
<TextField id: 'category',
label={t('minZoom')} }}
defaultValue={wpt()?.extensions?.['dyo:minZoom']?.toString()} onChange={(event: any, value: string) => {
onChange={(event: any, value: string) => { setEditedWpt({
const extensions = { ...editedWpt(),
...editedWpt()?.extensions, extensions: { ...editedWpt().extensions, category: value },
'dyo:minZoom': parseFloat(value), });
}; }}
setEditedWpt({ ...editedWpt(), extensions }); >
}} <option
/> value={Category.POI}
selected={editedWpt()?.extensions?.category === Category.POI}
>
Point d'intérêt
</option>
<option
value={Category.NOTE}
selected={editedWpt()?.extensions?.category === Category.NOTE}
>
Note
</option>
<option
value={undefined}
selected={!editedWpt()?.extensions?.category}
>
Autre
</option>
</NativeSelect>
</FormControl>
<TextField <TextField
label={t('gpxDesc')} label={t('gpxDesc')}
type='text' type='text'
@ -177,6 +239,34 @@ const WptEditDialog: Component<Props> = (props) => {
}} }}
InputProps={{ inputComponent: 'textarea' }} InputProps={{ inputComponent: 'textarea' }}
/> />
<TextField
label='Début'
type='datetime-local'
value={(editedWpt()?.extensions?.startTime || '').replace('Z', '')}
onChange={(event: any) => {
setEditedWpt({
...editedWpt(),
extensions: {
...editedWpt().extensions,
startTime: new Date(event.target.value).toISOString(),
},
});
}}
/>
<TextField
label='Fin'
type='datetime-local'
value={(editedWpt()?.extensions?.endTime || '').replace('Z', '')}
onChange={(event: any) => {
setEditedWpt({
...editedWpt(),
extensions: {
...editedWpt().extensions,
endTime: new Date(event.target.value).toISOString(),
},
});
}}
/>
<Stack spacing={2} direction='row' sx={{ marginTop: '20px' }}> <Stack spacing={2} direction='row' sx={{ marginTop: '20px' }}>
<Button variant='outlined' color='secondary' onClick={closeHandler}> <Button variant='outlined' color='secondary' onClick={closeHandler}>
{t('cancel')} {t('cancel')}

View File

@ -1,32 +1,106 @@
import { Component } from 'solid-js'; import { Button, IconButton } from '@suid/material';
import { Component, createSignal } from 'solid-js';
import WptIcon from '../../icons/location-pin-svgrepo-com.svg?component-solid'; import WptIcon from '../../icons/location-pin-svgrepo-com.svg?component-solid';
import { peekCachedSignal } from '../../workers/cached-signals'; import { peekCachedSignal } from '../../workers/cached-signals';
import dispatch from '../../workers/dispatcher-main';
import { useI18n } from '@solid-primitives/i18n';
import Tree from '../tree'; import Tree from '../tree';
import WptEditButtonAndDialog from './WptEditButtonAndDialog'; import WptEditButtonAndDialog from './WptEditButtonAndDialog';
import DeleteIcon from '@suid/icons-material/Delete';
import QuestionMarkIcon from '@suid/icons-material/QuestionMark';
import Alert from '../alert';
interface Props { interface Props {
wptId: string; wptId: string;
} }
const WptViewer: Component<Props> = ({ wptId }) => { const WptViewer: Component<Props> = ({ wptId }) => {
const [t, { add, locale, dict }] = useI18n();
const wpt = peekCachedSignal({ id: wptId, method: 'getWpt' }); const wpt = peekCachedSignal({ id: wptId, method: 'getWpt' });
console.log({ caller: 'WptViewer', wptId, wpt: wpt() }); console.log({ caller: 'WptViewer', wptId, wpt: wpt() });
const title = () => { const title = () => {
return wpt().name; return wpt().name;
}; };
const [confirmDelete, setConfirmDelete] = createSignal(false);
const confirmDeleteHandler = () => {
console.log({
caller: 'WptViewer / confirmDeleteHandler',
wptId,
});
setConfirmDelete(true);
};
const deleteHandler = () => {
console.log({
caller: 'WptViewer / deleteHandler',
wptId,
});
dispatch({ action: 'deleteWpt', params: { id: wptId } });
setConfirmDelete(false);
};
const cancelDeleteHandler = () => {
console.log({
caller: 'WptViewer / cancelDeleteHandler',
wptId,
});
setConfirmDelete(false);
};
return ( return (
<Tree <>
title={ <Tree
<> title={
<WptIcon fill='black' width='24' height='24' viewBox='0 0 300 300' /> <>
{title()} <WptIcon
<WptEditButtonAndDialog wptId={wptId} /> fill='black'
</> width='24'
} height='24'
content={undefined} viewBox='0 0 300 300'
subTree={undefined} />
/> {title()}
<WptEditButtonAndDialog wptId={wptId} />
<div>
<IconButton onClick={confirmDeleteHandler}>
<DeleteIcon fill='white' />
</IconButton>
</div>
</>
}
content={undefined}
subTree={undefined}
/>
<Alert
open={confirmDelete}
closeHandler={cancelDeleteHandler}
severity='warning'
icon={<QuestionMarkIcon />}
action={
<>
<Button
color='error'
variant='contained'
size='small'
onClick={deleteHandler}
>
{t('yes')}
</Button>
<Button
color='primary'
variant='outlined'
size='small'
onclick={cancelDeleteHandler}
>
{t('no')}
</Button>
</>
}
>
{t('deleteTrack')}
</Alert>
</>
); );
}; };

11
src/db/types.d.ts vendored
View File

@ -42,17 +42,6 @@ interface Bounds_ {
maxlon: number; maxlon: number;
} }
export enum Category {
NOTE = 'note',
POI = 'poi',
UNKNOWN = '',
}
export enum SubCategory {
ACCOMMODATION = 'accommodation',
UNKNOWN = '',
}
interface Extensions { interface Extensions {
'dyo:speed'?: number; 'dyo:speed'?: number;
'dyo:course'?: number; 'dyo:course'?: number;

View File

@ -1,6 +1,6 @@
import getUri from '../lib/ids'; import getUri from '../lib/ids';
import { putNewGpx } from './gpx'; import { putNewGpx } from './gpx';
import { get, put } from './lib'; import { get, getFamily, put } from './lib';
export const emptyWpt: Wpt = { export const emptyWpt: Wpt = {
$: { lat: 0, lon: 0 }, $: { lat: 0, lon: 0 },
@ -62,3 +62,17 @@ export const getWpt = async (params: any) => {
const doc = await get(id); const doc = await get(id);
return doc.doc; return doc.doc;
}; };
export const deleteWpt = async (params: any) => {
const { id } = params;
const docs = await getFamily(id, { include_docs: false });
console.log({ caller: 'deleteWpt' }, id, docs);
const deletedDocs = docs.rows.reverse().map((doc: any) => ({
_deleted: true,
_id: doc.id,
_rev: doc.value.rev,
}));
console.log({ caller: 'deleteWpt' }, id, docs, deletedDocs);
await db.bulkDocs(deletedDocs);
return id;
};

View File

@ -26,7 +26,7 @@ import { getState, setState } from '../db/state';
import { deleteTrk, getTrk, putNewTrk } from '../db/trk'; import { deleteTrk, getTrk, putNewTrk } from '../db/trk';
import { putTrkpt } from '../db/trkpt'; import { putTrkpt } from '../db/trkpt';
import { getTrkseg, appendTrkpt } from '../db/trkseg'; import { getTrkseg, appendTrkpt } from '../db/trkseg';
import { getWpt, putWpt } from '../db/wpt'; import { getWpt, putWpt, deleteWpt } from '../db/wpt';
import { until } from '../lib/async-wait'; import { until } from '../lib/async-wait';
//const self = globalThis as unknown as WorkerGlobalScope; //const self = globalThis as unknown as WorkerGlobalScope;
@ -57,6 +57,7 @@ onmessage = async function (e) {
deleteTrk, deleteTrk,
deleteRte, deleteRte,
deleteWpt,
getState, getState,
setState, setState,