incyclist-services 1.7.58 → 1.7.60

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.
@@ -222,7 +222,7 @@ let DeviceAccessService = (() => {
222
222
  this.interfaces[ifaceName].state = 'connecting';
223
223
  this.emit('interface-changed', ifaceName, { ...this.interfaces[ifaceName], state: 'connecting' });
224
224
  await impl.disconnect();
225
- const connected = await impl.connect();
225
+ const connected = await impl.connect(true);
226
226
  const state = connected ? 'connected' : 'disconnected';
227
227
  this.interfaces[ifaceName].state = state;
228
228
  this.emit('interface-changed', ifaceName, this.interfaces[ifaceName]);
@@ -205,9 +205,10 @@ let DevicePairingService = (() => {
205
205
  async prepareStart(adapterFilter = []) {
206
206
  const stillPairing = this.isPairing();
207
207
  const stillScanning = this.isScanning();
208
+ const adapters = this.state?.adapters ?? [];
208
209
  this.logEvent({ message: 'Stop Pairing (OK)', stillPairing, stillScanning });
209
210
  try {
210
- this.pauseAdapters(this.state.adapters.filter(a => !adapterFilter.includes(a.udid)));
211
+ this.pauseAdapters(adapters.filter(a => !adapterFilter.includes(a.udid)));
211
212
  if (this.isPairing()) {
212
213
  this.removePairingCallbacks();
213
214
  }
@@ -28,15 +28,16 @@ class OptionManager {
28
28
  for (const segment of segments ?? []) {
29
29
  try {
30
30
  const path = segment.path;
31
- const opts = await this.getNextOptions(segment);
31
+ const result = await this.getNextOptions(segment);
32
+ const opts = result.options;
32
33
  if (opts?.length === 1) {
33
34
  if (opts[0].id === segment.id) {
34
35
  (0, utils_1.concatPaths)(path, opts[0].path, 'after', opts[0].id);
35
36
  }
36
37
  else {
37
38
  let foundSameSegment = false;
38
- opts[0].path.forEach((point, j) => {
39
- if (j === 0)
39
+ opts[0].path.forEach((point) => {
40
+ if (point === opts[0].path[0])
40
41
  return;
41
42
  foundSameSegment = points.some(pAll => pAll.id === point.id);
42
43
  });
@@ -58,7 +59,7 @@ class OptionManager {
58
59
  async getNextOptions(from, props) {
59
60
  try {
60
61
  if (from?.id === undefined || from?.path === undefined || from?.path.length < 1) {
61
- return [];
62
+ return { options: [], isValid: true };
62
63
  }
63
64
  const lastPoint = from.path.length > 0 ? from.path.at(-1) : undefined;
64
65
  const fromWayId = lastPoint?.wayId ?? from.id;
@@ -68,7 +69,7 @@ class OptionManager {
68
69
  const query = { id: from.id, path: from.path, map: map };
69
70
  originalWay = this.getWay(query);
70
71
  if (!originalWay) {
71
- return [];
72
+ return { options: [], isValid: true };
72
73
  }
73
74
  }
74
75
  const way = { ...originalWay, path: from.path };
@@ -77,13 +78,13 @@ class OptionManager {
77
78
  if (way.path.length > 1)
78
79
  location = way.path.at(-2);
79
80
  else {
80
- return [];
81
+ return { options: [], isValid: true };
81
82
  }
82
83
  }
83
84
  const map = await this.service.load(location);
84
85
  if (!map) {
85
86
  if (!this.map?.isWithinBoundary(location))
86
- return [];
87
+ return { options: [], isValid: true };
87
88
  }
88
89
  else {
89
90
  this.setMap(map);
@@ -118,11 +119,11 @@ class OptionManager {
118
119
  const fromNode = from.path?.at(-2);
119
120
  options = options.filter(o => o.path.length > 0 && o.path[1].id !== fromNode.id);
120
121
  }
121
- return options;
122
+ return { options, isValid: true };
122
123
  }
123
124
  catch (err) {
124
125
  this.logError(err, 'getNextOptions');
125
- return [];
126
+ return { options: [], isValid: false };
126
127
  }
127
128
  }
128
129
  async checkMinDistance(options, minDistance) {
@@ -130,8 +131,8 @@ class OptionManager {
130
131
  if (option?.path?.length > 0) {
131
132
  const distance = this.getDistance(option.path);
132
133
  if (distance < minDistance) {
133
- const branches = await this.getNextOptions(option, { minDistance: minDistance - distance });
134
- option.options = branches ?? [];
134
+ const result = await this.getNextOptions(option, { minDistance: minDistance - distance });
135
+ option.options = result.options ?? [];
135
136
  }
136
137
  }
137
138
  }
@@ -619,25 +619,30 @@ let RideDisplayService = (() => {
619
619
  this.adjustCurrentStepDuration(time, newDuration);
620
620
  this.updateActivityWorkout();
621
621
  }
622
- adjustPower(increase, large) {
622
+ adjustPower(increase, large, gearFallback = true) {
623
623
  try {
624
624
  if (this.getWorkoutRide().inUse()) {
625
625
  const inc = large ? 5 : 1;
626
- if (increase)
627
- this.getWorkoutRide().powerUp(inc);
628
- else
629
- this.getWorkoutRide().powerDown(inc);
626
+ this.adjustWorkout(increase, inc);
630
627
  }
631
628
  else {
632
629
  const sgn = increase ? 1 : -1;
633
630
  const inc = large ? sgn * 50 : sgn * 5;
634
- this.devicePowerUp(inc);
631
+ this.devicePowerUp(inc, gearFallback);
635
632
  }
636
633
  }
637
634
  catch (err) {
638
635
  this.logError(err, 'adjustPower');
639
636
  }
640
637
  }
638
+ adjustWorkout(increase, inc) {
639
+ if (this.getWorkoutRide().inUse()) {
640
+ if (increase)
641
+ this.getWorkoutRide().powerUp(inc);
642
+ else
643
+ this.getWorkoutRide().powerDown(inc);
644
+ }
645
+ }
641
646
  adjustCurrentStepDuration(time, newDuration) {
642
647
  let delta = 0;
643
648
  this.actualWorkout?.steps?.forEach(s => {
@@ -845,7 +850,7 @@ let RideDisplayService = (() => {
845
850
  devices.off('cycling-mode-toggle', h.toggleCyclingModeHandler);
846
851
  delete this.startDeviceHandlers;
847
852
  }
848
- devicePowerUp(inc) {
853
+ devicePowerUp(inc, gearFallback = true) {
849
854
  const device = this.getDeviceRide().getControlAdapter();
850
855
  if (!device)
851
856
  return;
@@ -861,9 +866,12 @@ let RideDisplayService = (() => {
861
866
  if (Math.abs(gearDelta) > 1) {
862
867
  gearDelta = Math.sign(gearDelta) * 5;
863
868
  }
864
- this.getRideModeService().sendUpdate({ gearDelta });
869
+ this.gearChange(gearDelta);
865
870
  }
866
871
  }
872
+ gearChange(gearDelta) {
873
+ this.getRideModeService().sendUpdate({ gearDelta });
874
+ }
867
875
  simulatorPowerUp(mode, powerInc) {
868
876
  const simMode = mode.getSetting('mode');
869
877
  let request = {};
@@ -985,6 +993,22 @@ let RideDisplayService = (() => {
985
993
  this.adjustPower(true, duration > 1000);
986
994
  else if (key === 'down')
987
995
  this.adjustPower(false, duration > 1000);
996
+ else if (key == 'r-shift-up')
997
+ this.gearChange(1);
998
+ else if (key == 'r-shift-down')
999
+ this.gearChange(-1);
1000
+ else if (key == 'l-shift-up')
1001
+ this.gearChange(5);
1002
+ else if (key == 'l-shift-down')
1003
+ this.gearChange(-5);
1004
+ else if (key == 'y')
1005
+ this.adjustPower(true, duration > 1000, false);
1006
+ else if (key == 'b')
1007
+ this.adjustPower(false, duration > 1000, false);
1008
+ else if (key == 'a')
1009
+ this.forward();
1010
+ else if (key == 'z')
1011
+ this.backward();
988
1012
  }
989
1013
  startListeningForDeviceData() {
990
1014
  const { deviceDataHandler } = this.startDeviceHandlers ?? {};
@@ -292,7 +292,8 @@ let RidePageService = (() => {
292
292
  startOverlayProps: isStarting ? this.getRideDisplay().getStartOverlayProps() : null,
293
293
  menuProps: this.menuProps,
294
294
  rideView: props.rideView,
295
- route: props.route
295
+ route: props.route,
296
+ displayObserver: props.displayObserver
296
297
  };
297
298
  return displayProps;
298
299
  }
@@ -422,11 +422,11 @@ let FreeRideDisplayService = (() => {
422
422
  this.logError(err, 'getNextOptions');
423
423
  options = undefined;
424
424
  }
425
- if (!options?.length && !finished && !forStart) {
425
+ if (!options?.length && !finished && !forStart && !freeRide.isLastQueryValid()) {
426
426
  this.logEvent({ message: 'no options available - retry in 3000ms', });
427
427
  await (0, sleep_1.sleep)(3000);
428
428
  }
429
- } while (!options?.length && !finished && !forStart);
429
+ } while (!options?.length && !finished && !forStart && !freeRide.isLastQueryValid());
430
430
  this.internalEmitter.off('stop-query', setFinished);
431
431
  };
432
432
  return new Promise(done => {
@@ -103,6 +103,8 @@ let GpxDisplayService = (() => {
103
103
  displayPosition: this.mapLoaded ? null : this.position,
104
104
  sideViews
105
105
  };
106
+ if (this.isMobile()) {
107
+ }
106
108
  return props;
107
109
  }
108
110
  getSatelliteViewProps() {
@@ -120,7 +122,7 @@ let GpxDisplayService = (() => {
120
122
  }
121
123
  getStartOverlayProps() {
122
124
  const rideView = this.getRideView();
123
- if (rideView === 'map') {
125
+ if (rideView === 'map' || this.isMobile()) {
124
126
  return {
125
127
  mapType: this.getRideViewName(),
126
128
  mapState: 'Loaded'
@@ -134,7 +136,7 @@ let GpxDisplayService = (() => {
134
136
  }
135
137
  isStartRideCompleted() {
136
138
  const rideView = this.getRideView();
137
- if (rideView === 'map') {
139
+ if (rideView === 'map' || this.isMobile()) {
138
140
  this.mapLoaded = true;
139
141
  return true;
140
142
  }
@@ -159,7 +161,7 @@ let GpxDisplayService = (() => {
159
161
  }
160
162
  getRideView() {
161
163
  if (this.isMobile())
162
- return 'map';
164
+ return 'sv';
163
165
  const rideView = this.getUserSettings().get('preferences.rideView', 'sv');
164
166
  return rideView;
165
167
  }
@@ -75,6 +75,7 @@ let FreeRideService = (() => {
75
75
  selectedOption;
76
76
  options;
77
77
  currentSegment;
78
+ lastOptionsQueryValid = true;
78
79
  constructor() {
79
80
  super('FreeRide');
80
81
  this.options = [];
@@ -137,7 +138,12 @@ let FreeRideService = (() => {
137
138
  }
138
139
  async loadNextOptions(from, forStart) {
139
140
  const optionManager = (0, service_2.useMapArea)().getOptionManager();
140
- let opts = await optionManager.getNextOptions(from) ?? [];
141
+ const result = await optionManager.getNextOptions(from);
142
+ let opts = result.options;
143
+ this.lastOptionsQueryValid = result.isValid;
144
+ if (!result.isValid) {
145
+ return [];
146
+ }
141
147
  opts = this.evaluateOptions(opts, from);
142
148
  if (forStart)
143
149
  await this.getNextLevelOptions(opts);
@@ -289,8 +295,11 @@ let FreeRideService = (() => {
289
295
  let segment = o;
290
296
  let done = false;
291
297
  let isSingle = false;
298
+ let lastHandlerWasNoOption = false;
292
299
  do {
293
- const nextOpts = await optionManager.getNextOptions(segment);
300
+ const nextOptsResult = await optionManager.getNextOptions(segment);
301
+ const nextOpts = nextOptsResult.options;
302
+ const isValid = nextOptsResult.isValid;
294
303
  const handleSingleOption = () => {
295
304
  if (segmentUpdated)
296
305
  return;
@@ -315,6 +324,11 @@ let FreeRideService = (() => {
315
324
  done = true;
316
325
  return;
317
326
  }
327
+ if (lastHandlerWasNoOption) {
328
+ o.options = [];
329
+ done = true;
330
+ return;
331
+ }
318
332
  try {
319
333
  const map = segment.map ?? this.getMapArea().getMap(segment.path[0]);
320
334
  const newPath = [];
@@ -343,13 +357,21 @@ let FreeRideService = (() => {
343
357
  };
344
358
  if (nextOpts?.length === 1) {
345
359
  handleSingleOption();
360
+ lastHandlerWasNoOption = false;
346
361
  }
347
- else if (!nextOpts?.length) {
362
+ else if (!nextOpts?.length && isValid) {
348
363
  handleNoOption();
364
+ lastHandlerWasNoOption = true;
365
+ }
366
+ else if (!nextOpts?.length && !isValid) {
367
+ o.options = [];
368
+ done = true;
369
+ lastHandlerWasNoOption = false;
349
370
  }
350
371
  else {
351
372
  o.options = nextOpts;
352
373
  done = true;
374
+ lastHandlerWasNoOption = false;
353
375
  }
354
376
  await (0, utils_2.waitNextTick)();
355
377
  } while (!done);
@@ -364,6 +386,9 @@ let FreeRideService = (() => {
364
386
  getOptions() {
365
387
  return this.options;
366
388
  }
389
+ isLastQueryValid() {
390
+ return this.lastOptionsQueryValid;
391
+ }
367
392
  selectOption(option) {
368
393
  const current = this.selectedOption;
369
394
  const options = this.options ?? [];
@@ -183,15 +183,18 @@ let UserInterfaceServcie = (() => {
183
183
  this.isTerminated = false;
184
184
  this.isTerminating = false;
185
185
  try {
186
+ let resumeRequired = true;
187
+ if (this.backgroundTimer) {
188
+ clearTimeout(this.backgroundTimer);
189
+ resumeRequired = false;
190
+ }
191
+ this.logEvent({ message: 'onAppResume called' });
186
192
  this.startHeartbeatWorker();
187
193
  this.backgroundPausedByService = false;
188
194
  this.appState = 'Active';
189
- this.logEvent({ message: 'onAppResume called' });
190
- if (this.backgroundTimer) {
191
- clearTimeout(this.backgroundTimer);
192
- return;
195
+ if (resumeRequired) {
196
+ this.resume();
193
197
  }
194
- this.resume();
195
198
  }
196
199
  catch (err) {
197
200
  this.logError(err, 'onAppPause');
@@ -228,15 +231,15 @@ let UserInterfaceServcie = (() => {
228
231
  }
229
232
  async pause() {
230
233
  await pages_1.IncyclistPageService.pausePage();
231
- (0, devices_1.useDeviceAccess)().disconnect();
234
+ await (0, devices_1.useDeviceAccess)().disconnect();
232
235
  if (this.getMessageQueue().disconnect)
233
236
  this.getMessageQueue().disconnect();
234
237
  }
235
- resume() {
238
+ async resume() {
236
239
  if (this.getMessageQueue().connect)
237
240
  this.getMessageQueue().connect();
238
- (0, devices_1.useDeviceAccess)().connect();
239
- pages_1.IncyclistPageService.resumePage();
241
+ await (0, devices_1.useDeviceAccess)().connect();
242
+ await pages_1.IncyclistPageService.resumePage();
240
243
  }
241
244
  startHeartbeatWorker() {
242
245
  if (this.heartbeatIv)
@@ -442,7 +442,7 @@ class VideoSyncHelper extends service_1.IncyclistService {
442
442
  }
443
443
  const s0 = mapping.distance;
444
444
  const v = mapping.videoSpeed / 3.6;
445
- const t = mapping.time + (distance - s0) / v;
445
+ const t = mapping.time === 0 ? mapping.time + distance / v : mapping.time + (distance - s0) / v;
446
446
  if (!Number.isNaN(t)) {
447
447
  return t;
448
448
  }
@@ -216,7 +216,7 @@ let DeviceAccessService = (() => {
216
216
  this.interfaces[ifaceName].state = 'connecting';
217
217
  this.emit('interface-changed', ifaceName, { ...this.interfaces[ifaceName], state: 'connecting' });
218
218
  await impl.disconnect();
219
- const connected = await impl.connect();
219
+ const connected = await impl.connect(true);
220
220
  const state = connected ? 'connected' : 'disconnected';
221
221
  this.interfaces[ifaceName].state = state;
222
222
  this.emit('interface-changed', ifaceName, this.interfaces[ifaceName]);
@@ -199,9 +199,10 @@ let DevicePairingService = (() => {
199
199
  async prepareStart(adapterFilter = []) {
200
200
  const stillPairing = this.isPairing();
201
201
  const stillScanning = this.isScanning();
202
+ const adapters = this.state?.adapters ?? [];
202
203
  this.logEvent({ message: 'Stop Pairing (OK)', stillPairing, stillScanning });
203
204
  try {
204
- this.pauseAdapters(this.state.adapters.filter(a => !adapterFilter.includes(a.udid)));
205
+ this.pauseAdapters(adapters.filter(a => !adapterFilter.includes(a.udid)));
205
206
  if (this.isPairing()) {
206
207
  this.removePairingCallbacks();
207
208
  }
@@ -22,15 +22,16 @@ export class OptionManager {
22
22
  for (const segment of segments ?? []) {
23
23
  try {
24
24
  const path = segment.path;
25
- const opts = await this.getNextOptions(segment);
25
+ const result = await this.getNextOptions(segment);
26
+ const opts = result.options;
26
27
  if (opts?.length === 1) {
27
28
  if (opts[0].id === segment.id) {
28
29
  concatPaths(path, opts[0].path, 'after', opts[0].id);
29
30
  }
30
31
  else {
31
32
  let foundSameSegment = false;
32
- opts[0].path.forEach((point, j) => {
33
- if (j === 0)
33
+ opts[0].path.forEach((point) => {
34
+ if (point === opts[0].path[0])
34
35
  return;
35
36
  foundSameSegment = points.some(pAll => pAll.id === point.id);
36
37
  });
@@ -52,7 +53,7 @@ export class OptionManager {
52
53
  async getNextOptions(from, props) {
53
54
  try {
54
55
  if (from?.id === undefined || from?.path === undefined || from?.path.length < 1) {
55
- return [];
56
+ return { options: [], isValid: true };
56
57
  }
57
58
  const lastPoint = from.path.length > 0 ? from.path.at(-1) : undefined;
58
59
  const fromWayId = lastPoint?.wayId ?? from.id;
@@ -62,7 +63,7 @@ export class OptionManager {
62
63
  const query = { id: from.id, path: from.path, map: map };
63
64
  originalWay = this.getWay(query);
64
65
  if (!originalWay) {
65
- return [];
66
+ return { options: [], isValid: true };
66
67
  }
67
68
  }
68
69
  const way = { ...originalWay, path: from.path };
@@ -71,13 +72,13 @@ export class OptionManager {
71
72
  if (way.path.length > 1)
72
73
  location = way.path.at(-2);
73
74
  else {
74
- return [];
75
+ return { options: [], isValid: true };
75
76
  }
76
77
  }
77
78
  const map = await this.service.load(location);
78
79
  if (!map) {
79
80
  if (!this.map?.isWithinBoundary(location))
80
- return [];
81
+ return { options: [], isValid: true };
81
82
  }
82
83
  else {
83
84
  this.setMap(map);
@@ -112,11 +113,11 @@ export class OptionManager {
112
113
  const fromNode = from.path?.at(-2);
113
114
  options = options.filter(o => o.path.length > 0 && o.path[1].id !== fromNode.id);
114
115
  }
115
- return options;
116
+ return { options, isValid: true };
116
117
  }
117
118
  catch (err) {
118
119
  this.logError(err, 'getNextOptions');
119
- return [];
120
+ return { options: [], isValid: false };
120
121
  }
121
122
  }
122
123
  async checkMinDistance(options, minDistance) {
@@ -124,8 +125,8 @@ export class OptionManager {
124
125
  if (option?.path?.length > 0) {
125
126
  const distance = this.getDistance(option.path);
126
127
  if (distance < minDistance) {
127
- const branches = await this.getNextOptions(option, { minDistance: minDistance - distance });
128
- option.options = branches ?? [];
128
+ const result = await this.getNextOptions(option, { minDistance: minDistance - distance });
129
+ option.options = result.options ?? [];
129
130
  }
130
131
  }
131
132
  }
@@ -616,25 +616,30 @@ let RideDisplayService = (() => {
616
616
  this.adjustCurrentStepDuration(time, newDuration);
617
617
  this.updateActivityWorkout();
618
618
  }
619
- adjustPower(increase, large) {
619
+ adjustPower(increase, large, gearFallback = true) {
620
620
  try {
621
621
  if (this.getWorkoutRide().inUse()) {
622
622
  const inc = large ? 5 : 1;
623
- if (increase)
624
- this.getWorkoutRide().powerUp(inc);
625
- else
626
- this.getWorkoutRide().powerDown(inc);
623
+ this.adjustWorkout(increase, inc);
627
624
  }
628
625
  else {
629
626
  const sgn = increase ? 1 : -1;
630
627
  const inc = large ? sgn * 50 : sgn * 5;
631
- this.devicePowerUp(inc);
628
+ this.devicePowerUp(inc, gearFallback);
632
629
  }
633
630
  }
634
631
  catch (err) {
635
632
  this.logError(err, 'adjustPower');
636
633
  }
637
634
  }
635
+ adjustWorkout(increase, inc) {
636
+ if (this.getWorkoutRide().inUse()) {
637
+ if (increase)
638
+ this.getWorkoutRide().powerUp(inc);
639
+ else
640
+ this.getWorkoutRide().powerDown(inc);
641
+ }
642
+ }
638
643
  adjustCurrentStepDuration(time, newDuration) {
639
644
  let delta = 0;
640
645
  this.actualWorkout?.steps?.forEach(s => {
@@ -842,7 +847,7 @@ let RideDisplayService = (() => {
842
847
  devices.off('cycling-mode-toggle', h.toggleCyclingModeHandler);
843
848
  delete this.startDeviceHandlers;
844
849
  }
845
- devicePowerUp(inc) {
850
+ devicePowerUp(inc, gearFallback = true) {
846
851
  const device = this.getDeviceRide().getControlAdapter();
847
852
  if (!device)
848
853
  return;
@@ -858,9 +863,12 @@ let RideDisplayService = (() => {
858
863
  if (Math.abs(gearDelta) > 1) {
859
864
  gearDelta = Math.sign(gearDelta) * 5;
860
865
  }
861
- this.getRideModeService().sendUpdate({ gearDelta });
866
+ this.gearChange(gearDelta);
862
867
  }
863
868
  }
869
+ gearChange(gearDelta) {
870
+ this.getRideModeService().sendUpdate({ gearDelta });
871
+ }
864
872
  simulatorPowerUp(mode, powerInc) {
865
873
  const simMode = mode.getSetting('mode');
866
874
  let request = {};
@@ -982,6 +990,22 @@ let RideDisplayService = (() => {
982
990
  this.adjustPower(true, duration > 1000);
983
991
  else if (key === 'down')
984
992
  this.adjustPower(false, duration > 1000);
993
+ else if (key == 'r-shift-up')
994
+ this.gearChange(1);
995
+ else if (key == 'r-shift-down')
996
+ this.gearChange(-1);
997
+ else if (key == 'l-shift-up')
998
+ this.gearChange(5);
999
+ else if (key == 'l-shift-down')
1000
+ this.gearChange(-5);
1001
+ else if (key == 'y')
1002
+ this.adjustPower(true, duration > 1000, false);
1003
+ else if (key == 'b')
1004
+ this.adjustPower(false, duration > 1000, false);
1005
+ else if (key == 'a')
1006
+ this.forward();
1007
+ else if (key == 'z')
1008
+ this.backward();
985
1009
  }
986
1010
  startListeningForDeviceData() {
987
1011
  const { deviceDataHandler } = this.startDeviceHandlers ?? {};
@@ -289,7 +289,8 @@ let RidePageService = (() => {
289
289
  startOverlayProps: isStarting ? this.getRideDisplay().getStartOverlayProps() : null,
290
290
  menuProps: this.menuProps,
291
291
  rideView: props.rideView,
292
- route: props.route
292
+ route: props.route,
293
+ displayObserver: props.displayObserver
293
294
  };
294
295
  return displayProps;
295
296
  }
@@ -416,11 +416,11 @@ let FreeRideDisplayService = (() => {
416
416
  this.logError(err, 'getNextOptions');
417
417
  options = undefined;
418
418
  }
419
- if (!options?.length && !finished && !forStart) {
419
+ if (!options?.length && !finished && !forStart && !freeRide.isLastQueryValid()) {
420
420
  this.logEvent({ message: 'no options available - retry in 3000ms', });
421
421
  await sleep(3000);
422
422
  }
423
- } while (!options?.length && !finished && !forStart);
423
+ } while (!options?.length && !finished && !forStart && !freeRide.isLastQueryValid());
424
424
  this.internalEmitter.off('stop-query', setFinished);
425
425
  };
426
426
  return new Promise(done => {
@@ -100,6 +100,8 @@ let GpxDisplayService = (() => {
100
100
  displayPosition: this.mapLoaded ? null : this.position,
101
101
  sideViews
102
102
  };
103
+ if (this.isMobile()) {
104
+ }
103
105
  return props;
104
106
  }
105
107
  getSatelliteViewProps() {
@@ -117,7 +119,7 @@ let GpxDisplayService = (() => {
117
119
  }
118
120
  getStartOverlayProps() {
119
121
  const rideView = this.getRideView();
120
- if (rideView === 'map') {
122
+ if (rideView === 'map' || this.isMobile()) {
121
123
  return {
122
124
  mapType: this.getRideViewName(),
123
125
  mapState: 'Loaded'
@@ -131,7 +133,7 @@ let GpxDisplayService = (() => {
131
133
  }
132
134
  isStartRideCompleted() {
133
135
  const rideView = this.getRideView();
134
- if (rideView === 'map') {
136
+ if (rideView === 'map' || this.isMobile()) {
135
137
  this.mapLoaded = true;
136
138
  return true;
137
139
  }
@@ -156,7 +158,7 @@ let GpxDisplayService = (() => {
156
158
  }
157
159
  getRideView() {
158
160
  if (this.isMobile())
159
- return 'map';
161
+ return 'sv';
160
162
  const rideView = this.getUserSettings().get('preferences.rideView', 'sv');
161
163
  return rideView;
162
164
  }
@@ -69,6 +69,7 @@ let FreeRideService = (() => {
69
69
  selectedOption;
70
70
  options;
71
71
  currentSegment;
72
+ lastOptionsQueryValid = true;
72
73
  constructor() {
73
74
  super('FreeRide');
74
75
  this.options = [];
@@ -131,7 +132,12 @@ let FreeRideService = (() => {
131
132
  }
132
133
  async loadNextOptions(from, forStart) {
133
134
  const optionManager = useMapArea().getOptionManager();
134
- let opts = await optionManager.getNextOptions(from) ?? [];
135
+ const result = await optionManager.getNextOptions(from);
136
+ let opts = result.options;
137
+ this.lastOptionsQueryValid = result.isValid;
138
+ if (!result.isValid) {
139
+ return [];
140
+ }
135
141
  opts = this.evaluateOptions(opts, from);
136
142
  if (forStart)
137
143
  await this.getNextLevelOptions(opts);
@@ -283,8 +289,11 @@ let FreeRideService = (() => {
283
289
  let segment = o;
284
290
  let done = false;
285
291
  let isSingle = false;
292
+ let lastHandlerWasNoOption = false;
286
293
  do {
287
- const nextOpts = await optionManager.getNextOptions(segment);
294
+ const nextOptsResult = await optionManager.getNextOptions(segment);
295
+ const nextOpts = nextOptsResult.options;
296
+ const isValid = nextOptsResult.isValid;
288
297
  const handleSingleOption = () => {
289
298
  if (segmentUpdated)
290
299
  return;
@@ -309,6 +318,11 @@ let FreeRideService = (() => {
309
318
  done = true;
310
319
  return;
311
320
  }
321
+ if (lastHandlerWasNoOption) {
322
+ o.options = [];
323
+ done = true;
324
+ return;
325
+ }
312
326
  try {
313
327
  const map = segment.map ?? this.getMapArea().getMap(segment.path[0]);
314
328
  const newPath = [];
@@ -337,13 +351,21 @@ let FreeRideService = (() => {
337
351
  };
338
352
  if (nextOpts?.length === 1) {
339
353
  handleSingleOption();
354
+ lastHandlerWasNoOption = false;
340
355
  }
341
- else if (!nextOpts?.length) {
356
+ else if (!nextOpts?.length && isValid) {
342
357
  handleNoOption();
358
+ lastHandlerWasNoOption = true;
359
+ }
360
+ else if (!nextOpts?.length && !isValid) {
361
+ o.options = [];
362
+ done = true;
363
+ lastHandlerWasNoOption = false;
343
364
  }
344
365
  else {
345
366
  o.options = nextOpts;
346
367
  done = true;
368
+ lastHandlerWasNoOption = false;
347
369
  }
348
370
  await waitNextTick();
349
371
  } while (!done);
@@ -358,6 +380,9 @@ let FreeRideService = (() => {
358
380
  getOptions() {
359
381
  return this.options;
360
382
  }
383
+ isLastQueryValid() {
384
+ return this.lastOptionsQueryValid;
385
+ }
361
386
  selectOption(option) {
362
387
  const current = this.selectedOption;
363
388
  const options = this.options ?? [];
@@ -180,15 +180,18 @@ let UserInterfaceServcie = (() => {
180
180
  this.isTerminated = false;
181
181
  this.isTerminating = false;
182
182
  try {
183
+ let resumeRequired = true;
184
+ if (this.backgroundTimer) {
185
+ clearTimeout(this.backgroundTimer);
186
+ resumeRequired = false;
187
+ }
188
+ this.logEvent({ message: 'onAppResume called' });
183
189
  this.startHeartbeatWorker();
184
190
  this.backgroundPausedByService = false;
185
191
  this.appState = 'Active';
186
- this.logEvent({ message: 'onAppResume called' });
187
- if (this.backgroundTimer) {
188
- clearTimeout(this.backgroundTimer);
189
- return;
192
+ if (resumeRequired) {
193
+ this.resume();
190
194
  }
191
- this.resume();
192
195
  }
193
196
  catch (err) {
194
197
  this.logError(err, 'onAppPause');
@@ -225,15 +228,15 @@ let UserInterfaceServcie = (() => {
225
228
  }
226
229
  async pause() {
227
230
  await IncyclistPageService.pausePage();
228
- useDeviceAccess().disconnect();
231
+ await useDeviceAccess().disconnect();
229
232
  if (this.getMessageQueue().disconnect)
230
233
  this.getMessageQueue().disconnect();
231
234
  }
232
- resume() {
235
+ async resume() {
233
236
  if (this.getMessageQueue().connect)
234
237
  this.getMessageQueue().connect();
235
- useDeviceAccess().connect();
236
- IncyclistPageService.resumePage();
238
+ await useDeviceAccess().connect();
239
+ await IncyclistPageService.resumePage();
237
240
  }
238
241
  startHeartbeatWorker() {
239
242
  if (this.heartbeatIv)
@@ -439,7 +439,7 @@ export class VideoSyncHelper extends IncyclistService {
439
439
  }
440
440
  const s0 = mapping.distance;
441
441
  const v = mapping.videoSpeed / 3.6;
442
- const t = mapping.time + (distance - s0) / v;
442
+ const t = mapping.time === 0 ? mapping.time + distance / v : mapping.time + (distance - s0) / v;
443
443
  if (!Number.isNaN(t)) {
444
444
  return t;
445
445
  }
@@ -1,5 +1,5 @@
1
1
  import { EventLogger } from "gd-eventlog";
2
- import { FreeRideContinuation, GetNextOptionProps, IMapArea, IMapAreaService, IncyclistNode, IncyclistWay, PathCrossingInfo, WayInfo } from "./types";
2
+ import { FreeRideContinuation, GetNextOptionProps, IMapArea, IMapAreaService, IncyclistNode, IncyclistWay, NextOptionsResult, PathCrossingInfo, WayInfo } from "./types";
3
3
  export declare class OptionManager {
4
4
  protected service: IMapAreaService;
5
5
  protected map?: IMapArea;
@@ -7,7 +7,7 @@ export declare class OptionManager {
7
7
  constructor(service: IMapAreaService, map?: IMapArea);
8
8
  setMap(map: IMapArea): void;
9
9
  getStartOptions(way: IncyclistWay, crossing: PathCrossingInfo): Promise<Array<FreeRideContinuation>>;
10
- getNextOptions(from: WayInfo | FreeRideContinuation, props?: GetNextOptionProps): Promise<Array<FreeRideContinuation>>;
10
+ getNextOptions(from: WayInfo | FreeRideContinuation, props?: GetNextOptionProps): Promise<NextOptionsResult>;
11
11
  protected checkMinDistance(options: Array<FreeRideContinuation>, minDistance: number): Promise<void>;
12
12
  protected getDistance(path: IncyclistNode[]): number;
13
13
  protected checkOptionsOnDifferentWay(location: IncyclistNode, w: IncyclistWay, options: FreeRideContinuation[]): FreeRideContinuation[];
@@ -37,6 +37,10 @@ export type FreeRideContinuation = {
37
37
  map?: IMapArea;
38
38
  ui?: FreeRideOption;
39
39
  };
40
+ export type NextOptionsResult = {
41
+ options: Array<FreeRideContinuation>;
42
+ isValid: boolean;
43
+ };
40
44
  export interface IMapAreaService {
41
45
  load(location: IncyclistNode): Promise<IMapArea | undefined>;
42
46
  getMap(location: IncyclistNode): IMapArea | undefined;
@@ -11,6 +11,7 @@ import { FreeRideOption } from "../../routes/list/types";
11
11
  import { MapViewPort } from "../route/types";
12
12
  import { LatLng } from "../../utils/geo";
13
13
  import { Unit } from "../../i18n";
14
+ import { IObserver } from "../../types";
14
15
  export type RideType = 'Free-Ride' | 'GPX' | 'Video' | 'Workout';
15
16
  export type CurrentRideState = 'Idle' | 'Starting' | 'Started' | 'Active' | 'Paused' | 'Error' | 'Finished' | 'Closing';
16
17
  export type CurrentRideDeviceState = 'Starting' | 'Started' | 'Error';
@@ -102,6 +103,7 @@ export interface MapOverlayDisplayProps extends OverlayDisplayProps {
102
103
  export type RideViewType = 'sv' | 'map' | 'sat';
103
104
  export interface GpxDisplayProps extends RouteDisplayProps {
104
105
  rideView: RideViewType;
106
+ displayObserver?: IObserver;
105
107
  }
106
108
  export interface RouteOptionDisplayProps {
107
109
  optionsDelay?: number;
@@ -78,7 +78,8 @@ export declare class RideDisplayService extends IncyclistService implements ICur
78
78
  onRouteUpdated(route: Route): void;
79
79
  protected onRouteCompleted(): void;
80
80
  protected onForward(workoutTime: number, remaining: number): void;
81
- adjustPower(increase: boolean, large: boolean): void;
81
+ adjustPower(increase: boolean, large: boolean, gearFallback?: boolean): void;
82
+ adjustWorkout(increase: boolean, inc: number): void;
82
83
  protected adjustCurrentStepDuration(time: number, newDuration: number): void;
83
84
  protected onBackward(workoutTime: number, duration: number, jumpType: 'current' | 'previous', jumpFrom: Step, jumpTo: Step): void;
84
85
  protected adjustStepDuration(s: Step, newDuration: any): void;
@@ -95,7 +96,8 @@ export declare class RideDisplayService extends IncyclistService implements ICur
95
96
  protected stopDevices(exit?: boolean): void;
96
97
  protected initDeviceHandlers(): void;
97
98
  protected resetDeviceHandlers(): void;
98
- protected devicePowerUp(inc: number): void;
99
+ protected devicePowerUp(inc: number, gearFallback?: boolean): void;
100
+ protected gearChange(gearDelta: number): void;
99
101
  protected simulatorPowerUp(mode: CyclingMode, powerInc: number): void;
100
102
  protected onDeviceStartRequest(devices: AdapterStateInfo[]): void;
101
103
  protected updateStartOverlay(): void;
@@ -20,6 +20,7 @@ export interface VideoRidePageDisplayProps extends RidePageDisplayProps {
20
20
  }
21
21
  export interface GPXRidePageDisplayProps extends RidePageDisplayProps {
22
22
  rideView?: RideViewType;
23
+ displayObserver?: IObserver;
23
24
  }
24
25
  export type AnyRidePageDisplayProps = VideoRidePageDisplayProps | RidePageDisplayProps;
25
26
  export interface RideMenuProps {
@@ -8,6 +8,7 @@ export declare class FreeRideService extends IncyclistService {
8
8
  protected selectedOption: FreeRideContinuation;
9
9
  protected options: FreeRideContinuation[];
10
10
  protected currentSegment: FreeRideContinuation;
11
+ protected lastOptionsQueryValid: boolean;
11
12
  constructor();
12
13
  selectStartPosition(position?: LatLng | undefined): Promise<{
13
14
  position: IncyclistNode;
@@ -34,6 +35,7 @@ export declare class FreeRideService extends IncyclistService {
34
35
  protected getNextLevelOptions(opts: FreeRideContinuation[]): Promise<void>;
35
36
  protected buildId(opt: FreeRideContinuation): string;
36
37
  getOptions(): FreeRideContinuation[];
38
+ isLastQueryValid(): boolean;
37
39
  selectOption(option: FreeRideContinuation | string | number): FreeRideContinuation[];
38
40
  applyOption(option?: FreeRideContinuation): void;
39
41
  applyStartOption(startOption?: FreeRideOption): void;
@@ -29,7 +29,7 @@ export declare class UserInterfaceServcie extends IncyclistService {
29
29
  onAppResume(): Promise<boolean>;
30
30
  protected onSessionStart(): void;
31
31
  protected pause(): Promise<void>;
32
- protected resume(): void;
32
+ protected resume(): Promise<void>;
33
33
  protected startHeartbeatWorker(): void;
34
34
  protected stopHeartbeatWorker(): void;
35
35
  protected sendHeartbeat(): void;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "incyclist-services",
3
- "version": "1.7.58",
3
+ "version": "1.7.60",
4
4
  "peerDependencies": {
5
5
  "gd-eventlog": "^0.1.27"
6
6
  },
7
7
  "dependencies": {
8
8
  "@garmin/fitsdk": "^21.200.0",
9
9
  "axios": "^1.15.2",
10
- "incyclist-devices": "^3.0.14",
10
+ "incyclist-devices": "^3.0.16",
11
11
  "promise.any": "^2.0.6",
12
12
  "semver": "^7.7.4",
13
13
  "tcx-builder": "^1.1.1",