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.
- package/lib/cjs/activities/list/service.js +1 -1
- package/lib/cjs/activities/ride/service.js +2 -1
- package/lib/cjs/workouts/base/graph/index.js +18 -0
- package/lib/cjs/workouts/base/graph/series.js +162 -0
- package/lib/cjs/workouts/base/graph/types.js +2 -0
- package/lib/cjs/workouts/base/model/index.js +0 -1
- package/lib/cjs/workouts/calendar/service.js +14 -0
- package/lib/cjs/workouts/index.js +2 -0
- package/lib/cjs/workouts/list/cards/index.js +0 -1
- package/lib/cjs/workouts/list/index.js +0 -1
- package/lib/cjs/workouts/list/pageService.js +362 -5
- package/lib/cjs/workouts/list/service.js +39 -12
- package/lib/cjs/workouts/page/index.js +17 -0
- package/lib/cjs/workouts/page/service.js +455 -0
- package/lib/cjs/workouts/page/types.js +2 -0
- package/lib/cjs/workouts/ride/index.js +1 -0
- package/lib/cjs/workouts/ride/page/index.js +18 -0
- package/lib/cjs/workouts/ride/page/service.js +500 -0
- package/lib/cjs/workouts/ride/page/types.js +2 -0
- package/lib/cjs/workouts/types.js +2 -0
- package/lib/esm/activities/list/service.js +1 -1
- package/lib/esm/activities/ride/service.js +2 -1
- package/lib/esm/workouts/base/graph/index.js +2 -0
- package/lib/esm/workouts/base/graph/series.js +156 -0
- package/lib/esm/workouts/base/graph/types.js +1 -0
- package/lib/esm/workouts/base/model/index.js +0 -1
- package/lib/esm/workouts/calendar/service.js +14 -0
- package/lib/esm/workouts/index.js +2 -0
- package/lib/esm/workouts/list/cards/index.js +0 -1
- package/lib/esm/workouts/list/index.js +0 -1
- package/lib/esm/workouts/list/pageService.js +362 -5
- package/lib/esm/workouts/list/service.js +39 -12
- package/lib/esm/workouts/page/index.js +1 -0
- package/lib/esm/workouts/page/service.js +451 -0
- package/lib/esm/workouts/page/types.js +1 -0
- package/lib/esm/workouts/ride/index.js +1 -0
- package/lib/esm/workouts/ride/page/index.js +2 -0
- package/lib/esm/workouts/ride/page/service.js +496 -0
- package/lib/esm/workouts/ride/page/types.js +1 -0
- package/lib/esm/workouts/types.js +1 -0
- package/lib/types/types.d.ts +1 -0
- package/lib/types/workouts/base/graph/index.d.ts +2 -0
- package/lib/types/workouts/base/graph/series.d.ts +7 -0
- package/lib/types/workouts/base/graph/types.d.ts +11 -0
- package/lib/types/workouts/base/model/index.d.ts +0 -1
- package/lib/types/workouts/base/parsers/intervals/parser.d.ts +2 -1
- package/lib/types/workouts/calendar/service.d.ts +1 -0
- package/lib/types/workouts/index.d.ts +2 -0
- package/lib/types/workouts/list/cards/index.d.ts +0 -1
- package/lib/types/workouts/list/index.d.ts +0 -1
- package/lib/types/workouts/list/pageService.d.ts +136 -4
- package/lib/types/workouts/list/service.d.ts +4 -0
- package/lib/types/workouts/list/types.d.ts +1 -1
- package/lib/types/workouts/page/index.d.ts +1 -0
- package/lib/types/workouts/page/service.d.ts +57 -0
- package/lib/types/workouts/page/types.d.ts +95 -0
- package/lib/types/workouts/ride/index.d.ts +1 -0
- package/lib/types/workouts/ride/page/index.d.ts +2 -0
- package/lib/types/workouts/ride/page/service.d.ts +71 -0
- package/lib/types/workouts/ride/page/types.d.ts +72 -0
- package/lib/types/workouts/ride/service.d.ts +2 -1
- package/lib/types/workouts/ride/types.d.ts +1 -0
- package/lib/types/workouts/types.d.ts +8 -0
- 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;
|
|
@@ -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:
|
|
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,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
|
+
}
|