incyclist-services 1.7.76 → 1.7.77

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/list/service.js +1 -1
  2. package/lib/cjs/activities/ride/service.js +2 -1
  3. package/lib/cjs/workouts/base/graph/index.js +18 -0
  4. package/lib/cjs/workouts/base/graph/series.js +162 -0
  5. package/lib/cjs/workouts/base/graph/types.js +2 -0
  6. package/lib/cjs/workouts/base/model/index.js +0 -1
  7. package/lib/cjs/workouts/calendar/service.js +14 -0
  8. package/lib/cjs/workouts/index.js +2 -0
  9. package/lib/cjs/workouts/list/cards/index.js +0 -1
  10. package/lib/cjs/workouts/list/index.js +0 -1
  11. package/lib/cjs/workouts/list/pageService.js +362 -5
  12. package/lib/cjs/workouts/list/service.js +39 -12
  13. package/lib/cjs/workouts/page/index.js +17 -0
  14. package/lib/cjs/workouts/page/service.js +455 -0
  15. package/lib/cjs/workouts/page/types.js +2 -0
  16. package/lib/cjs/workouts/ride/index.js +1 -0
  17. package/lib/cjs/workouts/ride/page/index.js +18 -0
  18. package/lib/cjs/workouts/ride/page/service.js +500 -0
  19. package/lib/cjs/workouts/ride/page/types.js +2 -0
  20. package/lib/cjs/workouts/types.js +2 -0
  21. package/lib/esm/activities/list/service.js +1 -1
  22. package/lib/esm/activities/ride/service.js +2 -1
  23. package/lib/esm/workouts/base/graph/index.js +2 -0
  24. package/lib/esm/workouts/base/graph/series.js +156 -0
  25. package/lib/esm/workouts/base/graph/types.js +1 -0
  26. package/lib/esm/workouts/base/model/index.js +0 -1
  27. package/lib/esm/workouts/calendar/service.js +14 -0
  28. package/lib/esm/workouts/index.js +2 -0
  29. package/lib/esm/workouts/list/cards/index.js +0 -1
  30. package/lib/esm/workouts/list/index.js +0 -1
  31. package/lib/esm/workouts/list/pageService.js +362 -5
  32. package/lib/esm/workouts/list/service.js +39 -12
  33. package/lib/esm/workouts/page/index.js +1 -0
  34. package/lib/esm/workouts/page/service.js +451 -0
  35. package/lib/esm/workouts/page/types.js +1 -0
  36. package/lib/esm/workouts/ride/index.js +1 -0
  37. package/lib/esm/workouts/ride/page/index.js +2 -0
  38. package/lib/esm/workouts/ride/page/service.js +496 -0
  39. package/lib/esm/workouts/ride/page/types.js +1 -0
  40. package/lib/esm/workouts/types.js +1 -0
  41. package/lib/types/types.d.ts +1 -0
  42. package/lib/types/workouts/base/graph/index.d.ts +2 -0
  43. package/lib/types/workouts/base/graph/series.d.ts +7 -0
  44. package/lib/types/workouts/base/graph/types.d.ts +11 -0
  45. package/lib/types/workouts/base/model/index.d.ts +0 -1
  46. package/lib/types/workouts/base/parsers/intervals/parser.d.ts +2 -1
  47. package/lib/types/workouts/calendar/service.d.ts +1 -0
  48. package/lib/types/workouts/index.d.ts +2 -0
  49. package/lib/types/workouts/list/cards/index.d.ts +0 -1
  50. package/lib/types/workouts/list/index.d.ts +0 -1
  51. package/lib/types/workouts/list/pageService.d.ts +136 -4
  52. package/lib/types/workouts/list/service.d.ts +4 -0
  53. package/lib/types/workouts/list/types.d.ts +1 -1
  54. package/lib/types/workouts/page/index.d.ts +1 -0
  55. package/lib/types/workouts/page/service.d.ts +57 -0
  56. package/lib/types/workouts/page/types.d.ts +95 -0
  57. package/lib/types/workouts/ride/index.d.ts +1 -0
  58. package/lib/types/workouts/ride/page/index.d.ts +2 -0
  59. package/lib/types/workouts/ride/page/service.d.ts +71 -0
  60. package/lib/types/workouts/ride/page/types.d.ts +72 -0
  61. package/lib/types/workouts/ride/service.d.ts +2 -1
  62. package/lib/types/workouts/ride/types.d.ts +1 -0
  63. package/lib/types/workouts/types.d.ts +8 -0
  64. package/package.json +1 -1
@@ -0,0 +1,500 @@
1
+ "use strict";
2
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
3
+ var useValue = arguments.length > 2;
4
+ for (var i = 0; i < initializers.length; i++) {
5
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
6
+ }
7
+ return useValue ? value : void 0;
8
+ };
9
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
10
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
11
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
12
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
13
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
14
+ var _, done = false;
15
+ for (var i = decorators.length - 1; i >= 0; i--) {
16
+ var context = {};
17
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
18
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
19
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
20
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
21
+ if (kind === "accessor") {
22
+ if (result === void 0) continue;
23
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
24
+ if (_ = accept(result.get)) descriptor.get = _;
25
+ if (_ = accept(result.set)) descriptor.set = _;
26
+ if (_ = accept(result.init)) initializers.unshift(_);
27
+ }
28
+ else if (_ = accept(result)) {
29
+ if (kind === "field") initializers.unshift(_);
30
+ else descriptor[key] = _;
31
+ }
32
+ }
33
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
34
+ done = true;
35
+ };
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.getWorkoutRidePageService = exports.WorkoutRidePageService = void 0;
38
+ const gd_eventlog_1 = require("gd-eventlog");
39
+ const decorators_1 = require("../../../base/decorators");
40
+ const types_1 = require("../../../base/types");
41
+ const pages_1 = require("../../../base/pages");
42
+ const display_1 = require("../../../ride/display");
43
+ const activities_1 = require("../../../activities");
44
+ const service_1 = require("../service");
45
+ const graph_1 = require("../../base/graph");
46
+ const BACKGROUND_PAUSE_TIMEOUT_MS = 300000;
47
+ const UPCOMING_STEPS_COUNT = 3;
48
+ const DEFAULT_LOAD_INCREMENT = 1;
49
+ let WorkoutRidePageService = (() => {
50
+ let _classDecorators = [types_1.Singleton];
51
+ let _classDescriptor;
52
+ let _classExtraInitializers = [];
53
+ let _classThis;
54
+ let _classSuper = pages_1.IncyclistPageService;
55
+ let _instanceExtraInitializers = [];
56
+ let _getRideDisplay_decorators;
57
+ let _getWorkoutRide_decorators;
58
+ let _getActivityRide_decorators;
59
+ var WorkoutRidePageService = class extends _classSuper {
60
+ static { _classThis = this; }
61
+ static {
62
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
63
+ _getRideDisplay_decorators = [decorators_1.Injectable];
64
+ _getWorkoutRide_decorators = [decorators_1.Injectable];
65
+ _getActivityRide_decorators = [decorators_1.Injectable];
66
+ __esDecorate(this, null, _getRideDisplay_decorators, { kind: "method", name: "getRideDisplay", static: false, private: false, access: { has: obj => "getRideDisplay" in obj, get: obj => obj.getRideDisplay }, metadata: _metadata }, null, _instanceExtraInitializers);
67
+ __esDecorate(this, null, _getWorkoutRide_decorators, { kind: "method", name: "getWorkoutRide", static: false, private: false, access: { has: obj => "getWorkoutRide" in obj, get: obj => obj.getWorkoutRide }, metadata: _metadata }, null, _instanceExtraInitializers);
68
+ __esDecorate(this, null, _getActivityRide_decorators, { kind: "method", name: "getActivityRide", static: false, private: false, access: { has: obj => "getActivityRide" in obj, get: obj => obj.getActivityRide }, metadata: _metadata }, null, _instanceExtraInitializers);
69
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
70
+ WorkoutRidePageService = _classThis = _classDescriptor.value;
71
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
72
+ __runInitializers(_classThis, _classExtraInitializers);
73
+ }
74
+ rideEventHandler = (__runInitializers(this, _instanceExtraInitializers), {});
75
+ workoutEventHandler = {};
76
+ workoutObserverSubscribed = false;
77
+ backgroundTimer;
78
+ backgroundPausedByService = false;
79
+ menuProps = null;
80
+ isInitialized = false;
81
+ constructor() {
82
+ super('WorkoutRidePage');
83
+ this.rideEventHandler['state-update'] = this.onRideStateUpdate.bind(this);
84
+ this.workoutEventHandler['step-changed'] = this.onWorkoutUpdate.bind(this);
85
+ this.workoutEventHandler['update'] = this.onWorkoutUpdate.bind(this);
86
+ this.workoutEventHandler['forward'] = this.onWorkoutUpdate.bind(this);
87
+ this.workoutEventHandler['backward'] = this.onWorkoutUpdate.bind(this);
88
+ this.workoutEventHandler['completed'] = this.onWorkoutFinished.bind(this);
89
+ this.workoutEventHandler['stopped'] = this.onWorkoutFinished.bind(this);
90
+ }
91
+ openPage(simulate) {
92
+ try {
93
+ this.logEvent({ message: 'page shown', page: 'WorkoutRide' });
94
+ gd_eventlog_1.EventLogger.setGlobalConfig('page', 'WorkoutRide');
95
+ super.openPage();
96
+ try {
97
+ const service = this.getRideDisplay();
98
+ if (!this.isInitialized) {
99
+ service.init();
100
+ this.isInitialized = true;
101
+ }
102
+ this.registerRideEventHandlers();
103
+ this.subscribeToWorkoutObserver();
104
+ service.start(simulate);
105
+ }
106
+ catch (err) {
107
+ this.logError(err, 'openPage');
108
+ }
109
+ }
110
+ catch (err) {
111
+ this.logError(err, 'openPage');
112
+ }
113
+ return this.getPageObserver();
114
+ }
115
+ closePage() {
116
+ try {
117
+ gd_eventlog_1.EventLogger.setGlobalConfig('page', null);
118
+ this.logEvent({ message: 'page closed', page: 'WorkoutRide' });
119
+ this.getRideDisplay().stop(true);
120
+ this.unregisterRideEventHandlers();
121
+ this.unsubscribeFromWorkoutObserver();
122
+ this.menuProps = null;
123
+ this.isInitialized = false;
124
+ super.closePage();
125
+ }
126
+ catch (err) {
127
+ this.logError(err, 'closePage');
128
+ }
129
+ }
130
+ async pausePage() {
131
+ try {
132
+ this.backgroundTimer = setTimeout(() => {
133
+ this.getRideDisplay().pause('device');
134
+ this.backgroundPausedByService = true;
135
+ }, BACKGROUND_PAUSE_TIMEOUT_MS);
136
+ return super.pausePage();
137
+ }
138
+ catch (err) {
139
+ this.logError(err, 'pausePage');
140
+ }
141
+ }
142
+ async resumePage() {
143
+ try {
144
+ if (this.backgroundTimer) {
145
+ clearTimeout(this.backgroundTimer);
146
+ }
147
+ return super.resumePage();
148
+ }
149
+ catch (err) {
150
+ this.logError(err, 'resumePage');
151
+ }
152
+ }
153
+ getRideObserver() {
154
+ return this.rideObserver ?? null;
155
+ }
156
+ getPageDisplayProps() {
157
+ try {
158
+ const rideType = this.getRideDisplay().getRideType();
159
+ if (rideType !== 'Workout') {
160
+ this.logError(new Error(`unexpected ride type '${rideType}' for WorkoutRidePage`), 'getPageDisplayProps');
161
+ return this.getEmptyDisplayProps();
162
+ }
163
+ const base = this.buildBaseProps();
164
+ const wo = this.getWorkoutRide().getDashboardDisplayProperties();
165
+ const current = this.getRideDisplay().getDisplayProperties().workout;
166
+ return {
167
+ ...base,
168
+ title: wo.title ?? '',
169
+ graph: this.buildGraphPlan(current, wo.ftp),
170
+ steps: this.buildUpcomingSteps(current, wo.ftp),
171
+ dashboard: this.buildDashboardLine(wo)
172
+ };
173
+ }
174
+ catch (err) {
175
+ this.logError(err, 'getPageDisplayProps');
176
+ return this.getEmptyDisplayProps();
177
+ }
178
+ }
179
+ getGraphActuals() {
180
+ try {
181
+ const state = this.getRideDisplay().getState();
182
+ if (state === 'Idle' || state === 'Starting' || state === 'Started') {
183
+ return { power: [], heartrate: [], position: 0 };
184
+ }
185
+ const logs = this.getActivityRide().getActivity()?.logs ?? [];
186
+ const power = [];
187
+ const heartrate = [];
188
+ logs.forEach(log => {
189
+ if (log.power !== undefined)
190
+ power.push({ x: log.time, y: log.power });
191
+ if (log.heartrate !== undefined)
192
+ heartrate.push({ x: log.time, y: log.heartrate });
193
+ });
194
+ return { power, heartrate, position: this.getElapsedActivityTime() };
195
+ }
196
+ catch (err) {
197
+ this.logError(err, 'getGraphActuals');
198
+ return { power: [], heartrate: [], position: 0 };
199
+ }
200
+ }
201
+ onMenuOpen() {
202
+ try {
203
+ const state = this.getRideDisplay().getState();
204
+ this.menuProps = { showResume: state === 'Paused', ...this.getStepFlags() };
205
+ this.updatePageDisplay();
206
+ }
207
+ catch (err) {
208
+ this.logError(err, 'onMenuOpen');
209
+ }
210
+ }
211
+ onMenuClose() {
212
+ try {
213
+ this.menuProps = null;
214
+ this.updatePageDisplay();
215
+ }
216
+ catch (err) {
217
+ this.logError(err, 'onMenuClose');
218
+ }
219
+ }
220
+ onPause() {
221
+ try {
222
+ this.getRideDisplay().pause('user');
223
+ this.menuProps = { showResume: true, ...this.getStepFlags() };
224
+ this.updatePageDisplay();
225
+ }
226
+ catch (err) {
227
+ this.logError(err, 'onPause');
228
+ }
229
+ }
230
+ onResume() {
231
+ try {
232
+ this.getRideDisplay().resume();
233
+ this.menuProps = null;
234
+ this.updatePageDisplay();
235
+ }
236
+ catch (err) {
237
+ this.logError(err, 'onResume');
238
+ }
239
+ }
240
+ onStop() {
241
+ try {
242
+ this.getRideDisplay().stop(true);
243
+ this.emitNavigateBack();
244
+ }
245
+ catch (err) {
246
+ this.logError(err, 'onStop');
247
+ }
248
+ }
249
+ onStepBack() {
250
+ try {
251
+ this.getRideDisplay().backward();
252
+ }
253
+ catch (err) {
254
+ this.logError(err, 'onStepBack');
255
+ }
256
+ }
257
+ onStepForward() {
258
+ try {
259
+ this.getRideDisplay().forward();
260
+ }
261
+ catch (err) {
262
+ this.logError(err, 'onStepForward');
263
+ }
264
+ }
265
+ onIncreaseLoad() {
266
+ this.adjustLoad(DEFAULT_LOAD_INCREMENT);
267
+ }
268
+ onDecreaseLoad() {
269
+ this.adjustLoad(-DEFAULT_LOAD_INCREMENT);
270
+ }
271
+ onRetryStart() {
272
+ try {
273
+ this.getRideDisplay().retryStart();
274
+ }
275
+ catch (err) {
276
+ this.logError(err, 'onRetryStart');
277
+ }
278
+ }
279
+ onIgnoreStart() {
280
+ try {
281
+ this.getRideDisplay().startWithMissingSensors();
282
+ }
283
+ catch (err) {
284
+ this.logError(err, 'onIgnoreStart');
285
+ }
286
+ }
287
+ onCancelStart() {
288
+ try {
289
+ this.rideObserver?.stop();
290
+ this.getRideDisplay().cancelStart()
291
+ .then(() => {
292
+ this.moveToPreviousPage();
293
+ this.closePage();
294
+ })
295
+ .catch((err) => { this.logError(err, 'onCancelStart'); });
296
+ }
297
+ catch (err) {
298
+ this.logError(err, 'onCancelStart');
299
+ }
300
+ }
301
+ adjustLoad(deltaPct) {
302
+ try {
303
+ if (deltaPct >= 0)
304
+ this.getWorkoutRide().powerUp(deltaPct);
305
+ else
306
+ this.getWorkoutRide().powerDown(-deltaPct);
307
+ }
308
+ catch (err) {
309
+ this.logError(err, 'adjustLoad');
310
+ }
311
+ }
312
+ onRideStateUpdate(state) {
313
+ this.subscribeToWorkoutObserver();
314
+ switch (state) {
315
+ case 'Paused':
316
+ this.menuProps = { showResume: true, ...this.getStepFlags() };
317
+ this.updatePageDisplay();
318
+ break;
319
+ case 'Active':
320
+ this.menuProps = null;
321
+ this.updatePageDisplay();
322
+ break;
323
+ case 'Finished':
324
+ this.emitNavigateBack();
325
+ break;
326
+ case 'Error':
327
+ this.updatePageDisplay();
328
+ break;
329
+ }
330
+ }
331
+ onWorkoutUpdate() {
332
+ this.updatePageDisplay();
333
+ }
334
+ onWorkoutFinished() {
335
+ this.emitNavigateBack();
336
+ }
337
+ subscribeToWorkoutObserver() {
338
+ if (this.workoutObserverSubscribed)
339
+ return;
340
+ const observer = this.getWorkoutRide().getObserver();
341
+ if (!observer)
342
+ return;
343
+ Object.keys(this.workoutEventHandler).forEach(event => observer.on(event, this.workoutEventHandler[event]));
344
+ this.workoutObserverSubscribed = true;
345
+ }
346
+ unsubscribeFromWorkoutObserver() {
347
+ if (!this.workoutObserverSubscribed)
348
+ return;
349
+ const observer = this.getWorkoutRide().getObserver();
350
+ Object.keys(this.workoutEventHandler).forEach(event => observer?.off(event, this.workoutEventHandler[event]));
351
+ this.workoutObserverSubscribed = false;
352
+ }
353
+ registerRideEventHandlers() {
354
+ Object.keys(this.rideEventHandler).forEach(event => this.rideObserver?.on(event, this.rideEventHandler[event]));
355
+ }
356
+ unregisterRideEventHandlers() {
357
+ Object.keys(this.rideEventHandler).forEach(event => this.rideObserver?.off(event, this.rideEventHandler[event]));
358
+ }
359
+ buildBaseProps() {
360
+ const state = this.getRideDisplay().getState();
361
+ const isStarting = state === 'Idle' || state === 'Starting' || state === 'Error';
362
+ return {
363
+ rideState: state,
364
+ rideType: this.getRideDisplay().getRideType(),
365
+ startOverlayProps: isStarting ? this.getRideDisplay().getStartOverlayProps() : null,
366
+ menuProps: this.menuProps,
367
+ startGateProps: null
368
+ };
369
+ }
370
+ buildGraphPlan(current, ftp) {
371
+ if (!current) {
372
+ return { bars: [], ftp: ftp ?? 0, ftpLine: ftp ?? 0, domain: { x: [0, 0], y: [0, 0] } };
373
+ }
374
+ const bars = (0, graph_1.getWorkoutGraphSeries)(current, { ftp, absValues: true });
375
+ const lastBarX = bars.length ? bars.at(-1).x : 0;
376
+ const maxX = Math.max(this.getLastLogTime(), lastBarX, current.duration ?? 0);
377
+ const maxBarPower = bars.length ? Math.max(...bars.map(b => b.y)) : 0;
378
+ return {
379
+ bars,
380
+ ftp,
381
+ ftpLine: ftp,
382
+ domain: { x: [0, maxX], y: [0, maxBarPower * 1.1] }
383
+ };
384
+ }
385
+ buildUpcomingSteps(current, ftp) {
386
+ if (!current)
387
+ return { current: null, upcoming: [] };
388
+ const limits = this.getWorkoutRide().getCurrentLimits();
389
+ if (!limits)
390
+ return { current: null, upcoming: [] };
391
+ const currentStep = {
392
+ label: this.formatCurrentStepLabel(limits),
393
+ targetPower: limits.targetPower ?? null,
394
+ duration: limits.duration,
395
+ remaining: limits.remaining,
396
+ isCurrent: true
397
+ };
398
+ const elapsedTime = this.getElapsedActivityTime();
399
+ const steps = current.steps ?? [];
400
+ const currentIndex = steps.findIndex(s => s.start <= elapsedTime && s.end > elapsedTime);
401
+ const upcoming = currentIndex === -1
402
+ ? []
403
+ : steps.slice(currentIndex + 1, currentIndex + 1 + UPCOMING_STEPS_COUNT).map(step => ({
404
+ label: (0, graph_1.getStepPower)(step, ftp) || 'free',
405
+ targetPower: this.getStepAbsolutePower(step, ftp),
406
+ duration: step.duration,
407
+ remaining: null,
408
+ isCurrent: false
409
+ }));
410
+ return { current: currentStep, upcoming };
411
+ }
412
+ buildDashboardLine(wo) {
413
+ const limits = this.getWorkoutRide().getCurrentLimits();
414
+ return {
415
+ targetPower: limits?.targetPower ?? null,
416
+ targetDuration: limits?.duration ?? 0,
417
+ remaining: limits?.remaining ?? 0,
418
+ text: wo.title ?? '',
419
+ mode: wo.mode ?? null
420
+ };
421
+ }
422
+ formatCurrentStepLabel(limits) {
423
+ if (limits.targetPower === undefined || limits.targetPower === null)
424
+ return 'free';
425
+ if (limits.minPower !== undefined && limits.maxPower !== undefined && limits.minPower !== limits.maxPower)
426
+ return `${Math.round(limits.minPower)}-${Math.round(limits.maxPower)}W`;
427
+ return `${Math.round(limits.targetPower)}W`;
428
+ }
429
+ getStepAbsolutePower(step, ftp) {
430
+ const p = step.power;
431
+ if (!p)
432
+ return null;
433
+ const val = p.max ?? p.min;
434
+ if (val === undefined)
435
+ return null;
436
+ if (p.type === 'watt')
437
+ return Math.round(val);
438
+ if (ftp === undefined)
439
+ return null;
440
+ return Math.round(val / 100 * ftp);
441
+ }
442
+ getStepFlags() {
443
+ const wo = this.getWorkoutRide().getDashboardDisplayProperties();
444
+ return { canStepBack: !!wo.canShowBackward, canStepForward: !!wo.canShowForward };
445
+ }
446
+ getLastLogTime() {
447
+ const logs = this.getActivityRide().getActivity()?.logs;
448
+ if (!logs || logs.length === 0)
449
+ return 0;
450
+ return logs.at(-1).time ?? 0;
451
+ }
452
+ getElapsedActivityTime() {
453
+ return this.getActivityRide().getActivity()?.time ?? 0;
454
+ }
455
+ getEmptyDisplayProps() {
456
+ return {
457
+ rideState: 'Error',
458
+ rideType: null,
459
+ startOverlayProps: null,
460
+ menuProps: null,
461
+ startGateProps: null,
462
+ title: '',
463
+ graph: { bars: [], ftp: 0, ftpLine: 0, domain: { x: [0, 0], y: [0, 0] } },
464
+ steps: { current: null, upcoming: [] },
465
+ dashboard: { targetPower: null, targetDuration: 0, remaining: 0, text: '', mode: null }
466
+ };
467
+ }
468
+ updatePageDisplay() {
469
+ this.getPageObserver()?.emit('page-update');
470
+ }
471
+ emitNavigateBack() {
472
+ this.getPageObserver()?.emit('navigate-back');
473
+ }
474
+ moveToPreviousPage() {
475
+ this.moveTo('$contentPage');
476
+ }
477
+ get rideObserver() {
478
+ try {
479
+ return this.getRideDisplay()?.getObserver();
480
+ }
481
+ catch (err) {
482
+ this.logError(err, 'get rideObserver');
483
+ }
484
+ return null;
485
+ }
486
+ getRideDisplay() {
487
+ return (0, display_1.useRideDisplay)();
488
+ }
489
+ getWorkoutRide() {
490
+ return (0, service_1.useWorkoutRide)();
491
+ }
492
+ getActivityRide() {
493
+ return (0, activities_1.useActivityRide)();
494
+ }
495
+ };
496
+ return WorkoutRidePageService = _classThis;
497
+ })();
498
+ exports.WorkoutRidePageService = WorkoutRidePageService;
499
+ const getWorkoutRidePageService = () => new WorkoutRidePageService();
500
+ exports.getWorkoutRidePageService = getWorkoutRidePageService;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -401,7 +401,7 @@ let ActivityListService = (() => {
401
401
  startPos,
402
402
  segment: activity?.segment,
403
403
  started: activity ? new Date(activity.startTime) : undefined,
404
- showMap: true,
404
+ showMap: activity?.routeType !== 'None',
405
405
  points,
406
406
  activity,
407
407
  exports: this.exports,
@@ -419,10 +419,11 @@ let ActivityRideService = (() => {
419
419
  const route = this.getRouteList().getSelected();
420
420
  const startSettings = this.getRouteList().getStartSettings();
421
421
  const isFreeRide = startSettings?.type === 'Free-Ride';
422
+ const isWorkout = this.activity?.routeType === 'None';
422
423
  const showSave = this.activity !== undefined && !this.isSaveDone;
423
424
  const showContinue = this.state !== 'completed';
424
425
  const hasGPX = this.activity?.logs?.some((log) => !!log.lat && !!log.lng);
425
- const showMap = hasGPX || isFreeRide || (route?.description?.hasGpx);
426
+ const showMap = hasGPX || isFreeRide || isWorkout || (route?.description?.hasGpx);
426
427
  const preview = showMap ? undefined : route?.description?.previewUrl;
427
428
  if (!this.isSummaryShown) {
428
429
  this.logEvent({ message: 'activity summary shown', showSave, showContinue, showMap, fileLink: this.activity?.fitFileName });
@@ -0,0 +1,2 @@
1
+ export * from './series';
2
+ export * from './types';
@@ -0,0 +1,156 @@
1
+ import { Workout } from "../model";
2
+ export const WORKOUT_ZONE_COLORS = [
3
+ 'white',
4
+ '#7f7f7f',
5
+ '#338cff',
6
+ '#59bf59',
7
+ '#ffcc3f',
8
+ '#ff6639',
9
+ '#ff330c',
10
+ '#ea39ff'
11
+ ];
12
+ function getMinPower(step, ftp) {
13
+ if (step?.power === undefined)
14
+ return undefined;
15
+ const p = step.power;
16
+ if (p.max !== undefined && p.min !== undefined && p.max === p.min)
17
+ return 0;
18
+ if (p.min === undefined)
19
+ return undefined;
20
+ let res = Math.round(p.min);
21
+ if (p.type === 'watt' && ftp !== undefined)
22
+ res = Math.round(p.min / ftp * 100);
23
+ return res;
24
+ }
25
+ function getMaxPower(step, ftp) {
26
+ if (step?.power === undefined)
27
+ return 0;
28
+ const p = step.power;
29
+ if (p.max === undefined)
30
+ return undefined;
31
+ let res = Math.round(p.max);
32
+ if (p.type === 'watt' && ftp !== undefined)
33
+ res = Math.round(p.max / ftp * 100);
34
+ return res;
35
+ }
36
+ function getZone(power, ftp) {
37
+ if (power === undefined)
38
+ return undefined;
39
+ if (ftp === undefined)
40
+ return 0;
41
+ if (power <= 55)
42
+ return 1;
43
+ if (power <= 75)
44
+ return 2;
45
+ if (power <= 90)
46
+ return 3;
47
+ if (power <= 105)
48
+ return 4;
49
+ if (power <= 120)
50
+ return 5;
51
+ if (power <= 150)
52
+ return 6;
53
+ return 7;
54
+ }
55
+ export function getStepDuration(step) {
56
+ if (step.duration <= 60)
57
+ return `${step.duration}s`;
58
+ if (step.duration <= 3600)
59
+ return `${step.duration / 60}min`;
60
+ return `${step.duration / 3600}h`;
61
+ }
62
+ function formatPowerText(low, high, unit, rangePrefix) {
63
+ if (low !== undefined && high !== undefined && low !== high)
64
+ return `${rangePrefix}${Math.round(low)}-${Math.round(high)}${unit}`;
65
+ if (high !== undefined)
66
+ return `${Math.round(high)}${unit}`;
67
+ return '';
68
+ }
69
+ function appendConvertedSuffix(text, low, high, unit, ftp) {
70
+ if (text === '' || ftp === undefined)
71
+ return text;
72
+ const isPct = unit === '%';
73
+ const convert = (v) => isPct ? Math.round(v / 100 * ftp) : Math.round(v * 100 / ftp);
74
+ const suffixUnit = isPct ? 'W' : '%';
75
+ const range = (low !== undefined && high !== undefined && low !== high)
76
+ ? `${convert(low)}-${convert(high)}`
77
+ : `${convert(high)}`;
78
+ return `${text} (${range}${suffixUnit})`;
79
+ }
80
+ export function getStepPower(step, ftp) {
81
+ if (step.power === undefined)
82
+ return '';
83
+ const { min, max, type } = step.power;
84
+ const unit = type === 'watt' ? 'W' : '%';
85
+ const ascending = step.steady || step.cooldown === false;
86
+ const low = ascending ? min : max;
87
+ const high = ascending ? max : min;
88
+ const rangePrefix = step.steady ? '' : 'Ramp ';
89
+ const stepText = formatPowerText(low, high, unit, rangePrefix);
90
+ return appendConvertedSuffix(stepText, low, high, unit, ftp);
91
+ }
92
+ function coerceToWorkout(workout) {
93
+ if (workout instanceof Workout)
94
+ return workout;
95
+ try {
96
+ return new Workout(workout);
97
+ }
98
+ catch {
99
+ return undefined;
100
+ }
101
+ }
102
+ function buildSteadyBar(step, stepStart, ftp, absValues) {
103
+ const x0 = stepStart;
104
+ const x = stepStart + step.getDuration();
105
+ let y = getMaxPower(step, ftp);
106
+ let y0 = getMinPower(step, ftp);
107
+ const zone = getZone(y, ftp);
108
+ if (absValues && ftp) {
109
+ y = ftp * y / 100;
110
+ y0 = ftp * y0 / 100;
111
+ }
112
+ if (y === undefined || Number.isNaN(y))
113
+ return undefined;
114
+ if (y0 !== undefined && !Number.isNaN(y0))
115
+ return { x, x0, y, y0, zone };
116
+ if (y0 === undefined)
117
+ return { x, x0, y, y0: 0, zone };
118
+ return undefined;
119
+ }
120
+ function buildRampBars(wo, step, stepStart, ftp, absValues) {
121
+ const bars = [];
122
+ const stepSize = step.getDuration() / 10;
123
+ for (let i = 0; i < 10; i++) {
124
+ const x0 = stepStart + i * stepSize;
125
+ const x = x0 + stepSize;
126
+ const limit = wo.getLimits(x0 + 0.01);
127
+ let y = getMaxPower(limit, ftp);
128
+ const zone = getZone(y, ftp);
129
+ if (absValues && ftp)
130
+ y = ftp * y / 100;
131
+ if (y !== undefined && !Number.isNaN(y))
132
+ bars.push({ x, x0, y, y0: 0, zone });
133
+ }
134
+ return bars;
135
+ }
136
+ export function getWorkoutGraphSeries(workout, opts) {
137
+ const { ftp, absValues = false } = opts ?? {};
138
+ const wo = coerceToWorkout(workout);
139
+ if (!wo)
140
+ return [];
141
+ const bars = [];
142
+ let stepStart = 0;
143
+ for (let limits = wo.getLimits(0, true); limits !== undefined; limits = wo.getLimits(stepStart + 0.01, true)) {
144
+ const step = limits.step;
145
+ if (step.steady) {
146
+ const bar = buildSteadyBar(step, stepStart, ftp, absValues);
147
+ if (bar)
148
+ bars.push(bar);
149
+ }
150
+ else {
151
+ bars.push(...buildRampBars(wo, step, stepStart, ftp, absValues));
152
+ }
153
+ stepStart += step.getDuration();
154
+ }
155
+ return bars;
156
+ }