incyclist-services 1.0.57 → 1.0.58

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.
@@ -733,7 +733,7 @@ class DevicePairingService extends service_2.IncyclistService {
733
733
  run(props = {}) {
734
734
  return __awaiter(this, void 0, void 0, function* () {
735
735
  this.emit('run');
736
- if (this.isPairing() || this.isScanning() || !this.state.waiting) {
736
+ if ((this.isPairing() || this.isScanning()) && !this.state.waiting) {
737
737
  yield this._stop();
738
738
  }
739
739
  if (this.state.stopRequested || this.state.stopped) {
@@ -848,6 +848,10 @@ class DevicePairingService extends service_2.IncyclistService {
848
848
  this.state.tsPrevStart = Date.now();
849
849
  if (this.isScanning() && this.state.scan.preparing !== preparing)
850
850
  return;
851
+ this.logEvent({ message: 'Stopping Adapters', interfaces: interfaces.join(','), props });
852
+ const stopPromises = [];
853
+ interfaces.forEach(i => stopPromises.push(this.stopAdaptersOnInterface(i)));
854
+ yield Promise.allSettled(stopPromises);
851
855
  this.logEvent({ message: 'Start Scanning', interfaces: interfaces.join(','), props });
852
856
  this.initScanningCallbacks();
853
857
  const timeout = props.enforcedScan ? 1000 * 60 * 60 : undefined;
@@ -1017,6 +1021,8 @@ class DevicePairingService extends service_2.IncyclistService {
1017
1021
  stopAdaptersWithCapability(capability, udid) {
1018
1022
  return __awaiter(this, void 0, void 0, function* () {
1019
1023
  const c = this.getCapability(capability);
1024
+ if (!c)
1025
+ return;
1020
1026
  const { adapters = [], capabilities } = this.state;
1021
1027
  const maxRetries = 3;
1022
1028
  let target = udid ?
@@ -32,6 +32,10 @@ export declare class DeviceRideService extends EventEmitter {
32
32
  startCheck(filter: RideServiceCheckFilter): Promise<void>;
33
33
  getAdapters(filter: RideServiceCheckFilter): AdapterRideInfo[];
34
34
  setSerialPortInUse(adapter: IncyclistDeviceAdapter): void;
35
+ protected checkAntSameDeviceID(adapters: AdapterRideInfo[]): Array<{
36
+ udid: string;
37
+ info: AdapterInfo;
38
+ }>;
35
39
  startAdapters(adapters: AdapterRideInfo[], startType: 'start' | 'check' | 'pair', props?: RideServiceDeviceProperties): Promise<boolean>;
36
40
  start(props: RideServiceDeviceProperties): Promise<boolean>;
37
41
  startRetry(props: RideServiceDeviceProperties): Promise<boolean>;
@@ -336,11 +336,39 @@ class DeviceRideService extends events_1.default {
336
336
  serial.setInUse(device.getPort());
337
337
  }
338
338
  }
339
+ checkAntSameDeviceID(adapters) {
340
+ const antDevices = adapters.map((ai, idx) => (Object.assign(Object.assign({}, ai), { idx })))
341
+ .filter(ai => ai.adapter.getInterface() === 'ant')
342
+ .map(ai => ({ idx: ai.idx, deviceID: ai.adapter.getID(), capabilities: ai.capabilities, udid: ai.udid }));
343
+ const antDeviceIds = antDevices.map(di => di.deviceID);
344
+ const duplicateIds = antDeviceIds.filter((item, index) => antDeviceIds.indexOf(item) !== index);
345
+ const score = (capabilities) => {
346
+ let value = 0;
347
+ if (capabilities.includes(incyclist_devices_1.IncyclistCapability.Control))
348
+ value += 100;
349
+ if (capabilities.includes(incyclist_devices_1.IncyclistCapability.Power))
350
+ value += 50;
351
+ value += capabilities.length;
352
+ return value;
353
+ };
354
+ const duplicateDevices = antDevices.filter(di => duplicateIds.includes(di.deviceID)).sort((a, b) => score(b.capabilities) - score(a.capabilities));
355
+ const leading = duplicateDevices[0];
356
+ const duplicateAdapters = [];
357
+ duplicateDevices.forEach((di, i) => {
358
+ if (i == 0)
359
+ return;
360
+ duplicateAdapters.push({ udid: leading.udid, info: adapters[di.idx] });
361
+ });
362
+ return duplicateAdapters;
363
+ }
339
364
  startAdapters(adapters, startType, props) {
340
365
  return __awaiter(this, void 0, void 0, function* () {
341
366
  const { forceErgMode, startPos, realityFactor, rideMode, route } = props || {};
367
+ const duplicates = this.checkAntSameDeviceID(adapters);
342
368
  this.startPromises = adapters === null || adapters === void 0 ? void 0 : adapters.map((ai) => __awaiter(this, void 0, void 0, function* () {
343
369
  var _a;
370
+ if (duplicates.find(dai => dai.info.udid === ai.udid))
371
+ return;
344
372
  const startProps = (0, clone_1.default)(props || {});
345
373
  if (startType === 'check' || startType === 'pair') {
346
374
  startProps.timeout = 10000;
@@ -389,6 +417,9 @@ class DeviceRideService extends events_1.default {
389
417
  .then((success) => __awaiter(this, void 0, void 0, function* () {
390
418
  if (success) {
391
419
  this.emit(`${startType}-success`, this.getAdapterStateInfo(ai));
420
+ if (duplicates.find(dai => dai.udid === ai.udid)) {
421
+ duplicates.forEach(dai => { this.emit(`${startType}-success`, this.getAdapterStateInfo(dai.info)); });
422
+ }
392
423
  this.logEvent(Object.assign({ message: `${startType} ${sType} request finished` }, logProps));
393
424
  if (startType === 'check')
394
425
  yield ai.adapter.pause().catch(console.log);
@@ -400,6 +431,9 @@ class DeviceRideService extends events_1.default {
400
431
  }
401
432
  else {
402
433
  this.emit(`${startType}-error`, this.getAdapterStateInfo(ai));
434
+ if (duplicates.find(dai => dai.udid === ai.udid)) {
435
+ duplicates.forEach(dai => { this.emit(`${startType}-error`, this.getAdapterStateInfo(dai.info)); });
436
+ }
403
437
  this.logEvent(Object.assign({ message: `${startType} ${sType} request failed` }, logProps));
404
438
  if (startType === 'check' || startType === 'pair')
405
439
  yield ai.adapter.stop().catch(console.log);
@@ -411,6 +445,9 @@ class DeviceRideService extends events_1.default {
411
445
  ai.isStarted = false;
412
446
  this.logEvent(Object.assign(Object.assign({ message: `${startType} ${sType} request failed` }, logProps), { reason: err.message }));
413
447
  this.emit(`${startType}-error`, this.getAdapterStateInfo(ai), err);
448
+ if (duplicates.find(dai => dai.udid === ai.udid)) {
449
+ duplicates.forEach(dai => { this.emit(`${startType}-error`, this.getAdapterStateInfo(dai.info)); });
450
+ }
414
451
  if (startType === 'check' || startType === 'pair') {
415
452
  yield ai.adapter.stop().catch(console.log);
416
453
  }
@@ -529,10 +566,29 @@ class DeviceRideService extends events_1.default {
529
566
  this.verifySelected(selectedDevices, incyclist_devices_1.IncyclistCapability.Speed);
530
567
  this.verifySelected(selectedDevices, incyclist_devices_1.IncyclistCapability.Cadence);
531
568
  this.verifySelected(selectedDevices, incyclist_devices_1.IncyclistCapability.Power);
532
- const enabledCapabilities = this.enforceSimulator ?
533
- [incyclist_devices_1.IncyclistCapability.Control, incyclist_devices_1.IncyclistCapability.Power, incyclist_devices_1.IncyclistCapability.Speed, incyclist_devices_1.IncyclistCapability.HeartRate, incyclist_devices_1.IncyclistCapability.Cadence]
534
- :
535
- selectedDevices.filter(sd => sd.selected === adapterInfo.udid).map(c => c.capability);
569
+ const duplicates = this.checkAntSameDeviceID(adapters);
570
+ const toBeReplaced = duplicates.filter(dai => dai.udid === adapterInfo.udid).map(dai => dai.info.udid);
571
+ let enabledCapabilities = [];
572
+ if (this.simulatorEnforced) {
573
+ enabledCapabilities = [incyclist_devices_1.IncyclistCapability.Control, incyclist_devices_1.IncyclistCapability.Power, incyclist_devices_1.IncyclistCapability.Speed, incyclist_devices_1.IncyclistCapability.HeartRate, incyclist_devices_1.IncyclistCapability.Cadence];
574
+ }
575
+ else if (duplicates.length > 0 && duplicates.find(d => d.udid === adapterInfo.udid)) {
576
+ const selected = (0, clone_1.default)(selectedDevices);
577
+ selected.forEach(cd => {
578
+ if (toBeReplaced.includes(cd.selected))
579
+ cd.selected = adapterInfo.udid;
580
+ });
581
+ selected.forEach;
582
+ selected.forEach(sd => {
583
+ const duplicate = duplicates.find(dai => dai.info.udid === sd.selected);
584
+ if (duplicate)
585
+ sd.selected = duplicate.udid;
586
+ });
587
+ enabledCapabilities = selected.map(c => c.capability);
588
+ }
589
+ else {
590
+ enabledCapabilities = selectedDevices.filter(sd => sd.selected === adapterInfo.udid).map(c => c.capability);
591
+ }
536
592
  this.logEvent({ message: 'Data Update', device: adapterInfo.adapter.getName(), data, enabledCapabilities });
537
593
  enabledCapabilities.forEach(capability => {
538
594
  switch (capability) {
@@ -559,6 +615,11 @@ class DeviceRideService extends events_1.default {
559
615
  }
560
616
  });
561
617
  this.emit('data', this.data, adapterInfo.udid);
618
+ if (toBeReplaced) {
619
+ toBeReplaced.forEach(udid => {
620
+ this.emit('data', this.data, udid);
621
+ });
622
+ }
562
623
  }
563
624
  sendUpdate(request) {
564
625
  const adapters = this.getAdapterList();
@@ -35,7 +35,7 @@ export type RoutePoint = {
35
35
  lng: number;
36
36
  routeDistance: number;
37
37
  elevation: number;
38
- slope: number;
38
+ slope?: number;
39
39
  };
40
40
  export type RouteSegment = {
41
41
  start: number;
@@ -0,0 +1,8 @@
1
+ import { RouteState } from "../base/types";
2
+ import { CardInfo, CardType } from "./types";
3
+ export declare class FreeRideCard implements CardInfo {
4
+ type: CardType;
5
+ state: RouteState;
6
+ title?: string;
7
+ constructor(state: any, title?: any);
8
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FreeRideCard = void 0;
4
+ class FreeRideCard {
5
+ constructor(state, title) {
6
+ this.type = 'free-ride';
7
+ this.state = state;
8
+ this.title = title || 'Free Ride';
9
+ }
10
+ }
11
+ exports.FreeRideCard = FreeRideCard;
@@ -1,4 +1,4 @@
1
- import { Page, InternalRouteListState, List, RouteInfo, Route, RouteListEntry, RouteListStartProps, RouteListData, RouteStartSettings, onRouteStatusUpdateCallback, onCarouselStateChangedCallback } from "./types";
1
+ import { Page, InternalRouteListState, List, RouteInfo, Route, RouteListEntry, RouteListStartProps, RouteListData, RouteStartSettings, onRouteStatusUpdateCallback, onCarouselStateChangedCallback, RoutesDB } from "./types";
2
2
  import IncyclistRoutesApi from "../base/api";
3
3
  import { RouteApiDescription, RouteApiDetail } from "../base/api/types";
4
4
  import { IncyclistService } from "../../base/service";
@@ -9,17 +9,16 @@ export declare class RouteListService extends IncyclistService {
9
9
  static getInstance(binding?: IJsonRepositoryBinding): RouteListService;
10
10
  protected baseDir: string;
11
11
  protected state: InternalRouteListState;
12
- protected routes: Array<RouteListEntry>;
12
+ protected lists: Array<RouteListEntry>;
13
13
  protected videosRepo: JsonRepository;
14
14
  protected routesRepo: JsonRepository;
15
15
  protected api: IncyclistRoutesApi;
16
+ protected routeDescriptions: RoutesDB;
16
17
  constructor(binding?: IJsonRepositoryBinding);
17
18
  setBinding(binding: IJsonRepositoryBinding): void;
18
19
  protected getVideosRepo(): JsonRepository;
19
20
  protected getRoutesRepo(): JsonRepository;
20
21
  preload(carouselSize?: number): Promise<void>;
21
- protected _preload(carouselSize?: number, detailsOnly?: boolean): Promise<void>;
22
- protected preloadList(list: List, carouselSize?: number, detailsOnly?: boolean): Promise<void>;
23
22
  load(props: {
24
23
  list?: List;
25
24
  loadFrom?: number;
@@ -28,6 +27,7 @@ export declare class RouteListService extends IncyclistService {
28
27
  }): Promise<void>;
29
28
  openRouteSelection(pageId: string, props: RouteListStartProps): RouteListData;
30
29
  closeRouteSelection(pageId: string): void;
30
+ onResize(pageId: string): void;
31
31
  openStartSettings(r: RouteInfo | string): RouteStartSettings;
32
32
  cancelStartSettings(r: RouteInfo | string): {};
33
33
  setCardUpdateHandler(pageId: string, r: RouteInfo | string, list: List, idx: number, onRouteStateChanged: onRouteStatusUpdateCallback, onCarouselStateChanged: onCarouselStateChangedCallback): void;
@@ -40,6 +40,8 @@ export declare class RouteListService extends IncyclistService {
40
40
  stopRide(r?: RouteInfo | string): {};
41
41
  startDownload(routeId: any, onStatusUpdate: any): void;
42
42
  cancelDownload(routeId: any): void;
43
+ protected _preload(carouselSize?: number, detailsOnly?: boolean): Promise<void>;
44
+ protected preloadList(list: List, carouselSize?: number): Promise<void>;
43
45
  protected loadStartSettingsFromUserSettings(route: Route): {
44
46
  startPos: number;
45
47
  realityFactor: number;
@@ -71,7 +73,9 @@ export declare class RouteListService extends IncyclistService {
71
73
  title: string;
72
74
  }>;
73
75
  protected getRoute(criteria: string | filterFn): Route;
76
+ protected getAllRoutes(): Array<Route>;
74
77
  protected stopAllRides(): void;
78
+ protected addDescriptionsFromDB(descriptions: RoutesDB): void;
75
79
  protected addDescriptionsFromRepo(descriptions: Array<RouteApiDescription>, repo?: JsonRepository): void;
76
80
  protected addDescriptionsFromServer(descriptions: Array<RouteApiDescription>): void;
77
81
  protected updateRouteTitle(data: RouteInfo, route: {
@@ -83,12 +87,17 @@ export declare class RouteListService extends IncyclistService {
83
87
  protected getCountryPrefix(title?: string): string | undefined;
84
88
  protected removeCountryPrefix(title?: string): string;
85
89
  protected loadRouteDescriptionsFromRepo(): Promise<void>;
90
+ protected saveRouteDescriptions(): Promise<void>;
86
91
  protected loadRouteDescriptionsFromServer(): Promise<void>;
87
92
  protected loadRouteDescriptions(): Promise<void>;
88
- protected loadRouteDetais(route: Route): Promise<void>;
93
+ protected loadRouteDetais(route: Route): Promise<RouteInfo>;
94
+ saveRouteDetails(route: any): Promise<void>;
89
95
  protected emitRouteUpdated(route: Route): void;
90
96
  protected getListState(page: Page, list: List): import("./types").RouteListDateEntry;
91
97
  private getRouteFromStartProps;
98
+ private isDescriptionsLoaded;
99
+ private decodePoints;
100
+ private encodePoints;
92
101
  }
93
102
  export declare const useRouteList: () => RouteListService;
94
103
  export {};
@@ -20,6 +20,7 @@ const route_1 = require("../base/utils/route");
20
20
  const settings_1 = require("../../settings");
21
21
  const api_2 = require("../../api");
22
22
  const clone_1 = __importDefault(require("../../utils/clone"));
23
+ const FreeRideCard_1 = require("./FreeRideCard");
23
24
  class RouteListService extends service_1.IncyclistService {
24
25
  static getInstance(binding) {
25
26
  if (!RouteListService._instance)
@@ -30,12 +31,14 @@ class RouteListService extends service_1.IncyclistService {
30
31
  super('RouteList');
31
32
  this.state = {
32
33
  initialized: false,
33
- pages: []
34
+ pages: [],
35
+ preloadDone: false
34
36
  };
35
37
  if (binding)
36
38
  api_2.JsonRepository.setBinding(binding);
37
39
  this.api = api_1.default.getInstance();
38
40
  this.initRouteLists();
41
+ this.routeDescriptions = {};
39
42
  }
40
43
  setBinding(binding) {
41
44
  if (!binding)
@@ -57,56 +60,15 @@ class RouteListService extends service_1.IncyclistService {
57
60
  }
58
61
  preload(carouselSize = 5) {
59
62
  return __awaiter(this, void 0, void 0, function* () {
60
- if (this.state.loading)
63
+ if (this.state.loading || this.state.preloadDone)
61
64
  return;
62
65
  const promise = this._preload(carouselSize);
63
66
  this.state.loading = { promise };
64
67
  yield promise;
68
+ this.state.preloadDone = true;
65
69
  this.state.loading = null;
66
70
  });
67
71
  }
68
- _preload(carouselSize = 5, detailsOnly = false) {
69
- return __awaiter(this, void 0, void 0, function* () {
70
- if (!detailsOnly) {
71
- yield this.loadRouteDescriptions();
72
- }
73
- this.emitPageUpdate();
74
- const promises = [];
75
- const promisesApi = [];
76
- const lists = this.getRouteLists().map(li => li.list);
77
- lists.forEach((list) => {
78
- this.preloadList(list, carouselSize, detailsOnly);
79
- });
80
- this.logEvent({ message: 'preloading routes finished' });
81
- });
82
- }
83
- preloadList(list, carouselSize = 5, detailsOnly = false) {
84
- return __awaiter(this, void 0, void 0, function* () {
85
- const promises = [];
86
- const promisesApi = [];
87
- this.emitPageUpdate();
88
- this.routes.forEach(rle => {
89
- var _a;
90
- if (((_a = rle.routes) === null || _a === void 0 ? void 0 : _a.length) > 0) {
91
- this.sort(rle.routes);
92
- let cntApi = 0;
93
- rle.routes.forEach((r) => {
94
- if (!r.data.isLocal) {
95
- cntApi++;
96
- return;
97
- }
98
- if (cntApi <= Math.min(carouselSize, rle.routes.length)) {
99
- const loader = this.loadRouteDetais(r);
100
- promises.push(loader);
101
- }
102
- });
103
- }
104
- });
105
- yield Promise.allSettled(promises);
106
- this.emitPageUpdate();
107
- this.logEvent({ message: 'preloading routes finished' });
108
- });
109
- }
110
72
  load(props) {
111
73
  return __awaiter(this, void 0, void 0, function* () {
112
74
  const { routeId, list, loadFrom, loadTo } = props;
@@ -119,7 +81,7 @@ class RouteListService extends service_1.IncyclistService {
119
81
  if (list)
120
82
  lists.push(list);
121
83
  else
122
- lists = this.routes.map(rle => rle.list);
84
+ lists = this.lists.map(rle => rle.list);
123
85
  lists.forEach(l => {
124
86
  const routes = this.getRouteList(l);
125
87
  for (let i = loadFrom; i < loadTo; i++) {
@@ -137,12 +99,10 @@ class RouteListService extends service_1.IncyclistService {
137
99
  }
138
100
  this.stopAllRides();
139
101
  const page = this.registerPage(pageId, props);
140
- if (props.visibleCards) {
141
- this.preload(props.visibleCards);
142
- }
143
- else {
144
- this.preload(5);
145
- }
102
+ if (!this.state.preloadDone)
103
+ this.preload(props.visibleCards || 5);
104
+ else
105
+ this.onResize(pageId);
146
106
  return page.state;
147
107
  }
148
108
  closeRouteSelection(pageId) {
@@ -151,6 +111,20 @@ class RouteListService extends service_1.IncyclistService {
151
111
  this.state.pages.splice(idx, 1);
152
112
  }
153
113
  }
114
+ onResize(pageId) {
115
+ const page = this.getPage(pageId);
116
+ const routeIds = Object.keys(page.onCarouselStateChanged);
117
+ routeIds.forEach(id => {
118
+ var _a;
119
+ const route = this.getRoute(id);
120
+ const stateChange = (_a = page.onCarouselStateChanged[id]) === null || _a === void 0 ? void 0 : _a.onCarouselStateChanged;
121
+ if (stateChange)
122
+ stateChange({ initialized: true, visible: false });
123
+ const update = page.onRouteUpdate[id].onRouteStateChanged;
124
+ if (update)
125
+ update(route.data);
126
+ });
127
+ }
154
128
  openStartSettings(r) {
155
129
  var _a;
156
130
  const route = this.getRouteFromStartProps(r);
@@ -184,6 +158,8 @@ class RouteListService extends service_1.IncyclistService {
184
158
  }
185
159
  setCardUpdateHandler(pageId, r, list, idx, onRouteStateChanged, onCarouselStateChanged) {
186
160
  const route = this.getRouteFromStartProps(r);
161
+ if (!route)
162
+ return;
187
163
  const page = this.getPage(pageId);
188
164
  if (!page)
189
165
  return;
@@ -192,7 +168,6 @@ class RouteListService extends service_1.IncyclistService {
192
168
  }
193
169
  onCarouselInitialized(pageId, list, startIdx, visibleSize) {
194
170
  const page = this.getPage(pageId);
195
- console.log('~~~ DEBUG:onCarouselInitialied', pageId, list, this.getListState(page, list), startIdx, visibleSize);
196
171
  if (!page)
197
172
  return;
198
173
  const listState = this.getListState(page, list);
@@ -204,8 +179,6 @@ class RouteListService extends service_1.IncyclistService {
204
179
  routes.forEach((route) => {
205
180
  const { idx, onCarouselStateChanged } = page.onCarouselStateChanged[route.id] || {};
206
181
  const visible = listState !== undefined ? idx >= listState.startIdx && idx <= listState.endIdx : undefined;
207
- if (route.data.title === 'Malaga City Tour')
208
- console.log('~~~ DEBUG:Initialized -checked detail', visible, idx, listState.startIdx, listState.endIdx, route.data);
209
182
  if (onCarouselStateChanged)
210
183
  onCarouselStateChanged({ initialized: true, visible });
211
184
  if (visible && (route.data.state === 'prepared' || route.data.state === 'error')) {
@@ -215,7 +188,6 @@ class RouteListService extends service_1.IncyclistService {
215
188
  }
216
189
  onCarouselUpdated(pageId, list, startIdx, visibleSize) {
217
190
  const page = this.getPage(pageId);
218
- console.log('~~~ DEBUG:onCarouselUpdated', pageId, list, this.getListState(page, list), startIdx, visibleSize);
219
191
  if (!page)
220
192
  return;
221
193
  const listState = this.getListState(page, list);
@@ -227,8 +199,6 @@ class RouteListService extends service_1.IncyclistService {
227
199
  routes.forEach((route) => {
228
200
  const { idx, onCarouselStateChanged } = page.onCarouselStateChanged[route.id] || {};
229
201
  const visible = listState !== undefined ? idx >= listState.startIdx && idx <= listState.endIdx : undefined;
230
- if (route.data.title === 'Malaga City Tour')
231
- console.log('~~~ DEBUG:Initialized -checked detail', visible, idx, listState.startIdx, listState.endIdx, route.data);
232
202
  if (onCarouselStateChanged)
233
203
  onCarouselStateChanged({ initialized: true, visible });
234
204
  if (visible && route.data.state === 'prepared' || route.data.state === 'error') {
@@ -282,6 +252,44 @@ class RouteListService extends service_1.IncyclistService {
282
252
  }
283
253
  cancelDownload(routeId) {
284
254
  }
255
+ _preload(carouselSize = 5, detailsOnly = false) {
256
+ return __awaiter(this, void 0, void 0, function* () {
257
+ if (!detailsOnly) {
258
+ yield this.loadRouteDescriptions();
259
+ }
260
+ this.emitPageUpdate();
261
+ const lists = this.getRouteLists().map(li => li.list);
262
+ lists.forEach((list) => {
263
+ this.preloadList(list, carouselSize);
264
+ });
265
+ this.logEvent({ message: 'preloading routes finished' });
266
+ });
267
+ }
268
+ preloadList(list, carouselSize = 5) {
269
+ var _a;
270
+ return __awaiter(this, void 0, void 0, function* () {
271
+ this.emitPageUpdate();
272
+ const promises = [];
273
+ const rle = this.lists.find(l => l.list === list);
274
+ if (((_a = rle.routes) === null || _a === void 0 ? void 0 : _a.length) > 0) {
275
+ this.sort(rle.routes);
276
+ let cntApi = 0;
277
+ rle.routes.forEach((r) => {
278
+ if (!r.data.isLocal) {
279
+ cntApi++;
280
+ }
281
+ if (r.data.isLocal || cntApi <= Math.min(carouselSize, rle.routes.length)) {
282
+ promises.push(this.loadRouteDetais(r));
283
+ }
284
+ });
285
+ }
286
+ console.log('~~~ DEBUG: during preload', promises.length);
287
+ Promise.allSettled(promises).then((res) => {
288
+ console.log('~~~ DEBUG: after preload', (0, clone_1.default)(this.state), (0, clone_1.default)(this.routeDescriptions), res);
289
+ this.saveRouteDescriptions();
290
+ });
291
+ });
292
+ }
285
293
  loadStartSettingsFromUserSettings(route) {
286
294
  let startPos = 0, realityFactor = 100, segment;
287
295
  const { data } = route;
@@ -301,6 +309,10 @@ class RouteListService extends service_1.IncyclistService {
301
309
  if (prevRoutePosition !== null) {
302
310
  startPos = prevRoutePosition;
303
311
  }
312
+ if (data.videoFormat === 'avi') {
313
+ startPos = 0;
314
+ segment = undefined;
315
+ }
304
316
  return { startPos, realityFactor, segment };
305
317
  }
306
318
  writeStartSettingsToUserSettings(route, startSettings) {
@@ -335,10 +347,10 @@ class RouteListService extends service_1.IncyclistService {
335
347
  });
336
348
  }
337
349
  initRouteLists() {
338
- this.routes = [];
339
- this.routes.push({ list: "myRoutes", routes: [] });
340
- this.routes.push({ list: "selected", routes: [] });
341
- this.routes.push({ list: "alternatives", routes: [] });
350
+ this.lists = [];
351
+ this.lists.push({ list: "myRoutes", routes: [] });
352
+ this.lists.push({ list: "selected", routes: [] });
353
+ this.lists.push({ list: "alternatives", routes: [] });
342
354
  }
343
355
  getPageState(pageId, lang) {
344
356
  var _a;
@@ -347,6 +359,11 @@ class RouteListService extends service_1.IncyclistService {
347
359
  const language = lang || ((_a = this.getPage(pageId)) === null || _a === void 0 ? void 0 : _a.language) || 'en';
348
360
  const add = (list, listHeader) => {
349
361
  const data = this.getRouteList(list);
362
+ if (list === 'myRoutes' && !data.find(r => r.data.type === 'free-ride')) {
363
+ const card = new FreeRideCard_1.FreeRideCard('loaded');
364
+ const route = { data: card, id: 'free-ride', };
365
+ data.unshift(route);
366
+ }
350
367
  if ((data === null || data === void 0 ? void 0 : data.length) > 0) {
351
368
  lists.push(this.getRouteListDataEntry(list, data, language, listHeader));
352
369
  }
@@ -354,6 +371,7 @@ class RouteListService extends service_1.IncyclistService {
354
371
  add('myRoutes', 'My Routes');
355
372
  add('selected', 'Selected For Me');
356
373
  add('alternatives', 'Alternatives');
374
+ console.log('~~~ DEBUG:getPageState', state);
357
375
  return state;
358
376
  }
359
377
  registerPage(pageId, props) {
@@ -407,7 +425,9 @@ class RouteListService extends service_1.IncyclistService {
407
425
  local.distance = local.distance || details.distance;
408
426
  local.elevation = local.elevation || details.elevation;
409
427
  local.localizedTitle = local.localizedTitle || details.localizedTitle;
410
- local.points = details.points;
428
+ if (!local.points) {
429
+ local.points = details.points;
430
+ }
411
431
  if (!local.hasGpx && details.points) {
412
432
  local.hasGpx = true;
413
433
  }
@@ -434,7 +454,7 @@ class RouteListService extends service_1.IncyclistService {
434
454
  }
435
455
  convertDescription(descr, isLocal) {
436
456
  const { id, title, localizedTitle, country, distance, elevation, category, provider, video, points } = descr;
437
- const data = { state: 'prepared', id, title, localizedTitle, country, distance, elevation, provider, category };
457
+ const data = { type: 'route', state: 'prepared', id, title, localizedTitle, country, distance, elevation, provider, category };
438
458
  data.hasVideo = false;
439
459
  data.hasGpx = false;
440
460
  data.isLocal = isLocal || false;
@@ -460,7 +480,6 @@ class RouteListService extends service_1.IncyclistService {
460
480
  if (description.category === 'personal')
461
481
  return 'myRoutes';
462
482
  list = description.category ? 'alternatives' : 'selected';
463
- console.log('~~~ DEBUG', description.title, description.private, list);
464
483
  return list;
465
484
  }
466
485
  if (description.type === 'video') {
@@ -478,7 +497,7 @@ class RouteListService extends service_1.IncyclistService {
478
497
  }
479
498
  getRouteList(list) {
480
499
  var _a;
481
- return (_a = this.routes.find(rle => rle.list === list)) === null || _a === void 0 ? void 0 : _a.routes;
500
+ return (_a = this.lists.find(rle => rle.list === list)) === null || _a === void 0 ? void 0 : _a.routes;
482
501
  }
483
502
  getRouteLists() {
484
503
  const lists = [
@@ -492,19 +511,36 @@ class RouteListService extends service_1.IncyclistService {
492
511
  let compareFn = criteria;
493
512
  if (typeof criteria === 'string')
494
513
  compareFn = v => v.id === criteria;
495
- const lists = this.routes;
514
+ const lists = this.lists;
496
515
  let route = null;
497
516
  for (let i = 0; i < lists.length && !route; i++) {
498
517
  route = lists[i].routes.find(compareFn);
499
518
  }
500
519
  return route;
501
520
  }
521
+ getAllRoutes() {
522
+ const lists = this.lists;
523
+ const routes = [];
524
+ for (let i = 0; i < lists.length; i++) {
525
+ routes.push(...lists[i].routes);
526
+ }
527
+ return routes;
528
+ }
502
529
  stopAllRides() {
503
- const lists = this.routes;
530
+ const lists = this.lists;
504
531
  for (let i = 0; i < lists.length; i++) {
505
532
  lists[i].routes.forEach(r => r.startState = 'idle');
506
533
  }
507
534
  }
535
+ addDescriptionsFromDB(descriptions) {
536
+ const ids = Object.keys(descriptions);
537
+ const routes = ids.map(id => descriptions[id]);
538
+ this.routeDescriptions = descriptions;
539
+ routes.forEach((data) => {
540
+ data.state = "prepared";
541
+ this.addRouteToList(data.list, { data, id: data.id });
542
+ });
543
+ }
508
544
  addDescriptionsFromRepo(descriptions, repo) {
509
545
  const routes = descriptions;
510
546
  if (repo) {
@@ -526,13 +562,13 @@ class RouteListService extends service_1.IncyclistService {
526
562
  data.isLocal = true;
527
563
  const item = { id: data.id, data };
528
564
  this.addRouteToList(list, item);
565
+ this.routeDescriptions[data.id] = Object.assign(Object.assign({}, data), { list });
529
566
  }
530
567
  });
531
568
  }
532
569
  addDescriptionsFromServer(descriptions) {
533
570
  const converted = descriptions.map(this.convertDescription.bind(this));
534
571
  converted.forEach((data, idx) => {
535
- console.log('~~~ DEBUG:add route from Server', data.title);
536
572
  const existing = this.getRoute(data.id);
537
573
  if (existing) {
538
574
  return;
@@ -551,6 +587,7 @@ class RouteListService extends service_1.IncyclistService {
551
587
  data.isLocal = false;
552
588
  const item = { id: data.id, data };
553
589
  this.addRouteToList(list, item);
590
+ this.routeDescriptions[data.id] = Object.assign(Object.assign({}, data), { list });
554
591
  this.emitPageUpdate();
555
592
  }
556
593
  });
@@ -594,30 +631,53 @@ class RouteListService extends service_1.IncyclistService {
594
631
  }
595
632
  loadRouteDescriptionsFromRepo() {
596
633
  return __awaiter(this, void 0, void 0, function* () {
597
- console.log('~~~ DEBUG:loadRouteDescriptionsFromRepo');
598
- const enrichRepo = (v, type) => {
599
- const data = v;
600
- return Object.assign({ type }, data);
601
- };
602
- const videos = ((yield this.getVideosRepo().read('routes')) || []);
603
- console.log('~~~ DEBUG,videos:', videos);
604
- this.addDescriptionsFromRepo(videos, this.getVideosRepo());
605
- const routes = ((yield this.getRoutesRepo().read('routes')) || []);
606
- console.log('~~~ DEBUG,routes:', routes);
607
- this.addDescriptionsFromRepo(routes, this.getVideosRepo());
634
+ if (Object.keys(this.routeDescriptions).length > 0) {
635
+ this.emitPageUpdate();
636
+ return;
637
+ }
638
+ const all = yield this.getRoutesRepo().read('settings');
639
+ if (!all) {
640
+ const videos = ((yield this.getVideosRepo().read('routes')) || []);
641
+ this.addDescriptionsFromRepo(videos, this.getVideosRepo());
642
+ const routes = ((yield this.getRoutesRepo().read('routes')) || []);
643
+ this.addDescriptionsFromRepo(routes, this.getRoutesRepo());
644
+ }
645
+ else {
646
+ const ids = Object.keys(all);
647
+ ids.forEach(id => {
648
+ const r = all[id];
649
+ if (typeof r.points === 'string') {
650
+ this.decodePoints(r);
651
+ }
652
+ if (!Array.isArray(r.points))
653
+ delete r.points;
654
+ });
655
+ console.log('~~~ DEBUG:', all);
656
+ this.addDescriptionsFromDB(all);
657
+ }
658
+ this.saveRouteDescriptions();
659
+ });
660
+ }
661
+ saveRouteDescriptions() {
662
+ return __awaiter(this, void 0, void 0, function* () {
663
+ const ids = Object.keys(this.routeDescriptions);
664
+ const data = ids.map(id => {
665
+ const r = this.routeDescriptions[id];
666
+ if (r.points)
667
+ this.encodePoints(r);
668
+ return Object.assign({}, r);
669
+ });
670
+ yield this.getRoutesRepo().write('settings', data);
608
671
  });
609
672
  }
610
673
  loadRouteDescriptionsFromServer() {
611
674
  return __awaiter(this, void 0, void 0, function* () {
612
- console.log('~~~ DEBUG:loadRouteDescriptionsFromServer');
613
675
  const enrichApi = (v, type) => (Object.assign({ type }, v));
614
676
  const api = [
615
677
  this.api.getRouteDescriptions({ type: 'gpx' }).then(v => v.map(e => enrichApi(e, 'gpx'))),
616
678
  this.api.getRouteDescriptions({ type: 'video' }).then(v => v.map(e => enrichApi(e, 'video')))
617
679
  ];
618
680
  const res = yield Promise.allSettled(api);
619
- console.log('~~~ DEBUG:loadRouteDescriptionsFromServer done', (new Date()).toISOString(), res);
620
- this.emitPageUpdate();
621
681
  res.forEach(p => {
622
682
  if (p.status === 'fulfilled') {
623
683
  this.addDescriptionsFromServer(p.value);
@@ -627,34 +687,45 @@ class RouteListService extends service_1.IncyclistService {
627
687
  }
628
688
  loadRouteDescriptions() {
629
689
  return __awaiter(this, void 0, void 0, function* () {
690
+ console.log('~~~ DEBUG:loadRouteDescriptions', Object.keys(this.routeDescriptions));
630
691
  yield this.loadRouteDescriptionsFromRepo();
631
- console.log('~~~ DEBUG, routes after Repo', this.routes);
632
692
  this.emitPageUpdate();
633
- this.loadRouteDescriptionsFromServer().then(() => {
634
- console.log('~~~ DEBUG, routes after API', this.routes);
635
- });
693
+ if (this.isDescriptionsLoaded()) {
694
+ yield this.loadRouteDescriptionsFromServer();
695
+ this.emitPageUpdate();
696
+ this.saveRouteDescriptions();
697
+ }
698
+ else {
699
+ this.loadRouteDescriptionsFromServer().then(() => {
700
+ this.emitPageUpdate();
701
+ });
702
+ }
636
703
  });
637
704
  }
638
705
  loadRouteDetais(route) {
639
706
  return __awaiter(this, void 0, void 0, function* () {
640
- console.log('~~~ DEBUG:loadDetails()', (0, clone_1.default)(route.data));
641
- if (route.data.state === 'loaded')
707
+ if (route.data.state === 'loaded' || route.data.state === 'loading')
642
708
  return;
643
709
  try {
644
710
  route.data.state = 'loading';
645
711
  this.emitRouteUpdated(route);
646
- if (route.data.isLocal) {
712
+ try {
647
713
  route.details = route.data.hasVideo ?
648
- yield this.videosRepo.read(route.id)
714
+ yield this.getVideosRepo().read(route.id)
649
715
  :
650
- yield this.routesRepo.read(route.id);
716
+ yield this.getRoutesRepo().read(route.id);
651
717
  api_1.default.verify(route.details);
652
718
  }
653
- else {
719
+ catch (err) {
720
+ console.log('~~~ DEBUG:error', route.data.title, route.data.isLocal, err);
721
+ if (route.data.isLocal)
722
+ throw err;
723
+ }
724
+ if (!route.details) {
654
725
  route.details = yield this.api.getRouteDetails(route.id);
726
+ api_1.default.verify(route.details);
727
+ this.saveRouteDetails(route);
655
728
  }
656
- if (route.data.title === 'Arnbach')
657
- console.log('~~~ DEBUG: loaded details', route.details);
658
729
  if (route.details.id !== route.data.id) {
659
730
  route.data.state = 'error';
660
731
  delete route.details;
@@ -663,14 +734,30 @@ class RouteListService extends service_1.IncyclistService {
663
734
  this.mergeWithDetails(route);
664
735
  route.data.state = 'loaded';
665
736
  this.emitRouteUpdated(route);
737
+ const { list } = this.routeDescriptions[route.id];
738
+ this.routeDescriptions[route.id] = Object.assign(Object.assign({}, route.data), { list });
739
+ return route.data;
666
740
  }
667
741
  catch (e) {
668
- console.log('~~~ DEBUG:Error', e);
669
742
  route.data.state = 'error';
670
743
  this.emitRouteUpdated(route);
671
744
  }
672
745
  });
673
746
  }
747
+ saveRouteDetails(route) {
748
+ return __awaiter(this, void 0, void 0, function* () {
749
+ try {
750
+ route.details = route.data.hasVideo ?
751
+ yield this.videosRepo.write(route.id, route.details)
752
+ :
753
+ yield this.routesRepo.write(route.id, route.details);
754
+ }
755
+ catch (err) {
756
+ if (route.data.isLocal)
757
+ throw err;
758
+ }
759
+ });
760
+ }
674
761
  emitRouteUpdated(route) {
675
762
  this.emit('route-update', route.id, route.data);
676
763
  this.state.pages.forEach(page => {
@@ -688,7 +775,6 @@ class RouteListService extends service_1.IncyclistService {
688
775
  });
689
776
  }
690
777
  getListState(page, list) {
691
- console.log('~~~ DEBUG.getListState', list, page === null || page === void 0 ? void 0 : page.state, this.state);
692
778
  return page === null || page === void 0 ? void 0 : page.state.lists.find(pl => pl.list === list);
693
779
  }
694
780
  getRouteFromStartProps(r) {
@@ -707,6 +793,15 @@ class RouteListService extends service_1.IncyclistService {
707
793
  return;
708
794
  }
709
795
  }
796
+ isDescriptionsLoaded() {
797
+ return Object.keys(this.routeDescriptions).length === 0;
798
+ }
799
+ decodePoints(r) {
800
+ return;
801
+ }
802
+ encodePoints(r) {
803
+ return;
804
+ }
710
805
  }
711
806
  exports.RouteListService = RouteListService;
712
807
  const useRouteList = () => RouteListService.getInstance();
@@ -12,10 +12,14 @@ export type RouteListStartProps = {
12
12
  export type LocalizedText = {
13
13
  [index: string]: string;
14
14
  };
15
- export type RouteInfo = {
15
+ export type CardType = 'route' | 'free-ride' | 'import';
16
+ export interface CardInfo {
17
+ type: CardType;
16
18
  state: RouteState;
17
- id?: string;
18
19
  title?: string;
20
+ }
21
+ export interface RouteInfo extends CardInfo {
22
+ id?: string;
19
23
  localizedTitle?: LocalizedText;
20
24
  country?: string;
21
25
  isLoop?: boolean;
@@ -31,8 +35,21 @@ export type RouteInfo = {
31
35
  videoFormat?: string;
32
36
  videoUrl?: string;
33
37
  previewUrl?: string;
34
- points?: Array<RoutePoint>;
38
+ points?: Array<RoutePoint> | string;
35
39
  segments?: Array<RouteSegment>;
40
+ }
41
+ export interface RouteDBEntry extends RouteInfo {
42
+ list: List;
43
+ settings?: {
44
+ position?: number;
45
+ segment?: string;
46
+ realityFactor?: number;
47
+ };
48
+ cntStarts?: number;
49
+ lastStart?: number;
50
+ }
51
+ export type RoutesDB = {
52
+ [index: string]: RouteDBEntry;
36
53
  };
37
54
  export type RouteListDateEntry = {
38
55
  list: List;
@@ -87,17 +104,20 @@ export interface InternalRouteListState {
87
104
  initialized: boolean;
88
105
  pages: Array<Page>;
89
106
  loading?: LoadingState;
107
+ preloadDone: boolean;
90
108
  }
91
109
  export type RouteStartState = 'idle' | 'preparing' | 'selected' | 'started';
92
- export type Route = {
110
+ export type Card<T> = {
93
111
  id: string;
94
- data: RouteInfo;
112
+ data: T;
95
113
  details?: RouteApiDetail;
96
114
  startSettings?: RouteStartSettings;
97
115
  startState?: RouteStartState;
98
116
  };
99
- export type RouteListEntry = {
117
+ export type CardListEntry<T> = {
100
118
  list: List;
101
- routes: Array<Route>;
119
+ routes: Array<Card<T>>;
102
120
  };
121
+ export type Route = Card<RouteInfo>;
122
+ export type RouteListEntry = CardListEntry<RouteInfo>;
103
123
  export type List = 'myRoutes' | 'alternatives' | 'selected';
package/lib/utils/geo.js CHANGED
@@ -47,9 +47,11 @@ const getPointAfter = (p1, p2, offset) => {
47
47
  };
48
48
  exports.getPointAfter = getPointAfter;
49
49
  const getLatLng = (position) => {
50
- if (Array.isArray(position))
50
+ if (Array.isArray(position)) {
51
51
  return { lat: position[0], lng: position[1] };
52
- if (position.lat !== undefined && position.lng !== undefined)
52
+ }
53
+ if (position.lat !== undefined && position.lng !== undefined) {
53
54
  return position;
55
+ }
54
56
  };
55
57
  exports.getLatLng = getLatLng;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-services",
3
- "version": "1.0.57",
3
+ "version": "1.0.58",
4
4
  "peerDependencies": {
5
5
  "gd-eventlog": "^0.1.26"
6
6
  },