incyclist-services 1.7.95 → 1.7.97
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/ride/page/index.js +1 -0
- package/lib/cjs/ride/page/service.js +458 -124
- package/lib/cjs/workouts/ride/index.js +0 -1
- package/lib/esm/ride/page/index.js +1 -0
- package/lib/esm/ride/page/service.js +458 -124
- package/lib/esm/workouts/ride/index.js +0 -1
- package/lib/types/activities/ride/service.d.ts +1 -1
- package/lib/types/activities/ride/types.d.ts +1 -1
- package/lib/types/appstate/types.d.ts +1 -1
- package/lib/types/ride/page/index.d.ts +1 -0
- package/lib/types/ride/page/service.d.ts +69 -17
- package/lib/types/ride/page/types.d.ts +85 -8
- package/lib/types/workouts/ride/index.d.ts +0 -1
- package/lib/types/workouts/types.d.ts +0 -1
- package/package.json +1 -1
- package/lib/cjs/activities/upload/velohero.js +0 -136
- package/lib/cjs/apps/velohero/VeloHeroAppConnection.js +0 -232
- package/lib/cjs/apps/velohero/index.js +0 -18
- package/lib/cjs/apps/velohero/types.js +0 -2
- package/lib/cjs/workouts/list/pageService.js +0 -455
- package/lib/cjs/workouts/ride/page/index.js +0 -18
- package/lib/cjs/workouts/ride/page/service.js +0 -564
- package/lib/cjs/workouts/ride/page/types.js +0 -2
- package/lib/esm/activities/upload/trainingpeaks.js +0 -123
- package/lib/esm/activities/upload/velohero.js +0 -133
- package/lib/esm/apps/base/api/trainingpeaks/api.js +0 -143
- package/lib/esm/apps/base/api/trainingpeaks/index.js +0 -1
- package/lib/esm/apps/base/api/trainingpeaks/types.js +0 -1
- package/lib/esm/apps/trainingpeaks/TrainingPeaksAppConnection.js +0 -189
- package/lib/esm/apps/trainingpeaks/types.js +0 -1
- package/lib/esm/apps/velohero/VeloHeroAppConnection.js +0 -229
- package/lib/esm/apps/velohero/index.js +0 -2
- package/lib/esm/apps/velohero/types.js +0 -1
- package/lib/esm/workouts/calendar/sync/trainingpeaks/provider.js +0 -201
- package/lib/esm/workouts/list/pageService.js +0 -451
- package/lib/types/activities/upload/trainingpeaks.d.ts +0 -16
- package/lib/types/activities/upload/velohero.d.ts +0 -18
- package/lib/types/apps/base/api/trainingpeaks/api.d.ts +0 -30
- package/lib/types/apps/base/api/trainingpeaks/index.d.ts +0 -2
- package/lib/types/apps/base/api/trainingpeaks/types.d.ts +0 -55
- package/lib/types/apps/trainingpeaks/TrainingPeaksAppConnection.d.ts +0 -22
- package/lib/types/apps/trainingpeaks/types.d.ts +0 -6
- package/lib/types/apps/velohero/VeloHeroAppConnection.d.ts +0 -24
- package/lib/types/apps/velohero/index.d.ts +0 -2
- package/lib/types/apps/velohero/types.d.ts +0 -9
- package/lib/types/workouts/calendar/sync/trainingpeaks/provider.d.ts +0 -22
- package/lib/types/workouts/list/pageService.d.ts +0 -150
|
@@ -40,9 +40,16 @@ const decorators_1 = require("../../base/decorators");
|
|
|
40
40
|
const pages_1 = require("../../base/pages");
|
|
41
41
|
const display_1 = require("../display");
|
|
42
42
|
const sleep_1 = require("../../utils/sleep");
|
|
43
|
-
const api_1 = require("../../api");
|
|
44
43
|
const monitoring_1 = require("../../monitoring");
|
|
44
|
+
const activities_1 = require("../../activities");
|
|
45
|
+
const settings_1 = require("../../settings");
|
|
46
|
+
const service_1 = require("../../workouts/ride/service");
|
|
47
|
+
const graph_1 = require("../../workouts/base/graph");
|
|
45
48
|
const BACKGROUND_PAUSE_TIMEOUT_MS = 300000;
|
|
49
|
+
const UPCOMING_STEPS_COUNT = 3;
|
|
50
|
+
const DEFAULT_LOAD_INCREMENT = 1;
|
|
51
|
+
const HINTS_WORKOUT_GESTURES_KEY = 'hints.workoutRideGestures';
|
|
52
|
+
const LOAD_INCREMENT_SETTING_KEY = 'preferences.workouts.loadIncrement';
|
|
46
53
|
let RidePageService = (() => {
|
|
47
54
|
let _classDecorators = [decorators_1.Singleton];
|
|
48
55
|
let _classDescriptor;
|
|
@@ -51,17 +58,23 @@ let RidePageService = (() => {
|
|
|
51
58
|
let _classSuper = pages_1.IncyclistPageService;
|
|
52
59
|
let _instanceExtraInitializers = [];
|
|
53
60
|
let _getRideDisplay_decorators;
|
|
54
|
-
let
|
|
61
|
+
let _getWorkoutRide_decorators;
|
|
62
|
+
let _getActivityRide_decorators;
|
|
63
|
+
let _getUserSettings_decorators;
|
|
55
64
|
let _getOnlineStatusMonitoring_decorators;
|
|
56
65
|
var RidePageService = class extends _classSuper {
|
|
57
66
|
static { _classThis = this; }
|
|
58
67
|
static {
|
|
59
68
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
60
69
|
_getRideDisplay_decorators = [decorators_1.Injectable];
|
|
61
|
-
|
|
70
|
+
_getWorkoutRide_decorators = [decorators_1.Injectable];
|
|
71
|
+
_getActivityRide_decorators = [decorators_1.Injectable];
|
|
72
|
+
_getUserSettings_decorators = [decorators_1.Injectable];
|
|
62
73
|
_getOnlineStatusMonitoring_decorators = [decorators_1.Injectable];
|
|
63
74
|
__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);
|
|
64
|
-
__esDecorate(this, null,
|
|
75
|
+
__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);
|
|
76
|
+
__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);
|
|
77
|
+
__esDecorate(this, null, _getUserSettings_decorators, { kind: "method", name: "getUserSettings", static: false, private: false, access: { has: obj => "getUserSettings" in obj, get: obj => obj.getUserSettings }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
65
78
|
__esDecorate(this, null, _getOnlineStatusMonitoring_decorators, { kind: "method", name: "getOnlineStatusMonitoring", static: false, private: false, access: { has: obj => "getOnlineStatusMonitoring" in obj, get: obj => obj.getOnlineStatusMonitoring }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
66
79
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
67
80
|
RidePageService = _classThis = _classDescriptor.value;
|
|
@@ -69,15 +82,24 @@ let RidePageService = (() => {
|
|
|
69
82
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
70
83
|
}
|
|
71
84
|
eventHandler = (__runInitializers(this, _instanceExtraInitializers), {});
|
|
85
|
+
workoutEventHandler = {};
|
|
86
|
+
workoutObserverSubscribed = false;
|
|
72
87
|
backgroundTimer;
|
|
73
88
|
backgroundPausedByService = false;
|
|
74
89
|
menuProps = null;
|
|
75
90
|
isInitialized = false;
|
|
76
91
|
startGateProps = null;
|
|
92
|
+
gestureHintDismissed = false;
|
|
77
93
|
constructor() {
|
|
78
94
|
super('RidePage');
|
|
79
95
|
this.eventHandler['state-update'] = this.onDisplayStateUpdate.bind(this);
|
|
80
96
|
this.eventHandler['route-update'] = this.onRouteUpdate.bind(this);
|
|
97
|
+
this.workoutEventHandler['step-changed'] = this.onWorkoutUpdate.bind(this);
|
|
98
|
+
this.workoutEventHandler['update'] = this.onWorkoutUpdate.bind(this);
|
|
99
|
+
this.workoutEventHandler['forward'] = this.onWorkoutUpdate.bind(this);
|
|
100
|
+
this.workoutEventHandler['backward'] = this.onWorkoutUpdate.bind(this);
|
|
101
|
+
this.workoutEventHandler['completed'] = this.onWorkoutFinished.bind(this);
|
|
102
|
+
this.workoutEventHandler['stopped'] = this.onWorkoutFinished.bind(this);
|
|
81
103
|
}
|
|
82
104
|
async initPage() {
|
|
83
105
|
try {
|
|
@@ -92,19 +114,24 @@ let RidePageService = (() => {
|
|
|
92
114
|
}
|
|
93
115
|
openPage(simulate) {
|
|
94
116
|
try {
|
|
95
|
-
this.
|
|
96
|
-
|
|
117
|
+
const pageLogName = this.getPageLogName();
|
|
118
|
+
this.logEvent({ message: 'page shown', page: pageLogName });
|
|
119
|
+
gd_eventlog_1.EventLogger.setGlobalConfig('page', pageLogName);
|
|
120
|
+
this.gestureHintDismissed = false;
|
|
97
121
|
super.openPage();
|
|
98
122
|
try {
|
|
99
123
|
const service = this.getRideDisplay();
|
|
100
124
|
const registerAndStart = () => {
|
|
101
|
-
this.
|
|
125
|
+
this.registerHandlers(this.rideObserver, this.eventHandler);
|
|
126
|
+
this.subscribeToWorkoutObserver();
|
|
102
127
|
service.start(simulate);
|
|
103
|
-
(
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
if (this.isRideType('Video', 'GPX')) {
|
|
129
|
+
(0, sleep_1.sleep)(5).then(() => {
|
|
130
|
+
this.updatePageDisplay();
|
|
131
|
+
});
|
|
132
|
+
}
|
|
106
133
|
};
|
|
107
|
-
if (!this.isInitialized) {
|
|
134
|
+
if (this.requiresOwnInit() && !this.isInitialized) {
|
|
108
135
|
service.init()
|
|
109
136
|
.then(() => {
|
|
110
137
|
this.isInitialized = true;
|
|
@@ -129,8 +156,10 @@ let RidePageService = (() => {
|
|
|
129
156
|
closePage() {
|
|
130
157
|
try {
|
|
131
158
|
gd_eventlog_1.EventLogger.setGlobalConfig('page', null);
|
|
132
|
-
this.logEvent({ message: 'page closed', page:
|
|
159
|
+
this.logEvent({ message: 'page closed', page: this.getPageLogName() });
|
|
133
160
|
this.getRideDisplay().stop();
|
|
161
|
+
this.unregisterHandlers(this.rideObserver, this.eventHandler);
|
|
162
|
+
this.unsubscribeFromWorkoutObserver();
|
|
134
163
|
this.menuProps = null;
|
|
135
164
|
this.isInitialized = false;
|
|
136
165
|
super.closePage();
|
|
@@ -142,7 +171,7 @@ let RidePageService = (() => {
|
|
|
142
171
|
async pausePage() {
|
|
143
172
|
try {
|
|
144
173
|
this.backgroundTimer = setTimeout(() => {
|
|
145
|
-
this.getRideDisplay().pause(
|
|
174
|
+
this.getRideDisplay().pause(this.getBackgroundPauseRequester());
|
|
146
175
|
this.backgroundPausedByService = true;
|
|
147
176
|
}, BACKGROUND_PAUSE_TIMEOUT_MS);
|
|
148
177
|
this.isInitialized = false;
|
|
@@ -166,63 +195,13 @@ let RidePageService = (() => {
|
|
|
166
195
|
getRideObserver() {
|
|
167
196
|
return this.rideObserver ?? null;
|
|
168
197
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const noRideProps = {
|
|
172
|
-
rideState: 'Error',
|
|
173
|
-
startOverlayProps,
|
|
174
|
-
rideType: null,
|
|
175
|
-
menuProps: null,
|
|
176
|
-
startGateProps: null
|
|
177
|
-
};
|
|
178
|
-
try {
|
|
179
|
-
const rideType = this.getRideDisplay().getRideType();
|
|
180
|
-
switch (rideType) {
|
|
181
|
-
case 'Video': return this.getVideoRideDisplayProps();
|
|
182
|
-
case 'GPX': return this.getGPXRideDisplayProps();
|
|
183
|
-
default:
|
|
184
|
-
return noRideProps;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
return noRideProps;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
onRefreshSecrets() {
|
|
192
|
-
this.hideStartgate();
|
|
193
|
-
}
|
|
194
|
-
onContinueAnyway() {
|
|
195
|
-
this.hideStartgate();
|
|
196
|
-
}
|
|
197
|
-
onMenuOpen() {
|
|
198
|
-
try {
|
|
199
|
-
const state = this.getRideDisplay().getState();
|
|
200
|
-
this.menuProps = { showResume: state === 'Paused' };
|
|
201
|
-
this.updatePageDisplay();
|
|
202
|
-
}
|
|
203
|
-
catch (err) {
|
|
204
|
-
this.logError(err, 'onMenuOpen');
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
onMenuClose() {
|
|
208
|
-
try {
|
|
209
|
-
const state = this.getRideDisplay().getState();
|
|
210
|
-
if (state === 'Finished' || this.menuProps?.finished) {
|
|
211
|
-
this.moveToPreviousPage();
|
|
212
|
-
this.closePage();
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
this.menuProps = null;
|
|
216
|
-
this.updatePageDisplay();
|
|
217
|
-
}
|
|
218
|
-
catch (err) {
|
|
219
|
-
this.logError(err, 'onMenuClose');
|
|
220
|
-
}
|
|
198
|
+
getRideType() {
|
|
199
|
+
return this.getRideDisplay().getRideType();
|
|
221
200
|
}
|
|
222
201
|
onPause() {
|
|
223
202
|
try {
|
|
224
203
|
this.getRideDisplay().pause('user');
|
|
225
|
-
this.menuProps =
|
|
204
|
+
this.menuProps = this.buildPausedMenuProps();
|
|
226
205
|
this.updatePageDisplay();
|
|
227
206
|
}
|
|
228
207
|
catch (err) {
|
|
@@ -239,25 +218,6 @@ let RidePageService = (() => {
|
|
|
239
218
|
this.logError(err, 'onResume');
|
|
240
219
|
}
|
|
241
220
|
}
|
|
242
|
-
onFinished() {
|
|
243
|
-
try {
|
|
244
|
-
this.menuProps = { showResume: false, finished: true };
|
|
245
|
-
this.updatePageDisplay();
|
|
246
|
-
}
|
|
247
|
-
catch (err) {
|
|
248
|
-
this.logError(err, 'onResume');
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
onEndRide() {
|
|
252
|
-
try {
|
|
253
|
-
this.getRideDisplay().stop();
|
|
254
|
-
this.moveToPreviousPage();
|
|
255
|
-
this.closePage();
|
|
256
|
-
}
|
|
257
|
-
catch (err) {
|
|
258
|
-
this.logError(err, 'onEndRide');
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
221
|
onRetryStart() {
|
|
262
222
|
try {
|
|
263
223
|
this.getRideDisplay().retryStart();
|
|
@@ -288,17 +248,81 @@ let RidePageService = (() => {
|
|
|
288
248
|
this.logError(err, 'onCancelStart');
|
|
289
249
|
}
|
|
290
250
|
}
|
|
251
|
+
onMenuOpen() {
|
|
252
|
+
try {
|
|
253
|
+
const state = this.getRideDisplay().getState();
|
|
254
|
+
this.menuProps = this.isRideType('Workout')
|
|
255
|
+
? { showResume: state === 'Paused', ...this.getStepFlags() }
|
|
256
|
+
: { showResume: state === 'Paused' };
|
|
257
|
+
this.updatePageDisplay();
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
this.logError(err, 'onMenuOpen');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
onMenuClose() {
|
|
264
|
+
try {
|
|
265
|
+
if (this.isRideType('Workout')) {
|
|
266
|
+
this.menuProps = null;
|
|
267
|
+
this.updatePageDisplay();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const state = this.getRideDisplay().getState();
|
|
271
|
+
if (state === 'Finished' || this.menuProps?.finished) {
|
|
272
|
+
this.moveToPreviousPage();
|
|
273
|
+
this.closePage();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
this.menuProps = null;
|
|
277
|
+
this.updatePageDisplay();
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
this.logError(err, 'onMenuClose');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
getPageDisplayProps() {
|
|
284
|
+
const startOverlayProps = this.getRideDisplay().getStartOverlayProps();
|
|
285
|
+
const noRideProps = {
|
|
286
|
+
rideState: 'Error',
|
|
287
|
+
startOverlayProps,
|
|
288
|
+
rideType: null,
|
|
289
|
+
menuProps: null,
|
|
290
|
+
startGateProps: null
|
|
291
|
+
};
|
|
292
|
+
try {
|
|
293
|
+
const rideType = this.getRideDisplay().getRideType();
|
|
294
|
+
switch (rideType) {
|
|
295
|
+
case 'Video': return this.getVideoRideDisplayProps();
|
|
296
|
+
case 'GPX': return this.getGPXRideDisplayProps();
|
|
297
|
+
case 'Workout': return this.getWorkoutRideDisplayProps();
|
|
298
|
+
default:
|
|
299
|
+
return noRideProps;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return noRideProps;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
onRefreshSecrets() {
|
|
307
|
+
this.hideStartgate();
|
|
308
|
+
}
|
|
309
|
+
onContinueAnyway() {
|
|
310
|
+
this.hideStartgate();
|
|
311
|
+
}
|
|
312
|
+
onEndRide() {
|
|
313
|
+
try {
|
|
314
|
+
this.getRideDisplay().stop();
|
|
315
|
+
this.moveToPreviousPage();
|
|
316
|
+
this.closePage();
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
this.logError(err, 'onEndRide');
|
|
320
|
+
}
|
|
321
|
+
}
|
|
291
322
|
getVideoRideDisplayProps() {
|
|
292
323
|
const props = this.rideDisplayProps;
|
|
293
|
-
const state = this.getRideDisplay().getState();
|
|
294
|
-
const rideType = this.getRideDisplay().getRideType();
|
|
295
|
-
const isStarting = state === 'Idle' || state === 'Starting' || state === 'Error';
|
|
296
324
|
const displayProps = {
|
|
297
|
-
|
|
298
|
-
rideType,
|
|
299
|
-
startGateProps: this.startGateProps,
|
|
300
|
-
startOverlayProps: isStarting ? this.getRideDisplay().getStartOverlayProps() : null,
|
|
301
|
-
menuProps: this.menuProps,
|
|
325
|
+
...this.buildBaseDisplayProps(),
|
|
302
326
|
video: props.video,
|
|
303
327
|
videos: props.videos,
|
|
304
328
|
route: props.route
|
|
@@ -307,15 +331,8 @@ let RidePageService = (() => {
|
|
|
307
331
|
}
|
|
308
332
|
getGPXRideDisplayProps() {
|
|
309
333
|
const props = this.rideDisplayProps;
|
|
310
|
-
const state = this.getRideDisplay().getState();
|
|
311
|
-
const rideType = this.getRideDisplay().getRideType();
|
|
312
|
-
const isStarting = state === 'Idle' || state === 'Starting' || state === 'Error';
|
|
313
334
|
const displayProps = {
|
|
314
|
-
|
|
315
|
-
rideType,
|
|
316
|
-
startGateProps: this.startGateProps,
|
|
317
|
-
startOverlayProps: isStarting ? this.getRideDisplay().getStartOverlayProps() : null,
|
|
318
|
-
menuProps: this.menuProps,
|
|
335
|
+
...this.buildBaseDisplayProps(),
|
|
319
336
|
rideView: props.rideView,
|
|
320
337
|
route: props.route,
|
|
321
338
|
displayObserver: props.displayObserver
|
|
@@ -342,36 +359,351 @@ let RidePageService = (() => {
|
|
|
342
359
|
this.startGateProps = null;
|
|
343
360
|
this.updatePageDisplay();
|
|
344
361
|
}
|
|
345
|
-
|
|
346
|
-
|
|
362
|
+
onRouteUpdate() {
|
|
363
|
+
this.updatePageDisplay();
|
|
347
364
|
}
|
|
348
|
-
|
|
349
|
-
this.
|
|
365
|
+
get rideDisplayProps() {
|
|
366
|
+
return this.getRideDisplay().getDisplayProperties();
|
|
367
|
+
}
|
|
368
|
+
getSecretBinding() {
|
|
369
|
+
return this.getBindings().secret;
|
|
370
|
+
}
|
|
371
|
+
getGraphActuals() {
|
|
372
|
+
try {
|
|
373
|
+
const state = this.getRideDisplay().getState();
|
|
374
|
+
if (state === 'Idle' || state === 'Starting' || state === 'Started') {
|
|
375
|
+
return { power: [], heartrate: [], position: 0 };
|
|
376
|
+
}
|
|
377
|
+
const logs = this.getActivityRide().getActivity()?.logs ?? [];
|
|
378
|
+
const power = [];
|
|
379
|
+
const heartrate = [];
|
|
380
|
+
logs.forEach(log => {
|
|
381
|
+
if (log.power !== undefined)
|
|
382
|
+
power.push({ x: log.time, y: log.power });
|
|
383
|
+
if (log.heartrate !== undefined)
|
|
384
|
+
heartrate.push({ x: log.time, y: log.heartrate });
|
|
385
|
+
});
|
|
386
|
+
return { power, heartrate, position: this.getElapsedActivityTime() };
|
|
387
|
+
}
|
|
388
|
+
catch (err) {
|
|
389
|
+
this.logError(err, 'getGraphActuals');
|
|
390
|
+
return { power: [], heartrate: [], position: 0 };
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
onStop() {
|
|
394
|
+
try {
|
|
395
|
+
this.finishRide();
|
|
396
|
+
}
|
|
397
|
+
catch (err) {
|
|
398
|
+
this.logError(err, 'onStop');
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
onStepBack() {
|
|
402
|
+
try {
|
|
403
|
+
this.getRideDisplay().backward();
|
|
404
|
+
}
|
|
405
|
+
catch (err) {
|
|
406
|
+
this.logError(err, 'onStepBack');
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
onStepForward() {
|
|
410
|
+
try {
|
|
411
|
+
this.getRideDisplay().forward();
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
this.logError(err, 'onStepForward');
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
onIncreaseLoad() {
|
|
418
|
+
this.adjustLoad(this.getLoadIncrement());
|
|
350
419
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
420
|
+
onDecreaseLoad() {
|
|
421
|
+
this.adjustLoad(-this.getLoadIncrement());
|
|
422
|
+
}
|
|
423
|
+
onSetLoadIncrement(value) {
|
|
424
|
+
try {
|
|
425
|
+
this.getUserSettings().set(LOAD_INCREMENT_SETTING_KEY, value);
|
|
426
|
+
this.updatePageDisplay();
|
|
427
|
+
}
|
|
428
|
+
catch (err) {
|
|
429
|
+
this.logError(err, 'onSetLoadIncrement');
|
|
430
|
+
}
|
|
354
431
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
432
|
+
onGestureHintDismissed({ dontShowAgain }) {
|
|
433
|
+
try {
|
|
434
|
+
this.gestureHintDismissed = true;
|
|
435
|
+
if (dontShowAgain) {
|
|
436
|
+
this.getUserSettings().set(HINTS_WORKOUT_GESTURES_KEY, true);
|
|
437
|
+
}
|
|
438
|
+
this.updatePageDisplay();
|
|
439
|
+
}
|
|
440
|
+
catch (err) {
|
|
441
|
+
this.logError(err, 'onGestureHintDismissed');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
adjustLoad(deltaPct) {
|
|
445
|
+
try {
|
|
446
|
+
if (deltaPct >= 0)
|
|
447
|
+
return this.getWorkoutRide().powerUp(deltaPct);
|
|
448
|
+
else
|
|
449
|
+
return this.getWorkoutRide().powerDown(-deltaPct);
|
|
450
|
+
}
|
|
451
|
+
catch (err) {
|
|
452
|
+
this.logError(err, 'adjustLoad');
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
getLoadButtonMode() {
|
|
457
|
+
try {
|
|
458
|
+
return this.getWorkoutRide().getLoadButtonMode();
|
|
459
|
+
}
|
|
460
|
+
catch (err) {
|
|
461
|
+
this.logError(err, 'getLoadButtonMode');
|
|
462
|
+
return 'power';
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
getWorkoutRideDisplayProps() {
|
|
466
|
+
try {
|
|
467
|
+
const rideType = this.getRideDisplay().getRideType();
|
|
468
|
+
if (rideType !== 'Workout') {
|
|
469
|
+
this.logError(new Error(`unexpected ride type '${rideType}' for WorkoutRidePage`), 'getWorkoutRideDisplayProps');
|
|
470
|
+
return this.getEmptyWorkoutDisplayProps();
|
|
471
|
+
}
|
|
472
|
+
const base = this.buildBaseDisplayProps();
|
|
473
|
+
const wo = this.getWorkoutRide().getDashboardDisplayProperties();
|
|
474
|
+
const current = this.getRideDisplay().getDisplayProperties().workout;
|
|
475
|
+
return {
|
|
476
|
+
...base,
|
|
477
|
+
menuProps: base.menuProps,
|
|
478
|
+
title: wo.title ?? '',
|
|
479
|
+
graph: this.buildGraphPlan(current, wo.ftp),
|
|
480
|
+
steps: this.buildUpcomingSteps(current, wo.ftp),
|
|
481
|
+
dashboard: this.buildDashboardLine(wo),
|
|
482
|
+
gestureHint: this.buildGestureHint(base.startOverlayProps === null),
|
|
483
|
+
loadIncrement: this.getLoadIncrement(),
|
|
484
|
+
loadButtonMode: wo.loadButtonMode ?? 'power'
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
catch (err) {
|
|
488
|
+
this.logError(err, 'getWorkoutRideDisplayProps');
|
|
489
|
+
return this.getEmptyWorkoutDisplayProps();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
onWorkoutUpdate() {
|
|
493
|
+
this.updatePageDisplay();
|
|
494
|
+
}
|
|
495
|
+
onWorkoutFinished() {
|
|
496
|
+
try {
|
|
497
|
+
this.menuProps = this.buildFinishedMenuProps();
|
|
498
|
+
this.updatePageDisplay();
|
|
499
|
+
}
|
|
500
|
+
catch (err) {
|
|
501
|
+
this.logError(err, 'onWorkoutFinished');
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
finishRide() {
|
|
505
|
+
this.getRideDisplay().stop(true);
|
|
506
|
+
this.onWorkoutFinished();
|
|
507
|
+
}
|
|
508
|
+
subscribeToWorkoutObserver() {
|
|
509
|
+
if (!this.isRideType('Workout') || this.workoutObserverSubscribed)
|
|
510
|
+
return;
|
|
511
|
+
const observer = this.getWorkoutRide().getObserver();
|
|
512
|
+
if (!observer)
|
|
513
|
+
return;
|
|
514
|
+
this.registerHandlers(observer, this.workoutEventHandler);
|
|
515
|
+
this.workoutObserverSubscribed = true;
|
|
516
|
+
}
|
|
517
|
+
unsubscribeFromWorkoutObserver() {
|
|
518
|
+
if (!this.workoutObserverSubscribed)
|
|
519
|
+
return;
|
|
520
|
+
this.unregisterHandlers(this.getWorkoutRide().getObserver(), this.workoutEventHandler);
|
|
521
|
+
this.workoutObserverSubscribed = false;
|
|
522
|
+
}
|
|
523
|
+
buildGestureHint(startOverlayCleared) {
|
|
524
|
+
if (this.gestureHintDismissed || !startOverlayCleared)
|
|
525
|
+
return null;
|
|
526
|
+
if (this.getElapsedActivityTime() !== 0 || this.getCurrentCadence() !== 0)
|
|
527
|
+
return null;
|
|
528
|
+
if (this.getUserSettings().get(HINTS_WORKOUT_GESTURES_KEY, false))
|
|
529
|
+
return null;
|
|
530
|
+
return { visible: true };
|
|
531
|
+
}
|
|
532
|
+
getCurrentCadence() {
|
|
533
|
+
return this.getActivityRide().getCurrentValues?.()?.cadence ?? 0;
|
|
534
|
+
}
|
|
535
|
+
getLoadIncrement() {
|
|
536
|
+
return this.getUserSettings().get(LOAD_INCREMENT_SETTING_KEY, DEFAULT_LOAD_INCREMENT);
|
|
537
|
+
}
|
|
538
|
+
buildGraphPlan(current, ftp) {
|
|
539
|
+
if (!current) {
|
|
540
|
+
return { bars: [], ftp: ftp ?? 0, ftpLine: ftp ?? 0, domain: { x: [0, 0], y: [0, 0] } };
|
|
541
|
+
}
|
|
542
|
+
const bars = (0, graph_1.getWorkoutGraphSeries)(current, { ftp, absValues: true });
|
|
543
|
+
const lastBarX = bars.length ? bars.at(-1).x : 0;
|
|
544
|
+
const maxXCandidates = [this.getLastLogTime(), lastBarX, current.duration ?? 0].filter(Number.isFinite);
|
|
545
|
+
const maxX = maxXCandidates.length ? Math.max(...maxXCandidates) : 0;
|
|
546
|
+
const maxBarPower = bars.length ? Math.max(...bars.map(b => b.y)) : 0;
|
|
547
|
+
return {
|
|
548
|
+
bars,
|
|
549
|
+
ftp,
|
|
550
|
+
ftpLine: ftp,
|
|
551
|
+
domain: { x: [0, maxX], y: [0, maxBarPower * 1.1] }
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
buildUpcomingSteps(current, ftp) {
|
|
555
|
+
const empty = { previous: null, current: null, upcoming: [], hasMore: false };
|
|
556
|
+
if (!current)
|
|
557
|
+
return empty;
|
|
558
|
+
const limits = this.getWorkoutRide().getCurrentLimits();
|
|
559
|
+
if (!limits)
|
|
560
|
+
return empty;
|
|
561
|
+
const currentStep = {
|
|
562
|
+
label: (0, graph_1.getStepTargetText)(limits.step ?? {}, ftp),
|
|
563
|
+
targetPower: limits.targetPower ?? null,
|
|
564
|
+
duration: limits.duration,
|
|
565
|
+
remaining: limits.remaining,
|
|
566
|
+
isCurrent: true
|
|
567
|
+
};
|
|
568
|
+
const elapsedTime = this.getElapsedActivityTime();
|
|
569
|
+
const flattened = (0, graph_1.getFlattenedSteps)(current);
|
|
570
|
+
const currentIndex = flattened.findIndex(s => s.start <= elapsedTime && s.end > elapsedTime);
|
|
571
|
+
const toDisplay = (entry) => ({
|
|
572
|
+
label: (0, graph_1.getStepTargetText)(entry.step, ftp),
|
|
573
|
+
targetPower: this.getStepAbsolutePower(entry.step, ftp),
|
|
574
|
+
duration: entry.duration,
|
|
575
|
+
remaining: null,
|
|
576
|
+
isCurrent: false
|
|
577
|
+
});
|
|
578
|
+
if (currentIndex === -1)
|
|
579
|
+
return { previous: null, current: currentStep, upcoming: [], hasMore: false };
|
|
580
|
+
const previous = currentIndex > 0 ? toDisplay(flattened[currentIndex - 1]) : null;
|
|
581
|
+
const upcomingEntries = flattened.slice(currentIndex + 1, currentIndex + 1 + UPCOMING_STEPS_COUNT);
|
|
582
|
+
const upcoming = upcomingEntries.map(toDisplay);
|
|
583
|
+
const hasMore = currentIndex + 1 + UPCOMING_STEPS_COUNT < flattened.length;
|
|
584
|
+
return { previous, current: currentStep, upcoming, hasMore };
|
|
585
|
+
}
|
|
586
|
+
buildDashboardLine(wo) {
|
|
587
|
+
const limits = this.getWorkoutRide().getCurrentLimits();
|
|
588
|
+
const title = wo.title ?? '';
|
|
589
|
+
if (!limits)
|
|
590
|
+
return { text: title, mode: wo.mode ?? null };
|
|
591
|
+
const target = (0, graph_1.getStepTargetText)(limits.step ?? {}, wo.ftp);
|
|
592
|
+
const duration = (0, graph_1.getStepDuration)({ duration: limits.duration });
|
|
593
|
+
const base = `${target} for ${duration}`;
|
|
594
|
+
let text = base;
|
|
595
|
+
if (title && title !== base)
|
|
596
|
+
text = title.startsWith('(') ? `${base}${title}` : `${base} - ${title}`;
|
|
597
|
+
return { text, mode: wo.mode ?? null };
|
|
598
|
+
}
|
|
599
|
+
getStepAbsolutePower(step, ftp) {
|
|
600
|
+
const p = step.power;
|
|
601
|
+
if (!p)
|
|
602
|
+
return null;
|
|
603
|
+
const val = p.max ?? p.min;
|
|
604
|
+
if (val === undefined)
|
|
605
|
+
return null;
|
|
606
|
+
if (p.type === 'watt')
|
|
607
|
+
return Math.round(val);
|
|
608
|
+
if (ftp === undefined)
|
|
609
|
+
return null;
|
|
610
|
+
return Math.round(val / 100 * ftp);
|
|
611
|
+
}
|
|
612
|
+
getStepFlags() {
|
|
613
|
+
const wo = this.getWorkoutRide().getDashboardDisplayProperties();
|
|
614
|
+
return { canStepBack: !!wo.canShowBackward, canStepForward: !!wo.canShowForward };
|
|
615
|
+
}
|
|
616
|
+
getLastLogTime() {
|
|
617
|
+
const logs = this.getActivityRide().getActivity()?.logs;
|
|
618
|
+
if (!logs || logs.length === 0)
|
|
619
|
+
return 0;
|
|
620
|
+
return logs.at(-1).time ?? 0;
|
|
621
|
+
}
|
|
622
|
+
getElapsedActivityTime() {
|
|
623
|
+
return this.getActivityRide().getActivity()?.time ?? 0;
|
|
624
|
+
}
|
|
625
|
+
getEmptyWorkoutDisplayProps() {
|
|
626
|
+
return {
|
|
627
|
+
rideState: 'Error',
|
|
628
|
+
rideType: null,
|
|
629
|
+
startOverlayProps: null,
|
|
630
|
+
menuProps: null,
|
|
631
|
+
startGateProps: null,
|
|
632
|
+
title: '',
|
|
633
|
+
graph: { bars: [], ftp: 0, ftpLine: 0, domain: { x: [0, 0], y: [0, 0] } },
|
|
634
|
+
steps: { previous: null, current: null, upcoming: [], hasMore: false },
|
|
635
|
+
dashboard: { text: '', mode: null },
|
|
636
|
+
gestureHint: null,
|
|
637
|
+
loadIncrement: DEFAULT_LOAD_INCREMENT,
|
|
638
|
+
loadButtonMode: 'power'
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
getPageLogName() {
|
|
642
|
+
return this.isRideType('Workout') ? 'WorkoutRide' : 'Rides';
|
|
643
|
+
}
|
|
644
|
+
requiresOwnInit() {
|
|
645
|
+
return !this.isRideType('Workout');
|
|
646
|
+
}
|
|
647
|
+
getBackgroundPauseRequester() {
|
|
648
|
+
return this.isRideType('Workout') ? 'device' : 'user';
|
|
649
|
+
}
|
|
650
|
+
isRideType(...types) {
|
|
651
|
+
try {
|
|
652
|
+
return types.includes(this.getRideDisplay().getRideType());
|
|
653
|
+
}
|
|
654
|
+
catch {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
buildPausedMenuProps() {
|
|
659
|
+
return this.isRideType('Workout')
|
|
660
|
+
? { showResume: true, ...this.getStepFlags() }
|
|
661
|
+
: { showResume: true };
|
|
662
|
+
}
|
|
663
|
+
buildFinishedMenuProps() {
|
|
664
|
+
return this.isRideType('Workout')
|
|
665
|
+
? { showResume: false, finished: true, canStepBack: false, canStepForward: false }
|
|
666
|
+
: { showResume: false, finished: true };
|
|
667
|
+
}
|
|
668
|
+
buildBaseDisplayProps() {
|
|
669
|
+
const state = this.getRideDisplay().getState();
|
|
670
|
+
const isStarting = state === 'Idle' || state === 'Starting' || state === 'Error';
|
|
671
|
+
return {
|
|
672
|
+
rideState: state,
|
|
673
|
+
rideType: this.getRideDisplay().getRideType(),
|
|
674
|
+
startOverlayProps: isStarting ? this.getRideDisplay().getStartOverlayProps() : null,
|
|
675
|
+
menuProps: this.menuProps,
|
|
676
|
+
startGateProps: this.startGateProps
|
|
677
|
+
};
|
|
358
678
|
}
|
|
359
679
|
onDisplayStateUpdate(state) {
|
|
680
|
+
this.subscribeToWorkoutObserver();
|
|
360
681
|
switch (state) {
|
|
361
682
|
case 'Paused':
|
|
362
|
-
this.menuProps =
|
|
683
|
+
this.menuProps = this.buildPausedMenuProps();
|
|
363
684
|
break;
|
|
364
685
|
case 'Finished':
|
|
365
|
-
this.menuProps =
|
|
366
|
-
|
|
686
|
+
this.menuProps = this.buildFinishedMenuProps();
|
|
687
|
+
this.updatePageDisplay();
|
|
688
|
+
return;
|
|
367
689
|
case 'Active':
|
|
368
690
|
this.menuProps = null;
|
|
369
691
|
break;
|
|
370
692
|
}
|
|
371
693
|
this.updatePageDisplay();
|
|
372
694
|
}
|
|
373
|
-
|
|
374
|
-
this.
|
|
695
|
+
updatePageDisplay() {
|
|
696
|
+
this.getPageObserver()?.emit('page-update');
|
|
697
|
+
}
|
|
698
|
+
registerHandlers(observer, map) {
|
|
699
|
+
if (!observer)
|
|
700
|
+
return;
|
|
701
|
+
Object.keys(map).forEach(event => { observer.on(event, map[event]); });
|
|
702
|
+
}
|
|
703
|
+
unregisterHandlers(observer, map) {
|
|
704
|
+
if (!observer)
|
|
705
|
+
return;
|
|
706
|
+
Object.keys(map).forEach(event => { observer.off(event, map[event]); });
|
|
375
707
|
}
|
|
376
708
|
moveToPreviousPage() {
|
|
377
709
|
this.moveTo('$contentPage');
|
|
@@ -385,17 +717,17 @@ let RidePageService = (() => {
|
|
|
385
717
|
}
|
|
386
718
|
return null;
|
|
387
719
|
}
|
|
388
|
-
get rideDisplayProps() {
|
|
389
|
-
return this.getRideDisplay().getDisplayProperties();
|
|
390
|
-
}
|
|
391
720
|
getRideDisplay() {
|
|
392
721
|
return (0, display_1.useRideDisplay)();
|
|
393
722
|
}
|
|
394
|
-
|
|
395
|
-
return
|
|
723
|
+
getWorkoutRide() {
|
|
724
|
+
return (0, service_1.useWorkoutRide)();
|
|
725
|
+
}
|
|
726
|
+
getActivityRide() {
|
|
727
|
+
return (0, activities_1.useActivityRide)();
|
|
396
728
|
}
|
|
397
|
-
|
|
398
|
-
return (0,
|
|
729
|
+
getUserSettings() {
|
|
730
|
+
return (0, settings_1.useUserSettings)();
|
|
399
731
|
}
|
|
400
732
|
getOnlineStatusMonitoring() {
|
|
401
733
|
return (0, monitoring_1.useOnlineStatusMonitoring)();
|
|
@@ -404,5 +736,7 @@ let RidePageService = (() => {
|
|
|
404
736
|
return RidePageService = _classThis;
|
|
405
737
|
})();
|
|
406
738
|
exports.RidePageService = RidePageService;
|
|
407
|
-
const getRidePageService = () =>
|
|
739
|
+
const getRidePageService = () => {
|
|
740
|
+
return new RidePageService();
|
|
741
|
+
};
|
|
408
742
|
exports.getRidePageService = getRidePageService;
|