dyomedea/src/db/rte.ts

136 lines
3.4 KiB
TypeScript

import { debounce, memoize, property, wrap } from 'lodash';
import { delay } from '../lib/delay';
import getUri from '../lib/ids';
import { appendToArray, putNewGpx } from './gpx';
import { getFamily, put } from './lib';
export const emptyRte: Rte = {
name: undefined,
cmt: undefined,
desc: undefined,
src: undefined,
link: undefined,
number: 0,
type: undefined,
extensions: undefined,
rtept: undefined,
};
export const putNewRte = async (id?: IdRte | IdGpx) => {
let finalId = { ...id };
if (!('rte' in finalId)) {
const gpxId = await putNewGpx(id);
finalId = { ...gpxId, rte: 0 };
}
const uri = getUri('rte', finalId);
await put(
uri,
'rte',
(rte) => {
return rte;
},
emptyRte
);
return finalId as IdRte;
};
// See https://stackoverflow.com/questions/28787436/debounce-a-function-with-argument
const debounceByParam = (
targetFunc: (params: any) => any,
resolver: (params: any) => any,
...debounceParams: any
) =>
wrap(
memoize(() => debounce(targetFunc, ...debounceParams), resolver),
(getMemoizedFunc, ...params) => getMemoizedFunc(...params)(...params)
);
const compactRteOrTrkseg = (params: any) => {
const { getDocs } = params;
getDocs(params).then(({ docs }) => {
if (docs.rows.length > 1) {
const compactedDocs = docs.rows.map((row: any) => {
if (['rte', 'trkseg'].includes(row.doc.type)) {
return row.doc;
}
return { ...row.doc, doc: undefined, _deleted: true };
});
db.bulkDocs(compactedDocs);
console.log({ caller: 'compactRteOrTrkseg', params, compactedDocs });
}
});
};
export const compactRteOrTrksegDebounced = debounceByParam(
compactRteOrTrkseg,
property('id'),
60 * 60 * 1000 * (1 + Math.random())
);
export const getRteDocs: ({
id,
}: {
id: string;
}) => Promise<{ docs: any; rte: Rte }> = async (params) => {
const { id } = params;
const docs = await getFamily(id, { include_docs: true });
console.log({ caller: 'getRteDocs', id, docs });
let target: any[];
let rte: Rte | undefined = undefined;
docs.rows.every((row: any) => {
// level 0
if (row.doc.type === 'rte') {
if (!!rte) {
console.error({
caller: 'getRte',
id,
row,
target,
rte,
});
return false; // Hack to stop if getFamily fails
}
target = [row.doc.doc];
rte = row.doc.doc;
}
//level 1
if (row.doc.type === 'rtept') {
target.splice(1);
// row.doc.doc.id = row.doc._id;
appendToArray(target.at(-1), row.doc.type, row.doc.doc);
target.push(row.doc.doc);
}
return true;
});
return { docs, rte };
};
export const getRte = async (params: any) => {
const { id } = params;
const { docs, rte } = await getRteDocs(params);
if (docs.rows.length > 1) {
compactRteOrTrksegDebounced({ id, getDocs: getRteDocs });
}
return rte;
};
export const putRte = async (params: any) => {
const { id, rte } = params;
await put(id, 'rte', (doc) => rte, rte);
return rte;
};
export const deleteRte = async (params: any) => {
const { id } = params;
const docs = await getFamily(id, { include_docs: false });
console.log({ caller: 'deleteRte' }, id, docs);
const deletedDocs = docs.rows.reverse().map((doc: any) => ({
_deleted: true,
_id: doc.id,
_rev: doc.value.rev,
}));
console.log({ caller: 'deleteRte' }, id, docs, deletedDocs);
await db.bulkDocs(deletedDocs);
return id;
};