incyclist-services 1.7.113 → 1.7.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/activities/ride/service.js +34 -33
- package/lib/cjs/ride/page/service.js +86 -9
- package/lib/esm/activities/ride/service.js +34 -33
- package/lib/esm/ride/page/service.js +86 -9
- package/lib/types/activities/ride/service.d.ts +1 -0
- package/lib/types/ride/page/service.d.ts +18 -3
- package/lib/types/ride/page/types.d.ts +25 -1
- package/package.json +1 -1
|
@@ -617,49 +617,50 @@ let ActivityRideService = (() => {
|
|
|
617
617
|
const avatar = (0, avatars_1.useAvatars)().get(key);
|
|
618
618
|
return { position, avatar, ...a };
|
|
619
619
|
});
|
|
620
|
-
|
|
621
|
-
const currentIdx = props.findIndex(a => a.title === 'current');
|
|
622
|
-
if (currentIdx < maxEntries - 1) {
|
|
623
|
-
const deleted = props.splice(maxEntries - 1);
|
|
624
|
-
props.push({ title: `+${deleted.length}`, tsStart: null, distanceGap: '', timeGap: '' });
|
|
625
|
-
}
|
|
626
|
-
else if (currentIdx > maxEntries - 1) {
|
|
627
|
-
if (currentIdx < props.length - 2) {
|
|
628
|
-
const deleted = props.splice(currentIdx + 1);
|
|
629
|
-
props.splice(maxEntries - 2, currentIdx - (maxEntries - 2));
|
|
630
|
-
props.push({ title: `+${deleted.length}`, tsStart: null, distanceGap: '', timeGap: '' });
|
|
631
|
-
}
|
|
632
|
-
else if (currentIdx === props.length - 2) {
|
|
633
|
-
props.splice(maxEntries - 2, props.length - maxEntries);
|
|
634
|
-
}
|
|
635
|
-
else {
|
|
636
|
-
const keep = maxEntries - 1;
|
|
637
|
-
const cut = props.length - maxEntries;
|
|
638
|
-
props.splice(keep, cut);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
else if (currentIdx === props.length - 2) {
|
|
642
|
-
props.splice(maxEntries - 2, props.length - maxEntries);
|
|
643
|
-
}
|
|
644
|
-
else if (currentIdx === maxEntries - 1) {
|
|
645
|
-
const deleted = props.splice(currentIdx + 1, props.length - currentIdx);
|
|
646
|
-
props.push({ title: `+${deleted.length}`, tsStart: null, distanceGap: '', timeGap: '' });
|
|
647
|
-
props.splice(currentIdx - 1, 1);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
620
|
+
const selected = this.selectPrevRidesRows(props, maxEntries);
|
|
650
621
|
let logInfo = '';
|
|
651
622
|
try {
|
|
652
623
|
const buildLog = (pr) => {
|
|
653
624
|
const distanceGap = typeof pr.distance === 'object' ? `${pr.distance?.value}${pr.distance?.unit}` : pr.distance;
|
|
654
625
|
return `${pr.position}:${pr.avatar?.shirt}-${pr.avatar?.helmet}:${pr.title}:${pr.timeGap}:${distanceGap}`;
|
|
655
626
|
};
|
|
656
|
-
logInfo = `(${
|
|
657
|
-
+
|
|
627
|
+
logInfo = `(${selected.length}/${prevRides.length})`
|
|
628
|
+
+ selected.map(buildLog).join(',');
|
|
658
629
|
}
|
|
659
630
|
catch {
|
|
660
631
|
}
|
|
661
632
|
this.logEvent({ message: 'PrevRides', prevRides: logInfo });
|
|
662
|
-
return
|
|
633
|
+
return selected;
|
|
634
|
+
}
|
|
635
|
+
selectPrevRidesRows(props, maxEntries) {
|
|
636
|
+
if (props.length <= maxEntries)
|
|
637
|
+
return props;
|
|
638
|
+
const currentIdx = props.findIndex(a => a.title === 'current');
|
|
639
|
+
const selectedIdx = new Set();
|
|
640
|
+
if (maxEntries >= 3) {
|
|
641
|
+
selectedIdx.add(0);
|
|
642
|
+
selectedIdx.add(props.length - 1);
|
|
643
|
+
}
|
|
644
|
+
if (currentIdx >= 0)
|
|
645
|
+
selectedIdx.add(currentIdx);
|
|
646
|
+
let above = currentIdx - 1;
|
|
647
|
+
let below = currentIdx + 1;
|
|
648
|
+
let takeAbove = true;
|
|
649
|
+
while (selectedIdx.size < maxEntries && (above >= 0 || below < props.length)) {
|
|
650
|
+
const canAbove = above >= 0;
|
|
651
|
+
const canBelow = below < props.length;
|
|
652
|
+
const useAbove = canAbove && (takeAbove || !canBelow);
|
|
653
|
+
if (useAbove) {
|
|
654
|
+
selectedIdx.add(above);
|
|
655
|
+
above--;
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
selectedIdx.add(below);
|
|
659
|
+
below++;
|
|
660
|
+
}
|
|
661
|
+
takeAbove = !takeAbove;
|
|
662
|
+
}
|
|
663
|
+
return Array.from(selectedIdx).sort((a, b) => a - b).map(i => props[i]);
|
|
663
664
|
}
|
|
664
665
|
getPrevActivityLog(ai, current) {
|
|
665
666
|
try {
|
|
@@ -51,6 +51,7 @@ 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
53
|
const CORNER_WIDGET_SETTING_KEY = 'preferences.workouts.rideCornerWidget';
|
|
54
|
+
const DEFAULT_PREV_RIDES_VISIBLE_ROWS = 1;
|
|
54
55
|
let RidePageService = (() => {
|
|
55
56
|
let _classDecorators = [decorators_1.Singleton];
|
|
56
57
|
let _classDescriptor;
|
|
@@ -91,6 +92,8 @@ let RidePageService = (() => {
|
|
|
91
92
|
menuProps = null;
|
|
92
93
|
isInitialized = false;
|
|
93
94
|
startGateProps = null;
|
|
95
|
+
prevRidesVisibleRows = DEFAULT_PREV_RIDES_VISIBLE_ROWS;
|
|
96
|
+
prevRidesMode = 'list';
|
|
94
97
|
gestureHintDismissed = false;
|
|
95
98
|
viewTransition = false;
|
|
96
99
|
constructor() {
|
|
@@ -344,7 +347,7 @@ let RidePageService = (() => {
|
|
|
344
347
|
const base = this.buildBaseDisplayProps();
|
|
345
348
|
const displayProps = {
|
|
346
349
|
...base,
|
|
347
|
-
...this.buildWorkoutOverlayProps(props),
|
|
350
|
+
...this.buildWorkoutOverlayProps(props, base.prevRides),
|
|
348
351
|
video: props.video,
|
|
349
352
|
videos: props.videos,
|
|
350
353
|
route: props.route
|
|
@@ -356,7 +359,7 @@ let RidePageService = (() => {
|
|
|
356
359
|
const base = this.buildBaseDisplayProps();
|
|
357
360
|
const displayProps = {
|
|
358
361
|
...base,
|
|
359
|
-
...this.buildWorkoutOverlayProps(props),
|
|
362
|
+
...this.buildWorkoutOverlayProps(props, base.prevRides),
|
|
360
363
|
rideView: props.rideView,
|
|
361
364
|
route: props.route,
|
|
362
365
|
displayObserver: props.displayObserver,
|
|
@@ -365,8 +368,8 @@ let RidePageService = (() => {
|
|
|
365
368
|
};
|
|
366
369
|
return displayProps;
|
|
367
370
|
}
|
|
368
|
-
buildWorkoutOverlayProps(props) {
|
|
369
|
-
const cornerWidget = this.getCornerWidget();
|
|
371
|
+
buildWorkoutOverlayProps(props, prevRides) {
|
|
372
|
+
const cornerWidget = this.getCornerWidget(prevRides);
|
|
370
373
|
if (!this.isWorkoutAttached() || !props?.showWorkout)
|
|
371
374
|
return { workoutAttached: false, cornerWidget };
|
|
372
375
|
try {
|
|
@@ -385,21 +388,32 @@ let RidePageService = (() => {
|
|
|
385
388
|
return { workoutAttached: false, cornerWidget };
|
|
386
389
|
}
|
|
387
390
|
}
|
|
388
|
-
getCornerWidget() {
|
|
391
|
+
getCornerWidget(prevRides) {
|
|
389
392
|
try {
|
|
390
|
-
|
|
393
|
+
const available = this.getCornerWidgetStates(prevRides);
|
|
394
|
+
if (available.length <= 1)
|
|
391
395
|
return undefined;
|
|
392
|
-
|
|
396
|
+
const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
|
|
397
|
+
return available.includes(current) ? current : available[0];
|
|
393
398
|
}
|
|
394
399
|
catch (err) {
|
|
395
400
|
this.logError(err, 'getCornerWidget');
|
|
396
401
|
return undefined;
|
|
397
402
|
}
|
|
398
403
|
}
|
|
404
|
+
getCornerWidgetStates(prevRides) {
|
|
405
|
+
const states = ['elevation'];
|
|
406
|
+
if (this.isWorkoutAttached())
|
|
407
|
+
states.push('workout');
|
|
408
|
+
if (!!prevRides && prevRides.mode !== 'hidden')
|
|
409
|
+
states.push('prevRides');
|
|
410
|
+
return states;
|
|
411
|
+
}
|
|
399
412
|
onToggleCornerWidget() {
|
|
400
413
|
try {
|
|
414
|
+
const available = this.getCornerWidgetStates(this.buildPrevRides());
|
|
401
415
|
const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
|
|
402
|
-
const next = current
|
|
416
|
+
const next = available[(available.indexOf(current) + 1) % available.length];
|
|
403
417
|
this.getUserSettings().set(CORNER_WIDGET_SETTING_KEY, next);
|
|
404
418
|
this.updatePageDisplay();
|
|
405
419
|
}
|
|
@@ -407,6 +421,30 @@ let RidePageService = (() => {
|
|
|
407
421
|
this.logError(err, 'onToggleCornerWidget');
|
|
408
422
|
}
|
|
409
423
|
}
|
|
424
|
+
onExpandPrevRides() {
|
|
425
|
+
this.setPrevRidesMode('list');
|
|
426
|
+
}
|
|
427
|
+
onCollapsePrevRides() {
|
|
428
|
+
this.setPrevRidesMode('condensed');
|
|
429
|
+
}
|
|
430
|
+
setPrevRidesVisibleRows(n) {
|
|
431
|
+
try {
|
|
432
|
+
this.prevRidesVisibleRows = n;
|
|
433
|
+
this.updatePageDisplay();
|
|
434
|
+
}
|
|
435
|
+
catch (err) {
|
|
436
|
+
this.logError(err, 'setPrevRidesVisibleRows');
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
setPrevRidesMode(mode) {
|
|
440
|
+
try {
|
|
441
|
+
this.prevRidesMode = mode;
|
|
442
|
+
this.updatePageDisplay();
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
this.logError(err, 'setPrevRidesMode');
|
|
446
|
+
}
|
|
447
|
+
}
|
|
410
448
|
async checkSecretValidity() {
|
|
411
449
|
if (this.getBindings().appInfo?.getChannel() === 'mobile') {
|
|
412
450
|
const secretsStatus = this.getSecretBinding()?.getSecretsStatus?.();
|
|
@@ -794,7 +832,46 @@ let RidePageService = (() => {
|
|
|
794
832
|
startGateProps: this.startGateProps,
|
|
795
833
|
gestureHint: this.buildGestureHint(startOverlayProps === null),
|
|
796
834
|
loadIncrement: this.getLoadIncrement(),
|
|
797
|
-
loadButtonMode: this.getLoadButtonMode()
|
|
835
|
+
loadButtonMode: this.getLoadButtonMode(),
|
|
836
|
+
prevRides: this.buildPrevRides()
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
buildPrevRides() {
|
|
840
|
+
try {
|
|
841
|
+
const rows = this.getActivityRide().getPrevRidesListDisplay(this.prevRidesVisibleRows) ?? [];
|
|
842
|
+
if (rows.length === 0)
|
|
843
|
+
return { mode: 'hidden', rows: [], hasMore: false };
|
|
844
|
+
const hasMore = (rows.at(-1)?.position ?? rows.length) > rows.length;
|
|
845
|
+
return {
|
|
846
|
+
mode: this.prevRidesMode,
|
|
847
|
+
rows: rows.map(row => this.mapPrevRidesRow(row)),
|
|
848
|
+
hasMore
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
catch (err) {
|
|
852
|
+
this.logError(err, 'buildPrevRides');
|
|
853
|
+
return { mode: 'hidden', rows: [], hasMore: false };
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
mapPrevRidesRow(row) {
|
|
857
|
+
const isCurrent = row.title === 'current';
|
|
858
|
+
const speed = typeof row.speed === 'object' ? row.speed?.value : row.speed;
|
|
859
|
+
const distanceGap = typeof row.distanceGap === 'object'
|
|
860
|
+
? `${row.distanceGap.value}${row.distanceGap.unit}`
|
|
861
|
+
: row.distanceGap;
|
|
862
|
+
return {
|
|
863
|
+
position: row.position ?? 0,
|
|
864
|
+
label: isCurrent ? 'You' : row.title,
|
|
865
|
+
timeGap: row.timeGap,
|
|
866
|
+
distanceGap,
|
|
867
|
+
isCurrent,
|
|
868
|
+
avatar: row.avatar,
|
|
869
|
+
speed,
|
|
870
|
+
power: row.power,
|
|
871
|
+
heartrate: row.heartrate,
|
|
872
|
+
lat: row.lat,
|
|
873
|
+
lng: row.lng,
|
|
874
|
+
tsStart: row.tsStart
|
|
798
875
|
};
|
|
799
876
|
}
|
|
800
877
|
onDisplayStateUpdate(state) {
|
|
@@ -611,49 +611,50 @@ let ActivityRideService = (() => {
|
|
|
611
611
|
const avatar = useAvatars().get(key);
|
|
612
612
|
return { position, avatar, ...a };
|
|
613
613
|
});
|
|
614
|
-
|
|
615
|
-
const currentIdx = props.findIndex(a => a.title === 'current');
|
|
616
|
-
if (currentIdx < maxEntries - 1) {
|
|
617
|
-
const deleted = props.splice(maxEntries - 1);
|
|
618
|
-
props.push({ title: `+${deleted.length}`, tsStart: null, distanceGap: '', timeGap: '' });
|
|
619
|
-
}
|
|
620
|
-
else if (currentIdx > maxEntries - 1) {
|
|
621
|
-
if (currentIdx < props.length - 2) {
|
|
622
|
-
const deleted = props.splice(currentIdx + 1);
|
|
623
|
-
props.splice(maxEntries - 2, currentIdx - (maxEntries - 2));
|
|
624
|
-
props.push({ title: `+${deleted.length}`, tsStart: null, distanceGap: '', timeGap: '' });
|
|
625
|
-
}
|
|
626
|
-
else if (currentIdx === props.length - 2) {
|
|
627
|
-
props.splice(maxEntries - 2, props.length - maxEntries);
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
const keep = maxEntries - 1;
|
|
631
|
-
const cut = props.length - maxEntries;
|
|
632
|
-
props.splice(keep, cut);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
else if (currentIdx === props.length - 2) {
|
|
636
|
-
props.splice(maxEntries - 2, props.length - maxEntries);
|
|
637
|
-
}
|
|
638
|
-
else if (currentIdx === maxEntries - 1) {
|
|
639
|
-
const deleted = props.splice(currentIdx + 1, props.length - currentIdx);
|
|
640
|
-
props.push({ title: `+${deleted.length}`, tsStart: null, distanceGap: '', timeGap: '' });
|
|
641
|
-
props.splice(currentIdx - 1, 1);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
614
|
+
const selected = this.selectPrevRidesRows(props, maxEntries);
|
|
644
615
|
let logInfo = '';
|
|
645
616
|
try {
|
|
646
617
|
const buildLog = (pr) => {
|
|
647
618
|
const distanceGap = typeof pr.distance === 'object' ? `${pr.distance?.value}${pr.distance?.unit}` : pr.distance;
|
|
648
619
|
return `${pr.position}:${pr.avatar?.shirt}-${pr.avatar?.helmet}:${pr.title}:${pr.timeGap}:${distanceGap}`;
|
|
649
620
|
};
|
|
650
|
-
logInfo = `(${
|
|
651
|
-
+
|
|
621
|
+
logInfo = `(${selected.length}/${prevRides.length})`
|
|
622
|
+
+ selected.map(buildLog).join(',');
|
|
652
623
|
}
|
|
653
624
|
catch {
|
|
654
625
|
}
|
|
655
626
|
this.logEvent({ message: 'PrevRides', prevRides: logInfo });
|
|
656
|
-
return
|
|
627
|
+
return selected;
|
|
628
|
+
}
|
|
629
|
+
selectPrevRidesRows(props, maxEntries) {
|
|
630
|
+
if (props.length <= maxEntries)
|
|
631
|
+
return props;
|
|
632
|
+
const currentIdx = props.findIndex(a => a.title === 'current');
|
|
633
|
+
const selectedIdx = new Set();
|
|
634
|
+
if (maxEntries >= 3) {
|
|
635
|
+
selectedIdx.add(0);
|
|
636
|
+
selectedIdx.add(props.length - 1);
|
|
637
|
+
}
|
|
638
|
+
if (currentIdx >= 0)
|
|
639
|
+
selectedIdx.add(currentIdx);
|
|
640
|
+
let above = currentIdx - 1;
|
|
641
|
+
let below = currentIdx + 1;
|
|
642
|
+
let takeAbove = true;
|
|
643
|
+
while (selectedIdx.size < maxEntries && (above >= 0 || below < props.length)) {
|
|
644
|
+
const canAbove = above >= 0;
|
|
645
|
+
const canBelow = below < props.length;
|
|
646
|
+
const useAbove = canAbove && (takeAbove || !canBelow);
|
|
647
|
+
if (useAbove) {
|
|
648
|
+
selectedIdx.add(above);
|
|
649
|
+
above--;
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
selectedIdx.add(below);
|
|
653
|
+
below++;
|
|
654
|
+
}
|
|
655
|
+
takeAbove = !takeAbove;
|
|
656
|
+
}
|
|
657
|
+
return Array.from(selectedIdx).sort((a, b) => a - b).map(i => props[i]);
|
|
657
658
|
}
|
|
658
659
|
getPrevActivityLog(ai, current) {
|
|
659
660
|
try {
|
|
@@ -48,6 +48,7 @@ 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
50
|
const CORNER_WIDGET_SETTING_KEY = 'preferences.workouts.rideCornerWidget';
|
|
51
|
+
const DEFAULT_PREV_RIDES_VISIBLE_ROWS = 1;
|
|
51
52
|
let RidePageService = (() => {
|
|
52
53
|
let _classDecorators = [Singleton];
|
|
53
54
|
let _classDescriptor;
|
|
@@ -88,6 +89,8 @@ let RidePageService = (() => {
|
|
|
88
89
|
menuProps = null;
|
|
89
90
|
isInitialized = false;
|
|
90
91
|
startGateProps = null;
|
|
92
|
+
prevRidesVisibleRows = DEFAULT_PREV_RIDES_VISIBLE_ROWS;
|
|
93
|
+
prevRidesMode = 'list';
|
|
91
94
|
gestureHintDismissed = false;
|
|
92
95
|
viewTransition = false;
|
|
93
96
|
constructor() {
|
|
@@ -341,7 +344,7 @@ let RidePageService = (() => {
|
|
|
341
344
|
const base = this.buildBaseDisplayProps();
|
|
342
345
|
const displayProps = {
|
|
343
346
|
...base,
|
|
344
|
-
...this.buildWorkoutOverlayProps(props),
|
|
347
|
+
...this.buildWorkoutOverlayProps(props, base.prevRides),
|
|
345
348
|
video: props.video,
|
|
346
349
|
videos: props.videos,
|
|
347
350
|
route: props.route
|
|
@@ -353,7 +356,7 @@ let RidePageService = (() => {
|
|
|
353
356
|
const base = this.buildBaseDisplayProps();
|
|
354
357
|
const displayProps = {
|
|
355
358
|
...base,
|
|
356
|
-
...this.buildWorkoutOverlayProps(props),
|
|
359
|
+
...this.buildWorkoutOverlayProps(props, base.prevRides),
|
|
357
360
|
rideView: props.rideView,
|
|
358
361
|
route: props.route,
|
|
359
362
|
displayObserver: props.displayObserver,
|
|
@@ -362,8 +365,8 @@ let RidePageService = (() => {
|
|
|
362
365
|
};
|
|
363
366
|
return displayProps;
|
|
364
367
|
}
|
|
365
|
-
buildWorkoutOverlayProps(props) {
|
|
366
|
-
const cornerWidget = this.getCornerWidget();
|
|
368
|
+
buildWorkoutOverlayProps(props, prevRides) {
|
|
369
|
+
const cornerWidget = this.getCornerWidget(prevRides);
|
|
367
370
|
if (!this.isWorkoutAttached() || !props?.showWorkout)
|
|
368
371
|
return { workoutAttached: false, cornerWidget };
|
|
369
372
|
try {
|
|
@@ -382,21 +385,32 @@ let RidePageService = (() => {
|
|
|
382
385
|
return { workoutAttached: false, cornerWidget };
|
|
383
386
|
}
|
|
384
387
|
}
|
|
385
|
-
getCornerWidget() {
|
|
388
|
+
getCornerWidget(prevRides) {
|
|
386
389
|
try {
|
|
387
|
-
|
|
390
|
+
const available = this.getCornerWidgetStates(prevRides);
|
|
391
|
+
if (available.length <= 1)
|
|
388
392
|
return undefined;
|
|
389
|
-
|
|
393
|
+
const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
|
|
394
|
+
return available.includes(current) ? current : available[0];
|
|
390
395
|
}
|
|
391
396
|
catch (err) {
|
|
392
397
|
this.logError(err, 'getCornerWidget');
|
|
393
398
|
return undefined;
|
|
394
399
|
}
|
|
395
400
|
}
|
|
401
|
+
getCornerWidgetStates(prevRides) {
|
|
402
|
+
const states = ['elevation'];
|
|
403
|
+
if (this.isWorkoutAttached())
|
|
404
|
+
states.push('workout');
|
|
405
|
+
if (!!prevRides && prevRides.mode !== 'hidden')
|
|
406
|
+
states.push('prevRides');
|
|
407
|
+
return states;
|
|
408
|
+
}
|
|
396
409
|
onToggleCornerWidget() {
|
|
397
410
|
try {
|
|
411
|
+
const available = this.getCornerWidgetStates(this.buildPrevRides());
|
|
398
412
|
const current = this.getUserSettings().get(CORNER_WIDGET_SETTING_KEY, 'elevation');
|
|
399
|
-
const next = current
|
|
413
|
+
const next = available[(available.indexOf(current) + 1) % available.length];
|
|
400
414
|
this.getUserSettings().set(CORNER_WIDGET_SETTING_KEY, next);
|
|
401
415
|
this.updatePageDisplay();
|
|
402
416
|
}
|
|
@@ -404,6 +418,30 @@ let RidePageService = (() => {
|
|
|
404
418
|
this.logError(err, 'onToggleCornerWidget');
|
|
405
419
|
}
|
|
406
420
|
}
|
|
421
|
+
onExpandPrevRides() {
|
|
422
|
+
this.setPrevRidesMode('list');
|
|
423
|
+
}
|
|
424
|
+
onCollapsePrevRides() {
|
|
425
|
+
this.setPrevRidesMode('condensed');
|
|
426
|
+
}
|
|
427
|
+
setPrevRidesVisibleRows(n) {
|
|
428
|
+
try {
|
|
429
|
+
this.prevRidesVisibleRows = n;
|
|
430
|
+
this.updatePageDisplay();
|
|
431
|
+
}
|
|
432
|
+
catch (err) {
|
|
433
|
+
this.logError(err, 'setPrevRidesVisibleRows');
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
setPrevRidesMode(mode) {
|
|
437
|
+
try {
|
|
438
|
+
this.prevRidesMode = mode;
|
|
439
|
+
this.updatePageDisplay();
|
|
440
|
+
}
|
|
441
|
+
catch (err) {
|
|
442
|
+
this.logError(err, 'setPrevRidesMode');
|
|
443
|
+
}
|
|
444
|
+
}
|
|
407
445
|
async checkSecretValidity() {
|
|
408
446
|
if (this.getBindings().appInfo?.getChannel() === 'mobile') {
|
|
409
447
|
const secretsStatus = this.getSecretBinding()?.getSecretsStatus?.();
|
|
@@ -791,7 +829,46 @@ let RidePageService = (() => {
|
|
|
791
829
|
startGateProps: this.startGateProps,
|
|
792
830
|
gestureHint: this.buildGestureHint(startOverlayProps === null),
|
|
793
831
|
loadIncrement: this.getLoadIncrement(),
|
|
794
|
-
loadButtonMode: this.getLoadButtonMode()
|
|
832
|
+
loadButtonMode: this.getLoadButtonMode(),
|
|
833
|
+
prevRides: this.buildPrevRides()
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
buildPrevRides() {
|
|
837
|
+
try {
|
|
838
|
+
const rows = this.getActivityRide().getPrevRidesListDisplay(this.prevRidesVisibleRows) ?? [];
|
|
839
|
+
if (rows.length === 0)
|
|
840
|
+
return { mode: 'hidden', rows: [], hasMore: false };
|
|
841
|
+
const hasMore = (rows.at(-1)?.position ?? rows.length) > rows.length;
|
|
842
|
+
return {
|
|
843
|
+
mode: this.prevRidesMode,
|
|
844
|
+
rows: rows.map(row => this.mapPrevRidesRow(row)),
|
|
845
|
+
hasMore
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
catch (err) {
|
|
849
|
+
this.logError(err, 'buildPrevRides');
|
|
850
|
+
return { mode: 'hidden', rows: [], hasMore: false };
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
mapPrevRidesRow(row) {
|
|
854
|
+
const isCurrent = row.title === 'current';
|
|
855
|
+
const speed = typeof row.speed === 'object' ? row.speed?.value : row.speed;
|
|
856
|
+
const distanceGap = typeof row.distanceGap === 'object'
|
|
857
|
+
? `${row.distanceGap.value}${row.distanceGap.unit}`
|
|
858
|
+
: row.distanceGap;
|
|
859
|
+
return {
|
|
860
|
+
position: row.position ?? 0,
|
|
861
|
+
label: isCurrent ? 'You' : row.title,
|
|
862
|
+
timeGap: row.timeGap,
|
|
863
|
+
distanceGap,
|
|
864
|
+
isCurrent,
|
|
865
|
+
avatar: row.avatar,
|
|
866
|
+
speed,
|
|
867
|
+
power: row.power,
|
|
868
|
+
heartrate: row.heartrate,
|
|
869
|
+
lat: row.lat,
|
|
870
|
+
lng: row.lng,
|
|
871
|
+
tsStart: row.tsStart
|
|
795
872
|
};
|
|
796
873
|
}
|
|
797
874
|
onDisplayStateUpdate(state) {
|
|
@@ -158,6 +158,7 @@ export declare class ActivityRideService extends IncyclistService {
|
|
|
158
158
|
onRouteUpdate(points: Array<RoutePoint>): void;
|
|
159
159
|
getPrevRideStats(current: ActivityLogRecord): PastActivityInfo;
|
|
160
160
|
getPrevRidesListDisplay(maxEntries?: number): Array<PrevRidesListDisplayProps>;
|
|
161
|
+
protected selectPrevRidesRows(props: Array<PrevRidesListDisplayProps>, maxEntries: number): Array<PrevRidesListDisplayProps>;
|
|
161
162
|
protected getPrevActivityLog(ai: ActivityInfo, current: ActivityLogRecord): PastActivityLogEntry | null;
|
|
162
163
|
protected getRecordWithSameOrBiggerTime(logs: any, current: any): any;
|
|
163
164
|
protected calculateDistanceGap(ai: ActivityInfo, res: any, current: any): {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { IncyclistPageService } from "../../base/pages";
|
|
2
2
|
import { CurrentRideDisplayProps, CurrentRideState, IObserver, RideType } from "../../types";
|
|
3
|
-
import { AnyRidePageDisplayProps, GPXRidePageDisplayProps, IRidePageService, RideMenuProps, RidePageDisplayProps, StartGateProps, VideoRidePageDisplayProps, WorkoutDashboardLine, WorkoutGestureHint, WorkoutGraphActuals, WorkoutGraphPlan, WorkoutRideMenuProps, WorkoutRidePageDisplayProps, WorkoutUpcomingSteps } from "./types";
|
|
3
|
+
import { AnyRidePageDisplayProps, GPXRidePageDisplayProps, IRidePageService, PrevRidesRowProps, 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
6
|
import type { LoadButtonMode, PowerAdjustmentResult, WorkoutDisplayProperties } from "../../workouts/ride/types";
|
|
6
7
|
import type { Workout } from "../../workouts/base/model";
|
|
7
8
|
import type { StepDefinition } from "../../workouts/base/model/types";
|
|
@@ -15,6 +16,8 @@ export declare class RidePageService extends IncyclistPageService implements IRi
|
|
|
15
16
|
protected menuProps: RideMenuProps | null;
|
|
16
17
|
protected isInitialized: boolean;
|
|
17
18
|
protected startGateProps: StartGateProps | null;
|
|
19
|
+
protected prevRidesVisibleRows: number;
|
|
20
|
+
protected prevRidesMode: 'condensed' | 'list';
|
|
18
21
|
protected gestureHintDismissed: boolean;
|
|
19
22
|
protected viewTransition: boolean;
|
|
20
23
|
constructor();
|
|
@@ -40,9 +43,14 @@ export declare class RidePageService extends IncyclistPageService implements IRi
|
|
|
40
43
|
protected getGPXRideDisplayProps(): GPXRidePageDisplayProps;
|
|
41
44
|
protected buildWorkoutOverlayProps(props: CurrentRideDisplayProps & {
|
|
42
45
|
showWorkout?: boolean;
|
|
43
|
-
}): Partial<RidePageDisplayProps>;
|
|
44
|
-
protected getCornerWidget(): 'elevation' | 'workout' | undefined;
|
|
46
|
+
}, prevRides: RidePageDisplayProps['prevRides']): Partial<RidePageDisplayProps>;
|
|
47
|
+
protected getCornerWidget(prevRides: RidePageDisplayProps['prevRides']): 'elevation' | 'workout' | 'prevRides' | undefined;
|
|
48
|
+
protected getCornerWidgetStates(prevRides: RidePageDisplayProps['prevRides']): Array<'elevation' | 'workout' | 'prevRides'>;
|
|
45
49
|
onToggleCornerWidget(): void;
|
|
50
|
+
onExpandPrevRides(): void;
|
|
51
|
+
onCollapsePrevRides(): void;
|
|
52
|
+
setPrevRidesVisibleRows(n: number): void;
|
|
53
|
+
setPrevRidesMode(mode: 'condensed' | 'list'): void;
|
|
46
54
|
protected checkSecretValidity(): Promise<void>;
|
|
47
55
|
protected showStartGate(): void;
|
|
48
56
|
protected hideStartgate(): void;
|
|
@@ -99,7 +107,14 @@ export declare class RidePageService extends IncyclistPageService implements IRi
|
|
|
99
107
|
gestureHint: WorkoutGestureHint;
|
|
100
108
|
loadIncrement: number;
|
|
101
109
|
loadButtonMode: LoadButtonMode;
|
|
110
|
+
prevRides: {
|
|
111
|
+
mode: "hidden" | "condensed" | "list";
|
|
112
|
+
rows: PrevRidesRowProps[];
|
|
113
|
+
hasMore: boolean;
|
|
114
|
+
};
|
|
102
115
|
};
|
|
116
|
+
protected buildPrevRides(): RidePageDisplayProps['prevRides'];
|
|
117
|
+
protected mapPrevRidesRow(row: PrevRidesListDisplayProps): PrevRidesRowProps;
|
|
103
118
|
protected onDisplayStateUpdate(state: CurrentRideState): void;
|
|
104
119
|
protected updatePageDisplay(): void;
|
|
105
120
|
protected registerHandlers(observer: IObserver | null | undefined, map: Record<string, any>): void;
|
|
@@ -4,6 +4,7 @@ import { IObserver, RideType } from "../../types";
|
|
|
4
4
|
import { CurrentPosition, CurrentRideState, GPXStartOverlayProps, RideViewType, StartOverlayProps, StreetViewEvent, VideoDisplayProps, VideoStartOverlayProps } from "../types";
|
|
5
5
|
import type { LoadButtonMode, PowerAdjustmentResult } from "../../workouts/ride/types";
|
|
6
6
|
import type { WorkoutGraphPlanBar } from "../../workouts/base/graph/types";
|
|
7
|
+
import type { Avatar } from "../../avatars";
|
|
7
8
|
export interface StartGateProps {
|
|
8
9
|
title: string;
|
|
9
10
|
body: string;
|
|
@@ -29,7 +30,26 @@ export interface RidePageDisplayProps {
|
|
|
29
30
|
loadIncrement?: number;
|
|
30
31
|
workoutAttached?: boolean;
|
|
31
32
|
loadButtonMode?: LoadButtonMode;
|
|
32
|
-
cornerWidget?: 'elevation' | 'workout';
|
|
33
|
+
cornerWidget?: 'elevation' | 'workout' | 'prevRides';
|
|
34
|
+
prevRides?: {
|
|
35
|
+
mode: 'hidden' | 'condensed' | 'list';
|
|
36
|
+
rows: PrevRidesRowProps[];
|
|
37
|
+
hasMore: boolean;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface PrevRidesRowProps {
|
|
41
|
+
position: number;
|
|
42
|
+
label: string;
|
|
43
|
+
timeGap: string;
|
|
44
|
+
distanceGap?: string;
|
|
45
|
+
isCurrent: boolean;
|
|
46
|
+
avatar?: Avatar;
|
|
47
|
+
speed?: number;
|
|
48
|
+
power?: number;
|
|
49
|
+
heartrate?: number;
|
|
50
|
+
lat?: number;
|
|
51
|
+
lng?: number;
|
|
52
|
+
tsStart?: number;
|
|
33
53
|
}
|
|
34
54
|
export interface VideoRidePageDisplayProps extends RidePageDisplayProps {
|
|
35
55
|
video?: VideoDisplayProps;
|
|
@@ -64,6 +84,8 @@ interface RidePageCallbacks {
|
|
|
64
84
|
dontShowAgain: boolean;
|
|
65
85
|
}): void;
|
|
66
86
|
onToggleCornerWidget(): void;
|
|
87
|
+
onExpandPrevRides?(): void;
|
|
88
|
+
onCollapsePrevRides?(): void;
|
|
67
89
|
}
|
|
68
90
|
export interface IRidePageService extends RidePageCallbacks, IPageService {
|
|
69
91
|
initPage(): Promise<RideType | undefined>;
|
|
@@ -75,6 +97,8 @@ export interface IRidePageService extends RidePageCallbacks, IPageService {
|
|
|
75
97
|
adjustLoad(deltaPct: number): PowerAdjustmentResult | undefined;
|
|
76
98
|
getLoadButtonMode(): LoadButtonMode;
|
|
77
99
|
isWorkoutAttached(): boolean;
|
|
100
|
+
setPrevRidesVisibleRows(n: number): void;
|
|
101
|
+
setPrevRidesMode(mode: 'condensed' | 'list'): void;
|
|
78
102
|
}
|
|
79
103
|
export interface WorkoutGraphPoint {
|
|
80
104
|
x: number;
|