First version with a GPX export (#9) that works in a browser but, unfortunately, doesn't wotk on Adroid !
This commit is contained in:
parent
93054115a5
commit
cb4a257003
|
@ -1,28 +1,39 @@
|
||||||
import { IonButton, IonIcon } from '@ionic/react';
|
import { IonButton, IonIcon } from '@ionic/react';
|
||||||
import { cloudDownload } from 'ionicons/icons';
|
import { cloudDownload } from 'ionicons/icons';
|
||||||
import React, { Fragment, useLayoutEffect, useRef, useState } from 'react';
|
import React, { Fragment, useRef } from 'react';
|
||||||
import { useDB } from 'react-pouchdb';
|
import { useDB } from 'react-pouchdb';
|
||||||
|
import { getGpxAsXmlString } from '../../db/gpx';
|
||||||
|
|
||||||
const GpxExport: React.FC<{ gpxId: string }> = (props: { gpxId: string }) => {
|
const GpxExport: React.FC<{ gpx: any }> = (props: { gpx: any }) => {
|
||||||
const db = useDB();
|
const db = useDB();
|
||||||
|
|
||||||
const hiddenLinkElement = useRef<HTMLAnchorElement>(null);
|
const hiddenLinkElement = useRef<HTMLAnchorElement>(null);
|
||||||
|
|
||||||
const [fileDownloadUrl, setFileDownloadUrl] = useState('');
|
var downloadName;
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
if (props.gpx.metadata.name !== undefined) {
|
||||||
if (fileDownloadUrl !== '') {
|
downloadName = props.gpx.metadata.name;
|
||||||
|
} else if (props.gpx.gpx.metadata.name !== undefined) {
|
||||||
|
downloadName = `${props.gpx.gpx.metadata.name}.gpx`;
|
||||||
|
} else {
|
||||||
|
downloadName = `track-${props.gpx.metadata.lastModified}.gpx`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const download = async (event: any) => {
|
||||||
|
event.preventDefault();
|
||||||
|
console.log('download()');
|
||||||
|
const gpxAsXml = await getGpxAsXmlString(db, props.gpx._id);
|
||||||
|
console.log(`gpxAsXml: ${gpxAsXml}`);
|
||||||
|
const blob = new Blob([gpxAsXml], {
|
||||||
|
type: 'application/gpx+xml',
|
||||||
|
});
|
||||||
|
const fileDownloadUrl = URL.createObjectURL(blob);
|
||||||
|
console.log(`fileDownloadUrl: ${fileDownloadUrl}`);
|
||||||
|
console.log(`hiddenLinkElement.current: ${hiddenLinkElement.current}`);
|
||||||
|
hiddenLinkElement.current!.href = fileDownloadUrl;
|
||||||
hiddenLinkElement.current?.click();
|
hiddenLinkElement.current?.click();
|
||||||
URL.revokeObjectURL(fileDownloadUrl);
|
URL.revokeObjectURL(fileDownloadUrl);
|
||||||
setFileDownloadUrl('');
|
hiddenLinkElement.current!.href = '';
|
||||||
}
|
|
||||||
}, [fileDownloadUrl]);
|
|
||||||
|
|
||||||
const download = (event: any) => {
|
|
||||||
event.preventDefault();
|
|
||||||
const blob = new Blob(['<gpx/>'], { type: 'application/gpx+xml' });
|
|
||||||
const fileDownloadUrl = URL.createObjectURL(blob);
|
|
||||||
setFileDownloadUrl(fileDownloadUrl);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -30,13 +41,8 @@ const GpxExport: React.FC<{ gpxId: string }> = (props: { gpxId: string }) => {
|
||||||
<IonButton id='gpx-export-button' onClick={download}>
|
<IonButton id='gpx-export-button' onClick={download}>
|
||||||
<IonIcon icon={cloudDownload} title='Export' slot='icon-only' />
|
<IonIcon icon={cloudDownload} title='Export' slot='icon-only' />
|
||||||
</IonButton>
|
</IonButton>
|
||||||
<a
|
<a className='hidden' download={downloadName} ref={hiddenLinkElement}>
|
||||||
className='hidden'
|
download
|
||||||
download={props.gpxId + '.gpx'}
|
|
||||||
href={fileDownloadUrl}
|
|
||||||
ref={hiddenLinkElement}
|
|
||||||
>
|
|
||||||
download it
|
|
||||||
</a>
|
</a>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
|
@ -58,7 +58,7 @@ const TrackBrowser: React.FC<{}> = () => {
|
||||||
<IonItem key={gpx._id}>
|
<IonItem key={gpx._id}>
|
||||||
<span>{gpx.gpx.metadata.time}</span>
|
<span>{gpx.gpx.metadata.time}</span>
|
||||||
<IonButtons slot='end'>
|
<IonButtons slot='end'>
|
||||||
<GpxExport gpxId={gpx._id} />
|
<GpxExport gpx={gpx} />
|
||||||
<IonButton
|
<IonButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteGps(db, { _id: gpx._id, _rev: gpx._rev });
|
deleteGps(db, { _id: gpx._id, _rev: gpx._rev });
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
string_to_bytes,
|
string_to_bytes,
|
||||||
bytes_to_base64,
|
bytes_to_base64,
|
||||||
} from '@openpgp/asmcrypto.js';
|
} from '@openpgp/asmcrypto.js';
|
||||||
import { documentSharp } from 'ionicons/icons';
|
import GPX from '../lib/gpx-parser-builder';
|
||||||
|
|
||||||
export const pushGpx = async (db: any, payload: any) => {
|
export const pushGpx = async (db: any, payload: any) => {
|
||||||
const gpxString = JSON.stringify(payload.gpx);
|
const gpxString = JSON.stringify(payload.gpx);
|
||||||
|
@ -170,3 +170,27 @@ export const deleteCurrent = async (db: any) => {
|
||||||
await deleteGps(db, docs[i]);
|
await deleteGps(db, docs[i]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getGpx = async (db: any, gpxId: string) => {
|
||||||
|
var gpxResult = await db.get(gpxId);
|
||||||
|
var gpx = gpxResult.gpx;
|
||||||
|
var trkptResults = await db.find({
|
||||||
|
selector: {
|
||||||
|
type: 'trkpt',
|
||||||
|
gpx: gpx._id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var trkpts = trkptResults.docs;
|
||||||
|
trkpts.sort((first: any, second: any) =>
|
||||||
|
first.trkpt.time.localeCompare(second.trkpt.time)
|
||||||
|
);
|
||||||
|
trkpts.map((trkptDoc: any) => {
|
||||||
|
gpx.trk[0].trkseg[0].trkpt.push(trkptDoc.trkpt);
|
||||||
|
});
|
||||||
|
return gpx;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGpxAsXmlString = async (db: any, gpxId: string) => {
|
||||||
|
const gpx = new GPX(await getGpx(db, gpxId));
|
||||||
|
return gpx.toString({});
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue