incyclist-services 1.3.28 → 1.3.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/base/cardlist/index.d.ts +1 -0
- package/lib/base/service.js +1 -1
- package/lib/routes/base/api/index.d.ts +1 -0
- package/lib/routes/base/api/index.js +3 -0
- package/lib/routes/base/parsers/utils.js +1 -1
- package/lib/routes/list/cards/ActiveImportCard.js +2 -2
- package/lib/routes/list/cards/FreeRideCard.d.ts +1 -0
- package/lib/routes/list/cards/FreeRideCard.js +3 -0
- package/lib/routes/list/cards/RouteCard.js +3 -1
- package/lib/routes/list/cards/base.d.ts +1 -0
- package/lib/routes/list/cards/base.js +3 -0
- package/lib/routes/list/loaders/api.e2e.test copy.d.ts +1 -0
- package/lib/routes/list/loaders/api.e2e.test copy.js +69 -0
- package/lib/routes/list/loaders/api.js +1 -0
- package/lib/routes/list/loaders/db.d.ts +1 -2
- package/lib/routes/list/loaders/db.js +35 -25
- package/lib/routes/list/loaders/db_new.d.ts +44 -0
- package/lib/routes/list/loaders/db_new.js +349 -0
- package/lib/routes/list/loaders/db_old.d.ts +44 -0
- package/lib/routes/list/loaders/db_old.js +340 -0
- package/lib/routes/list/service.d.ts +2 -1
- package/lib/routes/list/service.js +37 -10
- package/lib/routes/list/types.d.ts +4 -0
- package/lib/workouts/list/cards/base.d.ts +1 -0
- package/lib/workouts/list/cards/base.js +3 -0
- package/package.json +1 -1
package/lib/base/service.js
CHANGED
|
@@ -24,7 +24,7 @@ class IncyclistService extends events_1.default {
|
|
|
24
24
|
}
|
|
25
25
|
logError(err, fn, args) {
|
|
26
26
|
const logInfo = args || {};
|
|
27
|
-
this.
|
|
27
|
+
this.logEvent(Object.assign(Object.assign({ message: 'Error', fn }, logInfo), { error: err.message, stack: err.stack }));
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
exports.IncyclistService = IncyclistService;
|
|
@@ -77,7 +77,7 @@ const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
|
|
|
77
77
|
return referenced.url;
|
|
78
78
|
}
|
|
79
79
|
if (!referenced.file) {
|
|
80
|
-
|
|
80
|
+
return;
|
|
81
81
|
}
|
|
82
82
|
let targetFileName = referenced.file;
|
|
83
83
|
const regex = /([\\/])/g;
|
|
@@ -22,7 +22,7 @@ class ActiveImportCard extends base_1.BaseCard {
|
|
|
22
22
|
const list = (0, service_1.useRouteList)().getLists().find(l => l.getId() === 'myRoutes');
|
|
23
23
|
if (list) {
|
|
24
24
|
list.remove(this);
|
|
25
|
-
(0, service_1.useRouteList)().emitLists('updated');
|
|
25
|
+
(0, service_1.useRouteList)().emitLists('updated', true);
|
|
26
26
|
}
|
|
27
27
|
return observer_1.PromiseObserver.alwaysReturning(true);
|
|
28
28
|
}
|
|
@@ -49,7 +49,7 @@ class ActiveImportCard extends base_1.BaseCard {
|
|
|
49
49
|
return types_1.DEFAULT_FILTERS;
|
|
50
50
|
}
|
|
51
51
|
getTitle() {
|
|
52
|
-
return types_1.DEFAULT_TITLE
|
|
52
|
+
return this.error ? `${types_1.DEFAULT_TITLE}:${this.getId()}:${this.error}` : `${types_1.DEFAULT_TITLE}:${this.getId()}`;
|
|
53
53
|
}
|
|
54
54
|
getDisplayProperties() {
|
|
55
55
|
const name = this.file.name;
|
|
@@ -28,6 +28,7 @@ export declare class FreeRideCard extends BaseCard implements Card<Route> {
|
|
|
28
28
|
getDisplayProperties(): FreeRideDisplayProps;
|
|
29
29
|
getPosition(): LatLng;
|
|
30
30
|
getData(): Route;
|
|
31
|
+
getTitle(): string;
|
|
31
32
|
setData(_data: Route): void;
|
|
32
33
|
openSettings(): FreeRideSettings;
|
|
33
34
|
changeSettings(props: FreeRideSettings): void;
|
|
@@ -251,6 +251,7 @@ class RouteCard extends base_1.BaseCard {
|
|
|
251
251
|
}
|
|
252
252
|
delete() {
|
|
253
253
|
try {
|
|
254
|
+
this.logger.logEvent({ message: 'delete card', card: this.getTitle() });
|
|
254
255
|
const service = (0, service_1.getRouteList)();
|
|
255
256
|
service.unselectCard(this);
|
|
256
257
|
if (this.deleteObserver)
|
|
@@ -290,7 +291,8 @@ class RouteCard extends base_1.BaseCard {
|
|
|
290
291
|
}
|
|
291
292
|
this.deleteFromUIList();
|
|
292
293
|
this.deleteRouteUserSettings();
|
|
293
|
-
(
|
|
294
|
+
this.logger.logEvent({ message: 'card deleted', card: this.getTitle() });
|
|
295
|
+
(0, service_1.getRouteList)().emitLists('updated', true);
|
|
294
296
|
deleted = true;
|
|
295
297
|
}
|
|
296
298
|
catch (err) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const api_1 = __importDefault(require("../../base/api"));
|
|
16
|
+
const consts_1 = require("../../base/api/consts");
|
|
17
|
+
const api_2 = require("./api");
|
|
18
|
+
const service_1 = require("../service");
|
|
19
|
+
const utils_1 = require("../../../utils");
|
|
20
|
+
describe('RouteApiLoader', () => {
|
|
21
|
+
describe('load', () => {
|
|
22
|
+
test('e2e test - should only be executed manually', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
|
+
var _a;
|
|
24
|
+
const api = api_1.default.getInstance();
|
|
25
|
+
api['getBaseUrl'] = jest.fn().mockReturnValue(consts_1.DEFAULT_ROUTE_API);
|
|
26
|
+
const list = new service_1.RouteListService();
|
|
27
|
+
list.createPreview = jest.fn().mockResolvedValue(true);
|
|
28
|
+
const loader = new api_2.RoutesApiLoader();
|
|
29
|
+
const routes = [];
|
|
30
|
+
const run = () => new Promise(done => {
|
|
31
|
+
let added = 0;
|
|
32
|
+
let updated = 0;
|
|
33
|
+
const observer = loader.load();
|
|
34
|
+
observer.on('route.added', route => {
|
|
35
|
+
routes.push(route);
|
|
36
|
+
added++;
|
|
37
|
+
});
|
|
38
|
+
observer.on('route.updated', route => {
|
|
39
|
+
updated++;
|
|
40
|
+
});
|
|
41
|
+
observer.on('done', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
console.log('added:', added);
|
|
43
|
+
console.log('updated:', updated);
|
|
44
|
+
yield (0, utils_1.waitNextTick)();
|
|
45
|
+
done({ added, updated });
|
|
46
|
+
}));
|
|
47
|
+
});
|
|
48
|
+
const run1 = yield run();
|
|
49
|
+
console.log(run1);
|
|
50
|
+
expect(routes.length).toBe(14);
|
|
51
|
+
expect(run1.updated).toBe(6);
|
|
52
|
+
const existing = Array.from(routes);
|
|
53
|
+
api['getRouteDescriptionFromDB'] = jest.fn((id) => existing.find(r => r.description.id === id));
|
|
54
|
+
const run2 = yield run();
|
|
55
|
+
expect(routes.length).toBe(14);
|
|
56
|
+
expect(run2.updated).toBe(0);
|
|
57
|
+
expect(run2.added).toBe(0);
|
|
58
|
+
const updateTarget = existing.find(r => { var _a; return (_a = r.description.version) !== null && _a !== void 0 ? _a : 0 > 1; });
|
|
59
|
+
if (updateTarget) {
|
|
60
|
+
const prevVersion = (_a = updateTarget.description.version) !== null && _a !== void 0 ? _a : 1;
|
|
61
|
+
updateTarget.description.version = prevVersion - 1;
|
|
62
|
+
const run3 = yield run();
|
|
63
|
+
expect(routes.length).toBe(14);
|
|
64
|
+
expect(run3.updated).toBe(1);
|
|
65
|
+
expect(run3.added).toBe(0);
|
|
66
|
+
}
|
|
67
|
+
}), 20000);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -39,6 +39,5 @@ export declare class RoutesDbLoader extends DBLoader<RouteInfoDBEntry> {
|
|
|
39
39
|
protected getRoutesRepo(): JsonRepository;
|
|
40
40
|
protected getDetailsRepo(route: Route): JsonRepository;
|
|
41
41
|
protected writeDetails(route: Route): Promise<void>;
|
|
42
|
-
protected
|
|
43
|
-
protected _deleteRoute(id: any): Promise<void>;
|
|
42
|
+
protected deleteRouteDetails(route: Route): Promise<void>;
|
|
44
43
|
}
|
|
@@ -46,6 +46,9 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
|
|
|
46
46
|
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
47
47
|
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
48
48
|
};
|
|
49
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
50
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
|
+
};
|
|
49
52
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
53
|
exports.RoutesDbLoader = void 0;
|
|
51
54
|
const api_1 = require("../../../api");
|
|
@@ -55,6 +58,7 @@ const LegacyDB_1 = require("./LegacyDB");
|
|
|
55
58
|
const DBLoader_1 = require("./DBLoader");
|
|
56
59
|
const utils_1 = require("../../../utils");
|
|
57
60
|
const route_1 = require("../../base/utils/route");
|
|
61
|
+
const clone_1 = __importDefault(require("../../../utils/clone"));
|
|
58
62
|
let RoutesDbLoader = (() => {
|
|
59
63
|
let _classDecorators = [types_1.Singleton];
|
|
60
64
|
let _classDescriptor;
|
|
@@ -74,15 +78,20 @@ let RoutesDbLoader = (() => {
|
|
|
74
78
|
return JSON.stringify(json);
|
|
75
79
|
}
|
|
76
80
|
catch (_a) { } };
|
|
77
|
-
|
|
81
|
+
const updatedDescr = this.buildRouteDBInfo(route.description);
|
|
78
82
|
const idx = this.routeDescriptions.findIndex(d => d.id === route.description.id);
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
let changed = false;
|
|
84
|
+
if (idx === -1) {
|
|
85
|
+
this.routeDescriptions.push((0, clone_1.default)(updatedDescr));
|
|
86
|
+
changed = true;
|
|
87
|
+
}
|
|
81
88
|
else {
|
|
82
|
-
prev = stringify(this.
|
|
83
|
-
|
|
89
|
+
const prev = stringify(this.routeDescriptions[idx]);
|
|
90
|
+
const updated = stringify(updatedDescr);
|
|
91
|
+
changed = (prev !== updated);
|
|
92
|
+
if (changed)
|
|
93
|
+
this.routeDescriptions[idx] = (0, clone_1.default)(updatedDescr);
|
|
84
94
|
}
|
|
85
|
-
const changed = !prev || stringify(this.buildRouteDBInfo(this.routeDescriptions[idx])) !== prev;
|
|
86
95
|
if (changed) {
|
|
87
96
|
this.isDirty = true;
|
|
88
97
|
this.write();
|
|
@@ -103,8 +112,8 @@ let RoutesDbLoader = (() => {
|
|
|
103
112
|
throw new Error('route not found');
|
|
104
113
|
this.routeDescriptions.splice(idx, 1);
|
|
105
114
|
this.isDirty = true;
|
|
106
|
-
this.
|
|
107
|
-
yield this.
|
|
115
|
+
yield this.writeRepo();
|
|
116
|
+
yield this.deleteRouteDetails(route);
|
|
108
117
|
});
|
|
109
118
|
}
|
|
110
119
|
getDescription(id) {
|
|
@@ -127,6 +136,7 @@ let RoutesDbLoader = (() => {
|
|
|
127
136
|
const save = () => __awaiter(this, void 0, void 0, function* () {
|
|
128
137
|
try {
|
|
129
138
|
yield this.routesRepo.write('db', this.routeDescriptions.map(this.buildRouteDBInfo.bind(this)));
|
|
139
|
+
this.isDirty = false;
|
|
130
140
|
}
|
|
131
141
|
catch (err) {
|
|
132
142
|
this.logger.logEvent({ message: 'could not safe repo', error: err.message });
|
|
@@ -302,29 +312,29 @@ let RoutesDbLoader = (() => {
|
|
|
302
312
|
process.nextTick(() => { delete this.routesSaveObserver[id]; });
|
|
303
313
|
});
|
|
304
314
|
}
|
|
305
|
-
|
|
315
|
+
deleteRouteDetails(route) {
|
|
306
316
|
return __awaiter(this, void 0, void 0, function* () {
|
|
307
|
-
|
|
317
|
+
const repo = this.getDetailsRepo(route);
|
|
318
|
+
if (!repo)
|
|
308
319
|
return;
|
|
320
|
+
const id = route.description.id;
|
|
309
321
|
if (this.routesSaveObserver[id])
|
|
310
|
-
|
|
311
|
-
|
|
322
|
+
yield this.routesSaveObserver[id].wait();
|
|
323
|
+
const _deleteRoute = (id) => __awaiter(this, void 0, void 0, function* () {
|
|
324
|
+
yield (0, utils_1.waitNextTick)();
|
|
325
|
+
try {
|
|
326
|
+
yield repo.delete(id);
|
|
327
|
+
(0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
|
|
328
|
+
}
|
|
329
|
+
catch (err) {
|
|
330
|
+
(0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
|
|
331
|
+
throw err;
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
this.routesSaveObserver[id] = new observer_1.PromiseObserver(_deleteRoute(id));
|
|
312
335
|
yield this.routesSaveObserver[id].start();
|
|
313
336
|
});
|
|
314
337
|
}
|
|
315
|
-
_deleteRoute(id) {
|
|
316
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
317
|
-
yield (0, utils_1.waitNextTick)();
|
|
318
|
-
try {
|
|
319
|
-
yield this.routesRepo.delete(id);
|
|
320
|
-
(0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
|
|
321
|
-
}
|
|
322
|
-
catch (err) {
|
|
323
|
-
(0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
|
|
324
|
-
throw err;
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
338
|
};
|
|
329
339
|
__setFunctionName(_classThis, "RoutesDbLoader");
|
|
330
340
|
(() => {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { JsonRepository } from "../../../api";
|
|
2
|
+
import { Observer, PromiseObserver } from "../../../base/types/observer";
|
|
3
|
+
import { Route } from "../../base/model/route";
|
|
4
|
+
import { RouteInfo } from "../../base/types";
|
|
5
|
+
import { RoutesLegacyDbLoader } from "./LegacyDB";
|
|
6
|
+
import { RouteInfoDBEntry } from "./types";
|
|
7
|
+
import { DBLoader } from "./DBLoader";
|
|
8
|
+
import { RouteApiDetail } from "../../base/api/types";
|
|
9
|
+
export declare class RoutesDbLoader extends DBLoader<RouteInfoDBEntry> {
|
|
10
|
+
protected loadObserver: Observer;
|
|
11
|
+
protected saveObserver: PromiseObserver<void>;
|
|
12
|
+
protected videosRepo: JsonRepository;
|
|
13
|
+
protected routesRepo: JsonRepository;
|
|
14
|
+
protected routeDescriptions: Array<RouteInfoDBEntry>;
|
|
15
|
+
protected tsLastWrite: number;
|
|
16
|
+
protected isDirty: boolean;
|
|
17
|
+
protected routesSaveObserver: {
|
|
18
|
+
[index: string]: PromiseObserver<void>;
|
|
19
|
+
};
|
|
20
|
+
constructor();
|
|
21
|
+
save(route: Route, enforcedWriteDetails?: boolean): Promise<void>;
|
|
22
|
+
delete(route: Route): Promise<void>;
|
|
23
|
+
getDescription(id: string): RouteInfo;
|
|
24
|
+
getDetails(id: string): Promise<RouteApiDetail>;
|
|
25
|
+
protected writeRepo(): Promise<void>;
|
|
26
|
+
protected write(): void;
|
|
27
|
+
protected getLegacyLoader(): RoutesLegacyDbLoader;
|
|
28
|
+
protected buildRouteInfo(descr: RouteInfoDBEntry): RouteInfo;
|
|
29
|
+
protected buildRouteDBInfo(descr: RouteInfo): RouteInfoDBEntry;
|
|
30
|
+
protected loadFromLegacy(): Promise<Array<RouteInfoDBEntry>>;
|
|
31
|
+
protected loadDescriptions(): Promise<Array<RouteInfoDBEntry>>;
|
|
32
|
+
protected removeDuplicates(routes: Array<RouteInfoDBEntry>): any[];
|
|
33
|
+
protected verifyImportDate(routes: Array<RouteInfoDBEntry>): void;
|
|
34
|
+
protected loadDetailRecord(target: Route | RouteInfo): Promise<RouteApiDetail>;
|
|
35
|
+
private validateDetails;
|
|
36
|
+
private validateDescription;
|
|
37
|
+
protected loadDetails(route: Route, alreadyAdded?: boolean): Promise<void>;
|
|
38
|
+
protected getVideoRepo(): JsonRepository;
|
|
39
|
+
protected getRoutesRepo(): JsonRepository;
|
|
40
|
+
protected getDetailsRepo(route: Route): JsonRepository;
|
|
41
|
+
protected writeDetails(route: Route): Promise<void>;
|
|
42
|
+
protected deleteRoute(id: string): Promise<PromiseObserver<void>>;
|
|
43
|
+
protected _deleteRoute(id: any): Promise<void>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
+
var _, done = false;
|
|
8
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
+
var context = {};
|
|
10
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
+
if (kind === "accessor") {
|
|
15
|
+
if (result === void 0) continue;
|
|
16
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
+
}
|
|
21
|
+
else if (_ = accept(result)) {
|
|
22
|
+
if (kind === "field") initializers.unshift(_);
|
|
23
|
+
else descriptor[key] = _;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
+
done = true;
|
|
28
|
+
};
|
|
29
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
+
var useValue = arguments.length > 2;
|
|
31
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
+
}
|
|
34
|
+
return useValue ? value : void 0;
|
|
35
|
+
};
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
|
46
|
+
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
47
|
+
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
48
|
+
};
|
|
49
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
50
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
|
+
};
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
exports.RoutesDbLoader = void 0;
|
|
54
|
+
const api_1 = require("../../../api");
|
|
55
|
+
const types_1 = require("../../../base/types");
|
|
56
|
+
const observer_1 = require("../../../base/types/observer");
|
|
57
|
+
const LegacyDB_1 = require("./LegacyDB");
|
|
58
|
+
const DBLoader_1 = require("./DBLoader");
|
|
59
|
+
const utils_1 = require("../../../utils");
|
|
60
|
+
const route_1 = require("../../base/utils/route");
|
|
61
|
+
const clone_1 = __importDefault(require("../../../utils/clone"));
|
|
62
|
+
let RoutesDbLoader = (() => {
|
|
63
|
+
let _classDecorators = [types_1.Singleton];
|
|
64
|
+
let _classDescriptor;
|
|
65
|
+
let _classExtraInitializers = [];
|
|
66
|
+
let _classThis;
|
|
67
|
+
let _classSuper = DBLoader_1.DBLoader;
|
|
68
|
+
var RoutesDbLoader = _classThis = class extends _classSuper {
|
|
69
|
+
constructor() {
|
|
70
|
+
super();
|
|
71
|
+
this.routesSaveObserver = {};
|
|
72
|
+
this.routeDescriptions = [];
|
|
73
|
+
this.isDirty = true;
|
|
74
|
+
}
|
|
75
|
+
save(route_2) {
|
|
76
|
+
return __awaiter(this, arguments, void 0, function* (route, enforcedWriteDetails = false) {
|
|
77
|
+
const stringify = (json) => { try {
|
|
78
|
+
return JSON.stringify(json);
|
|
79
|
+
}
|
|
80
|
+
catch (_a) { } };
|
|
81
|
+
const updatedDescr = this.buildRouteDBInfo(route.description);
|
|
82
|
+
const idx = this.routeDescriptions.findIndex(d => d.id === route.description.id);
|
|
83
|
+
let changed = false;
|
|
84
|
+
if (idx === -1) {
|
|
85
|
+
this.routeDescriptions.push((0, clone_1.default)(updatedDescr));
|
|
86
|
+
changed = true;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const prev = stringify(this.routeDescriptions[idx]);
|
|
90
|
+
const updated = stringify(updatedDescr);
|
|
91
|
+
changed = (prev !== updated);
|
|
92
|
+
if (changed)
|
|
93
|
+
this.routeDescriptions[idx] = (0, clone_1.default)(updatedDescr);
|
|
94
|
+
}
|
|
95
|
+
if (changed) {
|
|
96
|
+
this.isDirty = true;
|
|
97
|
+
this.write();
|
|
98
|
+
this.writeDetails(route);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const details = yield this.loadDetailRecord(route);
|
|
102
|
+
if (!details || enforcedWriteDetails)
|
|
103
|
+
this.writeDetails(route);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
delete(route) {
|
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
const id = route.description.id;
|
|
110
|
+
const idx = this.routeDescriptions.findIndex(d => d.id === id);
|
|
111
|
+
if (idx == -1)
|
|
112
|
+
throw new Error('route not found');
|
|
113
|
+
this.routeDescriptions.splice(idx, 1);
|
|
114
|
+
this.isDirty = true;
|
|
115
|
+
this.write();
|
|
116
|
+
yield this.deleteRoute(id);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
getDescription(id) {
|
|
120
|
+
const descr = this.routeDescriptions.find(d => d.id === id);
|
|
121
|
+
return descr;
|
|
122
|
+
}
|
|
123
|
+
getDetails(id) {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
const descr = this.routeDescriptions.find(d => d.id === id);
|
|
126
|
+
if (!descr)
|
|
127
|
+
return;
|
|
128
|
+
const details = yield this.loadDetailRecord(descr);
|
|
129
|
+
return details;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
writeRepo() {
|
|
133
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
134
|
+
if (this.saveObserver)
|
|
135
|
+
yield this.saveObserver.wait();
|
|
136
|
+
const save = () => __awaiter(this, void 0, void 0, function* () {
|
|
137
|
+
try {
|
|
138
|
+
yield this.routesRepo.write('db', this.routeDescriptions.map(this.buildRouteDBInfo.bind(this)));
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
this.logger.logEvent({ message: 'could not safe repo', error: err.message });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
this.saveObserver = new observer_1.PromiseObserver(save());
|
|
145
|
+
yield this.saveObserver.start();
|
|
146
|
+
process.nextTick(() => { delete this.saveObserver; });
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
write() {
|
|
150
|
+
if (this.isDirty && (this.tsLastWrite === undefined || Date.now() - this.tsLastWrite >= 1000)) {
|
|
151
|
+
this.isDirty = false;
|
|
152
|
+
this.tsLastWrite = Date.now();
|
|
153
|
+
this.writeRepo();
|
|
154
|
+
}
|
|
155
|
+
if (this.isDirty && Date.now() - this.tsLastWrite < 1000) {
|
|
156
|
+
setTimeout(() => { this.write(); }, this.tsLastWrite + 1000 - Date.now());
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
getLegacyLoader() {
|
|
160
|
+
return new LegacyDB_1.RoutesLegacyDbLoader();
|
|
161
|
+
}
|
|
162
|
+
buildRouteInfo(descr) {
|
|
163
|
+
return descr;
|
|
164
|
+
}
|
|
165
|
+
buildRouteDBInfo(descr) {
|
|
166
|
+
const data = Object.assign({}, descr);
|
|
167
|
+
delete data.points;
|
|
168
|
+
return data;
|
|
169
|
+
}
|
|
170
|
+
loadFromLegacy() {
|
|
171
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
172
|
+
return new Promise(done => {
|
|
173
|
+
const observer = this.getLegacyLoader().load();
|
|
174
|
+
const descriptions = [];
|
|
175
|
+
observer.on('route.added', (r) => { descriptions.push(r.description); });
|
|
176
|
+
observer.on('done', () => done(descriptions));
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
loadDescriptions() {
|
|
181
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
+
const descriptions = yield this.getRoutesRepo().read('db');
|
|
183
|
+
if (descriptions) {
|
|
184
|
+
const routes = descriptions;
|
|
185
|
+
const cleaned = this.removeDuplicates(routes);
|
|
186
|
+
return cleaned;
|
|
187
|
+
}
|
|
188
|
+
const legacy = yield this.loadFromLegacy();
|
|
189
|
+
return legacy;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
removeDuplicates(routes) {
|
|
193
|
+
const ids = routes.map(r => r.legacyId || r.id);
|
|
194
|
+
const uniqueIds = ids.filter((d, pos) => ids.indexOf(d) === pos);
|
|
195
|
+
const cleaned = [];
|
|
196
|
+
uniqueIds.forEach(id => {
|
|
197
|
+
const routeWithLegacy = routes.find(r => r.legacyId === id);
|
|
198
|
+
const route = routeWithLegacy || routes.find(r => r.id === id);
|
|
199
|
+
if (route)
|
|
200
|
+
cleaned.push(route);
|
|
201
|
+
});
|
|
202
|
+
return cleaned;
|
|
203
|
+
}
|
|
204
|
+
verifyImportDate(routes) {
|
|
205
|
+
let changed = false;
|
|
206
|
+
routes.forEach(r => {
|
|
207
|
+
if (!r.tsImported) {
|
|
208
|
+
r.tsImported = Date.now();
|
|
209
|
+
changed = true;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
if (changed) {
|
|
213
|
+
this.isDirty = true;
|
|
214
|
+
this.write();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
loadDetailRecord(target) {
|
|
218
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
219
|
+
var _a;
|
|
220
|
+
const description = (target.description || target);
|
|
221
|
+
const repo = (description === null || description === void 0 ? void 0 : description.hasVideo) ? this.getVideosRepo() : this.getRoutesRepo();
|
|
222
|
+
let details;
|
|
223
|
+
try {
|
|
224
|
+
const id = description.legacyId || description.id;
|
|
225
|
+
details = (yield repo.read(id));
|
|
226
|
+
description.originalName = details.title;
|
|
227
|
+
if (description.hasVideo) {
|
|
228
|
+
description.segments = ((_a = details.video) === null || _a === void 0 ? void 0 : _a.selectableSegments) || (details === null || details === void 0 ? void 0 : details.selectableSegments);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
this.logger.logEvent({ message: 'could not load route details', id: (description === null || description === void 0 ? void 0 : description.legacyId) || (description === null || description === void 0 ? void 0 : description.id), title: description === null || description === void 0 ? void 0 : description.title, reason: err.message, stack: err.stack });
|
|
233
|
+
if (description.id && description.legacyId !== description.id) {
|
|
234
|
+
try {
|
|
235
|
+
details = (yield repo.read(description.id));
|
|
236
|
+
description.originalName = details.title;
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
this.logger.logEvent({ message: 'could not load route details', id: (description === null || description === void 0 ? void 0 : description.legacyId) || (description === null || description === void 0 ? void 0 : description.id), title: description === null || description === void 0 ? void 0 : description.title, reason: err.message, stack: err.stack });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (!details) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
this.validateDetails(details);
|
|
247
|
+
this.validateDescription(description, details);
|
|
248
|
+
return details;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
validateDetails(details) {
|
|
252
|
+
if (!details.points || !Array.isArray(details.points)) {
|
|
253
|
+
details.points = details.decoded;
|
|
254
|
+
delete details.decoded;
|
|
255
|
+
}
|
|
256
|
+
if (details.localizedTitle && typeof (details.localizedTitle) === 'string') {
|
|
257
|
+
details.localizedTitle = { en: details.localizedTitle };
|
|
258
|
+
}
|
|
259
|
+
(0, route_1.validateDistance)(details.points);
|
|
260
|
+
(0, route_1.updateSlopes)(details.points);
|
|
261
|
+
}
|
|
262
|
+
validateDescription(description, details) {
|
|
263
|
+
if (!description.elevation) {
|
|
264
|
+
description.elevation = (0, route_1.getTotalElevation)(details);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
loadDetails(route, alreadyAdded) {
|
|
268
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
269
|
+
const details = yield this.loadDetailRecord(route);
|
|
270
|
+
(0, route_1.addDetails)(route, details);
|
|
271
|
+
this.verifyRouteHash(route);
|
|
272
|
+
if (alreadyAdded)
|
|
273
|
+
this.emitRouteUpdate(route);
|
|
274
|
+
this.emitRouteAdded(route);
|
|
275
|
+
this.verifyCountry(route);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
getVideoRepo() {
|
|
279
|
+
if (!this.videosRepo)
|
|
280
|
+
this.videosRepo = api_1.JsonRepository.create('videos');
|
|
281
|
+
return this.videosRepo;
|
|
282
|
+
}
|
|
283
|
+
getRoutesRepo() {
|
|
284
|
+
if (!this.routesRepo)
|
|
285
|
+
this.routesRepo = api_1.JsonRepository.create('routes');
|
|
286
|
+
return this.routesRepo;
|
|
287
|
+
}
|
|
288
|
+
getDetailsRepo(route) {
|
|
289
|
+
var _a;
|
|
290
|
+
return ((_a = route === null || route === void 0 ? void 0 : route.description) === null || _a === void 0 ? void 0 : _a.hasVideo) ? this.getVideoRepo() : this.getRoutesRepo();
|
|
291
|
+
}
|
|
292
|
+
writeDetails(route) {
|
|
293
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
294
|
+
const repo = this.getDetailsRepo(route);
|
|
295
|
+
if (!repo) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const id = route.description.legacyId || route.description.id;
|
|
299
|
+
if (this.routesSaveObserver[id]) {
|
|
300
|
+
yield this.routesSaveObserver[id].wait();
|
|
301
|
+
}
|
|
302
|
+
const save = () => __awaiter(this, void 0, void 0, function* () {
|
|
303
|
+
try {
|
|
304
|
+
yield repo.write(id, route.details);
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
this.routesSaveObserver[id] = new observer_1.PromiseObserver(save());
|
|
310
|
+
yield this.routesSaveObserver[id].start();
|
|
311
|
+
process.nextTick(() => { delete this.routesSaveObserver[id]; });
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
deleteRoute(id) {
|
|
315
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
316
|
+
if (!this.routesRepo)
|
|
317
|
+
return;
|
|
318
|
+
if (this.routesSaveObserver[id])
|
|
319
|
+
return this.routesSaveObserver[id];
|
|
320
|
+
this.routesSaveObserver[id] = new observer_1.PromiseObserver(this._deleteRoute(id));
|
|
321
|
+
yield this.routesSaveObserver[id].start();
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
_deleteRoute(id) {
|
|
325
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
326
|
+
yield (0, utils_1.waitNextTick)();
|
|
327
|
+
try {
|
|
328
|
+
yield this.routesRepo.delete(id);
|
|
329
|
+
(0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
(0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
__setFunctionName(_classThis, "RoutesDbLoader");
|
|
339
|
+
(() => {
|
|
340
|
+
var _a;
|
|
341
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_a = _classSuper[Symbol.metadata]) !== null && _a !== void 0 ? _a : null) : void 0;
|
|
342
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
343
|
+
RoutesDbLoader = _classThis = _classDescriptor.value;
|
|
344
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
345
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
346
|
+
})();
|
|
347
|
+
return RoutesDbLoader = _classThis;
|
|
348
|
+
})();
|
|
349
|
+
exports.RoutesDbLoader = RoutesDbLoader;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { JsonRepository } from "../../../api";
|
|
2
|
+
import { Observer, PromiseObserver } from "../../../base/types/observer";
|
|
3
|
+
import { Route } from "../../base/model/route";
|
|
4
|
+
import { RouteInfo } from "../../base/types";
|
|
5
|
+
import { RoutesLegacyDbLoader } from "./LegacyDB";
|
|
6
|
+
import { RouteInfoDBEntry } from "./types";
|
|
7
|
+
import { DBLoader } from "./DBLoader";
|
|
8
|
+
import { RouteApiDetail } from "../../base/api/types";
|
|
9
|
+
export declare class RoutesDbLoader extends DBLoader<RouteInfoDBEntry> {
|
|
10
|
+
protected loadObserver: Observer;
|
|
11
|
+
protected saveObserver: PromiseObserver<void>;
|
|
12
|
+
protected videosRepo: JsonRepository;
|
|
13
|
+
protected routesRepo: JsonRepository;
|
|
14
|
+
protected routeDescriptions: Array<RouteInfoDBEntry>;
|
|
15
|
+
protected tsLastWrite: number;
|
|
16
|
+
protected isDirty: boolean;
|
|
17
|
+
protected routesSaveObserver: {
|
|
18
|
+
[index: string]: PromiseObserver<void>;
|
|
19
|
+
};
|
|
20
|
+
constructor();
|
|
21
|
+
save(route: Route, enforcedWriteDetails?: boolean): Promise<void>;
|
|
22
|
+
delete(route: Route): Promise<void>;
|
|
23
|
+
getDescription(id: string): RouteInfo;
|
|
24
|
+
getDetails(id: string): Promise<RouteApiDetail>;
|
|
25
|
+
protected writeRepo(): Promise<void>;
|
|
26
|
+
protected write(): void;
|
|
27
|
+
protected getLegacyLoader(): RoutesLegacyDbLoader;
|
|
28
|
+
protected buildRouteInfo(descr: RouteInfoDBEntry): RouteInfo;
|
|
29
|
+
protected buildRouteDBInfo(descr: RouteInfo): RouteInfoDBEntry;
|
|
30
|
+
protected loadFromLegacy(): Promise<Array<RouteInfoDBEntry>>;
|
|
31
|
+
protected loadDescriptions(): Promise<Array<RouteInfoDBEntry>>;
|
|
32
|
+
protected removeDuplicates(routes: Array<RouteInfoDBEntry>): any[];
|
|
33
|
+
protected verifyImportDate(routes: Array<RouteInfoDBEntry>): void;
|
|
34
|
+
protected loadDetailRecord(target: Route | RouteInfo): Promise<RouteApiDetail>;
|
|
35
|
+
private validateDetails;
|
|
36
|
+
private validateDescription;
|
|
37
|
+
protected loadDetails(route: Route, alreadyAdded?: boolean): Promise<void>;
|
|
38
|
+
protected getVideoRepo(): JsonRepository;
|
|
39
|
+
protected getRoutesRepo(): JsonRepository;
|
|
40
|
+
protected getDetailsRepo(route: Route): JsonRepository;
|
|
41
|
+
protected writeDetails(route: Route): Promise<void>;
|
|
42
|
+
protected deleteRoute(id: string): Promise<PromiseObserver<void>>;
|
|
43
|
+
protected _deleteRoute(id: any): Promise<void>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
+
var _, done = false;
|
|
8
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
+
var context = {};
|
|
10
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
+
if (kind === "accessor") {
|
|
15
|
+
if (result === void 0) continue;
|
|
16
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
+
}
|
|
21
|
+
else if (_ = accept(result)) {
|
|
22
|
+
if (kind === "field") initializers.unshift(_);
|
|
23
|
+
else descriptor[key] = _;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
+
done = true;
|
|
28
|
+
};
|
|
29
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
+
var useValue = arguments.length > 2;
|
|
31
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
+
}
|
|
34
|
+
return useValue ? value : void 0;
|
|
35
|
+
};
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
|
46
|
+
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
47
|
+
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.RoutesDbLoader = void 0;
|
|
51
|
+
const api_1 = require("../../../api");
|
|
52
|
+
const types_1 = require("../../../base/types");
|
|
53
|
+
const observer_1 = require("../../../base/types/observer");
|
|
54
|
+
const LegacyDB_1 = require("./LegacyDB");
|
|
55
|
+
const DBLoader_1 = require("./DBLoader");
|
|
56
|
+
const utils_1 = require("../../../utils");
|
|
57
|
+
const route_1 = require("../../base/utils/route");
|
|
58
|
+
let RoutesDbLoader = (() => {
|
|
59
|
+
let _classDecorators = [types_1.Singleton];
|
|
60
|
+
let _classDescriptor;
|
|
61
|
+
let _classExtraInitializers = [];
|
|
62
|
+
let _classThis;
|
|
63
|
+
let _classSuper = DBLoader_1.DBLoader;
|
|
64
|
+
var RoutesDbLoader = _classThis = class extends _classSuper {
|
|
65
|
+
constructor() {
|
|
66
|
+
super();
|
|
67
|
+
this.routesSaveObserver = {};
|
|
68
|
+
this.routeDescriptions = [];
|
|
69
|
+
this.isDirty = true;
|
|
70
|
+
}
|
|
71
|
+
save(route_2) {
|
|
72
|
+
return __awaiter(this, arguments, void 0, function* (route, enforcedWriteDetails = false) {
|
|
73
|
+
const stringify = (json) => { try {
|
|
74
|
+
return JSON.stringify(json);
|
|
75
|
+
}
|
|
76
|
+
catch (_a) { } };
|
|
77
|
+
let prev;
|
|
78
|
+
const idx = this.routeDescriptions.findIndex(d => d.id === route.description.id);
|
|
79
|
+
if (idx === -1)
|
|
80
|
+
this.routeDescriptions.push(route.description);
|
|
81
|
+
else {
|
|
82
|
+
prev = stringify(this.buildRouteDBInfo(this.routeDescriptions[idx]));
|
|
83
|
+
this.routeDescriptions[idx] = route.description;
|
|
84
|
+
}
|
|
85
|
+
const changed = !prev || stringify(this.buildRouteDBInfo(this.routeDescriptions[idx])) !== prev;
|
|
86
|
+
if (changed) {
|
|
87
|
+
this.isDirty = true;
|
|
88
|
+
this.write();
|
|
89
|
+
this.writeDetails(route);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const details = yield this.loadDetailRecord(route);
|
|
93
|
+
if (!details || enforcedWriteDetails)
|
|
94
|
+
this.writeDetails(route);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
delete(route) {
|
|
99
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
const id = route.description.id;
|
|
101
|
+
const idx = this.routeDescriptions.findIndex(d => d.id === id);
|
|
102
|
+
if (idx == -1)
|
|
103
|
+
throw new Error('route not found');
|
|
104
|
+
this.routeDescriptions.splice(idx, 1);
|
|
105
|
+
this.isDirty = true;
|
|
106
|
+
this.write();
|
|
107
|
+
yield this.deleteRoute(id);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
getDescription(id) {
|
|
111
|
+
const descr = this.routeDescriptions.find(d => d.id === id);
|
|
112
|
+
return descr;
|
|
113
|
+
}
|
|
114
|
+
getDetails(id) {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
const descr = this.routeDescriptions.find(d => d.id === id);
|
|
117
|
+
if (!descr)
|
|
118
|
+
return;
|
|
119
|
+
const details = yield this.loadDetailRecord(descr);
|
|
120
|
+
return details;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
writeRepo() {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
if (this.saveObserver)
|
|
126
|
+
yield this.saveObserver.wait();
|
|
127
|
+
const save = () => __awaiter(this, void 0, void 0, function* () {
|
|
128
|
+
try {
|
|
129
|
+
yield this.routesRepo.write('db', this.routeDescriptions.map(this.buildRouteDBInfo.bind(this)));
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
this.logger.logEvent({ message: 'could not safe repo', error: err.message });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
this.saveObserver = new observer_1.PromiseObserver(save());
|
|
136
|
+
yield this.saveObserver.start();
|
|
137
|
+
process.nextTick(() => { delete this.saveObserver; });
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
write() {
|
|
141
|
+
if (this.isDirty && (this.tsLastWrite === undefined || Date.now() - this.tsLastWrite >= 1000)) {
|
|
142
|
+
this.isDirty = false;
|
|
143
|
+
this.tsLastWrite = Date.now();
|
|
144
|
+
this.writeRepo();
|
|
145
|
+
}
|
|
146
|
+
if (this.isDirty && Date.now() - this.tsLastWrite < 1000) {
|
|
147
|
+
setTimeout(() => { this.write(); }, this.tsLastWrite + 1000 - Date.now());
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
getLegacyLoader() {
|
|
151
|
+
return new LegacyDB_1.RoutesLegacyDbLoader();
|
|
152
|
+
}
|
|
153
|
+
buildRouteInfo(descr) {
|
|
154
|
+
return descr;
|
|
155
|
+
}
|
|
156
|
+
buildRouteDBInfo(descr) {
|
|
157
|
+
const data = Object.assign({}, descr);
|
|
158
|
+
delete data.points;
|
|
159
|
+
return data;
|
|
160
|
+
}
|
|
161
|
+
loadFromLegacy() {
|
|
162
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
163
|
+
return new Promise(done => {
|
|
164
|
+
const observer = this.getLegacyLoader().load();
|
|
165
|
+
const descriptions = [];
|
|
166
|
+
observer.on('route.added', (r) => { descriptions.push(r.description); });
|
|
167
|
+
observer.on('done', () => done(descriptions));
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
loadDescriptions() {
|
|
172
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
173
|
+
const descriptions = yield this.getRoutesRepo().read('db');
|
|
174
|
+
if (descriptions) {
|
|
175
|
+
const routes = descriptions;
|
|
176
|
+
const cleaned = this.removeDuplicates(routes);
|
|
177
|
+
return cleaned;
|
|
178
|
+
}
|
|
179
|
+
const legacy = yield this.loadFromLegacy();
|
|
180
|
+
return legacy;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
removeDuplicates(routes) {
|
|
184
|
+
const ids = routes.map(r => r.legacyId || r.id);
|
|
185
|
+
const uniqueIds = ids.filter((d, pos) => ids.indexOf(d) === pos);
|
|
186
|
+
const cleaned = [];
|
|
187
|
+
uniqueIds.forEach(id => {
|
|
188
|
+
const routeWithLegacy = routes.find(r => r.legacyId === id);
|
|
189
|
+
const route = routeWithLegacy || routes.find(r => r.id === id);
|
|
190
|
+
if (route)
|
|
191
|
+
cleaned.push(route);
|
|
192
|
+
});
|
|
193
|
+
return cleaned;
|
|
194
|
+
}
|
|
195
|
+
verifyImportDate(routes) {
|
|
196
|
+
let changed = false;
|
|
197
|
+
routes.forEach(r => {
|
|
198
|
+
if (!r.tsImported) {
|
|
199
|
+
r.tsImported = Date.now();
|
|
200
|
+
changed = true;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
if (changed) {
|
|
204
|
+
this.isDirty = true;
|
|
205
|
+
this.write();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
loadDetailRecord(target) {
|
|
209
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
210
|
+
var _a;
|
|
211
|
+
const description = (target.description || target);
|
|
212
|
+
const repo = (description === null || description === void 0 ? void 0 : description.hasVideo) ? this.getVideosRepo() : this.getRoutesRepo();
|
|
213
|
+
let details;
|
|
214
|
+
try {
|
|
215
|
+
const id = description.legacyId || description.id;
|
|
216
|
+
details = (yield repo.read(id));
|
|
217
|
+
description.originalName = details.title;
|
|
218
|
+
if (description.hasVideo) {
|
|
219
|
+
description.segments = ((_a = details.video) === null || _a === void 0 ? void 0 : _a.selectableSegments) || (details === null || details === void 0 ? void 0 : details.selectableSegments);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
this.logger.logEvent({ message: 'could not load route details', id: (description === null || description === void 0 ? void 0 : description.legacyId) || (description === null || description === void 0 ? void 0 : description.id), title: description === null || description === void 0 ? void 0 : description.title, reason: err.message, stack: err.stack });
|
|
224
|
+
if (description.id && description.legacyId !== description.id) {
|
|
225
|
+
try {
|
|
226
|
+
details = (yield repo.read(description.id));
|
|
227
|
+
description.originalName = details.title;
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
this.logger.logEvent({ message: 'could not load route details', id: (description === null || description === void 0 ? void 0 : description.legacyId) || (description === null || description === void 0 ? void 0 : description.id), title: description === null || description === void 0 ? void 0 : description.title, reason: err.message, stack: err.stack });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (!details) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
this.validateDetails(details);
|
|
238
|
+
this.validateDescription(description, details);
|
|
239
|
+
return details;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
validateDetails(details) {
|
|
243
|
+
if (!details.points || !Array.isArray(details.points)) {
|
|
244
|
+
details.points = details.decoded;
|
|
245
|
+
delete details.decoded;
|
|
246
|
+
}
|
|
247
|
+
if (details.localizedTitle && typeof (details.localizedTitle) === 'string') {
|
|
248
|
+
details.localizedTitle = { en: details.localizedTitle };
|
|
249
|
+
}
|
|
250
|
+
(0, route_1.validateDistance)(details.points);
|
|
251
|
+
(0, route_1.updateSlopes)(details.points);
|
|
252
|
+
}
|
|
253
|
+
validateDescription(description, details) {
|
|
254
|
+
if (!description.elevation) {
|
|
255
|
+
description.elevation = (0, route_1.getTotalElevation)(details);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
loadDetails(route, alreadyAdded) {
|
|
259
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
260
|
+
const details = yield this.loadDetailRecord(route);
|
|
261
|
+
(0, route_1.addDetails)(route, details);
|
|
262
|
+
this.verifyRouteHash(route);
|
|
263
|
+
if (alreadyAdded)
|
|
264
|
+
this.emitRouteUpdate(route);
|
|
265
|
+
this.emitRouteAdded(route);
|
|
266
|
+
this.verifyCountry(route);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
getVideoRepo() {
|
|
270
|
+
if (!this.videosRepo)
|
|
271
|
+
this.videosRepo = api_1.JsonRepository.create('videos');
|
|
272
|
+
return this.videosRepo;
|
|
273
|
+
}
|
|
274
|
+
getRoutesRepo() {
|
|
275
|
+
if (!this.routesRepo)
|
|
276
|
+
this.routesRepo = api_1.JsonRepository.create('routes');
|
|
277
|
+
return this.routesRepo;
|
|
278
|
+
}
|
|
279
|
+
getDetailsRepo(route) {
|
|
280
|
+
var _a;
|
|
281
|
+
return ((_a = route === null || route === void 0 ? void 0 : route.description) === null || _a === void 0 ? void 0 : _a.hasVideo) ? this.getVideoRepo() : this.getRoutesRepo();
|
|
282
|
+
}
|
|
283
|
+
writeDetails(route) {
|
|
284
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
285
|
+
const repo = this.getDetailsRepo(route);
|
|
286
|
+
if (!repo) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const id = route.description.legacyId || route.description.id;
|
|
290
|
+
if (this.routesSaveObserver[id]) {
|
|
291
|
+
yield this.routesSaveObserver[id].wait();
|
|
292
|
+
}
|
|
293
|
+
const save = () => __awaiter(this, void 0, void 0, function* () {
|
|
294
|
+
try {
|
|
295
|
+
yield repo.write(id, route.details);
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
this.routesSaveObserver[id] = new observer_1.PromiseObserver(save());
|
|
301
|
+
yield this.routesSaveObserver[id].start();
|
|
302
|
+
process.nextTick(() => { delete this.routesSaveObserver[id]; });
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
deleteRoute(id) {
|
|
306
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
307
|
+
if (!this.routesRepo)
|
|
308
|
+
return;
|
|
309
|
+
if (this.routesSaveObserver[id])
|
|
310
|
+
return this.routesSaveObserver[id];
|
|
311
|
+
this.routesSaveObserver[id] = new observer_1.PromiseObserver(this._deleteRoute(id));
|
|
312
|
+
yield this.routesSaveObserver[id].start();
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
_deleteRoute(id) {
|
|
316
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
317
|
+
yield (0, utils_1.waitNextTick)();
|
|
318
|
+
try {
|
|
319
|
+
yield this.routesRepo.delete(id);
|
|
320
|
+
(0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
|
|
321
|
+
}
|
|
322
|
+
catch (err) {
|
|
323
|
+
(0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
|
|
324
|
+
throw err;
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
__setFunctionName(_classThis, "RoutesDbLoader");
|
|
330
|
+
(() => {
|
|
331
|
+
var _a;
|
|
332
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_a = _classSuper[Symbol.metadata]) !== null && _a !== void 0 ? _a : null) : void 0;
|
|
333
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
334
|
+
RoutesDbLoader = _classThis = _classDescriptor.value;
|
|
335
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
336
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
337
|
+
})();
|
|
338
|
+
return RoutesDbLoader = _classThis;
|
|
339
|
+
})();
|
|
340
|
+
exports.RoutesDbLoader = RoutesDbLoader;
|
|
@@ -66,6 +66,7 @@ export declare class RouteListService extends IncyclistService {
|
|
|
66
66
|
onCarouselInitialized(list: CardList<Route>, item: any, itemsInSlide: any): void;
|
|
67
67
|
onCarouselUpdated(list: any, item: any, itemsInSlide: any): void;
|
|
68
68
|
preload(): PromiseObserver<void>;
|
|
69
|
+
private createRoutesLogEntry;
|
|
69
70
|
getLists(forUi?: boolean): Array<CardList<Route>>;
|
|
70
71
|
getRouteDetails(id: string, expectLocal?: boolean): Promise<RouteApiDetail>;
|
|
71
72
|
getSelectedRouteDetails(): Promise<RouteApiDetail>;
|
|
@@ -74,7 +75,7 @@ export declare class RouteListService extends IncyclistService {
|
|
|
74
75
|
selectCard(card: Card<Route>): void;
|
|
75
76
|
unselectCard(card: Card<Route>): void;
|
|
76
77
|
import(info: FileInfo | Array<FileInfo>, retry?: ActiveImportCard): void;
|
|
77
|
-
emitLists(event: 'loaded' | 'updated'): void;
|
|
78
|
+
emitLists(event: 'loaded' | 'updated', log?: boolean): void;
|
|
78
79
|
getCard(id: string): RouteCard;
|
|
79
80
|
addCardAgain(card: RouteCard): void;
|
|
80
81
|
protected addImportCard(file: FileInfo): ActiveImportCard;
|
|
@@ -294,15 +294,10 @@ let RouteListService = (() => {
|
|
|
294
294
|
this.preloadObserver = new observer_1.PromiseObserver(promise);
|
|
295
295
|
this.preloadObserver.start()
|
|
296
296
|
.then(() => {
|
|
297
|
-
|
|
298
|
-
myRoutes: this.myRoutes.length,
|
|
299
|
-
selected: this.selectedRoutes.length,
|
|
300
|
-
alternatives: this.alternatives.length
|
|
301
|
-
};
|
|
302
|
-
this.logEvent({ message: 'preload route list completed', cards });
|
|
297
|
+
this.logEvent({ message: 'preload route list completed' });
|
|
303
298
|
this.initialized = true;
|
|
304
299
|
(0, utils_1.updateRepoStats)();
|
|
305
|
-
this.emitLists('loaded');
|
|
300
|
+
this.emitLists('loaded', true);
|
|
306
301
|
process.nextTick(() => { delete this.preloadObserver; });
|
|
307
302
|
})
|
|
308
303
|
.catch((err) => {
|
|
@@ -315,6 +310,28 @@ let RouteListService = (() => {
|
|
|
315
310
|
}
|
|
316
311
|
return this.preloadObserver;
|
|
317
312
|
}
|
|
313
|
+
createRoutesLogEntry(includeDetails = false) {
|
|
314
|
+
try {
|
|
315
|
+
const log = {
|
|
316
|
+
counts: {
|
|
317
|
+
myRoutes: this.myRoutes.length,
|
|
318
|
+
selected: this.selectedRoutes.length,
|
|
319
|
+
alternatives: this.alternatives.length
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
if (includeDetails) {
|
|
323
|
+
log.titles = {};
|
|
324
|
+
const getTitleLog = (list) => { var _a; return (_a = list === null || list === void 0 ? void 0 : list.getCards()) === null || _a === void 0 ? void 0 : _a.map(r => r.getTitle()).join(','); };
|
|
325
|
+
log.titles.myRoutes = getTitleLog(this.myRoutes);
|
|
326
|
+
log.titles.selected = getTitleLog(this.selectedRoutes);
|
|
327
|
+
log.titles.alternaties = getTitleLog(this.alternatives);
|
|
328
|
+
}
|
|
329
|
+
return log;
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
this.logError(err, 'createRoutesLogEntry');
|
|
333
|
+
}
|
|
334
|
+
}
|
|
318
335
|
getLists(forUi = true) {
|
|
319
336
|
var _a, _b;
|
|
320
337
|
try {
|
|
@@ -398,11 +415,15 @@ let RouteListService = (() => {
|
|
|
398
415
|
importCards.push(retry);
|
|
399
416
|
}
|
|
400
417
|
files.forEach((file, idx) => __awaiter(this, void 0, void 0, function* () {
|
|
418
|
+
var _a, _b;
|
|
401
419
|
if (!file)
|
|
402
420
|
return;
|
|
403
421
|
const importCard = importCards[idx];
|
|
422
|
+
const name = (_b = (_a = file.url) !== null && _a !== void 0 ? _a : file.filename) !== null && _b !== void 0 ? _b : file.name;
|
|
404
423
|
try {
|
|
424
|
+
this.logEvent({ message: 'start import', name });
|
|
405
425
|
const { data, details } = yield parsers_1.RouteParser.parse(file);
|
|
426
|
+
this.logEvent({ message: 'import completed', name });
|
|
406
427
|
const route = new route_1.Route(data, details);
|
|
407
428
|
route.description.tsImported = Date.now();
|
|
408
429
|
const existing = this.findCard(route);
|
|
@@ -419,10 +440,11 @@ let RouteListService = (() => {
|
|
|
419
440
|
this.myRoutes.add(card, true);
|
|
420
441
|
this.myRoutes.remove(importCard);
|
|
421
442
|
card.enableDelete(true);
|
|
422
|
-
this.emitLists('updated');
|
|
443
|
+
this.emitLists('updated', true);
|
|
423
444
|
this.verifyPoints(card, route);
|
|
424
445
|
}
|
|
425
446
|
catch (err) {
|
|
447
|
+
this.logEvent({ message: 'import failed', name, reason: err.message, stack: err.stack });
|
|
426
448
|
importCard.setError(err);
|
|
427
449
|
}
|
|
428
450
|
}));
|
|
@@ -431,12 +453,17 @@ let RouteListService = (() => {
|
|
|
431
453
|
this.logError(err, 'import', info);
|
|
432
454
|
}
|
|
433
455
|
}
|
|
434
|
-
emitLists(event) {
|
|
456
|
+
emitLists(event, log = false) {
|
|
457
|
+
var _a;
|
|
435
458
|
try {
|
|
436
459
|
const lists = this.getLists();
|
|
437
460
|
const hash = lists ? lists.map(l => l.getCards().map(c => c.getId()).join(',')).join(':') : '';
|
|
438
461
|
if (this.observer)
|
|
439
462
|
this.observer.emit(event, lists, hash);
|
|
463
|
+
if (log) {
|
|
464
|
+
const logs = (_a = this.createRoutesLogEntry(true)) !== null && _a !== void 0 ? _a : {};
|
|
465
|
+
this.logEvent(Object.assign({ message: `RoutesList ${event}` }, logs));
|
|
466
|
+
}
|
|
440
467
|
}
|
|
441
468
|
catch (err) {
|
|
442
469
|
this.logError(err, 'emitLists', event);
|
|
@@ -452,7 +479,7 @@ let RouteListService = (() => {
|
|
|
452
479
|
list.add(card);
|
|
453
480
|
card.enableDelete(list.getId() === 'myRoutes');
|
|
454
481
|
card.setList(list);
|
|
455
|
-
this.emitLists('updated');
|
|
482
|
+
this.emitLists('updated', true);
|
|
456
483
|
}
|
|
457
484
|
addImportCard(file) {
|
|
458
485
|
const card = new ActiveImportCard_1.ActiveImportCard(file);
|