incyclist-services 1.7.99 → 1.7.101

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.
@@ -40,6 +40,7 @@ const decorators_1 = require("../../base/decorators");
40
40
  const pages_1 = require("../../base/pages");
41
41
  const list_1 = require("../list");
42
42
  const sleep_1 = require("../../utils/sleep");
43
+ const workouts_1 = require("../../workouts");
43
44
  let ActivitiesPageService = (() => {
44
45
  let _classDecorators = [decorators_1.Singleton];
45
46
  let _classDescriptor;
@@ -48,12 +49,15 @@ let ActivitiesPageService = (() => {
48
49
  let _classSuper = pages_1.IncyclistPageService;
49
50
  let _instanceExtraInitializers = [];
50
51
  let _getActivityList_decorators;
52
+ let _getWorkoutList_decorators;
51
53
  var ActivitiesPageService = class extends _classSuper {
52
54
  static { _classThis = this; }
53
55
  static {
54
56
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
55
57
  _getActivityList_decorators = [decorators_1.Injectable];
58
+ _getWorkoutList_decorators = [decorators_1.Injectable];
56
59
  __esDecorate(this, null, _getActivityList_decorators, { kind: "method", name: "getActivityList", static: false, private: false, access: { has: obj => "getActivityList" in obj, get: obj => obj.getActivityList }, metadata: _metadata }, null, _instanceExtraInitializers);
60
+ __esDecorate(this, null, _getWorkoutList_decorators, { kind: "method", name: "getWorkoutList", static: false, private: false, access: { has: obj => "getWorkoutList" in obj, get: obj => obj.getWorkoutList }, metadata: _metadata }, null, _instanceExtraInitializers);
57
61
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
58
62
  ActivitiesPageService = _classThis = _classDescriptor.value;
59
63
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -146,6 +150,27 @@ let ActivitiesPageService = (() => {
146
150
  this.logError(err, 'onCloseActivity');
147
151
  }
148
152
  }
153
+ getActivityDetailsProps(activityId) {
154
+ const comboEnabled = this.isWorkoutComboEnabled();
155
+ try {
156
+ const workout = this.getWorkoutList().getSelected();
157
+ const attachedWorkout = workout ? { id: workout.id, title: workout.name } : null;
158
+ return { activityId, attachedWorkout, comboEnabled };
159
+ }
160
+ catch (err) {
161
+ this.logError(err, 'getActivityDetailsProps');
162
+ return { activityId, attachedWorkout: null, comboEnabled };
163
+ }
164
+ }
165
+ onClearWorkoutSelection() {
166
+ try {
167
+ this.getWorkoutList().unselect();
168
+ this.updatePageDisplay();
169
+ }
170
+ catch (err) {
171
+ this.logError(err, 'onClearWorkoutSelection');
172
+ }
173
+ }
149
174
  updatePageDisplay() {
150
175
  this.getPageObserver()?.emit('page-update');
151
176
  }
@@ -169,6 +194,9 @@ let ActivitiesPageService = (() => {
169
194
  getActivityList() {
170
195
  return (0, list_1.useActivityList)();
171
196
  }
197
+ getWorkoutList() {
198
+ return (0, workouts_1.useWorkoutList)();
199
+ }
172
200
  };
173
201
  return ActivitiesPageService = _classThis;
174
202
  })();
@@ -122,6 +122,17 @@ let IncyclistPageService = (() => {
122
122
  getAppState() {
123
123
  return (0, appstate_1.useAppState)();
124
124
  }
125
+ isWorkoutComboEnabled() {
126
+ try {
127
+ const appState = this.getAppState();
128
+ return appState.hasFeature('MOBILE_WORKOUTS')
129
+ && appState.hasFeature('MOBILE_WORKOUT_ROUTE_COMBO');
130
+ }
131
+ catch (err) {
132
+ this.logError(err, 'isWorkoutComboEnabled');
133
+ return false;
134
+ }
135
+ }
125
136
  };
126
137
  })();
127
138
  exports.IncyclistPageService = IncyclistPageService;
@@ -50,6 +50,7 @@ const UPCOMING_STEPS_COUNT = 3;
50
50
  const DEFAULT_LOAD_INCREMENT = 1;
51
51
  const HINTS_WORKOUT_GESTURES_KEY = 'hints.workoutRideGestures';
52
52
  const LOAD_INCREMENT_SETTING_KEY = 'preferences.workouts.loadIncrement';
53
+ const CORNER_WIDGET_SETTING_KEY = 'preferences.workouts.rideCornerWidget';
53
54
  let RidePageService = (() => {
54
55
  let _classDecorators = [decorators_1.Singleton];
55
56
  let _classDescriptor;
@@ -84,16 +85,19 @@ let RidePageService = (() => {
84
85
  eventHandler = (__runInitializers(this, _instanceExtraInitializers), {});
85
86
  workoutEventHandler = {};
86
87
  workoutObserverSubscribed = false;
88
+ workoutObserver;
87
89
  backgroundTimer;
88
90
  backgroundPausedByService = false;
89
91
  menuProps = null;
90
92
  isInitialized = false;
91
93
  startGateProps = null;
92
94
  gestureHintDismissed = false;
95
+ viewTransition = false;
93
96
  constructor() {
94
97
  super('RidePage');
95
98
  this.eventHandler['state-update'] = this.onDisplayStateUpdate.bind(this);
96
99
  this.eventHandler['route-update'] = this.onRouteUpdate.bind(this);
100
+ this.eventHandler['view-changed'] = this.onViewChanged.bind(this);
97
101
  this.workoutEventHandler['step-changed'] = this.onWorkoutUpdate.bind(this);
98
102
  this.workoutEventHandler['update'] = this.onWorkoutUpdate.bind(this);
99
103
  this.workoutEventHandler['forward'] = this.onWorkoutUpdate.bind(this);
@@ -113,6 +117,14 @@ let RidePageService = (() => {
113
117
  }
114
118
  }
115
119
  openPage(simulate) {
120
+ if (this.viewTransition) {
121
+ this.viewTransition = false;
122
+ this.logEvent({ message: 'page shown', page: this.getPageLogName(), viewTransition: true });
123
+ gd_eventlog_1.EventLogger.setGlobalConfig('page', this.getPageLogName());
124
+ this.subscribeToWorkoutObserver();
125
+ this.updatePageDisplay();
126
+ return this.getPageObserver();
127
+ }
116
128
  try {
117
129
  const pageLogName = this.getPageLogName();
118
130
  this.logEvent({ message: 'page shown', page: pageLogName });
@@ -124,6 +136,12 @@ let RidePageService = (() => {
124
136
  const registerAndStart = () => {
125
137
  this.registerHandlers(this.rideObserver, this.eventHandler);
126
138
  this.subscribeToWorkoutObserver();
139
+ const state = service.getState();
140
+ if (state === 'Started' || state === 'Active' || state === 'Paused') {
141
+ this.logError(new Error(`start() requested while ride is ${state}`), 'openPage');
142
+ this.updatePageDisplay();
143
+ return;
144
+ }
127
145
  service.start(simulate);
128
146
  if (this.isRideType('Video', 'GPX')) {
129
147
  (0, sleep_1.sleep)(5).then(() => {
@@ -154,6 +172,8 @@ let RidePageService = (() => {
154
172
  return this.getPageObserver();
155
173
  }
156
174
  closePage() {
175
+ if (this.viewTransition)
176
+ return;
157
177
  try {
158
178
  gd_eventlog_1.EventLogger.setGlobalConfig('page', null);
159
179
  this.logEvent({ message: 'page closed', page: this.getPageLogName() });
@@ -251,7 +271,7 @@ let RidePageService = (() => {
251
271
  onMenuOpen() {
252
272
  try {
253
273
  const state = this.getRideDisplay().getState();
254
- this.menuProps = this.isRideType('Workout')
274
+ this.menuProps = this.isWorkoutAttached()
255
275
  ? { showResume: state === 'Paused', ...this.getStepFlags() }
256
276
  : { showResume: state === 'Paused' };
257
277
  this.updatePageDisplay();
@@ -321,8 +341,10 @@ let RidePageService = (() => {
321
341
  }
322
342
  getVideoRideDisplayProps() {
323
343
  const props = this.rideDisplayProps;
344
+ const base = this.buildBaseDisplayProps();
324
345
  const displayProps = {
325
- ...this.buildBaseDisplayProps(),
346
+ ...base,
347
+ ...this.buildWorkoutOverlayProps(props, base.startOverlayProps === null),
326
348
  video: props.video,
327
349
  videos: props.videos,
328
350
  route: props.route
@@ -331,14 +353,61 @@ let RidePageService = (() => {
331
353
  }
332
354
  getGPXRideDisplayProps() {
333
355
  const props = this.rideDisplayProps;
356
+ const base = this.buildBaseDisplayProps();
334
357
  const displayProps = {
335
- ...this.buildBaseDisplayProps(),
358
+ ...base,
359
+ ...this.buildWorkoutOverlayProps(props, base.startOverlayProps === null),
336
360
  rideView: props.rideView,
337
361
  route: props.route,
338
362
  displayObserver: props.displayObserver
339
363
  };
340
364
  return displayProps;
341
365
  }
366
+ buildWorkoutOverlayProps(props, startOverlayCleared) {
367
+ const cornerWidget = this.getCornerWidget();
368
+ if (!this.isWorkoutAttached() || !props?.showWorkout)
369
+ return { workoutAttached: false, cornerWidget };
370
+ try {
371
+ const wo = this.getWorkoutRide().getDashboardDisplayProperties();
372
+ return {
373
+ workoutAttached: true,
374
+ cornerWidget,
375
+ title: wo.title ?? '',
376
+ graph: this.buildGraphPlan(props.workout, wo.ftp),
377
+ steps: this.buildUpcomingSteps(props.workout, wo.ftp),
378
+ dashboard: this.buildDashboardLine(wo),
379
+ gestureHint: this.buildGestureHint(startOverlayCleared),
380
+ loadIncrement: this.getLoadIncrement(),
381
+ loadButtonMode: wo.loadButtonMode ?? 'power'
382
+ };
383
+ }
384
+ catch (err) {
385
+ this.logError(err, 'buildWorkoutOverlayProps');
386
+ return { workoutAttached: false, cornerWidget };
387
+ }
388
+ }
389
+ getCornerWidget() {
390
+ try {
391
+ if (!this.isWorkoutAttached() || !this.isWorkoutComboEnabled())
392
+ return undefined;
393
+ return this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
394
+ }
395
+ catch (err) {
396
+ this.logError(err, 'getCornerWidget');
397
+ return undefined;
398
+ }
399
+ }
400
+ onToggleCornerWidget() {
401
+ try {
402
+ const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
403
+ const next = current === 'elevation' ? 'workout' : 'elevation';
404
+ this.getUserSettings().set(CORNER_WIDGET_SETTING_KEY, next);
405
+ this.updatePageDisplay();
406
+ }
407
+ catch (err) {
408
+ this.logError(err, 'onToggleCornerWidget');
409
+ }
410
+ }
342
411
  async checkSecretValidity() {
343
412
  if (this.getBindings().appInfo?.getChannel() === 'mobile') {
344
413
  const secretsStatus = this.getSecretBinding()?.getSecretsStatus?.();
@@ -362,6 +431,18 @@ let RidePageService = (() => {
362
431
  onRouteUpdate() {
363
432
  this.updatePageDisplay();
364
433
  }
434
+ onViewChanged() {
435
+ try {
436
+ const rideType = this.getRideDisplay().getRideType();
437
+ this.logEvent({ message: 'ride type changed', rideType });
438
+ this.viewTransition = true;
439
+ this.getPageObserver()?.emit('ride-type-update', rideType);
440
+ this.updatePageDisplay();
441
+ }
442
+ catch (err) {
443
+ this.logError(err, 'onViewChanged');
444
+ }
445
+ }
365
446
  get rideDisplayProps() {
366
447
  return this.getRideDisplay().getDisplayProperties();
367
448
  }
@@ -370,6 +451,8 @@ let RidePageService = (() => {
370
451
  }
371
452
  getGraphActuals() {
372
453
  try {
454
+ if (!this.isWorkoutAttached())
455
+ return { power: [], heartrate: [], position: 0 };
373
456
  const state = this.getRideDisplay().getState();
374
457
  if (state === 'Idle' || state === 'Starting' || state === 'Started') {
375
458
  return { power: [], heartrate: [], position: 0 };
@@ -398,8 +481,21 @@ let RidePageService = (() => {
398
481
  this.logError(err, 'onStop');
399
482
  }
400
483
  }
484
+ onStopWorkout() {
485
+ try {
486
+ if (!this.isWorkoutAttached())
487
+ return;
488
+ this.getRideDisplay().stopWorkout();
489
+ this.updatePageDisplay();
490
+ }
491
+ catch (err) {
492
+ this.logError(err, 'onStopWorkout');
493
+ }
494
+ }
401
495
  onStepBack() {
402
496
  try {
497
+ if (!this.isWorkoutAttached())
498
+ return;
403
499
  this.getRideDisplay().backward();
404
500
  }
405
501
  catch (err) {
@@ -408,6 +504,8 @@ let RidePageService = (() => {
408
504
  }
409
505
  onStepForward() {
410
506
  try {
507
+ if (!this.isWorkoutAttached())
508
+ return;
411
509
  this.getRideDisplay().forward();
412
510
  }
413
511
  catch (err) {
@@ -475,6 +573,7 @@ let RidePageService = (() => {
475
573
  return {
476
574
  ...base,
477
575
  menuProps: base.menuProps,
576
+ workoutAttached: true,
478
577
  title: wo.title ?? '',
479
578
  graph: this.buildGraphPlan(current, wo.ftp),
480
579
  steps: this.buildUpcomingSteps(current, wo.ftp),
@@ -494,7 +593,8 @@ let RidePageService = (() => {
494
593
  }
495
594
  onWorkoutFinished() {
496
595
  try {
497
- this.menuProps = this.buildFinishedMenuProps();
596
+ if (this.isRideType('Workout'))
597
+ this.menuProps = this.buildFinishedMenuProps();
498
598
  this.updatePageDisplay();
499
599
  }
500
600
  catch (err) {
@@ -503,21 +603,24 @@ let RidePageService = (() => {
503
603
  }
504
604
  finishRide() {
505
605
  this.getRideDisplay().stop(true);
506
- this.onWorkoutFinished();
606
+ this.menuProps = this.buildFinishedMenuProps();
607
+ this.updatePageDisplay();
507
608
  }
508
609
  subscribeToWorkoutObserver() {
509
- if (!this.isRideType('Workout') || this.workoutObserverSubscribed)
610
+ if (this.workoutObserverSubscribed || !this.isWorkoutAttached())
510
611
  return;
511
612
  const observer = this.getWorkoutRide().getObserver();
512
613
  if (!observer)
513
614
  return;
514
615
  this.registerHandlers(observer, this.workoutEventHandler);
616
+ this.workoutObserver = observer;
515
617
  this.workoutObserverSubscribed = true;
516
618
  }
517
619
  unsubscribeFromWorkoutObserver() {
518
620
  if (!this.workoutObserverSubscribed)
519
621
  return;
520
- this.unregisterHandlers(this.getWorkoutRide().getObserver(), this.workoutEventHandler);
622
+ this.unregisterHandlers(this.workoutObserver, this.workoutEventHandler);
623
+ this.workoutObserver = undefined;
521
624
  this.workoutObserverSubscribed = false;
522
625
  }
523
626
  buildGestureHint(startOverlayCleared) {
@@ -655,13 +758,28 @@ let RidePageService = (() => {
655
758
  return false;
656
759
  }
657
760
  }
761
+ isWorkoutAttached() {
762
+ try {
763
+ if (this.isRideType('Workout'))
764
+ return true;
765
+ if (!this.isRideType('Video', 'GPX'))
766
+ return false;
767
+ if (!this.isWorkoutComboEnabled())
768
+ return false;
769
+ return this.getWorkoutRide().inUse();
770
+ }
771
+ catch (err) {
772
+ this.logError(err, 'isWorkoutAttached');
773
+ return false;
774
+ }
775
+ }
658
776
  buildPausedMenuProps() {
659
- return this.isRideType('Workout')
777
+ return this.isWorkoutAttached()
660
778
  ? { showResume: true, ...this.getStepFlags() }
661
779
  : { showResume: true };
662
780
  }
663
781
  buildFinishedMenuProps() {
664
- return this.isRideType('Workout')
782
+ return this.isWorkoutAttached()
665
783
  ? { showResume: false, finished: true, canStepBack: false, canStepForward: false }
666
784
  : { showResume: false, finished: true };
667
785
  }
@@ -45,6 +45,7 @@ const sleep_1 = require("../../utils/sleep");
45
45
  const devices_1 = require("../../devices");
46
46
  const service_1 = require("../download/service");
47
47
  const service_2 = require("../library/service");
48
+ const workouts_1 = require("../../workouts");
48
49
  let RoutesPageService = (() => {
49
50
  let _classDecorators = [decorators_1.Singleton];
50
51
  let _classDescriptor;
@@ -57,6 +58,7 @@ let RoutesPageService = (() => {
57
58
  let _getUserSettings_decorators;
58
59
  let _getRouteDownload_decorators;
59
60
  let _getRouteLibraryScanner_decorators;
61
+ let _getWorkoutList_decorators;
60
62
  var RoutesPageService = class extends _classSuper {
61
63
  static { _classThis = this; }
62
64
  static {
@@ -66,11 +68,13 @@ let RoutesPageService = (() => {
66
68
  _getUserSettings_decorators = [decorators_1.Injectable];
67
69
  _getRouteDownload_decorators = [decorators_1.Injectable];
68
70
  _getRouteLibraryScanner_decorators = [decorators_1.Injectable];
71
+ _getWorkoutList_decorators = [decorators_1.Injectable];
69
72
  __esDecorate(this, null, _getRouteList_decorators, { kind: "method", name: "getRouteList", static: false, private: false, access: { has: obj => "getRouteList" in obj, get: obj => obj.getRouteList }, metadata: _metadata }, null, _instanceExtraInitializers);
70
73
  __esDecorate(this, null, _getDevicePairing_decorators, { kind: "method", name: "getDevicePairing", static: false, private: false, access: { has: obj => "getDevicePairing" in obj, get: obj => obj.getDevicePairing }, metadata: _metadata }, null, _instanceExtraInitializers);
71
74
  __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);
72
75
  __esDecorate(this, null, _getRouteDownload_decorators, { kind: "method", name: "getRouteDownload", static: false, private: false, access: { has: obj => "getRouteDownload" in obj, get: obj => obj.getRouteDownload }, metadata: _metadata }, null, _instanceExtraInitializers);
73
76
  __esDecorate(this, null, _getRouteLibraryScanner_decorators, { kind: "method", name: "getRouteLibraryScanner", static: false, private: false, access: { has: obj => "getRouteLibraryScanner" in obj, get: obj => obj.getRouteLibraryScanner }, metadata: _metadata }, null, _instanceExtraInitializers);
77
+ __esDecorate(this, null, _getWorkoutList_decorators, { kind: "method", name: "getWorkoutList", static: false, private: false, access: { has: obj => "getWorkoutList" in obj, get: obj => obj.getWorkoutList }, metadata: _metadata }, null, _instanceExtraInitializers);
74
78
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
75
79
  RoutesPageService = _classThis = _classDescriptor.value;
76
80
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -316,6 +320,31 @@ let RoutesPageService = (() => {
316
320
  this.logError(err, 'start');
317
321
  }
318
322
  }
323
+ getRouteDetailsProps(routeId) {
324
+ const comboEnabled = this.isWorkoutComboEnabled();
325
+ try {
326
+ return { routeId, attachedWorkout: this.getAttachedWorkoutProps(), comboEnabled };
327
+ }
328
+ catch (err) {
329
+ this.logError(err, 'getRouteDetailsProps');
330
+ return { routeId, attachedWorkout: null, comboEnabled };
331
+ }
332
+ }
333
+ getAttachedWorkoutProps() {
334
+ const workout = this.getWorkoutList().getSelected();
335
+ if (!workout)
336
+ return null;
337
+ return { id: workout.id, title: workout.name };
338
+ }
339
+ onClearWorkoutSelection() {
340
+ try {
341
+ this.getWorkoutList().unselect();
342
+ this.updatePageDisplay();
343
+ }
344
+ catch (err) {
345
+ this.logError(err, 'onClearWorkoutSelection');
346
+ }
347
+ }
319
348
  getDownloadDisplayProps() {
320
349
  return Array.from(this.downloadCache.values());
321
350
  }
@@ -453,6 +482,9 @@ let RoutesPageService = (() => {
453
482
  getRouteLibraryScanner() {
454
483
  return (0, service_2.useRouteLibraryScanner)();
455
484
  }
485
+ getWorkoutList() {
486
+ return (0, workouts_1.useWorkoutList)();
487
+ }
456
488
  };
457
489
  return RoutesPageService = _classThis;
458
490
  })();
@@ -44,6 +44,7 @@ const settings_1 = require("../../settings");
44
44
  const calendar_1 = require("../calendar");
45
45
  const service_1 = require("../list/service");
46
46
  const devices_1 = require("../../devices");
47
+ const routes_1 = require("../../routes");
47
48
  const DEFAULT_IMPORT_GROUP = 'My Workouts';
48
49
  const UPCOMING_COLLAPSED_COUNT = 3;
49
50
  let WorkoutListPageService = (() => {
@@ -55,6 +56,7 @@ let WorkoutListPageService = (() => {
55
56
  let _instanceExtraInitializers = [];
56
57
  let _getDevicePairing_decorators;
57
58
  let _getWorkoutList_decorators;
59
+ let _getRouteList_decorators;
58
60
  let _getWorkoutCalendar_decorators;
59
61
  let _getUserSettings_decorators;
60
62
  let _getAppState_decorators;
@@ -64,11 +66,13 @@ let WorkoutListPageService = (() => {
64
66
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
65
67
  _getDevicePairing_decorators = [decorators_1.Injectable];
66
68
  _getWorkoutList_decorators = [decorators_1.Injectable];
69
+ _getRouteList_decorators = [decorators_1.Injectable];
67
70
  _getWorkoutCalendar_decorators = [decorators_1.Injectable];
68
71
  _getUserSettings_decorators = [decorators_1.Injectable];
69
72
  _getAppState_decorators = [decorators_1.Injectable];
70
73
  __esDecorate(this, null, _getDevicePairing_decorators, { kind: "method", name: "getDevicePairing", static: false, private: false, access: { has: obj => "getDevicePairing" in obj, get: obj => obj.getDevicePairing }, metadata: _metadata }, null, _instanceExtraInitializers);
71
74
  __esDecorate(this, null, _getWorkoutList_decorators, { kind: "method", name: "getWorkoutList", static: false, private: false, access: { has: obj => "getWorkoutList" in obj, get: obj => obj.getWorkoutList }, metadata: _metadata }, null, _instanceExtraInitializers);
75
+ __esDecorate(this, null, _getRouteList_decorators, { kind: "method", name: "getRouteList", static: false, private: false, access: { has: obj => "getRouteList" in obj, get: obj => obj.getRouteList }, metadata: _metadata }, null, _instanceExtraInitializers);
72
76
  __esDecorate(this, null, _getWorkoutCalendar_decorators, { kind: "method", name: "getWorkoutCalendar", static: false, private: false, access: { has: obj => "getWorkoutCalendar" in obj, get: obj => obj.getWorkoutCalendar }, metadata: _metadata }, null, _instanceExtraInitializers);
73
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);
74
78
  __esDecorate(this, null, _getAppState_decorators, { kind: "method", name: "getAppState", static: false, private: false, access: { has: obj => "getAppState" in obj, get: obj => obj.getAppState }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -171,7 +175,9 @@ let WorkoutListPageService = (() => {
171
175
  group: settingsProps.category,
172
176
  canDelete: card.canDelete(),
173
177
  isScheduled,
174
- date: isScheduled ? settingsProps.date : undefined
178
+ date: isScheduled ? settingsProps.date : undefined,
179
+ attachedRoute: this.getAttachedRouteProps(),
180
+ comboEnabled: this.isWorkoutComboEnabled()
175
181
  };
176
182
  }
177
183
  catch (err) {
@@ -179,6 +185,18 @@ let WorkoutListPageService = (() => {
179
185
  return null;
180
186
  }
181
187
  }
188
+ getAttachedRouteProps() {
189
+ try {
190
+ const route = this.getRouteList().getSelected();
191
+ if (!route)
192
+ return null;
193
+ return { id: route.description?.id, title: route.title };
194
+ }
195
+ catch (err) {
196
+ this.logError(err, 'getAttachedRouteProps');
197
+ return null;
198
+ }
199
+ }
182
200
  getImportDisplayProps() {
183
201
  try {
184
202
  const phase = this.importPhase ?? 'landing';
@@ -309,6 +327,15 @@ let WorkoutListPageService = (() => {
309
327
  this.logError(err, 'onClearSelection');
310
328
  }
311
329
  }
330
+ onClearRouteSelection() {
331
+ try {
332
+ this.getRouteList().unselect();
333
+ this.emitPageUpdate();
334
+ }
335
+ catch (err) {
336
+ this.logError(err, 'onClearRouteSelection');
337
+ }
338
+ }
312
339
  onImportOpen() {
313
340
  try {
314
341
  this.importPhase = 'landing';
@@ -489,6 +516,9 @@ let WorkoutListPageService = (() => {
489
516
  getWorkoutList() {
490
517
  return (0, service_1.useWorkoutList)();
491
518
  }
519
+ getRouteList() {
520
+ return (0, routes_1.useRouteList)();
521
+ }
492
522
  getWorkoutCalendar() {
493
523
  return (0, calendar_1.useWorkoutCalendar)();
494
524
  }
@@ -37,6 +37,7 @@ import { Injectable, Singleton } from "../../base/decorators";
37
37
  import { IncyclistPageService } from "../../base/pages";
38
38
  import { useActivityList } from "../list";
39
39
  import { sleep } from "../../utils/sleep";
40
+ import { useWorkoutList } from "../../workouts";
40
41
  let ActivitiesPageService = (() => {
41
42
  let _classDecorators = [Singleton];
42
43
  let _classDescriptor;
@@ -45,12 +46,15 @@ let ActivitiesPageService = (() => {
45
46
  let _classSuper = IncyclistPageService;
46
47
  let _instanceExtraInitializers = [];
47
48
  let _getActivityList_decorators;
49
+ let _getWorkoutList_decorators;
48
50
  var ActivitiesPageService = class extends _classSuper {
49
51
  static { _classThis = this; }
50
52
  static {
51
53
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
52
54
  _getActivityList_decorators = [Injectable];
55
+ _getWorkoutList_decorators = [Injectable];
53
56
  __esDecorate(this, null, _getActivityList_decorators, { kind: "method", name: "getActivityList", static: false, private: false, access: { has: obj => "getActivityList" in obj, get: obj => obj.getActivityList }, metadata: _metadata }, null, _instanceExtraInitializers);
57
+ __esDecorate(this, null, _getWorkoutList_decorators, { kind: "method", name: "getWorkoutList", static: false, private: false, access: { has: obj => "getWorkoutList" in obj, get: obj => obj.getWorkoutList }, metadata: _metadata }, null, _instanceExtraInitializers);
54
58
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
55
59
  ActivitiesPageService = _classThis = _classDescriptor.value;
56
60
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -143,6 +147,27 @@ let ActivitiesPageService = (() => {
143
147
  this.logError(err, 'onCloseActivity');
144
148
  }
145
149
  }
150
+ getActivityDetailsProps(activityId) {
151
+ const comboEnabled = this.isWorkoutComboEnabled();
152
+ try {
153
+ const workout = this.getWorkoutList().getSelected();
154
+ const attachedWorkout = workout ? { id: workout.id, title: workout.name } : null;
155
+ return { activityId, attachedWorkout, comboEnabled };
156
+ }
157
+ catch (err) {
158
+ this.logError(err, 'getActivityDetailsProps');
159
+ return { activityId, attachedWorkout: null, comboEnabled };
160
+ }
161
+ }
162
+ onClearWorkoutSelection() {
163
+ try {
164
+ this.getWorkoutList().unselect();
165
+ this.updatePageDisplay();
166
+ }
167
+ catch (err) {
168
+ this.logError(err, 'onClearWorkoutSelection');
169
+ }
170
+ }
146
171
  updatePageDisplay() {
147
172
  this.getPageObserver()?.emit('page-update');
148
173
  }
@@ -166,6 +191,9 @@ let ActivitiesPageService = (() => {
166
191
  getActivityList() {
167
192
  return useActivityList();
168
193
  }
194
+ getWorkoutList() {
195
+ return useWorkoutList();
196
+ }
169
197
  };
170
198
  return ActivitiesPageService = _classThis;
171
199
  })();
@@ -119,6 +119,17 @@ let IncyclistPageService = (() => {
119
119
  getAppState() {
120
120
  return useAppState();
121
121
  }
122
+ isWorkoutComboEnabled() {
123
+ try {
124
+ const appState = this.getAppState();
125
+ return appState.hasFeature('MOBILE_WORKOUTS')
126
+ && appState.hasFeature('MOBILE_WORKOUT_ROUTE_COMBO');
127
+ }
128
+ catch (err) {
129
+ this.logError(err, 'isWorkoutComboEnabled');
130
+ return false;
131
+ }
132
+ }
122
133
  };
123
134
  })();
124
135
  export { IncyclistPageService };
@@ -47,6 +47,7 @@ const UPCOMING_STEPS_COUNT = 3;
47
47
  const DEFAULT_LOAD_INCREMENT = 1;
48
48
  const HINTS_WORKOUT_GESTURES_KEY = 'hints.workoutRideGestures';
49
49
  const LOAD_INCREMENT_SETTING_KEY = 'preferences.workouts.loadIncrement';
50
+ const CORNER_WIDGET_SETTING_KEY = 'preferences.workouts.rideCornerWidget';
50
51
  let RidePageService = (() => {
51
52
  let _classDecorators = [Singleton];
52
53
  let _classDescriptor;
@@ -81,16 +82,19 @@ let RidePageService = (() => {
81
82
  eventHandler = (__runInitializers(this, _instanceExtraInitializers), {});
82
83
  workoutEventHandler = {};
83
84
  workoutObserverSubscribed = false;
85
+ workoutObserver;
84
86
  backgroundTimer;
85
87
  backgroundPausedByService = false;
86
88
  menuProps = null;
87
89
  isInitialized = false;
88
90
  startGateProps = null;
89
91
  gestureHintDismissed = false;
92
+ viewTransition = false;
90
93
  constructor() {
91
94
  super('RidePage');
92
95
  this.eventHandler['state-update'] = this.onDisplayStateUpdate.bind(this);
93
96
  this.eventHandler['route-update'] = this.onRouteUpdate.bind(this);
97
+ this.eventHandler['view-changed'] = this.onViewChanged.bind(this);
94
98
  this.workoutEventHandler['step-changed'] = this.onWorkoutUpdate.bind(this);
95
99
  this.workoutEventHandler['update'] = this.onWorkoutUpdate.bind(this);
96
100
  this.workoutEventHandler['forward'] = this.onWorkoutUpdate.bind(this);
@@ -110,6 +114,14 @@ let RidePageService = (() => {
110
114
  }
111
115
  }
112
116
  openPage(simulate) {
117
+ if (this.viewTransition) {
118
+ this.viewTransition = false;
119
+ this.logEvent({ message: 'page shown', page: this.getPageLogName(), viewTransition: true });
120
+ EventLogger.setGlobalConfig('page', this.getPageLogName());
121
+ this.subscribeToWorkoutObserver();
122
+ this.updatePageDisplay();
123
+ return this.getPageObserver();
124
+ }
113
125
  try {
114
126
  const pageLogName = this.getPageLogName();
115
127
  this.logEvent({ message: 'page shown', page: pageLogName });
@@ -121,6 +133,12 @@ let RidePageService = (() => {
121
133
  const registerAndStart = () => {
122
134
  this.registerHandlers(this.rideObserver, this.eventHandler);
123
135
  this.subscribeToWorkoutObserver();
136
+ const state = service.getState();
137
+ if (state === 'Started' || state === 'Active' || state === 'Paused') {
138
+ this.logError(new Error(`start() requested while ride is ${state}`), 'openPage');
139
+ this.updatePageDisplay();
140
+ return;
141
+ }
124
142
  service.start(simulate);
125
143
  if (this.isRideType('Video', 'GPX')) {
126
144
  sleep(5).then(() => {
@@ -151,6 +169,8 @@ let RidePageService = (() => {
151
169
  return this.getPageObserver();
152
170
  }
153
171
  closePage() {
172
+ if (this.viewTransition)
173
+ return;
154
174
  try {
155
175
  EventLogger.setGlobalConfig('page', null);
156
176
  this.logEvent({ message: 'page closed', page: this.getPageLogName() });
@@ -248,7 +268,7 @@ let RidePageService = (() => {
248
268
  onMenuOpen() {
249
269
  try {
250
270
  const state = this.getRideDisplay().getState();
251
- this.menuProps = this.isRideType('Workout')
271
+ this.menuProps = this.isWorkoutAttached()
252
272
  ? { showResume: state === 'Paused', ...this.getStepFlags() }
253
273
  : { showResume: state === 'Paused' };
254
274
  this.updatePageDisplay();
@@ -318,8 +338,10 @@ let RidePageService = (() => {
318
338
  }
319
339
  getVideoRideDisplayProps() {
320
340
  const props = this.rideDisplayProps;
341
+ const base = this.buildBaseDisplayProps();
321
342
  const displayProps = {
322
- ...this.buildBaseDisplayProps(),
343
+ ...base,
344
+ ...this.buildWorkoutOverlayProps(props, base.startOverlayProps === null),
323
345
  video: props.video,
324
346
  videos: props.videos,
325
347
  route: props.route
@@ -328,14 +350,61 @@ let RidePageService = (() => {
328
350
  }
329
351
  getGPXRideDisplayProps() {
330
352
  const props = this.rideDisplayProps;
353
+ const base = this.buildBaseDisplayProps();
331
354
  const displayProps = {
332
- ...this.buildBaseDisplayProps(),
355
+ ...base,
356
+ ...this.buildWorkoutOverlayProps(props, base.startOverlayProps === null),
333
357
  rideView: props.rideView,
334
358
  route: props.route,
335
359
  displayObserver: props.displayObserver
336
360
  };
337
361
  return displayProps;
338
362
  }
363
+ buildWorkoutOverlayProps(props, startOverlayCleared) {
364
+ const cornerWidget = this.getCornerWidget();
365
+ if (!this.isWorkoutAttached() || !props?.showWorkout)
366
+ return { workoutAttached: false, cornerWidget };
367
+ try {
368
+ const wo = this.getWorkoutRide().getDashboardDisplayProperties();
369
+ return {
370
+ workoutAttached: true,
371
+ cornerWidget,
372
+ title: wo.title ?? '',
373
+ graph: this.buildGraphPlan(props.workout, wo.ftp),
374
+ steps: this.buildUpcomingSteps(props.workout, wo.ftp),
375
+ dashboard: this.buildDashboardLine(wo),
376
+ gestureHint: this.buildGestureHint(startOverlayCleared),
377
+ loadIncrement: this.getLoadIncrement(),
378
+ loadButtonMode: wo.loadButtonMode ?? 'power'
379
+ };
380
+ }
381
+ catch (err) {
382
+ this.logError(err, 'buildWorkoutOverlayProps');
383
+ return { workoutAttached: false, cornerWidget };
384
+ }
385
+ }
386
+ getCornerWidget() {
387
+ try {
388
+ if (!this.isWorkoutAttached() || !this.isWorkoutComboEnabled())
389
+ return undefined;
390
+ return this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
391
+ }
392
+ catch (err) {
393
+ this.logError(err, 'getCornerWidget');
394
+ return undefined;
395
+ }
396
+ }
397
+ onToggleCornerWidget() {
398
+ try {
399
+ const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
400
+ const next = current === 'elevation' ? 'workout' : 'elevation';
401
+ this.getUserSettings().set(CORNER_WIDGET_SETTING_KEY, next);
402
+ this.updatePageDisplay();
403
+ }
404
+ catch (err) {
405
+ this.logError(err, 'onToggleCornerWidget');
406
+ }
407
+ }
339
408
  async checkSecretValidity() {
340
409
  if (this.getBindings().appInfo?.getChannel() === 'mobile') {
341
410
  const secretsStatus = this.getSecretBinding()?.getSecretsStatus?.();
@@ -359,6 +428,18 @@ let RidePageService = (() => {
359
428
  onRouteUpdate() {
360
429
  this.updatePageDisplay();
361
430
  }
431
+ onViewChanged() {
432
+ try {
433
+ const rideType = this.getRideDisplay().getRideType();
434
+ this.logEvent({ message: 'ride type changed', rideType });
435
+ this.viewTransition = true;
436
+ this.getPageObserver()?.emit('ride-type-update', rideType);
437
+ this.updatePageDisplay();
438
+ }
439
+ catch (err) {
440
+ this.logError(err, 'onViewChanged');
441
+ }
442
+ }
362
443
  get rideDisplayProps() {
363
444
  return this.getRideDisplay().getDisplayProperties();
364
445
  }
@@ -367,6 +448,8 @@ let RidePageService = (() => {
367
448
  }
368
449
  getGraphActuals() {
369
450
  try {
451
+ if (!this.isWorkoutAttached())
452
+ return { power: [], heartrate: [], position: 0 };
370
453
  const state = this.getRideDisplay().getState();
371
454
  if (state === 'Idle' || state === 'Starting' || state === 'Started') {
372
455
  return { power: [], heartrate: [], position: 0 };
@@ -395,8 +478,21 @@ let RidePageService = (() => {
395
478
  this.logError(err, 'onStop');
396
479
  }
397
480
  }
481
+ onStopWorkout() {
482
+ try {
483
+ if (!this.isWorkoutAttached())
484
+ return;
485
+ this.getRideDisplay().stopWorkout();
486
+ this.updatePageDisplay();
487
+ }
488
+ catch (err) {
489
+ this.logError(err, 'onStopWorkout');
490
+ }
491
+ }
398
492
  onStepBack() {
399
493
  try {
494
+ if (!this.isWorkoutAttached())
495
+ return;
400
496
  this.getRideDisplay().backward();
401
497
  }
402
498
  catch (err) {
@@ -405,6 +501,8 @@ let RidePageService = (() => {
405
501
  }
406
502
  onStepForward() {
407
503
  try {
504
+ if (!this.isWorkoutAttached())
505
+ return;
408
506
  this.getRideDisplay().forward();
409
507
  }
410
508
  catch (err) {
@@ -472,6 +570,7 @@ let RidePageService = (() => {
472
570
  return {
473
571
  ...base,
474
572
  menuProps: base.menuProps,
573
+ workoutAttached: true,
475
574
  title: wo.title ?? '',
476
575
  graph: this.buildGraphPlan(current, wo.ftp),
477
576
  steps: this.buildUpcomingSteps(current, wo.ftp),
@@ -491,7 +590,8 @@ let RidePageService = (() => {
491
590
  }
492
591
  onWorkoutFinished() {
493
592
  try {
494
- this.menuProps = this.buildFinishedMenuProps();
593
+ if (this.isRideType('Workout'))
594
+ this.menuProps = this.buildFinishedMenuProps();
495
595
  this.updatePageDisplay();
496
596
  }
497
597
  catch (err) {
@@ -500,21 +600,24 @@ let RidePageService = (() => {
500
600
  }
501
601
  finishRide() {
502
602
  this.getRideDisplay().stop(true);
503
- this.onWorkoutFinished();
603
+ this.menuProps = this.buildFinishedMenuProps();
604
+ this.updatePageDisplay();
504
605
  }
505
606
  subscribeToWorkoutObserver() {
506
- if (!this.isRideType('Workout') || this.workoutObserverSubscribed)
607
+ if (this.workoutObserverSubscribed || !this.isWorkoutAttached())
507
608
  return;
508
609
  const observer = this.getWorkoutRide().getObserver();
509
610
  if (!observer)
510
611
  return;
511
612
  this.registerHandlers(observer, this.workoutEventHandler);
613
+ this.workoutObserver = observer;
512
614
  this.workoutObserverSubscribed = true;
513
615
  }
514
616
  unsubscribeFromWorkoutObserver() {
515
617
  if (!this.workoutObserverSubscribed)
516
618
  return;
517
- this.unregisterHandlers(this.getWorkoutRide().getObserver(), this.workoutEventHandler);
619
+ this.unregisterHandlers(this.workoutObserver, this.workoutEventHandler);
620
+ this.workoutObserver = undefined;
518
621
  this.workoutObserverSubscribed = false;
519
622
  }
520
623
  buildGestureHint(startOverlayCleared) {
@@ -652,13 +755,28 @@ let RidePageService = (() => {
652
755
  return false;
653
756
  }
654
757
  }
758
+ isWorkoutAttached() {
759
+ try {
760
+ if (this.isRideType('Workout'))
761
+ return true;
762
+ if (!this.isRideType('Video', 'GPX'))
763
+ return false;
764
+ if (!this.isWorkoutComboEnabled())
765
+ return false;
766
+ return this.getWorkoutRide().inUse();
767
+ }
768
+ catch (err) {
769
+ this.logError(err, 'isWorkoutAttached');
770
+ return false;
771
+ }
772
+ }
655
773
  buildPausedMenuProps() {
656
- return this.isRideType('Workout')
774
+ return this.isWorkoutAttached()
657
775
  ? { showResume: true, ...this.getStepFlags() }
658
776
  : { showResume: true };
659
777
  }
660
778
  buildFinishedMenuProps() {
661
- return this.isRideType('Workout')
779
+ return this.isWorkoutAttached()
662
780
  ? { showResume: false, finished: true, canStepBack: false, canStepForward: false }
663
781
  : { showResume: false, finished: true };
664
782
  }
@@ -42,6 +42,7 @@ import { sleep } from "../../utils/sleep";
42
42
  import { useDevicePairing } from "../../devices";
43
43
  import { useRouteDownload } from "../download/service";
44
44
  import { useRouteLibraryScanner } from "../library/service";
45
+ import { useWorkoutList } from "../../workouts";
45
46
  let RoutesPageService = (() => {
46
47
  let _classDecorators = [Singleton];
47
48
  let _classDescriptor;
@@ -54,6 +55,7 @@ let RoutesPageService = (() => {
54
55
  let _getUserSettings_decorators;
55
56
  let _getRouteDownload_decorators;
56
57
  let _getRouteLibraryScanner_decorators;
58
+ let _getWorkoutList_decorators;
57
59
  var RoutesPageService = class extends _classSuper {
58
60
  static { _classThis = this; }
59
61
  static {
@@ -63,11 +65,13 @@ let RoutesPageService = (() => {
63
65
  _getUserSettings_decorators = [Injectable];
64
66
  _getRouteDownload_decorators = [Injectable];
65
67
  _getRouteLibraryScanner_decorators = [Injectable];
68
+ _getWorkoutList_decorators = [Injectable];
66
69
  __esDecorate(this, null, _getRouteList_decorators, { kind: "method", name: "getRouteList", static: false, private: false, access: { has: obj => "getRouteList" in obj, get: obj => obj.getRouteList }, metadata: _metadata }, null, _instanceExtraInitializers);
67
70
  __esDecorate(this, null, _getDevicePairing_decorators, { kind: "method", name: "getDevicePairing", static: false, private: false, access: { has: obj => "getDevicePairing" in obj, get: obj => obj.getDevicePairing }, metadata: _metadata }, null, _instanceExtraInitializers);
68
71
  __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);
69
72
  __esDecorate(this, null, _getRouteDownload_decorators, { kind: "method", name: "getRouteDownload", static: false, private: false, access: { has: obj => "getRouteDownload" in obj, get: obj => obj.getRouteDownload }, metadata: _metadata }, null, _instanceExtraInitializers);
70
73
  __esDecorate(this, null, _getRouteLibraryScanner_decorators, { kind: "method", name: "getRouteLibraryScanner", static: false, private: false, access: { has: obj => "getRouteLibraryScanner" in obj, get: obj => obj.getRouteLibraryScanner }, metadata: _metadata }, null, _instanceExtraInitializers);
74
+ __esDecorate(this, null, _getWorkoutList_decorators, { kind: "method", name: "getWorkoutList", static: false, private: false, access: { has: obj => "getWorkoutList" in obj, get: obj => obj.getWorkoutList }, metadata: _metadata }, null, _instanceExtraInitializers);
71
75
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
72
76
  RoutesPageService = _classThis = _classDescriptor.value;
73
77
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -313,6 +317,31 @@ let RoutesPageService = (() => {
313
317
  this.logError(err, 'start');
314
318
  }
315
319
  }
320
+ getRouteDetailsProps(routeId) {
321
+ const comboEnabled = this.isWorkoutComboEnabled();
322
+ try {
323
+ return { routeId, attachedWorkout: this.getAttachedWorkoutProps(), comboEnabled };
324
+ }
325
+ catch (err) {
326
+ this.logError(err, 'getRouteDetailsProps');
327
+ return { routeId, attachedWorkout: null, comboEnabled };
328
+ }
329
+ }
330
+ getAttachedWorkoutProps() {
331
+ const workout = this.getWorkoutList().getSelected();
332
+ if (!workout)
333
+ return null;
334
+ return { id: workout.id, title: workout.name };
335
+ }
336
+ onClearWorkoutSelection() {
337
+ try {
338
+ this.getWorkoutList().unselect();
339
+ this.updatePageDisplay();
340
+ }
341
+ catch (err) {
342
+ this.logError(err, 'onClearWorkoutSelection');
343
+ }
344
+ }
316
345
  getDownloadDisplayProps() {
317
346
  return Array.from(this.downloadCache.values());
318
347
  }
@@ -450,6 +479,9 @@ let RoutesPageService = (() => {
450
479
  getRouteLibraryScanner() {
451
480
  return useRouteLibraryScanner();
452
481
  }
482
+ getWorkoutList() {
483
+ return useWorkoutList();
484
+ }
453
485
  };
454
486
  return RoutesPageService = _classThis;
455
487
  })();
@@ -41,6 +41,7 @@ import { useUserSettings } from "../../settings";
41
41
  import { useWorkoutCalendar } from "../calendar";
42
42
  import { useWorkoutList } from "../list/service";
43
43
  import { useDevicePairing } from "../../devices";
44
+ import { useRouteList } from "../../routes";
44
45
  const DEFAULT_IMPORT_GROUP = 'My Workouts';
45
46
  const UPCOMING_COLLAPSED_COUNT = 3;
46
47
  let WorkoutListPageService = (() => {
@@ -52,6 +53,7 @@ let WorkoutListPageService = (() => {
52
53
  let _instanceExtraInitializers = [];
53
54
  let _getDevicePairing_decorators;
54
55
  let _getWorkoutList_decorators;
56
+ let _getRouteList_decorators;
55
57
  let _getWorkoutCalendar_decorators;
56
58
  let _getUserSettings_decorators;
57
59
  let _getAppState_decorators;
@@ -61,11 +63,13 @@ let WorkoutListPageService = (() => {
61
63
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
62
64
  _getDevicePairing_decorators = [Injectable];
63
65
  _getWorkoutList_decorators = [Injectable];
66
+ _getRouteList_decorators = [Injectable];
64
67
  _getWorkoutCalendar_decorators = [Injectable];
65
68
  _getUserSettings_decorators = [Injectable];
66
69
  _getAppState_decorators = [Injectable];
67
70
  __esDecorate(this, null, _getDevicePairing_decorators, { kind: "method", name: "getDevicePairing", static: false, private: false, access: { has: obj => "getDevicePairing" in obj, get: obj => obj.getDevicePairing }, metadata: _metadata }, null, _instanceExtraInitializers);
68
71
  __esDecorate(this, null, _getWorkoutList_decorators, { kind: "method", name: "getWorkoutList", static: false, private: false, access: { has: obj => "getWorkoutList" in obj, get: obj => obj.getWorkoutList }, metadata: _metadata }, null, _instanceExtraInitializers);
72
+ __esDecorate(this, null, _getRouteList_decorators, { kind: "method", name: "getRouteList", static: false, private: false, access: { has: obj => "getRouteList" in obj, get: obj => obj.getRouteList }, metadata: _metadata }, null, _instanceExtraInitializers);
69
73
  __esDecorate(this, null, _getWorkoutCalendar_decorators, { kind: "method", name: "getWorkoutCalendar", static: false, private: false, access: { has: obj => "getWorkoutCalendar" in obj, get: obj => obj.getWorkoutCalendar }, metadata: _metadata }, null, _instanceExtraInitializers);
70
74
  __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);
71
75
  __esDecorate(this, null, _getAppState_decorators, { kind: "method", name: "getAppState", static: false, private: false, access: { has: obj => "getAppState" in obj, get: obj => obj.getAppState }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -168,7 +172,9 @@ let WorkoutListPageService = (() => {
168
172
  group: settingsProps.category,
169
173
  canDelete: card.canDelete(),
170
174
  isScheduled,
171
- date: isScheduled ? settingsProps.date : undefined
175
+ date: isScheduled ? settingsProps.date : undefined,
176
+ attachedRoute: this.getAttachedRouteProps(),
177
+ comboEnabled: this.isWorkoutComboEnabled()
172
178
  };
173
179
  }
174
180
  catch (err) {
@@ -176,6 +182,18 @@ let WorkoutListPageService = (() => {
176
182
  return null;
177
183
  }
178
184
  }
185
+ getAttachedRouteProps() {
186
+ try {
187
+ const route = this.getRouteList().getSelected();
188
+ if (!route)
189
+ return null;
190
+ return { id: route.description?.id, title: route.title };
191
+ }
192
+ catch (err) {
193
+ this.logError(err, 'getAttachedRouteProps');
194
+ return null;
195
+ }
196
+ }
179
197
  getImportDisplayProps() {
180
198
  try {
181
199
  const phase = this.importPhase ?? 'landing';
@@ -306,6 +324,15 @@ let WorkoutListPageService = (() => {
306
324
  this.logError(err, 'onClearSelection');
307
325
  }
308
326
  }
327
+ onClearRouteSelection() {
328
+ try {
329
+ this.getRouteList().unselect();
330
+ this.emitPageUpdate();
331
+ }
332
+ catch (err) {
333
+ this.logError(err, 'onClearRouteSelection');
334
+ }
335
+ }
309
336
  onImportOpen() {
310
337
  try {
311
338
  this.importPhase = 'landing';
@@ -486,6 +513,9 @@ let WorkoutListPageService = (() => {
486
513
  getWorkoutList() {
487
514
  return useWorkoutList();
488
515
  }
516
+ getRouteList() {
517
+ return useRouteList();
518
+ }
489
519
  getWorkoutCalendar() {
490
520
  return useWorkoutCalendar();
491
521
  }
@@ -1,5 +1,5 @@
1
1
  import { IncyclistPageService } from "../../base/pages";
2
- import { ActivitiesPageDisplayProps, IActivitiesPageService } from "./types";
2
+ import { ActivitiesPageDisplayProps, ActivityDetailsProps, IActivitiesPageService } from "./types";
3
3
  import { IObserver } from "../../types";
4
4
  import { ActivityListDisplayProperties } from "../list";
5
5
  export declare class ActivitiesPageService extends IncyclistPageService implements IActivitiesPageService {
@@ -15,10 +15,13 @@ export declare class ActivitiesPageService extends IncyclistPageService implemen
15
15
  getPageDisplayProps(): ActivitiesPageDisplayProps;
16
16
  onOpenActivity(id: string | null): void;
17
17
  onCloseActivity(): void;
18
+ getActivityDetailsProps(activityId: string): ActivityDetailsProps;
19
+ onClearWorkoutSelection(): void;
18
20
  protected updatePageDisplay(): void;
19
21
  protected onStateUpdate(): void;
20
22
  protected startEventListener(): void;
21
23
  protected stopEventListener(final?: boolean): void;
22
24
  getActivityList(): import("../list").ActivityListService;
25
+ protected getWorkoutList(): import("../../workouts").WorkoutListService;
23
26
  }
24
27
  export declare const getActivitiesPageService: () => ActivitiesPageService;
@@ -1,10 +1,19 @@
1
1
  import { IPageService } from "../../base/pages";
2
2
  import { ActivityInfoUI } from "../base";
3
+ import { AttachedWorkoutProps } from "../../routes/page/types";
3
4
  export interface ActivitiesPageDisplayProps {
4
5
  loading: boolean;
5
6
  activities?: Array<ActivityInfoUI>;
6
7
  detailActivityId?: string;
7
8
  }
9
+ export type { AttachedWorkoutProps };
10
+ export interface ActivityDetailsProps {
11
+ activityId: string;
12
+ attachedWorkout: AttachedWorkoutProps | null;
13
+ comboEnabled: boolean;
14
+ }
8
15
  export interface IActivitiesPageService extends IPageService {
9
16
  getPageDisplayProps(): ActivitiesPageDisplayProps;
17
+ getActivityDetailsProps(activityId: string): ActivityDetailsProps;
18
+ onClearWorkoutSelection(): void;
10
19
  }
@@ -20,4 +20,5 @@ export declare class IncyclistPageService extends IncyclistService implements IP
20
20
  protected getUIBinding(): import("../../api").INativeUI;
21
21
  protected getBindings(): import("../../api").IncyclistBindings;
22
22
  protected getAppState(): import("../../appstate").AppStateService;
23
+ protected isWorkoutComboEnabled(): boolean;
23
24
  }
@@ -1,6 +1,6 @@
1
1
  import { IncyclistPageService } from "../../base/pages";
2
2
  import { CurrentRideDisplayProps, CurrentRideState, IObserver, RideType } from "../../types";
3
- import { AnyRidePageDisplayProps, GPXRidePageDisplayProps, IRidePageService, RideMenuProps, StartGateProps, VideoRidePageDisplayProps, WorkoutDashboardLine, WorkoutGestureHint, WorkoutGraphActuals, WorkoutGraphPlan, WorkoutRideMenuProps, WorkoutRidePageDisplayProps, WorkoutUpcomingSteps } from "./types";
3
+ import { AnyRidePageDisplayProps, GPXRidePageDisplayProps, IRidePageService, RideMenuProps, RidePageDisplayProps, StartGateProps, VideoRidePageDisplayProps, WorkoutDashboardLine, WorkoutGestureHint, WorkoutGraphActuals, WorkoutGraphPlan, WorkoutRideMenuProps, WorkoutRidePageDisplayProps, WorkoutUpcomingSteps } from "./types";
4
4
  import { ISecretBinding } from "../../api/secret";
5
5
  import type { LoadButtonMode, PowerAdjustmentResult, WorkoutDisplayProperties } from "../../workouts/ride/types";
6
6
  import type { Workout } from "../../workouts/base/model";
@@ -9,12 +9,14 @@ export declare class RidePageService extends IncyclistPageService implements IRi
9
9
  protected eventHandler: Record<string, any>;
10
10
  protected workoutEventHandler: Record<string, any>;
11
11
  protected workoutObserverSubscribed: boolean;
12
+ protected workoutObserver: IObserver | undefined;
12
13
  protected backgroundTimer: NodeJS.Timeout | undefined;
13
14
  protected backgroundPausedByService: boolean;
14
15
  protected menuProps: RideMenuProps | null;
15
16
  protected isInitialized: boolean;
16
17
  protected startGateProps: StartGateProps | null;
17
18
  protected gestureHintDismissed: boolean;
19
+ protected viewTransition: boolean;
18
20
  constructor();
19
21
  initPage(): Promise<RideType | undefined>;
20
22
  openPage(simulate?: boolean): IObserver;
@@ -36,14 +38,21 @@ export declare class RidePageService extends IncyclistPageService implements IRi
36
38
  onEndRide(): void;
37
39
  protected getVideoRideDisplayProps(): VideoRidePageDisplayProps;
38
40
  protected getGPXRideDisplayProps(): GPXRidePageDisplayProps;
41
+ protected buildWorkoutOverlayProps(props: CurrentRideDisplayProps & {
42
+ showWorkout?: boolean;
43
+ }, startOverlayCleared: boolean): Partial<RidePageDisplayProps>;
44
+ protected getCornerWidget(): 'elevation' | 'workout' | undefined;
45
+ onToggleCornerWidget(): void;
39
46
  protected checkSecretValidity(): Promise<void>;
40
47
  protected showStartGate(): void;
41
48
  protected hideStartgate(): void;
42
49
  protected onRouteUpdate(): void;
50
+ protected onViewChanged(): void;
43
51
  protected get rideDisplayProps(): CurrentRideDisplayProps;
44
52
  protected getSecretBinding(): ISecretBinding | undefined;
45
53
  getGraphActuals(): WorkoutGraphActuals;
46
54
  onStop(): void;
55
+ onStopWorkout(): void;
47
56
  onStepBack(): void;
48
57
  onStepForward(): void;
49
58
  onIncreaseLoad(): void;
@@ -78,6 +87,7 @@ export declare class RidePageService extends IncyclistPageService implements IRi
78
87
  protected requiresOwnInit(): boolean;
79
88
  protected getBackgroundPauseRequester(): 'user' | 'device';
80
89
  protected isRideType(...types: RideType[]): boolean;
90
+ protected isWorkoutAttached(): boolean;
81
91
  protected buildPausedMenuProps(): RideMenuProps | WorkoutRideMenuProps;
82
92
  protected buildFinishedMenuProps(): RideMenuProps | WorkoutRideMenuProps;
83
93
  protected buildBaseDisplayProps(): {
@@ -27,6 +27,9 @@ export interface RidePageDisplayProps {
27
27
  title?: string;
28
28
  gestureHint?: WorkoutGestureHint | null;
29
29
  loadIncrement?: number;
30
+ workoutAttached?: boolean;
31
+ loadButtonMode?: LoadButtonMode;
32
+ cornerWidget?: 'elevation' | 'workout';
30
33
  }
31
34
  export interface VideoRidePageDisplayProps extends RidePageDisplayProps {
32
35
  video?: VideoDisplayProps;
@@ -49,6 +52,7 @@ interface RidePageCallbacks {
49
52
  onRefreshSecrets(): void;
50
53
  onContinueAnyway(): void;
51
54
  onStop(): void;
55
+ onStopWorkout(): void;
52
56
  onStepBack(): void;
53
57
  onStepForward(): void;
54
58
  onIncreaseLoad(): void;
@@ -57,6 +61,7 @@ interface RidePageCallbacks {
57
61
  onGestureHintDismissed(props: {
58
62
  dontShowAgain: boolean;
59
63
  }): void;
64
+ onToggleCornerWidget(): void;
60
65
  }
61
66
  export interface IRidePageService extends RidePageCallbacks, IPageService {
62
67
  initPage(): Promise<RideType | undefined>;
@@ -1,7 +1,7 @@
1
1
  import { IncyclistPageService } from "../../base/pages";
2
2
  import { FileInfo, FolderInfo, ImportDisplayProps, IObserver, ParsedRoute, ScannedRoute } from "../../types";
3
3
  import { SearchFilter, SearchState } from "../list/types";
4
- import { DownloadRowDisplayProps, IRoutePageService, RouteItemProps, RoutePageDisplayProps } from "./types";
4
+ import { AttachedWorkoutProps, DownloadRowDisplayProps, IRoutePageService, RouteDetailsProps, RouteItemProps, RoutePageDisplayProps } from "./types";
5
5
  import { Observer } from "../../base/types";
6
6
  import { Route } from "../base/model/route";
7
7
  import { DownloadObserver } from "../download/types";
@@ -42,6 +42,9 @@ export declare class RoutesPageService extends IncyclistPageService implements I
42
42
  onSelect(id: string): void;
43
43
  onDelete(id: string): void;
44
44
  start(): void;
45
+ getRouteDetailsProps(routeId: string): RouteDetailsProps;
46
+ protected getAttachedWorkoutProps(): AttachedWorkoutProps | null;
47
+ onClearWorkoutSelection(): void;
45
48
  protected getDownloadDisplayProps(): DownloadRowDisplayProps[];
46
49
  protected updatePageDisplay(): void;
47
50
  protected getRoutesDisplayProps(): Array<RouteItemProps>;
@@ -62,5 +65,6 @@ export declare class RoutesPageService extends IncyclistPageService implements I
62
65
  protected getUserSettings(): import("../../settings").UserSettingsService;
63
66
  protected getRouteDownload(): import("../download/service").RouteDownloadService;
64
67
  protected getRouteLibraryScanner(): import("../library/service").RouteLibraryScannerService;
68
+ protected getWorkoutList(): import("../../workouts").WorkoutListService;
65
69
  }
66
70
  export declare const getRoutesPageService: () => RoutesPageService;
@@ -31,6 +31,17 @@ export interface IRoutePageService extends IPageService, IPageCallBacks {
31
31
  getPageDisplayProps(): RoutePageDisplayProps;
32
32
  onImportClosed(): void;
33
33
  getImportDisplayProps(): ImportDisplayProps;
34
+ getRouteDetailsProps(routeId: string): RouteDetailsProps;
35
+ onClearWorkoutSelection(): void;
36
+ }
37
+ export interface AttachedWorkoutProps {
38
+ id: string;
39
+ title: string;
40
+ }
41
+ export interface RouteDetailsProps {
42
+ routeId: string;
43
+ attachedWorkout: AttachedWorkoutProps | null;
44
+ comboEnabled: boolean;
34
45
  }
35
46
  export type DownloadStatus = 'downloading' | 'done' | 'failed' | 'required';
36
47
  export interface DownloadRowDisplayProps {
@@ -3,7 +3,7 @@ import { IObserver } from "../../types";
3
3
  import { FileInfo } from "../../api";
4
4
  import { WorkoutCard } from "../list/cards/WorkoutCard";
5
5
  import { WorkoutListService } from "../list/service";
6
- import { GroupFilterProps, IWorkoutListPageService, UpcomingTrainingProps, WorkoutDetailsProps, WorkoutImportDisplayProps, WorkoutListItemProps, WorkoutListPageDisplayProps } from "./types";
6
+ import { AttachedRouteProps, GroupFilterProps, IWorkoutListPageService, UpcomingTrainingProps, WorkoutDetailsProps, WorkoutImportDisplayProps, WorkoutListItemProps, WorkoutListPageDisplayProps } from "./types";
7
7
  export declare class WorkoutListPageService extends IncyclistPageService implements IWorkoutListPageService {
8
8
  protected groupFilter: string | null;
9
9
  protected detailWorkoutId: string | null;
@@ -24,6 +24,7 @@ export declare class WorkoutListPageService extends IncyclistPageService impleme
24
24
  closePage(): void;
25
25
  getPageDisplayProps(): WorkoutListPageDisplayProps;
26
26
  getWorkoutDetailsProps(id: string): WorkoutDetailsProps | null;
27
+ protected getAttachedRouteProps(): AttachedRouteProps | null;
27
28
  getImportDisplayProps(): WorkoutImportDisplayProps;
28
29
  onSelectGroup(group: string | null): void;
29
30
  onOpenDetails(id: string): void;
@@ -37,6 +38,7 @@ export declare class WorkoutListPageService extends IncyclistPageService impleme
37
38
  }): void;
38
39
  onMarkForRoute(id: string): void;
39
40
  onClearSelection(): void;
41
+ onClearRouteSelection(): void;
40
42
  onImportOpen(): void;
41
43
  onImportFile(file: FileInfo): IObserver;
42
44
  onImportSetGroup(id: string, group: string): void;
@@ -55,6 +57,7 @@ export declare class WorkoutListPageService extends IncyclistPageService impleme
55
57
  protected emitImportUpdate(): void;
56
58
  protected getDevicePairing(): import("../../devices").DevicePairingService;
57
59
  protected getWorkoutList(): WorkoutListService;
60
+ protected getRouteList(): import("../../routes").RouteListService;
58
61
  protected getWorkoutCalendar(): import("../calendar").WorkoutCalendarService;
59
62
  protected getUserSettings(): import("../../settings").UserSettingsService;
60
63
  protected getAppState(): import("../../appstate").AppStateService;
@@ -41,6 +41,10 @@ export interface WorkoutListItemProps {
41
41
  canDelete: boolean;
42
42
  workout: Workout;
43
43
  }
44
+ export interface AttachedRouteProps {
45
+ id: string;
46
+ title: string;
47
+ }
44
48
  export interface WorkoutDetailsProps {
45
49
  id: string;
46
50
  title: string;
@@ -57,6 +61,8 @@ export interface WorkoutDetailsProps {
57
61
  canDelete: boolean;
58
62
  isScheduled: boolean;
59
63
  date?: Date;
64
+ attachedRoute: AttachedRouteProps | null;
65
+ comboEnabled: boolean;
60
66
  }
61
67
  export interface WorkoutImportDisplayProps {
62
68
  phase: 'landing' | 'importing' | 'result' | 'error';
@@ -84,6 +90,7 @@ export interface WorkoutListPageCallbacks {
84
90
  }) => void;
85
91
  onMarkForRoute?: (id: string) => void;
86
92
  onClearSelection: () => void;
93
+ onClearRouteSelection: () => void;
87
94
  onImportOpen: () => void;
88
95
  onImportFile: (file: FileInfo) => IObserver;
89
96
  onImportSetGroup: (id: string, group: string) => void;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "incyclist-services",
3
- "version": "1.7.99",
3
+ "version": "1.7.101",
4
4
  "peerDependencies": {
5
5
  "gd-eventlog": "^0.1.27"
6
6
  },
7
7
  "dependencies": {
8
8
  "@garmin/fitsdk": "^21.200.0",
9
9
  "axios": "^1.15.2",
10
- "incyclist-devices": "^3.0.31",
10
+ "incyclist-devices": "^3.0.32",
11
11
  "promise.any": "^2.0.6",
12
12
  "semver": "^7.7.4",
13
13
  "tcx-builder": "^1.1.1",