incyclist-services 1.7.50 → 1.7.52

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.
Files changed (64) hide show
  1. package/lib/cjs/activities/base/convert/fit/local-fit.js +37 -5
  2. package/lib/cjs/activities/base/convert/fit/remote-fit.js +2 -2
  3. package/lib/cjs/activities/base/convert/tcx/tcx.js +10 -1
  4. package/lib/cjs/activities/base/model/index.js +6 -1
  5. package/lib/cjs/activities/base/repo/db.js +1 -1
  6. package/lib/cjs/activities/base/utils/activity.js +2 -1
  7. package/lib/cjs/activities/base/utils/helpers.js +2 -1
  8. package/lib/cjs/activities/list/utils.js +1 -1
  9. package/lib/cjs/activities/ride/service.js +80 -42
  10. package/lib/cjs/api/rest/api.js +4 -1
  11. package/lib/cjs/api/rest/incyclist.js +1 -0
  12. package/lib/cjs/devices/ride/service.js +13 -2
  13. package/lib/cjs/ride/display/service.js +1 -1
  14. package/lib/cjs/ride/route/FreeRideDisplayService.js +35 -14
  15. package/lib/cjs/ride/route/RouteDisplayService.js +2 -2
  16. package/lib/cjs/routes/base/parsers/epm.js +6 -0
  17. package/lib/cjs/routes/base/parsers/factory.js +5 -0
  18. package/lib/cjs/routes/base/parsers/geometry.js +6 -0
  19. package/lib/cjs/routes/base/parsers/gpx.js +6 -0
  20. package/lib/cjs/routes/base/parsers/multixml.js +6 -0
  21. package/lib/cjs/routes/base/parsers/tacx/TacxParser.js +6 -0
  22. package/lib/cjs/routes/base/parsers/xml.js +6 -0
  23. package/lib/cjs/routes/free-ride/service.js +2 -2
  24. package/lib/cjs/routes/library/service.js +327 -0
  25. package/lib/cjs/routes/library/types.js +2 -0
  26. package/lib/cjs/services/overpass/overpass.js +3 -2
  27. package/lib/esm/activities/base/convert/fit/local-fit.js +26 -2
  28. package/lib/esm/api/rest/api.js +4 -1
  29. package/lib/esm/api/rest/incyclist.js +1 -0
  30. package/lib/esm/ride/display/service.js +1 -1
  31. package/lib/esm/ride/route/FreeRideDisplayService.js +32 -14
  32. package/lib/esm/routes/base/parsers/epm.js +6 -0
  33. package/lib/esm/routes/base/parsers/factory.js +5 -0
  34. package/lib/esm/routes/base/parsers/geometry.js +6 -0
  35. package/lib/esm/routes/base/parsers/gpx.js +6 -0
  36. package/lib/esm/routes/base/parsers/multixml.js +6 -0
  37. package/lib/esm/routes/base/parsers/tacx/TacxParser.js +6 -0
  38. package/lib/esm/routes/base/parsers/xml.js +6 -0
  39. package/lib/esm/routes/free-ride/service.js +2 -2
  40. package/lib/esm/routes/library/service.js +323 -0
  41. package/lib/esm/routes/library/types.js +1 -0
  42. package/lib/esm/services/overpass/overpass.js +3 -2
  43. package/lib/types/activities/base/convert/fit/local-fit.d.ts +2 -0
  44. package/lib/types/activities/ride/service.d.ts +1 -1
  45. package/lib/types/api/fs/index.d.ts +12 -1
  46. package/lib/types/api/rest/types.d.ts +1 -0
  47. package/lib/types/api/ui/index.d.ts +1 -0
  48. package/lib/types/ride/display/service.d.ts +2 -2
  49. package/lib/types/ride/route/FreeRideDisplayService.d.ts +2 -0
  50. package/lib/types/routes/base/parsers/epm.d.ts +2 -0
  51. package/lib/types/routes/base/parsers/factory.d.ts +2 -1
  52. package/lib/types/routes/base/parsers/geometry.d.ts +4 -1
  53. package/lib/types/routes/base/parsers/gpx.d.ts +2 -0
  54. package/lib/types/routes/base/parsers/index.d.ts +1 -1
  55. package/lib/types/routes/base/parsers/multixml.d.ts +3 -1
  56. package/lib/types/routes/base/parsers/tacx/TacxParser.d.ts +4 -1
  57. package/lib/types/routes/base/parsers/types.d.ts +14 -0
  58. package/lib/types/routes/base/parsers/xml.d.ts +4 -1
  59. package/lib/types/routes/base/types/index.d.ts +0 -11
  60. package/lib/types/routes/library/service.d.ts +28 -0
  61. package/lib/types/routes/library/types.d.ts +57 -0
  62. package/lib/types/routes/list/cards/RouteCard.d.ts +1 -1
  63. package/lib/types/routes/types.d.ts +2 -0
  64. package/package.json +4 -4
@@ -40,6 +40,7 @@ const gd_eventlog_1 = require("gd-eventlog");
40
40
  const settings_1 = require("../../../../settings");
41
41
  const decorators_1 = require("../../../../base/decorators");
42
42
  const DEG_TO_SEMICIRCLES = (2 ** 31) / 180;
43
+ const DEFAULT_PRODUCT = 3843;
43
44
  let LocalFitConverter = (() => {
44
45
  let _instanceExtraInitializers = [];
45
46
  let _getUserSettings_decorators;
@@ -68,7 +69,7 @@ let LocalFitConverter = (() => {
68
69
  }
69
70
  }
70
71
  getFitActivity(activity) {
71
- const { id, title, time, timeTotal, timePause, distance } = activity;
72
+ const { id, title, time, timeTotal, timePause, distance, sport = 'cycling' } = activity;
72
73
  const status = 'created';
73
74
  const startTime = new Date(activity.startTime).toISOString();
74
75
  const logs = activity.logs.map(this.mapLogToFit.bind(this));
@@ -78,7 +79,7 @@ let LocalFitConverter = (() => {
78
79
  id: this.getUserSettings().get('uuid', undefined),
79
80
  weight: activity.user.weight
80
81
  };
81
- return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots };
82
+ return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots, sport };
82
83
  }
83
84
  mapLapsToFit(laps, activityStartTime) {
84
85
  const activityStartMs = new Date(activityStartTime).getTime();
@@ -102,13 +103,36 @@ let LocalFitConverter = (() => {
102
103
  const power = Math.round(powerOrg);
103
104
  return { time, speed, slope, cadence, heartrate, distance, power, lat, lon: lng, elevation };
104
105
  }
106
+ buildSerialNo() {
107
+ return Date.now() & 0xFFFFFFFF;
108
+ }
109
+ findProduct(name) {
110
+ const entry = Object.entries(fitsdk_1.Profile.types.garminProduct)
111
+ .find(([, value]) => value === name);
112
+ return entry ? Number(entry[0]) : DEFAULT_PRODUCT;
113
+ }
105
114
  encode(activity) {
106
115
  const encoder = new fitsdk_1.Encoder();
107
116
  const startTime = new Date(activity.startTime);
117
+ const manufacturer = this.getUserSettings().get('fitexport.manufacturer', 'garmin');
118
+ const productIdentifier = this.getUserSettings().get('fitexport.device', 'edge1040');
119
+ const storedSerial = this.getUserSettings().get('fitexport.serialNo', undefined);
120
+ const serialNumber = storedSerial ?? this.buildSerialNo();
121
+ if (!storedSerial) {
122
+ this.getUserSettings().set('fitexport.serialNo', serialNumber);
123
+ }
124
+ let product;
125
+ if (Number.isNaN(Number(productIdentifier))) {
126
+ product = this.findProduct(productIdentifier);
127
+ }
128
+ else {
129
+ product = productIdentifier;
130
+ }
108
131
  encoder.onMesg(fitsdk_1.Profile.MesgNum.FILE_ID, {
109
132
  type: 'activity',
110
- manufacturer: 'development',
111
- product: 0,
133
+ manufacturer,
134
+ product,
135
+ serialNumber,
112
136
  timeCreated: startTime,
113
137
  });
114
138
  encoder.onMesg(fitsdk_1.Profile.MesgNum.EVENT, {
@@ -184,7 +208,7 @@ let LocalFitConverter = (() => {
184
208
  totalElapsedTime: activity.timeTotal,
185
209
  totalTimerTime: activity.time,
186
210
  totalDistance: activity.distance,
187
- sport: 'cycling',
211
+ sport: this.mapSport(activity.sport),
188
212
  subSport: 'virtualActivity',
189
213
  event: 'session',
190
214
  eventType: 'stopDisableAll',
@@ -200,6 +224,14 @@ let LocalFitConverter = (() => {
200
224
  const uint8Array = encoder.close();
201
225
  return uint8Array.buffer;
202
226
  }
227
+ mapSport(incyclistSport) {
228
+ if (!incyclistSport || incyclistSport === 'cycling')
229
+ return 'cycling';
230
+ if (incyclistSport === 'running')
231
+ return 'running';
232
+ if (incyclistSport === 'rowing')
233
+ return 'rowing';
234
+ }
203
235
  getUserSettings() {
204
236
  return (0, settings_1.useUserSettings)();
205
237
  }
@@ -29,7 +29,7 @@ class RemoteFitConverter {
29
29
  return this.api;
30
30
  }
31
31
  getFitActivity(activity) {
32
- const { id, title, time, timeTotal, timePause, distance } = activity;
32
+ const { id, title, time, timeTotal, timePause, distance, sport = 'cycling' } = activity;
33
33
  const status = 'created';
34
34
  const startTime = new Date(activity.startTime).toISOString();
35
35
  const logs = activity.logs.map(this.mapLogToFit.bind(this));
@@ -39,7 +39,7 @@ class RemoteFitConverter {
39
39
  id: this.getUserSettings().get('uuid', undefined),
40
40
  weight: activity.user.weight
41
41
  };
42
- return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots };
42
+ return { id, title, status, logs, laps, startTime, time, timeTotal, timePause, distance, user, screenshots, sport };
43
43
  }
44
44
  mapLogToFit(log) {
45
45
  const { time, speed, slope, cadence: cadenceOrg, heartrate: heartrateOrg, distance, power: powerOrg, lat, lng, elevation } = log;
@@ -88,7 +88,8 @@ class TcxConverter {
88
88
  const trackPoints = this.creatTrackPoints(activity, startTime);
89
89
  const laps = this.createLaps(startTime, activity, trackPoints);
90
90
  const creator = new IncyclistAttribution();
91
- const tcxActivity = new tcx_builder_1.Activity('Biking', { Id: startTime, Notes: 'Incyclist Ride', Laps: laps, Creator: creator });
91
+ const sport = this.mapSport(activity.sport);
92
+ const tcxActivity = new tcx_builder_1.Activity(sport, { Id: startTime, Notes: 'Incyclist Ride', Laps: laps, Creator: creator });
92
93
  const activityList = new tcx_builder_1.ActivityList({ activity: [tcxActivity] });
93
94
  const tcxObj = new tcx_builder_1.TrainingCenterDatabase({ activities: activityList });
94
95
  const xml = tcxObj.toXml();
@@ -100,6 +101,14 @@ class TcxConverter {
100
101
  throw err;
101
102
  }
102
103
  }
104
+ mapSport(incyclistSport) {
105
+ if (!incyclistSport || incyclistSport === 'cycling')
106
+ return 'Biking';
107
+ if (incyclistSport === 'running')
108
+ return 'Running';
109
+ if (incyclistSport === 'rowing')
110
+ return 'Other';
111
+ }
103
112
  createLaps(startTime, activity, trackPoints) {
104
113
  if (!activity.workout && !activity.laps)
105
114
  return this.createActivityLap(startTime, activity, trackPoints);
@@ -1,4 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_ACTIVITY_TITLE = void 0;
3
+ exports.DEFAULT_SPORT_ACTIVITY_TITLE = exports.DEFAULT_ACTIVITY_TITLE = void 0;
4
4
  exports.DEFAULT_ACTIVITY_TITLE = 'Incyclist Ride';
5
+ exports.DEFAULT_SPORT_ACTIVITY_TITLE = {
6
+ cycling: 'Incyclist Ride',
7
+ rowing: 'Incyclist Rowing',
8
+ running: 'Incyclist Run'
9
+ };
@@ -45,7 +45,7 @@ const routes_1 = require("../../../routes");
45
45
  const factory_1 = require("./migration/factory");
46
46
  const Injection_1 = require("../../../base/decorators/Injection");
47
47
  const i18n_1 = require("../../../i18n");
48
- exports.DB_VERSION = '4';
48
+ exports.DB_VERSION = '5';
49
49
  exports.DB_NAME = 'db';
50
50
  let ActivitiesRepository = (() => {
51
51
  let _classDecorators = [types_1.Singleton];
@@ -118,7 +118,8 @@ let Activity = (() => {
118
118
  }
119
119
  getTitle() {
120
120
  let title = this.info.summary.title;
121
- if (title === model_1.DEFAULT_ACTIVITY_TITLE) {
121
+ const sport = this.details?.sport ?? 'cycling';
122
+ if (title === model_1.DEFAULT_ACTIVITY_TITLE || title === model_1.DEFAULT_SPORT_ACTIVITY_TITLE[sport]) {
122
123
  if (this.details?.route?.title) {
123
124
  title = this.details?.route.title;
124
125
  }
@@ -27,7 +27,8 @@ const buildSummary = (activity, proposedName) => {
27
27
  const previewImage = preview?.fileName;
28
28
  const totalElevation = activity.totalElevation;
29
29
  let title = activity.title;
30
- if (title === model_1.DEFAULT_ACTIVITY_TITLE) {
30
+ const sport = activity.sport ?? 'cycling';
31
+ if (title === model_1.DEFAULT_ACTIVITY_TITLE || title === model_1.DEFAULT_SPORT_ACTIVITY_TITLE[sport]) {
31
32
  if (activity.routeType === 'Video' && activity.route)
32
33
  title = activity.route.title ?? activity.route.name;
33
34
  else if (activity.route)
@@ -5,7 +5,7 @@ const i18n_1 = require("../../i18n");
5
5
  const createUIActivityInfo = (a) => {
6
6
  if (!a)
7
7
  return a;
8
- const { summary, details } = a;
8
+ const { summary } = a;
9
9
  return {
10
10
  summary: (0, exports.createUIActivitySummary)(summary),
11
11
  details: undefined
@@ -62,7 +62,6 @@ const decorators_1 = require("../../base/decorators");
62
62
  const i18n_1 = require("../../i18n");
63
63
  const utils_3 = require("../list/utils");
64
64
  const sleep_1 = require("../../utils/sleep");
65
- const node_events_1 = require("node:events");
66
65
  const SAVE_INTERVAL = 5000;
67
66
  let ActivityRideService = (() => {
68
67
  let _classDecorators = [types_1.Singleton];
@@ -106,6 +105,11 @@ let ActivityRideService = (() => {
106
105
  deviceDataHandler = this.onDeviceData.bind(this);
107
106
  gearChangeHandler = this.onGearChange.bind(this);
108
107
  dataHealthHandler = this.onDeviceHealthUpdate.bind(this);
108
+ speedHandler = {
109
+ 'cycling': this.getCyclingSpeed.bind(this),
110
+ 'rowing': this.getRowingPace.bind(this),
111
+ 'running': this.getRunningPace.bind(this)
112
+ };
109
113
  current;
110
114
  constructor() {
111
115
  super('ActivityRide');
@@ -168,7 +172,7 @@ let ActivityRideService = (() => {
168
172
  this.isSaveDone = false;
169
173
  this.isDonateShown = false;
170
174
  this.isSummaryShown = false;
171
- this.logEvent({ message: 'activity started' });
175
+ this.logEvent({ message: 'activity started', sport: this.activity.sport ?? 'cycling' });
172
176
  this.startWorker();
173
177
  this.enableDeviceHealthCheck();
174
178
  this.emit('started');
@@ -266,6 +270,34 @@ let ActivityRideService = (() => {
266
270
  return [];
267
271
  }
268
272
  }
273
+ getRowingPace(speed) {
274
+ if (speed > 0) {
275
+ const value = 30 / speed;
276
+ const min = Math.floor(value);
277
+ const sec = Math.floor((value - min) * 60);
278
+ const p = sec < 10 ? '0' : '';
279
+ return { value: `${min}:${p}${sec}`, unit: '' };
280
+ }
281
+ else {
282
+ return { value: '', unit: '' };
283
+ }
284
+ }
285
+ getRunningPace(speed) {
286
+ if (speed > 0) {
287
+ const value = 60 / speed;
288
+ const min = Math.floor(value);
289
+ const sec = Math.floor((value - min) * 60);
290
+ const p = sec < 10 ? '0' : '';
291
+ return { value: `${min}:${p}${sec}`, unit: '' };
292
+ }
293
+ else {
294
+ return { value: '', unit: '' };
295
+ }
296
+ }
297
+ getCyclingSpeed(speed) {
298
+ const [C, U] = this.getUnitConversionShortcuts();
299
+ return { value: C(speed, 'speed')?.toFixed(1) ?? '', unit: U('speed') };
300
+ }
269
301
  buildDashboardInfo(currentValues, avgMaxStats, display) {
270
302
  const { distance, time, speed, power, slope, heartrate, cadence, distanceRemaining, timeRemaining, gear } = currentValues;
271
303
  const { speedDetails, powerDetails, elevationGain, heartrateDetails, cadenceDetails } = avgMaxStats;
@@ -283,9 +315,10 @@ let ActivityRideService = (() => {
283
315
  { value: distanceRemaining === undefined ? undefined : `-${C(distanceRemaining, 'distance', { from: 'km' }).toFixed(2)}` }
284
316
  ]
285
317
  });
318
+ const sport = this.activity.sport ?? 'cycling';
286
319
  info.push({ title: 'Speed',
287
320
  data: [
288
- { value: C(speed, 'speed')?.toFixed(1) ?? '', unit: U('speed') },
321
+ this.speedHandler[sport](speed),
289
322
  speedDetails
290
323
  ],
291
324
  dataState: this.current.dataState?.speed
@@ -297,7 +330,9 @@ let ActivityRideService = (() => {
297
330
  info.push({ title: 'Slope', data: [{ value: (0, utils_1.formatNumber)(slope, 1), unit: '%', info: slopeInfo }, elevationGain] });
298
331
  }
299
332
  info.push({ title: 'Heartrate', data: [{ value: (0, utils_1.formatNumber)(heartrate, 0), unit: 'bpm' }, heartrateDetails], dataState: this.current.dataState?.heartrate });
300
- info.push({ title: 'Cadence', data: [{ value: (0, utils_1.formatNumber)(cadence, 0), unit: 'rpm' }, cadenceDetails], dataState: this.current.dataState?.cadence });
333
+ const cadenceUnit = this.activity.sport === 'rowing' ? 'spm' : 'rpm';
334
+ const cadenceInfo = { title: 'Cadence', data: [{ value: (0, utils_1.formatNumber)(cadence, 0), unit: cadenceUnit }, cadenceDetails], dataState: this.current.dataState?.cadence };
335
+ info.push(cadenceInfo);
301
336
  if (gear) {
302
337
  info.push({ title: 'Gear', data: [{ value: gear }] });
303
338
  }
@@ -334,7 +369,11 @@ let ActivityRideService = (() => {
334
369
  const [C, U] = this.getUnitConversionShortcuts();
335
370
  const stats = this.activity?.stats;
336
371
  const maxSpeed = stats?.speed?.max;
337
- const speedDetails = { value: (0, utils_1.formatNumber)(C(maxSpeed, 'speed'), 1), label: 'max' };
372
+ const sport = this.activity.sport ?? 'cycling';
373
+ const data = this.speedHandler[sport](maxSpeed);
374
+ const speedDetails = this.activity.sport === 'cycling' ?
375
+ { value: (0, utils_1.formatNumber)(C(maxSpeed, 'speed'), 1), label: 'max' } :
376
+ { value: data.value, label: 'max' };
338
377
  const powerDetails = { value: (0, utils_1.formatNumber)(stats?.power?.max, 0), label: 'max' };
339
378
  const heartrateDetails = { value: (0, utils_1.formatNumber)(stats?.hrm?.max, 0), label: 'max' };
340
379
  const cadenceDetails = { value: (0, utils_1.formatNumber)(stats?.cadence?.max, 0), label: 'max' };
@@ -351,7 +390,11 @@ let ActivityRideService = (() => {
351
390
  const [C, U] = this.getUnitConversionShortcuts();
352
391
  const stats = this.activity?.stats;
353
392
  const avgSpeed = stats?.speed?.avg;
354
- const speedDetails = { value: (0, utils_1.formatNumber)(C(avgSpeed, 'speed'), 1), label: 'avg' };
393
+ const sport = this.activity.sport ?? 'cycling';
394
+ const data = this.speedHandler[sport](avgSpeed);
395
+ const speedDetails = this.activity.sport === 'cycling' ?
396
+ { value: (0, utils_1.formatNumber)(C(avgSpeed, 'speed'), 1), label: 'avg' } :
397
+ { value: data.value, label: 'max' };
355
398
  const powerDetails = { value: (0, utils_1.formatNumber)(stats?.power?.avg, 0), label: 'avg' };
356
399
  const heartrateDetails = { value: (0, utils_1.formatNumber)(stats?.hrm?.avg, 0), label: 'avg' };
357
400
  const cadenceDetails = { value: (0, utils_1.formatNumber)(stats?.cadence?.avg, 0), label: 'avg' };
@@ -466,47 +509,17 @@ let ActivityRideService = (() => {
466
509
  };
467
510
  const run = async () => {
468
511
  await (0, sleep_1.sleep)(5);
469
- let cntCompleted = 0;
470
512
  let success = false;
471
- const localEmitter = new node_events_1.EventEmitter();
472
- const incCompleted = () => {
473
- cntCompleted++;
474
- if (cntCompleted >= 2) {
475
- localEmitter.emit('completed');
476
- }
477
- };
478
- localEmitter.once('completed', () => {
479
- (0, sleep_1.sleep)(0).then(() => {
480
- delete this.saveObserver;
481
- });
482
- });
483
513
  try {
484
514
  emit('start', success);
485
515
  await this._save();
486
- let format = undefined;
487
- let uploadSuccess = false;
488
- let convertSuccess = await this.convert('TCX');
489
- incCompleted();
490
- if (convertSuccess) {
491
- format = 'TCX';
492
- this.convert('FIT')
493
- .then(incCompleted)
494
- .catch(incCompleted);
495
- }
496
- else {
497
- convertSuccess = await this.convert('FIT');
498
- cntCompleted++;
499
- if (convertSuccess)
500
- format = 'FIT';
501
- }
502
- if (convertSuccess) {
503
- uploadSuccess = await this.upload(format);
504
- }
505
- success = convertSuccess && uploadSuccess;
516
+ success = await this._convertAndUpload();
517
+ (0, sleep_1.sleep)(0).then(() => {
518
+ delete this.saveObserver;
519
+ });
506
520
  this.isSaveDone = true;
507
521
  }
508
522
  catch {
509
- cntCompleted++;
510
523
  success = false;
511
524
  }
512
525
  emit('done', success);
@@ -836,6 +849,29 @@ let ActivityRideService = (() => {
836
849
  this.logError(err, '_save');
837
850
  }
838
851
  }
852
+ async _convertAndUpload() {
853
+ const successes = [];
854
+ const promises = [
855
+ this.convert('TCX').then(() => successes.push('tcx')),
856
+ this.convert('FIT').then(() => successes.push('fit')),
857
+ ];
858
+ await Promise.allSettled(promises);
859
+ this.logEvent({ message: 'conversion finished, ' });
860
+ const convertSuccess = successes.length > 0;
861
+ let uploadSuccess;
862
+ let format = 'tcx';
863
+ if (this.activity.sport === 'rowing') {
864
+ if (successes.some(e => e === 'fit'))
865
+ format = 'fit';
866
+ }
867
+ else if (!successes.some(e => e === 'tcx')) {
868
+ format = 'fit';
869
+ }
870
+ if (convertSuccess) {
871
+ uploadSuccess = await this.upload(format);
872
+ }
873
+ return convertSuccess && uploadSuccess;
874
+ }
839
875
  getSaveInterval() {
840
876
  return SAVE_INTERVAL;
841
877
  }
@@ -1003,7 +1039,8 @@ let ActivityRideService = (() => {
1003
1039
  this.current.position = undefined;
1004
1040
  }
1005
1041
  }
1006
- const title = base_1.DEFAULT_ACTIVITY_TITLE;
1042
+ const sport = this.getDeviceRide().getSport() ?? 'cycling';
1043
+ const title = base_1.DEFAULT_SPORT_ACTIVITY_TITLE[sport];
1007
1044
  const id = requestedId ?? (0, uuid_1.v4)();
1008
1045
  const date = (0, utils_1.formatDateTime)(new Date(), "%Y%m%d%H%M%S", false);
1009
1046
  const name = `${title}-${date}`;
@@ -1030,7 +1067,8 @@ let ActivityRideService = (() => {
1030
1067
  totalElevation: 0,
1031
1068
  logs: [],
1032
1069
  startPos, endPos, segment, realityFactor,
1033
- fileName
1070
+ fileName,
1071
+ sport
1034
1072
  };
1035
1073
  return activity;
1036
1074
  }
@@ -12,7 +12,7 @@ class ApiClient {
12
12
  logger;
13
13
  requests;
14
14
  init(props) {
15
- const { uuid, apiKey, requestLog = true, version, appVersion, channel = 'desktop' } = props;
15
+ const { uuid, apiKey, requestLog = true, version, appVersion, channel = 'desktop', platform } = props;
16
16
  const headers = {
17
17
  "X-uuid": uuid,
18
18
  "X-API-Key": apiKey,
@@ -20,6 +20,9 @@ class ApiClient {
20
20
  "x-app-version": appVersion,
21
21
  "x-react-version": version
22
22
  };
23
+ if (platform) {
24
+ headers['x-app-platform'] = platform;
25
+ }
23
26
  this.axios = axios_1.default.create({ headers });
24
27
  if (requestLog) {
25
28
  this.logger = new gd_eventlog_1.EventLogger('API');
@@ -20,6 +20,7 @@ class IncyclistRestApiClient {
20
20
  channel: appInfo?.getChannel(),
21
21
  appVersion: appInfo?.getAppVersion(),
22
22
  version: appInfo?.getUIVersion(),
23
+ platform: appInfo?.getOS()?.platform,
23
24
  requestLog: requestLog ?? true,
24
25
  apiKey,
25
26
  uuid
@@ -135,7 +135,6 @@ let DeviceRideService = (() => {
135
135
  return this.data;
136
136
  }
137
137
  pauseLogging() {
138
- console.log('# PAUSE LOGGING');
139
138
  this.logPaused = true;
140
139
  const interfaces = this.getEnabledInterfaces();
141
140
  interfaces.forEach(i => i.pauseLogging());
@@ -145,7 +144,6 @@ let DeviceRideService = (() => {
145
144
  .forEach(a => a.pauseLogging());
146
145
  }
147
146
  resumeLogging() {
148
- console.log('# RESUME LOGGING');
149
147
  this.logPaused = false;
150
148
  const interfaces = this.getEnabledInterfaces();
151
149
  interfaces.forEach(i => i.resumeLogging());
@@ -927,6 +925,19 @@ let DeviceRideService = (() => {
927
925
  this.storeOriginalCyclingMode();
928
926
  return this.startAdapters(adapters, 'start', props);
929
927
  }
928
+ getSport() {
929
+ try {
930
+ const info = this.getControlAdapter();
931
+ const adapter = info?.adapter;
932
+ const sports = adapter?.getSupportedSports?.() ?? ['cycling'];
933
+ this.logEvent({ message: '[DeviceRide] getSports', adapter: info.adapter.getName(), sports: sports.join(',') });
934
+ return sports[0];
935
+ }
936
+ catch (err) {
937
+ this.logError(err, 'getSports');
938
+ return 'cycling';
939
+ }
940
+ }
930
941
  async startRetry(props) {
931
942
  await this.lazyInit();
932
943
  const selected = this.getRideAdapters();
@@ -306,7 +306,7 @@ let RideDisplayService = (() => {
306
306
  toggleLeftSideView() {
307
307
  this.toggleOverlay('sv-left');
308
308
  }
309
- hideRightSideView() {
309
+ toggleRightSideView() {
310
310
  this.toggleOverlay('sv-right');
311
311
  }
312
312
  onArrowKey(event) {
@@ -33,6 +33,9 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
33
33
  if (target) Object.defineProperty(target, contextIn.name, descriptor);
34
34
  done = true;
35
35
  };
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
36
39
  Object.defineProperty(exports, "__esModule", { value: true });
37
40
  exports.FreeRideDisplayService = void 0;
38
41
  const activities_1 = require("../../activities");
@@ -44,13 +47,8 @@ const route_1 = require("../../routes/base/model/route");
44
47
  const geo_1 = require("../../utils/geo");
45
48
  const GpxDisplayService_1 = require("./GpxDisplayService");
46
49
  const utils_2 = require("../../utils");
47
- const pathInfo = (path) => {
48
- return path.map(p => {
49
- const pp = p;
50
- const id = pp.id ?? '{lat:' + Number(p.lat).toFixed(4) + ',lng:' + Number(p.lng).toFixed(4) + '}';
51
- return `${p.cnt},${p.routeDistance.toFixed(0)},${id})`;
52
- });
53
- };
50
+ const node_events_1 = __importDefault(require("node:events"));
51
+ const sleep_1 = require("../../utils/sleep");
54
52
  const DEFAULT_OPTIONS_DELAY = 5000;
55
53
  let FreeRideDisplayService = (() => {
56
54
  let _classSuper = GpxDisplayService_1.GpxDisplayService;
@@ -85,6 +83,7 @@ let FreeRideDisplayService = (() => {
85
83
  onViewportChange = this.saveViewport.bind(this);
86
84
  onOptionsVisibleChangedHandler = this.onOptionsVisibleChanged.bind(this);
87
85
  onTurnHandler = this.onTurn.bind(this);
86
+ internalEmitter = new node_events_1.default();
88
87
  start() {
89
88
  try {
90
89
  super.start();
@@ -409,13 +408,29 @@ let FreeRideDisplayService = (() => {
409
408
  return false;
410
409
  }
411
410
  getNextOptions(forStart) {
411
+ let options;
412
+ const freeRide = this.getFreeRideService();
413
+ const getOptions = async () => {
414
+ let finished = false;
415
+ const setFinished = () => { finished = true; };
416
+ this.internalEmitter.on('stop-query', setFinished);
417
+ do {
418
+ try {
419
+ options = await freeRide.getNextOptions(forStart);
420
+ }
421
+ catch (err) {
422
+ this.logError(err, 'getNextOptions');
423
+ options = undefined;
424
+ }
425
+ if (!options?.length && !finished && !forStart) {
426
+ this.logEvent({ message: 'no options available - retry in 3000ms', });
427
+ await (0, sleep_1.sleep)(3000);
428
+ }
429
+ } while (!options?.length && !finished && !forStart);
430
+ this.internalEmitter.off('stop-query', setFinished);
431
+ };
412
432
  return new Promise(done => {
413
- const freeRide = this.getFreeRideService();
414
- freeRide.getNextOptions(forStart)
415
- .catch(err => {
416
- this.logError(err, 'getNextOptions');
417
- done();
418
- })
433
+ getOptions()
419
434
  .then(() => {
420
435
  done();
421
436
  if (this.isStarting) {
@@ -489,9 +504,15 @@ let FreeRideDisplayService = (() => {
489
504
  const prev = { ...this.position, totalDistance: this.position.routeDistance };
490
505
  let route = this.route;
491
506
  if (newRouteDistance > route.description.distance) {
507
+ this.internalEmitter.emit('stop-query');
492
508
  route = this.route.clone();
493
509
  const selectedOption = this.getFreeRideService().getSelectedOption();
494
- this.appendOption(route, selectedOption);
510
+ if (selectedOption)
511
+ this.appendOption(route, selectedOption);
512
+ else {
513
+ this.logEvent({ message: 'no continuation available' });
514
+ return this.position;
515
+ }
495
516
  }
496
517
  const nextPos = (0, routes_1.getNextPosition)(route, { routeDistance: activityPos.routeDistance, prev });
497
518
  if (!nextPos) {
@@ -206,8 +206,8 @@ let RouteDisplayService = (() => {
206
206
  const nearbyRides = this.getNearbyRidesProps(props);
207
207
  const [C, U] = this.getUnitConversionShortcuts();
208
208
  const isLoop = this.currentRoute?.description?.isLoop;
209
- const xScale = { value: C(1, 'distance', { from: U('distance') }), unit: U('distance') };
210
- const yScale = { value: C(1, 'elevation', { from: U('elevation') }), unit: U('elevation') };
209
+ const xScale = { value: C(1, 'distance', { from: 'm' }), unit: U('distance') };
210
+ const yScale = { value: C(1, 'elevation', { from: 'm' }), unit: U('elevation') };
211
211
  const mapStartPos = (isLoop && !loopOverwrite) ? undefined : startPos;
212
212
  return {
213
213
  ...parent,
@@ -10,6 +10,12 @@ class EPMParser extends xml_1.XMLParser {
10
10
  supportsExtension(extension) {
11
11
  return extension.toLowerCase() === 'epm';
12
12
  }
13
+ getPrimaryExtension() {
14
+ return 'epm';
15
+ }
16
+ getCompanionExtensions() {
17
+ return ['epp'];
18
+ }
13
19
  async loadPoints(context) {
14
20
  const { data, route } = context;
15
21
  route.points = [];
@@ -24,6 +24,11 @@ class ParserFactory {
24
24
  throw new Error(`invalid file format ${extension}`);
25
25
  return matching;
26
26
  }
27
+ isPrimaryExtension(extension) {
28
+ const ext = extension.toLowerCase();
29
+ const isPrimary = this.parsers.some(p => p.getPrimaryExtension() === ext);
30
+ return isPrimary;
31
+ }
27
32
  findMatching(extension, data) {
28
33
  const matching = this.parsers
29
34
  .filter(p => p.supportsExtension(extension))
@@ -52,6 +52,12 @@ let GeometryParser = (() => {
52
52
  const geometry = await this.getData(file, data);
53
53
  return await this.parse(file, geometry);
54
54
  }
55
+ getPrimaryExtension() {
56
+ return 'xml';
57
+ }
58
+ getCompanionExtensions() {
59
+ return [];
60
+ }
55
61
  supportsExtension(extension) {
56
62
  return extension.toLowerCase() === 'json';
57
63
  }
@@ -14,6 +14,12 @@ class GPXParser extends xml_1.XMLParser {
14
14
  super();
15
15
  this.props = props;
16
16
  }
17
+ getPrimaryExtension() {
18
+ return 'gpx';
19
+ }
20
+ getCompanionExtensions() {
21
+ return [];
22
+ }
17
23
  supportsExtension(extension) {
18
24
  return extension.toLowerCase() === 'gpx';
19
25
  }
@@ -20,6 +20,12 @@ class MultipleXMLParser {
20
20
  supportsExtension(extension) {
21
21
  return extension?.toLowerCase() === 'xml';
22
22
  }
23
+ getPrimaryExtension() {
24
+ return 'xml';
25
+ }
26
+ getCompanionExtensions() {
27
+ return [];
28
+ }
23
29
  supportsContent() {
24
30
  return true;
25
31
  }
@@ -39,6 +39,12 @@ class TacxParser {
39
39
  supportsContent(data) {
40
40
  return TacxReader_1.TacxFileReader.isValid(data);
41
41
  }
42
+ getPrimaryExtension() {
43
+ return 'rlv';
44
+ }
45
+ getCompanionExtensions() {
46
+ return ['pgmf'];
47
+ }
42
48
  buildContext(file) {
43
49
  const { dir, delimiter: d } = file;
44
50
  if (file.ext === 'rlv') {