incyclist-services 1.7.119 → 1.7.121

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.
@@ -342,6 +342,7 @@ let ActiveRidesService = (() => {
342
342
  return;
343
343
  const displayItem = {
344
344
  isUser: item.sessionId === this.session,
345
+ isCoach: item.isCoach,
345
346
  name: this.getName(item),
346
347
  diffDistance: this.getDistanceDiff(item),
347
348
  distance: this.getDistance(item),
@@ -685,6 +686,7 @@ let ActiveRidesService = (() => {
685
686
  if (!payload.user) {
686
687
  this.getRemoteActivityDetails(session);
687
688
  }
689
+ this.observer.emit('update', this.getDisplayProps());
688
690
  }
689
691
  catch (err) {
690
692
  this.logError(err, 'onActivityStartEvent');
@@ -762,6 +764,7 @@ let ActiveRidesService = (() => {
762
764
  this.logEvent({ message: 'group ride user joined', active: this.others.length + 1, activityId: this.activity?.id, route: this.activity.route?.title, routeHash: this.getRouteHash(), remote });
763
765
  }
764
766
  }
767
+ this.observer.emit('update', this.getDisplayProps());
765
768
  }
766
769
  catch (err) {
767
770
  this.logError(err, 'onActivityStartEvent');
@@ -784,8 +787,10 @@ let ActiveRidesService = (() => {
784
787
  if (!this.others?.length)
785
788
  return;
786
789
  const idx = this.others.findIndex(ar => ar.sessionId === session);
787
- if (idx !== -1)
788
- this.others.splice(idx, 1);
790
+ if (idx === -1)
791
+ return;
792
+ this.others.splice(idx, 1);
793
+ this.observer.emit('update', this.getDisplayProps());
789
794
  if (!this.isStarted)
790
795
  return;
791
796
  if (this.others.length === 0) {
@@ -47,6 +47,7 @@ const Injection_1 = require("../../../base/decorators/Injection");
47
47
  const i18n_1 = require("../../../i18n");
48
48
  exports.DB_VERSION = '5';
49
49
  exports.DB_NAME = 'db';
50
+ const SHORT_RIDE_FILTER_CUTOFF = new Date('2026-08-28').getTime();
50
51
  let ActivitiesRepository = (() => {
51
52
  let _classDecorators = [types_1.Singleton];
52
53
  let _classDescriptor;
@@ -109,7 +110,7 @@ let ActivitiesRepository = (() => {
109
110
  this.write();
110
111
  }
111
112
  if (writeDetails) {
112
- this.writeDetails(activity);
113
+ await this.writeDetails(activity);
113
114
  }
114
115
  }
115
116
  migrate(activity, writeSummary = true) {
@@ -224,6 +225,16 @@ let ActivitiesRepository = (() => {
224
225
  }
225
226
  search(criteria) {
226
227
  let result = this.activities;
228
+ if (!this.isMobile()) {
229
+ result = result?.filter(ai => {
230
+ const { rideTime, startTime } = ai.summary;
231
+ if (rideTime < 1)
232
+ return false;
233
+ if (rideTime <= 30 && startTime < SHORT_RIDE_FILTER_CUTOFF)
234
+ return false;
235
+ return true;
236
+ });
237
+ }
227
238
  result = this.checkIdFilter(result, criteria);
228
239
  result = this.checkHashFilter(result, criteria);
229
240
  result = this.checkStartPosFilter(result, criteria);
@@ -475,7 +486,7 @@ let ActivitiesRepository = (() => {
475
486
  }
476
487
  async writeDetails(activity) {
477
488
  const name = activity.summary.name;
478
- this.getRepo().write(name, activity.details);
489
+ await this.getRepo().write(name, activity.details);
479
490
  }
480
491
  async scanForNewActivities() {
481
492
  try {
@@ -509,6 +520,13 @@ let ActivitiesRepository = (() => {
509
520
  getRouteList() {
510
521
  return (0, routes_1.useRouteList)();
511
522
  }
523
+ isMobile() {
524
+ const { appInfo } = this.getBindings();
525
+ if (!appInfo) {
526
+ return false;
527
+ }
528
+ return appInfo?.getChannel() === 'mobile';
529
+ }
512
530
  getBindings() {
513
531
  return (0, api_1.getBindings)();
514
532
  }
@@ -254,7 +254,7 @@ let Activity = (() => {
254
254
  isRouteAvailable() {
255
255
  if (!this.details?.route)
256
256
  return false;
257
- if (this.details.routeType === 'Free-Ride')
257
+ if (this.details.routeType === 'Free-Ride' || this.details.routeType === 'None')
258
258
  return false;
259
259
  return this.getRouteList().getRouteDescription(this.details.route.id) !== undefined;
260
260
  }
@@ -277,7 +277,7 @@ let Activity = (() => {
277
277
  canStart() {
278
278
  if (!this.details?.route)
279
279
  return false;
280
- if (this.details.routeType === 'Free-Ride')
280
+ if (this.details.routeType === 'Free-Ride' || this.details.routeType === 'None')
281
281
  return false;
282
282
  if (this.details.routeType === 'Video') {
283
283
  const isOnline = this.getOnlineMonitoring().onlineStatus;
@@ -198,7 +198,7 @@ let ActivityRideService = (() => {
198
198
  this.disableDeviceHealthCheck();
199
199
  this.state = 'completed';
200
200
  this.updateActivityTime();
201
- this._save();
201
+ await this._save();
202
202
  this.emit('completed');
203
203
  this.tsPauseStart = undefined;
204
204
  delete this.tsStart;
@@ -536,7 +536,7 @@ let ActivityRideService = (() => {
536
536
  if (this.state !== 'completed') {
537
537
  await this.stop();
538
538
  }
539
- this.getRepo().delete(this.activity.id);
539
+ await this.getRepo().delete(this.activity.id);
540
540
  this.state = 'idle';
541
541
  }
542
542
  save() {
@@ -552,7 +552,7 @@ let ActivityRideService = (() => {
552
552
  let success = false;
553
553
  try {
554
554
  emit('start', success);
555
- await this._save();
555
+ this._save();
556
556
  success = await this._convertAndUpload();
557
557
  (0, sleep_1.sleep)(0).then(() => {
558
558
  delete this.saveObserver;
@@ -965,7 +965,7 @@ let ActivityRideService = (() => {
965
965
  await fs.writeFile(fileName, Buffer.from(data));
966
966
  this.activity.tcxFileName = fileName;
967
967
  }
968
- await this._save();
968
+ this._save();
969
969
  emit('save.done', format, true);
970
970
  return true;
971
971
  }
@@ -397,7 +397,7 @@ let DevicePairingService = (() => {
397
397
  if (this.isScanning())
398
398
  await this.stopScanning();
399
399
  }
400
- this.state.capabilities.forEach(c => c.connectState = 'waiting');
400
+ (this.state.capabilities ?? []).forEach(c => c.connectState = 'waiting');
401
401
  this.state.waiting = true;
402
402
  }
403
403
  getCapability(capability) {
@@ -1361,7 +1361,7 @@ let DevicePairingService = (() => {
1361
1361
  pauseAdapters(adapters, enforced = false) {
1362
1362
  if (!adapters)
1363
1363
  return;
1364
- const capabilities = this.state.capabilities;
1364
+ const capabilities = this.state.capabilities ?? [];
1365
1365
  capabilities.forEach(c => {
1366
1366
  if (!enforced && !c.selected && c.connectState === 'failed')
1367
1367
  return;
@@ -1372,7 +1372,7 @@ let DevicePairingService = (() => {
1372
1372
  resumeAdapters(adapters = this.state.adapters) {
1373
1373
  if (!adapters?.length)
1374
1374
  return;
1375
- const capabilities = this.state.capabilities;
1375
+ const capabilities = this.state.capabilities ?? [];
1376
1376
  capabilities.forEach(c => {
1377
1377
  if (!c.selected && c.connectState === 'failed')
1378
1378
  return;
@@ -22,8 +22,11 @@ function isVirtualShiftingEnabled(mode) {
22
22
  function getLoadButtonMode(mode) {
23
23
  if (!mode)
24
24
  return 'power';
25
- if (mode.isSIM() || mode.isResistance()) {
26
- return isVirtualShiftingEnabled(mode) ? 'gear' : 'hidden';
27
- }
28
- return 'power';
25
+ if (mode.isERG())
26
+ return 'power';
27
+ if (mode.isSIM() && isVirtualShiftingEnabled(mode))
28
+ return 'gear';
29
+ if (mode.isResistance())
30
+ return 'gear';
31
+ return 'hidden';
29
32
  }
@@ -39,6 +39,7 @@ const gd_eventlog_1 = require("gd-eventlog");
39
39
  const decorators_1 = require("../../base/decorators");
40
40
  const pages_1 = require("../../base/pages");
41
41
  const display_1 = require("../display");
42
+ const devices_1 = require("../../devices");
42
43
  const sleep_1 = require("../../utils/sleep");
43
44
  const monitoring_1 = require("../../monitoring");
44
45
  const activities_1 = require("../../activities");
@@ -64,6 +65,7 @@ let RidePageService = (() => {
64
65
  let _getActivityRide_decorators;
65
66
  let _getUserSettings_decorators;
66
67
  let _getOnlineStatusMonitoring_decorators;
68
+ let _getDeviceConfiguration_decorators;
67
69
  var RidePageService = class extends _classSuper {
68
70
  static { _classThis = this; }
69
71
  static {
@@ -73,11 +75,13 @@ let RidePageService = (() => {
73
75
  _getActivityRide_decorators = [decorators_1.Injectable];
74
76
  _getUserSettings_decorators = [decorators_1.Injectable];
75
77
  _getOnlineStatusMonitoring_decorators = [decorators_1.Injectable];
78
+ _getDeviceConfiguration_decorators = [decorators_1.Injectable];
76
79
  __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);
77
80
  __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);
78
81
  __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);
79
82
  __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);
80
83
  __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);
84
+ __esDecorate(this, null, _getDeviceConfiguration_decorators, { kind: "method", name: "getDeviceConfiguration", static: false, private: false, access: { has: obj => "getDeviceConfiguration" in obj, get: obj => obj.getDeviceConfiguration }, metadata: _metadata }, null, _instanceExtraInitializers);
81
85
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
82
86
  RidePageService = _classThis = _classDescriptor.value;
83
87
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -92,10 +96,13 @@ let RidePageService = (() => {
92
96
  menuProps = null;
93
97
  isInitialized = false;
94
98
  startGateProps = null;
99
+ onDeviceModeChangedBound = this.onDeviceModeChanged.bind(this);
95
100
  prevRidesVisibleRows = DEFAULT_PREV_RIDES_VISIBLE_ROWS;
96
101
  prevRidesMode = 'list';
97
102
  gestureHintDismissed = false;
98
103
  prevRidesShown = false;
104
+ nearbyRiders;
105
+ nearbyRidersSubscribed = false;
99
106
  viewTransition = false;
100
107
  constructor() {
101
108
  super('RidePage');
@@ -137,12 +144,14 @@ let RidePageService = (() => {
137
144
  gd_eventlog_1.EventLogger.setGlobalConfig('page', pageLogName);
138
145
  this.gestureHintDismissed = false;
139
146
  this.prevRidesShown = false;
147
+ this.nearbyRidersSubscribed = false;
140
148
  super.openPage();
141
149
  try {
142
150
  const service = this.getRideDisplay();
143
151
  const registerAndStart = () => {
144
152
  this.registerHandlers(this.rideObserver, this.eventHandler);
145
153
  this.subscribeToWorkoutObserver();
154
+ this.getDeviceConfiguration().on('mode-changed', this.onDeviceModeChangedBound);
146
155
  const state = service.getState();
147
156
  if (state === 'Started' || state === 'Active' || state === 'Paused') {
148
157
  this.logError(new Error(`start() requested while ride is ${state}`), 'openPage');
@@ -187,6 +196,7 @@ let RidePageService = (() => {
187
196
  this.getRideDisplay().stop();
188
197
  this.unregisterHandlers(this.rideObserver, this.eventHandler);
189
198
  this.unsubscribeFromWorkoutObserver();
199
+ this.getDeviceConfiguration().off('mode-changed', this.onDeviceModeChangedBound);
190
200
  this.menuProps = null;
191
201
  this.isInitialized = false;
192
202
  super.closePage();
@@ -278,9 +288,11 @@ let RidePageService = (() => {
278
288
  onMenuOpen() {
279
289
  try {
280
290
  const state = this.getRideDisplay().getState();
291
+ const loadControl = this.getLoadControlProps();
292
+ const showRideSettings = !this.isRideType('Workout');
281
293
  this.menuProps = this.isWorkoutAttached()
282
- ? { showResume: state === 'Paused', ...this.getStepFlags() }
283
- : { showResume: state === 'Paused' };
294
+ ? { showResume: state === 'Paused', ...this.getStepFlags(), loadControl, showRideSettings }
295
+ : { showResume: state === 'Paused', loadControl, showRideSettings };
284
296
  this.updatePageDisplay();
285
297
  }
286
298
  catch (err) {
@@ -397,7 +409,7 @@ let RidePageService = (() => {
397
409
  const available = this.getCornerWidgetStates();
398
410
  if (available.length <= 1)
399
411
  return undefined;
400
- const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
412
+ const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'workout');
401
413
  return available.includes(current) ? current : available[0];
402
414
  }
403
415
  catch (err) {
@@ -414,7 +426,7 @@ let RidePageService = (() => {
414
426
  onToggleCornerWidget() {
415
427
  try {
416
428
  const available = this.getCornerWidgetStates();
417
- const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
429
+ const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'workout');
418
430
  const next = available[(available.indexOf(current) + 1) % available.length];
419
431
  this.getUserSettings().set(CORNER_WIDGET_SETTING_KEY, next);
420
432
  this.updatePageDisplay();
@@ -583,8 +595,7 @@ let RidePageService = (() => {
583
595
  const plainErgAdjustment = !this.getWorkoutRide().inUse() && this.getLoadButtonMode() === 'power';
584
596
  if (plainErgAdjustment) {
585
597
  const sign = deltaPct >= 0 ? 1 : -1;
586
- const nominal = Math.abs(deltaPct) === 1 ? 5 : 50;
587
- return this.getRideDisplay().adjustDevicePower(sign * nominal);
598
+ return this.getRideDisplay().adjustDevicePower(sign * this.getNominalErgWatts(deltaPct));
588
599
  }
589
600
  return deltaPct >= 0
590
601
  ? this.getWorkoutRide().powerUp(deltaPct)
@@ -764,6 +775,40 @@ let RidePageService = (() => {
764
775
  const wo = this.getWorkoutRide().getDashboardDisplayProperties();
765
776
  return { canStepBack: !!wo.canShowBackward, canStepForward: !!wo.canShowForward };
766
777
  }
778
+ getLoadControlProps() {
779
+ const mode = this.getLoadButtonMode();
780
+ if (mode === 'hidden')
781
+ return { visible: false };
782
+ if (mode === 'gear')
783
+ return { visible: true, label: 'Gear', buttons: { inc1: '+1', dec1: '-1', inc5: '+5', dec5: '-5' } };
784
+ if (this.isWorkoutAttached()) {
785
+ const buttons = this.getWorkoutRide().getDashboardDisplayProperties().loadButtons;
786
+ return { visible: true, label: 'Load', buttons };
787
+ }
788
+ const small = this.getNominalErgWatts(1);
789
+ const big = this.getNominalErgWatts(5);
790
+ return {
791
+ visible: true,
792
+ label: 'Load',
793
+ buttons: { inc1: `+${small}W`, dec1: `-${small}W`, inc5: `+${big}W`, dec5: `-${big}W` }
794
+ };
795
+ }
796
+ getNominalErgWatts(magnitude) {
797
+ return Math.abs(magnitude) === 1 ? 5 : 50;
798
+ }
799
+ onDeviceModeChanged() {
800
+ setTimeout(() => {
801
+ try {
802
+ if (!this.menuProps)
803
+ return;
804
+ this.menuProps = { ...this.menuProps, loadControl: this.getLoadControlProps() };
805
+ this.updatePageDisplay();
806
+ }
807
+ catch (err) {
808
+ this.logError(err, 'onDeviceModeChanged');
809
+ }
810
+ }, 0);
811
+ }
767
812
  getLastLogTime() {
768
813
  const logs = this.getActivityRide().getActivity()?.logs;
769
814
  if (!logs || logs.length === 0)
@@ -820,14 +865,18 @@ let RidePageService = (() => {
820
865
  }
821
866
  }
822
867
  buildPausedMenuProps() {
868
+ const loadControl = this.getLoadControlProps();
869
+ const showRideSettings = !this.isRideType('Workout');
823
870
  return this.isWorkoutAttached()
824
- ? { showResume: true, ...this.getStepFlags() }
825
- : { showResume: true };
871
+ ? { showResume: true, ...this.getStepFlags(), loadControl, showRideSettings }
872
+ : { showResume: true, loadControl, showRideSettings };
826
873
  }
827
874
  buildFinishedMenuProps() {
875
+ const loadControl = this.getLoadControlProps();
876
+ const showRideSettings = !this.isRideType('Workout');
828
877
  return this.isWorkoutAttached()
829
- ? { showResume: false, finished: true, canStepBack: false, canStepForward: false }
830
- : { showResume: false, finished: true };
878
+ ? { showResume: false, finished: true, canStepBack: false, canStepForward: false, loadControl, showRideSettings }
879
+ : { showResume: false, finished: true, loadControl, showRideSettings };
831
880
  }
832
881
  buildBaseDisplayProps() {
833
882
  const state = this.getRideDisplay().getState();
@@ -842,7 +891,8 @@ let RidePageService = (() => {
842
891
  gestureHint: this.buildGestureHint(startOverlayProps === null),
843
892
  loadIncrement: this.getLoadIncrement(),
844
893
  loadButtonMode: this.getLoadButtonMode(),
845
- prevRides: this.buildPrevRides()
894
+ prevRides: this.buildPrevRides(),
895
+ nearbyRiders: this.nearbyRiders
846
896
  };
847
897
  }
848
898
  buildPrevRides() {
@@ -905,8 +955,55 @@ let RidePageService = (() => {
905
955
  tsStart: row.tsStart
906
956
  };
907
957
  }
958
+ subscribeToNearbyRiders() {
959
+ try {
960
+ if (this.nearbyRidersSubscribed)
961
+ return;
962
+ const observer = this.getActiveRidesObserver();
963
+ if (!observer)
964
+ return;
965
+ observer.on('update', (rows) => {
966
+ this.nearbyRiders = { rows: (rows ?? []).map(row => this.mapNearbyRiderRow(row)) };
967
+ this.rideObserver?.emit('nearby-riders-update', this.nearbyRiders);
968
+ this.updatePageDisplay();
969
+ });
970
+ this.nearbyRidersSubscribed = true;
971
+ }
972
+ catch (err) {
973
+ this.logError(err, 'subscribeToNearbyRiders');
974
+ }
975
+ }
976
+ mapNearbyRiderRow(row) {
977
+ return {
978
+ isUser: !!row.isUser,
979
+ isPaused: !!row.isPaused,
980
+ isCoach: !!row.isCoach,
981
+ name: row.name,
982
+ distance: row.distance ?? { value: 0, unit: '' },
983
+ diffDistance: row.diffDistance ?? { value: 0, unit: '' },
984
+ power: row.power,
985
+ mpower: row.mpower,
986
+ speed: row.speed,
987
+ avatar: row.avatar,
988
+ backgroundColor: row.backgroundColor,
989
+ textColor: row.textColor,
990
+ lat: row.lat,
991
+ lng: row.lng
992
+ };
993
+ }
994
+ getActiveRidesObserver() {
995
+ try {
996
+ const modeService = this.getRideDisplay()?.getRideModeService?.();
997
+ return modeService?.getActiveRidesObserver?.();
998
+ }
999
+ catch (err) {
1000
+ this.logError(err, 'getActiveRidesObserver');
1001
+ return undefined;
1002
+ }
1003
+ }
908
1004
  onDisplayStateUpdate(state) {
909
1005
  this.subscribeToWorkoutObserver();
1006
+ this.subscribeToNearbyRiders();
910
1007
  switch (state) {
911
1008
  case 'Paused':
912
1009
  this.menuProps = this.buildPausedMenuProps();
@@ -961,6 +1058,9 @@ let RidePageService = (() => {
961
1058
  getOnlineStatusMonitoring() {
962
1059
  return (0, monitoring_1.useOnlineStatusMonitoring)();
963
1060
  }
1061
+ getDeviceConfiguration() {
1062
+ return (0, devices_1.useDeviceConfiguration)();
1063
+ }
964
1064
  };
965
1065
  return RidePageService = _classThis;
966
1066
  })();
@@ -83,6 +83,7 @@ let RouteDisplayService = (() => {
83
83
  prevPowerTs;
84
84
  nearbyRiders;
85
85
  _startSettings;
86
+ activeRidesObserver;
86
87
  init(service) {
87
88
  try {
88
89
  super.init(service);
@@ -369,6 +370,7 @@ let RouteDisplayService = (() => {
369
370
  const session = this.getAppInfo().session;
370
371
  try {
371
372
  const observer = this.getActiveRides().init(session);
373
+ this.activeRidesObserver = observer;
372
374
  observer.on('update', (data) => {
373
375
  this.nearbyRiders = data.filter(ar => !ar.isUser);
374
376
  });
@@ -380,11 +382,15 @@ let RouteDisplayService = (() => {
380
382
  cleanupActiveRides() {
381
383
  try {
382
384
  this.getActiveRides().stop();
385
+ delete this.activeRidesObserver;
383
386
  }
384
387
  catch (err) {
385
388
  this.logError(err, 'cleanupActiveRides');
386
389
  }
387
390
  }
391
+ getActiveRidesObserver() {
392
+ return this.activeRidesObserver;
393
+ }
388
394
  savePosition(_startPos) {
389
395
  }
390
396
  getOriginalRoute() {
@@ -609,6 +609,8 @@ let RouteListService = (() => {
609
609
  }
610
610
  getRouteDescription(id) {
611
611
  try {
612
+ if (!id)
613
+ return undefined;
612
614
  return this.getVisibleRoutes().find(r => r.description.id === id)?.description;
613
615
  }
614
616
  catch (err) {
@@ -617,6 +619,8 @@ let RouteListService = (() => {
617
619
  }
618
620
  getRoute(id) {
619
621
  try {
622
+ if (!id)
623
+ return undefined;
620
624
  return this.getVisibleRoutes().find(r => r.description.id === id);
621
625
  }
622
626
  catch (err) {
@@ -339,6 +339,7 @@ let ActiveRidesService = (() => {
339
339
  return;
340
340
  const displayItem = {
341
341
  isUser: item.sessionId === this.session,
342
+ isCoach: item.isCoach,
342
343
  name: this.getName(item),
343
344
  diffDistance: this.getDistanceDiff(item),
344
345
  distance: this.getDistance(item),
@@ -682,6 +683,7 @@ let ActiveRidesService = (() => {
682
683
  if (!payload.user) {
683
684
  this.getRemoteActivityDetails(session);
684
685
  }
686
+ this.observer.emit('update', this.getDisplayProps());
685
687
  }
686
688
  catch (err) {
687
689
  this.logError(err, 'onActivityStartEvent');
@@ -759,6 +761,7 @@ let ActiveRidesService = (() => {
759
761
  this.logEvent({ message: 'group ride user joined', active: this.others.length + 1, activityId: this.activity?.id, route: this.activity.route?.title, routeHash: this.getRouteHash(), remote });
760
762
  }
761
763
  }
764
+ this.observer.emit('update', this.getDisplayProps());
762
765
  }
763
766
  catch (err) {
764
767
  this.logError(err, 'onActivityStartEvent');
@@ -781,8 +784,10 @@ let ActiveRidesService = (() => {
781
784
  if (!this.others?.length)
782
785
  return;
783
786
  const idx = this.others.findIndex(ar => ar.sessionId === session);
784
- if (idx !== -1)
785
- this.others.splice(idx, 1);
787
+ if (idx === -1)
788
+ return;
789
+ this.others.splice(idx, 1);
790
+ this.observer.emit('update', this.getDisplayProps());
786
791
  if (!this.isStarted)
787
792
  return;
788
793
  if (this.others.length === 0) {
@@ -44,6 +44,7 @@ import { Injectable } from "../../../base/decorators/Injection";
44
44
  import { useUnitConverter } from "../../../i18n";
45
45
  export const DB_VERSION = '5';
46
46
  export const DB_NAME = 'db';
47
+ const SHORT_RIDE_FILTER_CUTOFF = new Date('2026-08-28').getTime();
47
48
  let ActivitiesRepository = (() => {
48
49
  let _classDecorators = [Singleton];
49
50
  let _classDescriptor;
@@ -106,7 +107,7 @@ let ActivitiesRepository = (() => {
106
107
  this.write();
107
108
  }
108
109
  if (writeDetails) {
109
- this.writeDetails(activity);
110
+ await this.writeDetails(activity);
110
111
  }
111
112
  }
112
113
  migrate(activity, writeSummary = true) {
@@ -221,6 +222,16 @@ let ActivitiesRepository = (() => {
221
222
  }
222
223
  search(criteria) {
223
224
  let result = this.activities;
225
+ if (!this.isMobile()) {
226
+ result = result?.filter(ai => {
227
+ const { rideTime, startTime } = ai.summary;
228
+ if (rideTime < 1)
229
+ return false;
230
+ if (rideTime <= 30 && startTime < SHORT_RIDE_FILTER_CUTOFF)
231
+ return false;
232
+ return true;
233
+ });
234
+ }
224
235
  result = this.checkIdFilter(result, criteria);
225
236
  result = this.checkHashFilter(result, criteria);
226
237
  result = this.checkStartPosFilter(result, criteria);
@@ -472,7 +483,7 @@ let ActivitiesRepository = (() => {
472
483
  }
473
484
  async writeDetails(activity) {
474
485
  const name = activity.summary.name;
475
- this.getRepo().write(name, activity.details);
486
+ await this.getRepo().write(name, activity.details);
476
487
  }
477
488
  async scanForNewActivities() {
478
489
  try {
@@ -506,6 +517,13 @@ let ActivitiesRepository = (() => {
506
517
  getRouteList() {
507
518
  return useRouteList();
508
519
  }
520
+ isMobile() {
521
+ const { appInfo } = this.getBindings();
522
+ if (!appInfo) {
523
+ return false;
524
+ }
525
+ return appInfo?.getChannel() === 'mobile';
526
+ }
509
527
  getBindings() {
510
528
  return getBindings();
511
529
  }
@@ -251,7 +251,7 @@ let Activity = (() => {
251
251
  isRouteAvailable() {
252
252
  if (!this.details?.route)
253
253
  return false;
254
- if (this.details.routeType === 'Free-Ride')
254
+ if (this.details.routeType === 'Free-Ride' || this.details.routeType === 'None')
255
255
  return false;
256
256
  return this.getRouteList().getRouteDescription(this.details.route.id) !== undefined;
257
257
  }
@@ -274,7 +274,7 @@ let Activity = (() => {
274
274
  canStart() {
275
275
  if (!this.details?.route)
276
276
  return false;
277
- if (this.details.routeType === 'Free-Ride')
277
+ if (this.details.routeType === 'Free-Ride' || this.details.routeType === 'None')
278
278
  return false;
279
279
  if (this.details.routeType === 'Video') {
280
280
  const isOnline = this.getOnlineMonitoring().onlineStatus;
@@ -192,7 +192,7 @@ let ActivityRideService = (() => {
192
192
  this.disableDeviceHealthCheck();
193
193
  this.state = 'completed';
194
194
  this.updateActivityTime();
195
- this._save();
195
+ await this._save();
196
196
  this.emit('completed');
197
197
  this.tsPauseStart = undefined;
198
198
  delete this.tsStart;
@@ -530,7 +530,7 @@ let ActivityRideService = (() => {
530
530
  if (this.state !== 'completed') {
531
531
  await this.stop();
532
532
  }
533
- this.getRepo().delete(this.activity.id);
533
+ await this.getRepo().delete(this.activity.id);
534
534
  this.state = 'idle';
535
535
  }
536
536
  save() {
@@ -546,7 +546,7 @@ let ActivityRideService = (() => {
546
546
  let success = false;
547
547
  try {
548
548
  emit('start', success);
549
- await this._save();
549
+ this._save();
550
550
  success = await this._convertAndUpload();
551
551
  sleep(0).then(() => {
552
552
  delete this.saveObserver;
@@ -959,7 +959,7 @@ let ActivityRideService = (() => {
959
959
  await fs.writeFile(fileName, Buffer.from(data));
960
960
  this.activity.tcxFileName = fileName;
961
961
  }
962
- await this._save();
962
+ this._save();
963
963
  emit('save.done', format, true);
964
964
  return true;
965
965
  }
@@ -391,7 +391,7 @@ let DevicePairingService = (() => {
391
391
  if (this.isScanning())
392
392
  await this.stopScanning();
393
393
  }
394
- this.state.capabilities.forEach(c => c.connectState = 'waiting');
394
+ (this.state.capabilities ?? []).forEach(c => c.connectState = 'waiting');
395
395
  this.state.waiting = true;
396
396
  }
397
397
  getCapability(capability) {
@@ -1355,7 +1355,7 @@ let DevicePairingService = (() => {
1355
1355
  pauseAdapters(adapters, enforced = false) {
1356
1356
  if (!adapters)
1357
1357
  return;
1358
- const capabilities = this.state.capabilities;
1358
+ const capabilities = this.state.capabilities ?? [];
1359
1359
  capabilities.forEach(c => {
1360
1360
  if (!enforced && !c.selected && c.connectState === 'failed')
1361
1361
  return;
@@ -1366,7 +1366,7 @@ let DevicePairingService = (() => {
1366
1366
  resumeAdapters(adapters = this.state.adapters) {
1367
1367
  if (!adapters?.length)
1368
1368
  return;
1369
- const capabilities = this.state.capabilities;
1369
+ const capabilities = this.state.capabilities ?? [];
1370
1370
  capabilities.forEach(c => {
1371
1371
  if (!c.selected && c.connectState === 'failed')
1372
1372
  return;
@@ -18,8 +18,11 @@ export function isVirtualShiftingEnabled(mode) {
18
18
  export function getLoadButtonMode(mode) {
19
19
  if (!mode)
20
20
  return 'power';
21
- if (mode.isSIM() || mode.isResistance()) {
22
- return isVirtualShiftingEnabled(mode) ? 'gear' : 'hidden';
23
- }
24
- return 'power';
21
+ if (mode.isERG())
22
+ return 'power';
23
+ if (mode.isSIM() && isVirtualShiftingEnabled(mode))
24
+ return 'gear';
25
+ if (mode.isResistance())
26
+ return 'gear';
27
+ return 'hidden';
25
28
  }
@@ -36,6 +36,7 @@ import { EventLogger } from "gd-eventlog";
36
36
  import { Injectable, Singleton } from "../../base/decorators";
37
37
  import { IncyclistPageService } from "../../base/pages";
38
38
  import { useRideDisplay } from "../display";
39
+ import { useDeviceConfiguration } from "../../devices";
39
40
  import { sleep } from "../../utils/sleep";
40
41
  import { useOnlineStatusMonitoring } from "../../monitoring";
41
42
  import { useActivityRide } from "../../activities";
@@ -61,6 +62,7 @@ let RidePageService = (() => {
61
62
  let _getActivityRide_decorators;
62
63
  let _getUserSettings_decorators;
63
64
  let _getOnlineStatusMonitoring_decorators;
65
+ let _getDeviceConfiguration_decorators;
64
66
  var RidePageService = class extends _classSuper {
65
67
  static { _classThis = this; }
66
68
  static {
@@ -70,11 +72,13 @@ let RidePageService = (() => {
70
72
  _getActivityRide_decorators = [Injectable];
71
73
  _getUserSettings_decorators = [Injectable];
72
74
  _getOnlineStatusMonitoring_decorators = [Injectable];
75
+ _getDeviceConfiguration_decorators = [Injectable];
73
76
  __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);
74
77
  __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);
75
78
  __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);
76
79
  __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);
77
80
  __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);
81
+ __esDecorate(this, null, _getDeviceConfiguration_decorators, { kind: "method", name: "getDeviceConfiguration", static: false, private: false, access: { has: obj => "getDeviceConfiguration" in obj, get: obj => obj.getDeviceConfiguration }, metadata: _metadata }, null, _instanceExtraInitializers);
78
82
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
79
83
  RidePageService = _classThis = _classDescriptor.value;
80
84
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -89,10 +93,13 @@ let RidePageService = (() => {
89
93
  menuProps = null;
90
94
  isInitialized = false;
91
95
  startGateProps = null;
96
+ onDeviceModeChangedBound = this.onDeviceModeChanged.bind(this);
92
97
  prevRidesVisibleRows = DEFAULT_PREV_RIDES_VISIBLE_ROWS;
93
98
  prevRidesMode = 'list';
94
99
  gestureHintDismissed = false;
95
100
  prevRidesShown = false;
101
+ nearbyRiders;
102
+ nearbyRidersSubscribed = false;
96
103
  viewTransition = false;
97
104
  constructor() {
98
105
  super('RidePage');
@@ -134,12 +141,14 @@ let RidePageService = (() => {
134
141
  EventLogger.setGlobalConfig('page', pageLogName);
135
142
  this.gestureHintDismissed = false;
136
143
  this.prevRidesShown = false;
144
+ this.nearbyRidersSubscribed = false;
137
145
  super.openPage();
138
146
  try {
139
147
  const service = this.getRideDisplay();
140
148
  const registerAndStart = () => {
141
149
  this.registerHandlers(this.rideObserver, this.eventHandler);
142
150
  this.subscribeToWorkoutObserver();
151
+ this.getDeviceConfiguration().on('mode-changed', this.onDeviceModeChangedBound);
143
152
  const state = service.getState();
144
153
  if (state === 'Started' || state === 'Active' || state === 'Paused') {
145
154
  this.logError(new Error(`start() requested while ride is ${state}`), 'openPage');
@@ -184,6 +193,7 @@ let RidePageService = (() => {
184
193
  this.getRideDisplay().stop();
185
194
  this.unregisterHandlers(this.rideObserver, this.eventHandler);
186
195
  this.unsubscribeFromWorkoutObserver();
196
+ this.getDeviceConfiguration().off('mode-changed', this.onDeviceModeChangedBound);
187
197
  this.menuProps = null;
188
198
  this.isInitialized = false;
189
199
  super.closePage();
@@ -275,9 +285,11 @@ let RidePageService = (() => {
275
285
  onMenuOpen() {
276
286
  try {
277
287
  const state = this.getRideDisplay().getState();
288
+ const loadControl = this.getLoadControlProps();
289
+ const showRideSettings = !this.isRideType('Workout');
278
290
  this.menuProps = this.isWorkoutAttached()
279
- ? { showResume: state === 'Paused', ...this.getStepFlags() }
280
- : { showResume: state === 'Paused' };
291
+ ? { showResume: state === 'Paused', ...this.getStepFlags(), loadControl, showRideSettings }
292
+ : { showResume: state === 'Paused', loadControl, showRideSettings };
281
293
  this.updatePageDisplay();
282
294
  }
283
295
  catch (err) {
@@ -394,7 +406,7 @@ let RidePageService = (() => {
394
406
  const available = this.getCornerWidgetStates();
395
407
  if (available.length <= 1)
396
408
  return undefined;
397
- const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
409
+ const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'workout');
398
410
  return available.includes(current) ? current : available[0];
399
411
  }
400
412
  catch (err) {
@@ -411,7 +423,7 @@ let RidePageService = (() => {
411
423
  onToggleCornerWidget() {
412
424
  try {
413
425
  const available = this.getCornerWidgetStates();
414
- const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
426
+ const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'workout');
415
427
  const next = available[(available.indexOf(current) + 1) % available.length];
416
428
  this.getUserSettings().set(CORNER_WIDGET_SETTING_KEY, next);
417
429
  this.updatePageDisplay();
@@ -580,8 +592,7 @@ let RidePageService = (() => {
580
592
  const plainErgAdjustment = !this.getWorkoutRide().inUse() && this.getLoadButtonMode() === 'power';
581
593
  if (plainErgAdjustment) {
582
594
  const sign = deltaPct >= 0 ? 1 : -1;
583
- const nominal = Math.abs(deltaPct) === 1 ? 5 : 50;
584
- return this.getRideDisplay().adjustDevicePower(sign * nominal);
595
+ return this.getRideDisplay().adjustDevicePower(sign * this.getNominalErgWatts(deltaPct));
585
596
  }
586
597
  return deltaPct >= 0
587
598
  ? this.getWorkoutRide().powerUp(deltaPct)
@@ -761,6 +772,40 @@ let RidePageService = (() => {
761
772
  const wo = this.getWorkoutRide().getDashboardDisplayProperties();
762
773
  return { canStepBack: !!wo.canShowBackward, canStepForward: !!wo.canShowForward };
763
774
  }
775
+ getLoadControlProps() {
776
+ const mode = this.getLoadButtonMode();
777
+ if (mode === 'hidden')
778
+ return { visible: false };
779
+ if (mode === 'gear')
780
+ return { visible: true, label: 'Gear', buttons: { inc1: '+1', dec1: '-1', inc5: '+5', dec5: '-5' } };
781
+ if (this.isWorkoutAttached()) {
782
+ const buttons = this.getWorkoutRide().getDashboardDisplayProperties().loadButtons;
783
+ return { visible: true, label: 'Load', buttons };
784
+ }
785
+ const small = this.getNominalErgWatts(1);
786
+ const big = this.getNominalErgWatts(5);
787
+ return {
788
+ visible: true,
789
+ label: 'Load',
790
+ buttons: { inc1: `+${small}W`, dec1: `-${small}W`, inc5: `+${big}W`, dec5: `-${big}W` }
791
+ };
792
+ }
793
+ getNominalErgWatts(magnitude) {
794
+ return Math.abs(magnitude) === 1 ? 5 : 50;
795
+ }
796
+ onDeviceModeChanged() {
797
+ setTimeout(() => {
798
+ try {
799
+ if (!this.menuProps)
800
+ return;
801
+ this.menuProps = { ...this.menuProps, loadControl: this.getLoadControlProps() };
802
+ this.updatePageDisplay();
803
+ }
804
+ catch (err) {
805
+ this.logError(err, 'onDeviceModeChanged');
806
+ }
807
+ }, 0);
808
+ }
764
809
  getLastLogTime() {
765
810
  const logs = this.getActivityRide().getActivity()?.logs;
766
811
  if (!logs || logs.length === 0)
@@ -817,14 +862,18 @@ let RidePageService = (() => {
817
862
  }
818
863
  }
819
864
  buildPausedMenuProps() {
865
+ const loadControl = this.getLoadControlProps();
866
+ const showRideSettings = !this.isRideType('Workout');
820
867
  return this.isWorkoutAttached()
821
- ? { showResume: true, ...this.getStepFlags() }
822
- : { showResume: true };
868
+ ? { showResume: true, ...this.getStepFlags(), loadControl, showRideSettings }
869
+ : { showResume: true, loadControl, showRideSettings };
823
870
  }
824
871
  buildFinishedMenuProps() {
872
+ const loadControl = this.getLoadControlProps();
873
+ const showRideSettings = !this.isRideType('Workout');
825
874
  return this.isWorkoutAttached()
826
- ? { showResume: false, finished: true, canStepBack: false, canStepForward: false }
827
- : { showResume: false, finished: true };
875
+ ? { showResume: false, finished: true, canStepBack: false, canStepForward: false, loadControl, showRideSettings }
876
+ : { showResume: false, finished: true, loadControl, showRideSettings };
828
877
  }
829
878
  buildBaseDisplayProps() {
830
879
  const state = this.getRideDisplay().getState();
@@ -839,7 +888,8 @@ let RidePageService = (() => {
839
888
  gestureHint: this.buildGestureHint(startOverlayProps === null),
840
889
  loadIncrement: this.getLoadIncrement(),
841
890
  loadButtonMode: this.getLoadButtonMode(),
842
- prevRides: this.buildPrevRides()
891
+ prevRides: this.buildPrevRides(),
892
+ nearbyRiders: this.nearbyRiders
843
893
  };
844
894
  }
845
895
  buildPrevRides() {
@@ -902,8 +952,55 @@ let RidePageService = (() => {
902
952
  tsStart: row.tsStart
903
953
  };
904
954
  }
955
+ subscribeToNearbyRiders() {
956
+ try {
957
+ if (this.nearbyRidersSubscribed)
958
+ return;
959
+ const observer = this.getActiveRidesObserver();
960
+ if (!observer)
961
+ return;
962
+ observer.on('update', (rows) => {
963
+ this.nearbyRiders = { rows: (rows ?? []).map(row => this.mapNearbyRiderRow(row)) };
964
+ this.rideObserver?.emit('nearby-riders-update', this.nearbyRiders);
965
+ this.updatePageDisplay();
966
+ });
967
+ this.nearbyRidersSubscribed = true;
968
+ }
969
+ catch (err) {
970
+ this.logError(err, 'subscribeToNearbyRiders');
971
+ }
972
+ }
973
+ mapNearbyRiderRow(row) {
974
+ return {
975
+ isUser: !!row.isUser,
976
+ isPaused: !!row.isPaused,
977
+ isCoach: !!row.isCoach,
978
+ name: row.name,
979
+ distance: row.distance ?? { value: 0, unit: '' },
980
+ diffDistance: row.diffDistance ?? { value: 0, unit: '' },
981
+ power: row.power,
982
+ mpower: row.mpower,
983
+ speed: row.speed,
984
+ avatar: row.avatar,
985
+ backgroundColor: row.backgroundColor,
986
+ textColor: row.textColor,
987
+ lat: row.lat,
988
+ lng: row.lng
989
+ };
990
+ }
991
+ getActiveRidesObserver() {
992
+ try {
993
+ const modeService = this.getRideDisplay()?.getRideModeService?.();
994
+ return modeService?.getActiveRidesObserver?.();
995
+ }
996
+ catch (err) {
997
+ this.logError(err, 'getActiveRidesObserver');
998
+ return undefined;
999
+ }
1000
+ }
905
1001
  onDisplayStateUpdate(state) {
906
1002
  this.subscribeToWorkoutObserver();
1003
+ this.subscribeToNearbyRiders();
907
1004
  switch (state) {
908
1005
  case 'Paused':
909
1006
  this.menuProps = this.buildPausedMenuProps();
@@ -958,6 +1055,9 @@ let RidePageService = (() => {
958
1055
  getOnlineStatusMonitoring() {
959
1056
  return useOnlineStatusMonitoring();
960
1057
  }
1058
+ getDeviceConfiguration() {
1059
+ return useDeviceConfiguration();
1060
+ }
961
1061
  };
962
1062
  return RidePageService = _classThis;
963
1063
  })();
@@ -77,6 +77,7 @@ let RouteDisplayService = (() => {
77
77
  prevPowerTs;
78
78
  nearbyRiders;
79
79
  _startSettings;
80
+ activeRidesObserver;
80
81
  init(service) {
81
82
  try {
82
83
  super.init(service);
@@ -363,6 +364,7 @@ let RouteDisplayService = (() => {
363
364
  const session = this.getAppInfo().session;
364
365
  try {
365
366
  const observer = this.getActiveRides().init(session);
367
+ this.activeRidesObserver = observer;
366
368
  observer.on('update', (data) => {
367
369
  this.nearbyRiders = data.filter(ar => !ar.isUser);
368
370
  });
@@ -374,11 +376,15 @@ let RouteDisplayService = (() => {
374
376
  cleanupActiveRides() {
375
377
  try {
376
378
  this.getActiveRides().stop();
379
+ delete this.activeRidesObserver;
377
380
  }
378
381
  catch (err) {
379
382
  this.logError(err, 'cleanupActiveRides');
380
383
  }
381
384
  }
385
+ getActiveRidesObserver() {
386
+ return this.activeRidesObserver;
387
+ }
382
388
  savePosition(_startPos) {
383
389
  }
384
390
  getOriginalRoute() {
@@ -603,6 +603,8 @@ let RouteListService = (() => {
603
603
  }
604
604
  getRouteDescription(id) {
605
605
  try {
606
+ if (!id)
607
+ return undefined;
606
608
  return this.getVisibleRoutes().find(r => r.description.id === id)?.description;
607
609
  }
608
610
  catch (err) {
@@ -611,6 +613,8 @@ let RouteListService = (() => {
611
613
  }
612
614
  getRoute(id) {
613
615
  try {
616
+ if (!id)
617
+ return undefined;
614
618
  return this.getVisibleRoutes().find(r => r.description.id === id);
615
619
  }
616
620
  catch (err) {
@@ -78,6 +78,7 @@ export type ActiveRideListDisplayItem = {
78
78
  isUser?: boolean;
79
79
  avatar?: ActiveRideListAvatar;
80
80
  isPaused?: boolean;
81
+ isCoach?: boolean;
81
82
  name: string;
82
83
  distance?: number | {
83
84
  value: number;
@@ -55,6 +55,7 @@ export declare class ActivitiesRepository {
55
55
  protected logError(err: Error, fn: string, logProps?: any): void;
56
56
  protected getRepo(): JsonRepository;
57
57
  protected getRouteList(): import("../../../routes").RouteListService;
58
+ protected isMobile(): boolean;
58
59
  protected getBindings(): import("../../../api").IncyclistBindings;
59
60
  protected getUnitConverter(): import("../../../i18n").UnitConverterService;
60
61
  }
@@ -1,8 +1,8 @@
1
1
  import { IncyclistPageService } from "../../base/pages";
2
2
  import { CurrentRideDisplayProps, CurrentRideState, IObserver, RideType } from "../../types";
3
- import { AnyRidePageDisplayProps, GPXRidePageDisplayProps, IRidePageService, PrevRidesRowProps, RideMenuProps, RidePageDisplayProps, StartGateProps, VideoRidePageDisplayProps, WorkoutDashboardLine, WorkoutGestureHint, WorkoutGraphActuals, WorkoutGraphPlan, WorkoutRideMenuProps, WorkoutRidePageDisplayProps, WorkoutUpcomingSteps } from "./types";
3
+ import { AnyRidePageDisplayProps, GPXRidePageDisplayProps, IRidePageService, PrevRidesRowProps, NearbyRiderRowProps, LoadControlButtonLabels, RideMenuProps, RidePageDisplayProps, StartGateProps, VideoRidePageDisplayProps, WorkoutDashboardLine, WorkoutGestureHint, WorkoutGraphActuals, WorkoutGraphPlan, WorkoutRideMenuProps, WorkoutRidePageDisplayProps, WorkoutUpcomingSteps } from "./types";
4
4
  import { ISecretBinding } from "../../api/secret";
5
- import { PrevRidesListDisplayProps } from "../../activities";
5
+ import { ActiveRideListDisplayItem, PrevRidesListDisplayProps } from "../../activities";
6
6
  import type { LoadButtonMode, PowerAdjustmentResult, StepCountdownTick, WorkoutDisplayProperties } from "../../workouts/ride/types";
7
7
  import type { Workout } from "../../workouts/base/model";
8
8
  import type { StepDefinition } from "../../workouts/base/model/types";
@@ -16,10 +16,13 @@ export declare class RidePageService extends IncyclistPageService implements IRi
16
16
  protected menuProps: RideMenuProps | null;
17
17
  protected isInitialized: boolean;
18
18
  protected startGateProps: StartGateProps | null;
19
+ protected onDeviceModeChangedBound: any;
19
20
  protected prevRidesVisibleRows: number;
20
21
  protected prevRidesMode: 'condensed' | 'list';
21
22
  protected gestureHintDismissed: boolean;
22
23
  protected prevRidesShown: boolean;
24
+ protected nearbyRiders?: RidePageDisplayProps['nearbyRiders'];
25
+ protected nearbyRidersSubscribed: boolean;
23
26
  protected viewTransition: boolean;
24
27
  constructor();
25
28
  initPage(): Promise<RideType | undefined>;
@@ -91,6 +94,13 @@ export declare class RidePageService extends IncyclistPageService implements IRi
91
94
  canStepBack: boolean;
92
95
  canStepForward: boolean;
93
96
  };
97
+ protected getLoadControlProps(): {
98
+ visible: boolean;
99
+ label?: 'Load' | 'Gear';
100
+ buttons?: LoadControlButtonLabels;
101
+ };
102
+ protected getNominalErgWatts(magnitude: number): number;
103
+ protected onDeviceModeChanged(): void;
94
104
  protected getLastLogTime(): number;
95
105
  protected getElapsedActivityTime(): number;
96
106
  protected getEmptyWorkoutDisplayProps(): WorkoutRidePageDisplayProps;
@@ -115,11 +125,17 @@ export declare class RidePageService extends IncyclistPageService implements IRi
115
125
  rows: PrevRidesRowProps[];
116
126
  hasMore: boolean;
117
127
  };
128
+ nearbyRiders: {
129
+ rows: NearbyRiderRowProps[];
130
+ };
118
131
  };
119
132
  protected buildPrevRides(): RidePageDisplayProps['prevRides'];
120
133
  protected onPrevRidesTick(): void;
121
134
  getPrevRidesRows(): PrevRidesRowProps[];
122
135
  protected mapPrevRidesRow(row: PrevRidesListDisplayProps): PrevRidesRowProps;
136
+ protected subscribeToNearbyRiders(): void;
137
+ protected mapNearbyRiderRow(row: ActiveRideListDisplayItem): NearbyRiderRowProps;
138
+ protected getActiveRidesObserver(): IObserver | undefined;
123
139
  protected onDisplayStateUpdate(state: CurrentRideState): void;
124
140
  protected updatePageDisplay(): void;
125
141
  protected registerHandlers(observer: IObserver | null | undefined, map: Record<string, any>): void;
@@ -131,5 +147,6 @@ export declare class RidePageService extends IncyclistPageService implements IRi
131
147
  protected getActivityRide(): import("../../activities").ActivityRideService;
132
148
  protected getUserSettings(): import("../../settings").UserSettingsService;
133
149
  protected getOnlineStatusMonitoring(): import("../../monitoring").OnlineStateMonitoringService;
150
+ protected getDeviceConfiguration(): import("../../devices").DeviceConfigurationService;
134
151
  }
135
152
  export declare const getRidePageService: () => IRidePageService;
@@ -5,15 +5,28 @@ import { CurrentPosition, CurrentRideState, GPXStartOverlayProps, RideViewType,
5
5
  import type { LoadButtonMode, PowerAdjustmentResult } from "../../workouts/ride/types";
6
6
  import type { WorkoutGraphPlanBar } from "../../workouts/base/graph/types";
7
7
  import type { Avatar } from "../../avatars";
8
+ import type { ActiveRideListAvatar } from "../../activities";
8
9
  export interface StartGateProps {
9
10
  title: string;
10
11
  body: string;
11
12
  }
13
+ export interface LoadControlButtonLabels {
14
+ inc1: string;
15
+ dec1: string;
16
+ inc5: string;
17
+ dec5: string;
18
+ }
12
19
  export interface RideMenuProps {
13
20
  showResume?: boolean;
14
21
  finished?: boolean;
15
22
  canStepBack?: boolean;
16
23
  canStepForward?: boolean;
24
+ loadControl?: {
25
+ visible: boolean;
26
+ label?: 'Load' | 'Gear';
27
+ buttons?: LoadControlButtonLabels;
28
+ };
29
+ showRideSettings?: boolean;
17
30
  }
18
31
  export interface RidePageDisplayProps {
19
32
  rideState: CurrentRideState;
@@ -36,6 +49,9 @@ export interface RidePageDisplayProps {
36
49
  rows: PrevRidesRowProps[];
37
50
  hasMore: boolean;
38
51
  };
52
+ nearbyRiders?: {
53
+ rows: NearbyRiderRowProps[];
54
+ };
39
55
  }
40
56
  export interface PrevRidesRowProps {
41
57
  position: number;
@@ -51,6 +67,31 @@ export interface PrevRidesRowProps {
51
67
  lng?: number;
52
68
  tsStart?: number;
53
69
  }
70
+ export interface NearbyRiderRowProps {
71
+ isUser: boolean;
72
+ isPaused: boolean;
73
+ isCoach: boolean;
74
+ name: string;
75
+ distance: {
76
+ value: number;
77
+ unit: string;
78
+ };
79
+ diffDistance: {
80
+ value: number;
81
+ unit: string;
82
+ };
83
+ power?: number;
84
+ mpower?: number;
85
+ speed?: {
86
+ value: number;
87
+ unit: string;
88
+ };
89
+ avatar: ActiveRideListAvatar;
90
+ backgroundColor?: string;
91
+ textColor?: string;
92
+ lat?: number;
93
+ lng?: number;
94
+ }
54
95
  export interface VideoRidePageDisplayProps extends RidePageDisplayProps {
55
96
  video?: VideoDisplayProps;
56
97
  videos?: VideoDisplayProps[];
@@ -139,6 +180,12 @@ export interface WorkoutDashboardLine {
139
180
  export type WorkoutRideMenuProps = RideMenuProps & {
140
181
  canStepBack: boolean;
141
182
  canStepForward: boolean;
183
+ loadControl: {
184
+ visible: boolean;
185
+ label?: 'Load' | 'Gear';
186
+ buttons?: LoadControlButtonLabels;
187
+ };
188
+ showRideSettings: boolean;
142
189
  };
143
190
  export interface WorkoutGestureHint {
144
191
  visible: boolean;
@@ -8,6 +8,7 @@ import { RouteSettings } from "../../routes/list/cards/types";
8
8
  import { ActiveRideListDisplayItem, ScreenShotInfo } from "../../activities";
9
9
  import { ActivityUpdate, CurrentActivityData } from "../../activities/ride/types";
10
10
  import { RoutePoint } from "../../types";
11
+ import { Observer } from "../../base/types";
11
12
  export declare class RouteDisplayService extends RideModeService {
12
13
  protected prevRequestSlope?: number;
13
14
  protected position?: CurrentPosition;
@@ -18,6 +19,7 @@ export declare class RouteDisplayService extends RideModeService {
18
19
  protected prevPowerTs?: number;
19
20
  protected nearbyRiders?: ActiveRideListDisplayItem[];
20
21
  protected _startSettings?: RouteSettings;
22
+ protected activeRidesObserver?: Observer;
21
23
  init(service: ICurrentRideService): void;
22
24
  getDeviceStartSettings(): {
23
25
  realityFactor: number;
@@ -60,6 +62,7 @@ export declare class RouteDisplayService extends RideModeService {
60
62
  protected checkFinishOptions(position: CurrentPosition): boolean;
61
63
  protected prepareActiveRides(): void;
62
64
  cleanupActiveRides(): void;
65
+ getActiveRidesObserver(): Observer | undefined;
63
66
  protected savePosition(_startPos?: number): void;
64
67
  getOriginalRoute(): Route;
65
68
  getCurrentRoute(): Route;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "incyclist-services",
3
- "version": "1.7.119",
3
+ "version": "1.7.121",
4
4
  "peerDependencies": {
5
5
  "gd-eventlog": "^0.1.27"
6
6
  },
7
7
  "dependencies": {
8
8
  "@garmin/fitsdk": "^21.213.0",
9
9
  "axios": "^1.19.0",
10
- "incyclist-devices": "^3.0.33",
10
+ "incyclist-devices": "^3.0.34",
11
11
  "promise.any": "^2.0.6",
12
12
  "semver": "^7.7.4",
13
13
  "tcx-builder": "^1.1.1",