incyclist-services 1.3.14 → 1.3.16
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/activities/base/model/index.d.ts +1 -0
- package/lib/activities/base/repo/db.d.ts +1 -0
- package/lib/activities/base/repo/db.js +24 -0
- package/lib/activities/base/repo/types.d.ts +1 -0
- package/lib/activities/base/utils/index.js +2 -1
- package/lib/activities/list/index.d.ts +1 -0
- package/lib/activities/list/index.js +1 -0
- package/lib/activities/list/service.d.ts +4 -1
- package/lib/activities/list/service.js +7 -0
- package/lib/activities/list/type.d.ts +6 -0
- package/lib/activities/list/type.js +2 -0
- package/lib/activities/list/types.d.ts +18 -0
- package/lib/activities/list/types.js +2 -0
- package/lib/activities/ride/service.d.ts +10 -1
- package/lib/activities/ride/service.js +127 -2
- package/lib/avatars/index.d.ts +2 -0
- package/lib/avatars/index.js +18 -0
- package/lib/avatars/service.d.ts +17 -0
- package/lib/avatars/service.js +139 -0
- package/lib/avatars/types.d.ts +9 -0
- package/lib/avatars/types.js +2 -0
- package/lib/devices/ride/service.d.ts +2 -0
- package/lib/devices/ride/service.js +35 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/routes/list/cards/RouteCard.d.ts +1 -0
- package/lib/workouts/ride/service.d.ts +4 -0
- package/lib/workouts/ride/service.js +28 -2
- package/package.json +2 -2
|
@@ -31,6 +31,7 @@ export declare class ActivitiesRepository {
|
|
|
31
31
|
protected loadSummaries(): Promise<void>;
|
|
32
32
|
protected buildFromLegacy(): Promise<void>;
|
|
33
33
|
protected listActivities(): Promise<Array<string>>;
|
|
34
|
+
protected fixMissingHash(): Promise<void>;
|
|
34
35
|
protected loadDetailsByName(name: string): Promise<ActivityDetails>;
|
|
35
36
|
protected loadDetails(name: string, id?: string): Promise<void>;
|
|
36
37
|
protected bulkAddActivities(names: Array<string>): Promise<void>;
|
|
@@ -148,6 +148,9 @@ let ActivitiesRepository = (() => {
|
|
|
148
148
|
if ((result === null || result === void 0 ? void 0 : result.length) > 0 && (criteria === null || criteria === void 0 ? void 0 : criteria.routeId)) {
|
|
149
149
|
result = result.filter(ai => ai.summary.routeId === criteria.routeId);
|
|
150
150
|
}
|
|
151
|
+
if ((result === null || result === void 0 ? void 0 : result.length) > 0 && (criteria === null || criteria === void 0 ? void 0 : criteria.routeHash)) {
|
|
152
|
+
result = result.filter(ai => ai.summary.routeHash === criteria.routeHash);
|
|
153
|
+
}
|
|
151
154
|
if ((result === null || result === void 0 ? void 0 : result.length) > 0 && (criteria === null || criteria === void 0 ? void 0 : criteria.startPos) !== undefined) {
|
|
152
155
|
result = result.filter(ai => ai.summary.startPos === criteria.startPos);
|
|
153
156
|
}
|
|
@@ -245,6 +248,7 @@ let ActivitiesRepository = (() => {
|
|
|
245
248
|
if (cnt !== this.activities.length) {
|
|
246
249
|
yield this.write(true);
|
|
247
250
|
}
|
|
251
|
+
yield this.fixMissingHash();
|
|
248
252
|
return;
|
|
249
253
|
}
|
|
250
254
|
yield this.buildFromLegacy();
|
|
@@ -266,6 +270,26 @@ let ActivitiesRepository = (() => {
|
|
|
266
270
|
return this.getRepo().list(['db']);
|
|
267
271
|
});
|
|
268
272
|
}
|
|
273
|
+
fixMissingHash() {
|
|
274
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
275
|
+
try {
|
|
276
|
+
const target = this.activities.filter(ai => ai.summary && ai.summary.routeHash === undefined);
|
|
277
|
+
if (!target || target.length === 0) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
this.logger.logEvent({ message: 'fixing missing hashes in activities', cnt: target.length });
|
|
281
|
+
const promises = [];
|
|
282
|
+
target.forEach(ai => {
|
|
283
|
+
promises.push(this.loadDetails(ai.summary.name).then(() => { var _a, _b; return ai.summary.routeHash = (_b = (_a = ai.details) === null || _a === void 0 ? void 0 : _a.route) === null || _b === void 0 ? void 0 : _b.hash; }));
|
|
284
|
+
});
|
|
285
|
+
yield Promise.allSettled(promises);
|
|
286
|
+
yield this.write(true);
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
this.logError(err, 'fixMissingHash');
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
269
293
|
loadDetailsByName(name) {
|
|
270
294
|
return __awaiter(this, void 0, void 0, function* () {
|
|
271
295
|
let details;
|
|
@@ -6,6 +6,7 @@ const buildSummary = (activity, proposedName) => {
|
|
|
6
6
|
const { id, title, route, screenshots, startTime: startTimeUTC, time: rideTime, distance, startPos, realityFactor = 100, links, laps } = activity;
|
|
7
7
|
const name = proposedName !== null && proposedName !== void 0 ? proposedName : activity.name;
|
|
8
8
|
const routeId = route === null || route === void 0 ? void 0 : route.id;
|
|
9
|
+
const routeHash = route === null || route === void 0 ? void 0 : route.hash;
|
|
9
10
|
const shots = screenshots !== null && screenshots !== void 0 ? screenshots : [];
|
|
10
11
|
const preview = (_a = shots.find(s => s.isHighlight)) !== null && _a !== void 0 ? _a : shots[0];
|
|
11
12
|
const previewImage = preview === null || preview === void 0 ? void 0 : preview.fileName;
|
|
@@ -17,7 +18,7 @@ const buildSummary = (activity, proposedName) => {
|
|
|
17
18
|
uploadStatus.push({ service: 'strava', status: 'success' });
|
|
18
19
|
}
|
|
19
20
|
return {
|
|
20
|
-
id, title, name, routeId, previewImage, startTime, rideTime, distance, startPos, realityFactor, uploadStatus, laps
|
|
21
|
+
id, title, name, routeId, routeHash, previewImage, startTime, rideTime, distance, startPos, realityFactor, uploadStatus, laps
|
|
21
22
|
};
|
|
22
23
|
};
|
|
23
24
|
exports.buildSummary = buildSummary;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IncyclistService } from "../../base/service";
|
|
2
2
|
import { Observer, PromiseObserver } from "../../base/types/observer";
|
|
3
|
-
import { ActivitiesRepository } from "../base";
|
|
3
|
+
import { ActivitiesRepository, ActivitySearchCriteria } from "../base";
|
|
4
4
|
import { ActivityInfo } from "../base/model";
|
|
5
5
|
export declare class ActivityListService extends IncyclistService {
|
|
6
6
|
protected preloadObserver: PromiseObserver<void>;
|
|
@@ -9,6 +9,9 @@ export declare class ActivityListService extends IncyclistService {
|
|
|
9
9
|
protected observer: Observer;
|
|
10
10
|
constructor();
|
|
11
11
|
preload(): PromiseObserver<void>;
|
|
12
|
+
getPastActivities(filter: ActivitySearchCriteria, props?: {
|
|
13
|
+
details: boolean;
|
|
14
|
+
}): Array<ActivityInfo>;
|
|
12
15
|
protected loadActivities(): Promise<void>;
|
|
13
16
|
protected getRepo(): ActivitiesRepository;
|
|
14
17
|
protected get activities(): Array<ActivityInfo>;
|
|
@@ -92,6 +92,13 @@ let ActivityListService = (() => {
|
|
|
92
92
|
}
|
|
93
93
|
return this.preloadObserver;
|
|
94
94
|
}
|
|
95
|
+
getPastActivities(filter, props) {
|
|
96
|
+
const activities = this.getRepo().search(filter);
|
|
97
|
+
if (props === null || props === void 0 ? void 0 : props.details) {
|
|
98
|
+
activities.forEach(ai => this.getRepo().getWithDetails(ai.summary.id));
|
|
99
|
+
}
|
|
100
|
+
return activities;
|
|
101
|
+
}
|
|
95
102
|
loadActivities() {
|
|
96
103
|
return __awaiter(this, void 0, void 0, function* () {
|
|
97
104
|
return new Promise(done => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type PastActivityLogEntry = {
|
|
2
|
+
routeHash?: string;
|
|
3
|
+
routeId?: string;
|
|
4
|
+
tsStart: number;
|
|
5
|
+
title: string;
|
|
6
|
+
time: number;
|
|
7
|
+
power: number;
|
|
8
|
+
heartrate?: number;
|
|
9
|
+
speed?: number;
|
|
10
|
+
distance: number;
|
|
11
|
+
routeDistance: number;
|
|
12
|
+
lap: number;
|
|
13
|
+
timeGap: string;
|
|
14
|
+
distanceGap: string;
|
|
15
|
+
lat?: number;
|
|
16
|
+
lng?: number;
|
|
17
|
+
};
|
|
18
|
+
export type PastActivityInfo = Array<PastActivityLogEntry | null>;
|
|
@@ -3,14 +3,16 @@ import { IncyclistService } from "../../base/service";
|
|
|
3
3
|
import { Observer, PromiseObserver } from "../../base/types/observer";
|
|
4
4
|
import { DeviceData } from "incyclist-devices";
|
|
5
5
|
import { ExtendedIncyclistCapability, HealthStatus } from "../../devices";
|
|
6
|
-
import { ActivitiesRepository, ActivityConverterFactory, ActivityDetails, ActivityLogRecord, ActivityRouteType, ScreenShotInfo } from "../base";
|
|
6
|
+
import { ActivitiesRepository, ActivityConverterFactory, ActivityDetails, ActivityInfo, ActivityLogRecord, ActivityRouteType, ScreenShotInfo } from "../base";
|
|
7
7
|
import { FreeRideStartSettings } from "../../routes/list/types";
|
|
8
|
+
import { RouteSettings } from "../../routes/list/cards/RouteCard";
|
|
8
9
|
import { RoutePoint } from "../../routes/base/types";
|
|
9
10
|
import { ActivityState } from "./types";
|
|
10
11
|
import { Route } from "../../routes/base/model/route";
|
|
11
12
|
import { ActivityStatsCalculator } from "./stats";
|
|
12
13
|
import { ActivityDuration } from "./duration";
|
|
13
14
|
import { ActivityUploadFactory } from "../upload";
|
|
15
|
+
import { PastActivityInfo, PastActivityLogEntry } from "../list";
|
|
14
16
|
export declare class ActivityRideService extends IncyclistService {
|
|
15
17
|
protected observer: Observer;
|
|
16
18
|
protected activity: ActivityDetails;
|
|
@@ -45,7 +47,10 @@ export declare class ActivityRideService extends IncyclistService {
|
|
|
45
47
|
elevationGainDisplay?: number;
|
|
46
48
|
elevation?: number;
|
|
47
49
|
isAutoResume?: boolean;
|
|
50
|
+
showPrev?: boolean;
|
|
48
51
|
dataState: Record<string, HealthStatus>;
|
|
52
|
+
prevRides?: Array<ActivityInfo>;
|
|
53
|
+
prevRidesLogs?: PastActivityInfo;
|
|
49
54
|
};
|
|
50
55
|
constructor();
|
|
51
56
|
init(id?: string): Observer;
|
|
@@ -63,8 +68,12 @@ export declare class ActivityRideService extends IncyclistService {
|
|
|
63
68
|
getObserver(): Observer;
|
|
64
69
|
addSceenshot(screenshot: ScreenShotInfo): void;
|
|
65
70
|
onRouteUpdate(points: Array<RoutePoint>): void;
|
|
71
|
+
getPrevRideStats(activities: Array<ActivityInfo>, current: ActivityLogRecord): PastActivityInfo;
|
|
72
|
+
protected getActivityLog(ai: ActivityInfo, current: ActivityLogRecord): PastActivityLogEntry | null;
|
|
73
|
+
protected getCurrentActivityLog(ai: ActivityInfo, current: ActivityLogRecord): PastActivityLogEntry | null;
|
|
66
74
|
protected onPositionUpdate(position: RoutePoint): void;
|
|
67
75
|
protected onDeviceData(data: DeviceData): void;
|
|
76
|
+
protected initPrevActivities(settings: RouteSettings): void;
|
|
68
77
|
protected onDeviceHealthUpdate(udid: string, status: HealthStatus, capabilities: Array<ExtendedIncyclistCapability>): void;
|
|
69
78
|
emit(eventName: string | symbol, ...args: any[]): boolean;
|
|
70
79
|
protected _save(): Promise<void>;
|
|
@@ -65,6 +65,7 @@ const stats_1 = require("./stats");
|
|
|
65
65
|
const duration_1 = require("./duration");
|
|
66
66
|
const upload_1 = require("../upload");
|
|
67
67
|
const api_1 = require("../../api");
|
|
68
|
+
const list_1 = require("../list");
|
|
68
69
|
const SAVE_INTERVAL = 5000;
|
|
69
70
|
let ActivityRideService = (() => {
|
|
70
71
|
let _classDecorators = [types_1.Singleton];
|
|
@@ -101,6 +102,13 @@ let ActivityRideService = (() => {
|
|
|
101
102
|
}));
|
|
102
103
|
}
|
|
103
104
|
this.activity = this.createActivity(id);
|
|
105
|
+
const settings = (0, routes_1.useRouteList)().getStartSettings();
|
|
106
|
+
if ((settings === null || settings === void 0 ? void 0 : settings.type) === 'Route') {
|
|
107
|
+
const rs = settings;
|
|
108
|
+
if (rs.showPrev) {
|
|
109
|
+
this.initPrevActivities(rs);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
104
112
|
this.durationCalculator = new duration_1.ActivityDuration(this.activity);
|
|
105
113
|
this.getDeviceRide().on('data', this.deviceDataHandler);
|
|
106
114
|
return observer;
|
|
@@ -337,6 +345,101 @@ let ActivityRideService = (() => {
|
|
|
337
345
|
this.current.route.details.distance = points[points.length - 1].routeDistance;
|
|
338
346
|
(0, route_1.addDetails)(this.current.route, this.current.route.details);
|
|
339
347
|
}
|
|
348
|
+
getPrevRideStats(activities, current) {
|
|
349
|
+
var _a;
|
|
350
|
+
if (!(activities === null || activities === void 0 ? void 0 : activities.length))
|
|
351
|
+
return [];
|
|
352
|
+
const logs = (_a = activities.map(ai => { return this.getActivityLog(ai, current); })) === null || _a === void 0 ? void 0 : _a.filter(a => a !== null);
|
|
353
|
+
if (logs.length === 0)
|
|
354
|
+
return logs;
|
|
355
|
+
logs.push(this.getCurrentActivityLog(activities[0], current));
|
|
356
|
+
const sorted = logs.sort((a, b) => b.distance - a.distance);
|
|
357
|
+
return sorted;
|
|
358
|
+
}
|
|
359
|
+
getActivityLog(ai, current) {
|
|
360
|
+
var _a, _b, _c;
|
|
361
|
+
try {
|
|
362
|
+
if (!ai.details) {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
const logs = ai.details.logs;
|
|
366
|
+
const totalDistance = ai.summary.distance;
|
|
367
|
+
if (current.distance > totalDistance)
|
|
368
|
+
return null;
|
|
369
|
+
let prev = undefined;
|
|
370
|
+
let res = logs.find((log, idx) => {
|
|
371
|
+
if (log.time >= current.time) {
|
|
372
|
+
if (idx > 0)
|
|
373
|
+
prev = logs[idx - 1];
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
return false;
|
|
377
|
+
});
|
|
378
|
+
if (!res) {
|
|
379
|
+
res = logs[logs.length - 1];
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
if (Math.abs(res.time - current.time) > 0.1) {
|
|
383
|
+
const t = res.time - current.time;
|
|
384
|
+
const v = res.speed / 3.6;
|
|
385
|
+
res.distance -= (v * t);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const distanceDelta = res.distance - current.distance;
|
|
389
|
+
const { power, heartrate, distance, speed } = res;
|
|
390
|
+
const routeDistance = res.distance + ai.summary.startPos;
|
|
391
|
+
let lat, lng;
|
|
392
|
+
if ((_c = (_b = (_a = this.current) === null || _a === void 0 ? void 0 : _a.route) === null || _b === void 0 ? void 0 : _b.description) === null || _c === void 0 ? void 0 : _c.hasGpx) {
|
|
393
|
+
const point = (0, route_1.getPosition)(this.current.route, { distance: routeDistance, nearest: true });
|
|
394
|
+
lat = point === null || point === void 0 ? void 0 : point.lat;
|
|
395
|
+
lng = point === null || point === void 0 ? void 0 : point.lng;
|
|
396
|
+
}
|
|
397
|
+
const distanceGap = Math.abs(distanceDelta) > 1000 ? `${(distanceDelta / 1000).toFixed(1)}km` : `${distanceDelta.toFixed(0)}m`;
|
|
398
|
+
prev = undefined;
|
|
399
|
+
res = logs.find((log, idx) => {
|
|
400
|
+
if (log.distance >= current.distance) {
|
|
401
|
+
if (idx > 0)
|
|
402
|
+
prev = logs[idx - 1];
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
return false;
|
|
406
|
+
});
|
|
407
|
+
const s = res.distance - current.distance;
|
|
408
|
+
const v = res.speed / 3.6;
|
|
409
|
+
const t = v === 0 ? Infinity : s / v;
|
|
410
|
+
res.time -= t;
|
|
411
|
+
const timeGap = `${Number(current.time - res.time).toFixed(1)}s`;
|
|
412
|
+
const routeHash = ai.summary.routeHash;
|
|
413
|
+
const routeId = ai.summary.routeId;
|
|
414
|
+
const tsStart = ai.summary.startTime;
|
|
415
|
+
const title = new Date(ai.summary.startTime).toLocaleDateString();
|
|
416
|
+
const calculate = { time: current.time, routeHash, routeId, tsStart, title, power, heartrate, speed, distance, timeGap, distanceGap, routeDistance, lat, lng };
|
|
417
|
+
return calculate;
|
|
418
|
+
}
|
|
419
|
+
catch (err) {
|
|
420
|
+
this.logError(err, 'getActivityLog');
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
getCurrentActivityLog(ai, current) {
|
|
425
|
+
try {
|
|
426
|
+
const timeGap = '';
|
|
427
|
+
const distanceGap = '';
|
|
428
|
+
const routeHash = ai.summary.routeHash;
|
|
429
|
+
const routeId = ai.summary.routeId;
|
|
430
|
+
const tsStart = ai.summary.startTime;
|
|
431
|
+
const title = 'current';
|
|
432
|
+
const routeDistance = current.distance;
|
|
433
|
+
const { power, heartrate, distance, speed, lat, lng } = current;
|
|
434
|
+
const calculate = { routeHash, routeId, tsStart, title, power, heartrate, speed, distance, timeGap, distanceGap, lat, lng };
|
|
435
|
+
calculate.routeDistance = calculate.distance + ai.summary.startPos;
|
|
436
|
+
return calculate;
|
|
437
|
+
}
|
|
438
|
+
catch (err) {
|
|
439
|
+
this.logError(err, 'getCurrentActivityLog');
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
340
443
|
onPositionUpdate(position) {
|
|
341
444
|
this.current.position = position;
|
|
342
445
|
}
|
|
@@ -370,6 +473,23 @@ let ActivityRideService = (() => {
|
|
|
370
473
|
this.logError(err, 'onDeviceData');
|
|
371
474
|
}
|
|
372
475
|
}
|
|
476
|
+
initPrevActivities(settings) {
|
|
477
|
+
var _a;
|
|
478
|
+
try {
|
|
479
|
+
if (!this.current || !((_a = this.activity) === null || _a === void 0 ? void 0 : _a.route))
|
|
480
|
+
return;
|
|
481
|
+
this.current.showPrev = settings.showPrev;
|
|
482
|
+
const { startPos, realityFactor } = settings;
|
|
483
|
+
const routeId = this.activity.route.id;
|
|
484
|
+
const routeHash = this.activity.route.hash;
|
|
485
|
+
const filter = { routeId, routeHash, startPos, realityFactor };
|
|
486
|
+
const prevRides = (0, list_1.useActivityList)().getPastActivities(filter, { details: true });
|
|
487
|
+
this.current.prevRides = prevRides;
|
|
488
|
+
}
|
|
489
|
+
catch (err) {
|
|
490
|
+
this.logError(err, 'initPrevActivities');
|
|
491
|
+
}
|
|
492
|
+
}
|
|
373
493
|
onDeviceHealthUpdate(udid, status, capabilities) {
|
|
374
494
|
if (!this.current)
|
|
375
495
|
return;
|
|
@@ -659,8 +779,6 @@ let ActivityRideService = (() => {
|
|
|
659
779
|
routeDistance: this.current.routeDistance,
|
|
660
780
|
distance
|
|
661
781
|
};
|
|
662
|
-
this.emit('data', data);
|
|
663
|
-
this.prevEmit = data;
|
|
664
782
|
const logRecord = this.createLogRecord();
|
|
665
783
|
if (logRecord) {
|
|
666
784
|
this.activity.logs.push(logRecord);
|
|
@@ -668,6 +786,13 @@ let ActivityRideService = (() => {
|
|
|
668
786
|
}
|
|
669
787
|
this.activity.distance = this.current.routeDistance - this.activity.startPos;
|
|
670
788
|
this.activity.totalElevation = this.current.elevationGain;
|
|
789
|
+
let list;
|
|
790
|
+
if (this.current.showPrev) {
|
|
791
|
+
list = this.getPrevRideStats(this.current.prevRides, logRecord);
|
|
792
|
+
this.emit('prevRides', list);
|
|
793
|
+
}
|
|
794
|
+
this.emit('data', data, list);
|
|
795
|
+
this.prevEmit = data;
|
|
671
796
|
this.logActivityUpdateMessage();
|
|
672
797
|
this.updateRepo();
|
|
673
798
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./service"), exports);
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { IncyclistService } from "../base/service";
|
|
2
|
+
import { Avatar, AvatarOption } from "./types";
|
|
3
|
+
export declare class AvatarService extends IncyclistService {
|
|
4
|
+
protected options: Array<AvatarOption>;
|
|
5
|
+
protected registered: Record<string, Avatar>;
|
|
6
|
+
protected currentBucket: number;
|
|
7
|
+
constructor();
|
|
8
|
+
setCurrentRiderAvatar(avatar: Avatar): void;
|
|
9
|
+
get(id: string): Avatar;
|
|
10
|
+
delete(id: string): void;
|
|
11
|
+
deleteAll(): void;
|
|
12
|
+
protected buildOptions(): void;
|
|
13
|
+
protected getNextAvailable(): Avatar;
|
|
14
|
+
protected cleanup(id: string, isCurrent?: boolean): void;
|
|
15
|
+
protected find(avatar: any): AvatarOption;
|
|
16
|
+
}
|
|
17
|
+
export declare const useAvatars: () => AvatarService;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
+
var _, done = false;
|
|
8
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
+
var context = {};
|
|
10
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
+
if (kind === "accessor") {
|
|
15
|
+
if (result === void 0) continue;
|
|
16
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
+
}
|
|
21
|
+
else if (_ = accept(result)) {
|
|
22
|
+
if (kind === "field") initializers.unshift(_);
|
|
23
|
+
else descriptor[key] = _;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
+
done = true;
|
|
28
|
+
};
|
|
29
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
+
var useValue = arguments.length > 2;
|
|
31
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
+
}
|
|
34
|
+
return useValue ? value : void 0;
|
|
35
|
+
};
|
|
36
|
+
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
|
37
|
+
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
38
|
+
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
39
|
+
};
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.useAvatars = exports.AvatarService = void 0;
|
|
42
|
+
const service_1 = require("../base/service");
|
|
43
|
+
const types_1 = require("../base/types");
|
|
44
|
+
let AvatarService = (() => {
|
|
45
|
+
let _classDecorators = [types_1.Singleton];
|
|
46
|
+
let _classDescriptor;
|
|
47
|
+
let _classExtraInitializers = [];
|
|
48
|
+
let _classThis;
|
|
49
|
+
let _classSuper = service_1.IncyclistService;
|
|
50
|
+
var AvatarService = _classThis = class extends _classSuper {
|
|
51
|
+
constructor() {
|
|
52
|
+
super('Avatars');
|
|
53
|
+
this.options = [];
|
|
54
|
+
this.registered = {};
|
|
55
|
+
this.currentBucket = 0;
|
|
56
|
+
this.buildOptions();
|
|
57
|
+
}
|
|
58
|
+
setCurrentRiderAvatar(avatar) {
|
|
59
|
+
this.cleanup('current', true);
|
|
60
|
+
let currentOption = this.find(avatar);
|
|
61
|
+
if (!currentOption) {
|
|
62
|
+
currentOption = { avatar, usageCnt: 0 };
|
|
63
|
+
this.options.push(currentOption);
|
|
64
|
+
}
|
|
65
|
+
currentOption.usageCnt++;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
get(id) {
|
|
69
|
+
if (!this.registered[id])
|
|
70
|
+
this.registered[id] = this.getNextAvailable();
|
|
71
|
+
return this.registered[id];
|
|
72
|
+
}
|
|
73
|
+
delete(id) {
|
|
74
|
+
this.cleanup(id);
|
|
75
|
+
}
|
|
76
|
+
deleteAll() {
|
|
77
|
+
const currentAvatar = this.registered.current;
|
|
78
|
+
this.options.forEach(o => { o.usageCnt = 0; });
|
|
79
|
+
this.currentBucket = 0;
|
|
80
|
+
const currentOption = this.find(currentAvatar);
|
|
81
|
+
currentOption.usageCnt = 1;
|
|
82
|
+
this.registered = { current: currentAvatar };
|
|
83
|
+
}
|
|
84
|
+
buildOptions() {
|
|
85
|
+
var _a;
|
|
86
|
+
if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.length) > 0)
|
|
87
|
+
return;
|
|
88
|
+
this.options = [];
|
|
89
|
+
const colors = ['yellow', 'red', 'blue', 'green', 'purple', 'orange', 'pink', 'brown', 'grey', 'black', 'white'];
|
|
90
|
+
colors.forEach(helmet => {
|
|
91
|
+
colors.forEach(shirt => {
|
|
92
|
+
this.options.push({ avatar: { helmet, shirt }, usageCnt: 0 });
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
getNextAvailable() {
|
|
97
|
+
let available = this.options.find(a => a.usageCnt === this.currentBucket);
|
|
98
|
+
if (!available) {
|
|
99
|
+
this.currentBucket++;
|
|
100
|
+
const currentOption = this.find('current');
|
|
101
|
+
currentOption.usageCnt++;
|
|
102
|
+
available = this.options.find(a => a.usageCnt === this.currentBucket);
|
|
103
|
+
}
|
|
104
|
+
available.usageCnt++;
|
|
105
|
+
return available === null || available === void 0 ? void 0 : available.avatar;
|
|
106
|
+
}
|
|
107
|
+
cleanup(id, isCurrent = false) {
|
|
108
|
+
if (this.registered[id]) {
|
|
109
|
+
const prev = this.find(this.registered[id]);
|
|
110
|
+
if (prev) {
|
|
111
|
+
if (isCurrent)
|
|
112
|
+
prev.usageCnt = 0;
|
|
113
|
+
else
|
|
114
|
+
prev.usageCnt--;
|
|
115
|
+
}
|
|
116
|
+
delete this.registered[id];
|
|
117
|
+
}
|
|
118
|
+
const currentAvatar = this.registered.current;
|
|
119
|
+
const inUse = this.options.filter(a => !currentAvatar || (a.avatar.helmet !== currentAvatar.helmet && a.avatar.shirt !== currentAvatar.shirt)).sort((a, b) => b.usageCnt - a.usageCnt);
|
|
120
|
+
this.currentBucket = inUse[0].usageCnt;
|
|
121
|
+
}
|
|
122
|
+
find(avatar) {
|
|
123
|
+
return this.options.find(a => a.avatar.helmet === avatar.helmet && a.avatar.shirt === avatar.shirt);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
__setFunctionName(_classThis, "AvatarService");
|
|
127
|
+
(() => {
|
|
128
|
+
var _a;
|
|
129
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_a = _classSuper[Symbol.metadata]) !== null && _a !== void 0 ? _a : null) : void 0;
|
|
130
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
131
|
+
AvatarService = _classThis = _classDescriptor.value;
|
|
132
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
133
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
134
|
+
})();
|
|
135
|
+
return AvatarService = _classThis;
|
|
136
|
+
})();
|
|
137
|
+
exports.AvatarService = AvatarService;
|
|
138
|
+
const useAvatars = () => new AvatarService();
|
|
139
|
+
exports.useAvatars = useAvatars;
|
|
@@ -50,6 +50,7 @@ export declare class DeviceRideService extends EventEmitter {
|
|
|
50
50
|
cancelStart(): Promise<boolean>;
|
|
51
51
|
startRide(_props: any): void;
|
|
52
52
|
stop(udid?: string): Promise<boolean>;
|
|
53
|
+
resetLimits(): Promise<void>;
|
|
53
54
|
pause(): void;
|
|
54
55
|
resume(): void;
|
|
55
56
|
protected verifySelected(selectedDevices: any, capability: IncyclistCapability): void;
|
|
@@ -60,6 +61,7 @@ export declare class DeviceRideService extends EventEmitter {
|
|
|
60
61
|
sendUpdate(request: UpdateRequest): Promise<void>;
|
|
61
62
|
getCyclingMode(udid?: string): CyclingMode;
|
|
62
63
|
resetCyclingMode(sendInit?: boolean): Promise<void>;
|
|
64
|
+
enforceERG(): Promise<void>;
|
|
63
65
|
onCyclingModeChanged(udid: string, mode: string, settings: any): Promise<void>;
|
|
64
66
|
onDeviceDeleted(settings: IncyclistDeviceSettings): void;
|
|
65
67
|
enforceSimulator(enforced?: boolean): void;
|
|
@@ -727,6 +727,17 @@ class DeviceRideService extends events_1.default {
|
|
|
727
727
|
return true;
|
|
728
728
|
});
|
|
729
729
|
}
|
|
730
|
+
resetLimits() {
|
|
731
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
732
|
+
var _a;
|
|
733
|
+
try {
|
|
734
|
+
const mode = this.getCyclingMode();
|
|
735
|
+
const resetRequest = (_a = mode === null || mode === void 0 ? void 0 : mode.getBikeInitRequest()) !== null && _a !== void 0 ? _a : {};
|
|
736
|
+
yield this.sendUpdate(Object.assign(Object.assign({}, resetRequest), { slope: 0, forced: true }));
|
|
737
|
+
}
|
|
738
|
+
catch (_b) { }
|
|
739
|
+
});
|
|
740
|
+
}
|
|
730
741
|
pause() {
|
|
731
742
|
const adapters = this.getAdapterList();
|
|
732
743
|
adapters === null || adapters === void 0 ? void 0 : adapters.forEach(ai => {
|
|
@@ -897,6 +908,30 @@ class DeviceRideService extends events_1.default {
|
|
|
897
908
|
}
|
|
898
909
|
});
|
|
899
910
|
}
|
|
911
|
+
enforceERG() {
|
|
912
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
913
|
+
try {
|
|
914
|
+
const adapters = this.getAdapterList();
|
|
915
|
+
const adapterInfo = adapters === null || adapters === void 0 ? void 0 : adapters.find(ai => ai.adapter.hasCapability(incyclist_devices_1.IncyclistCapability.Control));
|
|
916
|
+
if (!(adapterInfo === null || adapterInfo === void 0 ? void 0 : adapterInfo.adapter))
|
|
917
|
+
return;
|
|
918
|
+
const { udid, adapter } = adapterInfo;
|
|
919
|
+
if (adapter.getCyclingMode().getModeProperty('eppSupport'))
|
|
920
|
+
return;
|
|
921
|
+
const modes = adapter.getSupportedCyclingModes().filter(C => C.supportsERGMode());
|
|
922
|
+
if (modes.length > 0) {
|
|
923
|
+
const mode = new modes[0](adapter);
|
|
924
|
+
const modeInfo = this.configurationService.getModeSettings(udid, mode.getName());
|
|
925
|
+
const settings = modeInfo.settings;
|
|
926
|
+
const device = adapter;
|
|
927
|
+
device.setCyclingMode(mode, settings);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
catch (err) {
|
|
931
|
+
this.logEvent({ message: 'error', error: err.message, fn: 'enforceERG' });
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
}
|
|
900
935
|
onCyclingModeChanged(udid, mode, settings) {
|
|
901
936
|
return __awaiter(this, void 0, void 0, function* () {
|
|
902
937
|
var _a;
|
package/lib/index.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export * from './api';
|
|
|
4
4
|
export * from './routes';
|
|
5
5
|
export * from './workouts';
|
|
6
6
|
export * from './coaches';
|
|
7
|
+
export * from './avatars';
|
|
7
8
|
export * from './activities';
|
|
8
9
|
export { useUserSettings, initUserSettings, UserSettingsService, UserSettingsBinding, IUserSettingsBinding } from './settings';
|
package/lib/index.js
CHANGED
|
@@ -21,6 +21,7 @@ __exportStar(require("./api"), exports);
|
|
|
21
21
|
__exportStar(require("./routes"), exports);
|
|
22
22
|
__exportStar(require("./workouts"), exports);
|
|
23
23
|
__exportStar(require("./coaches"), exports);
|
|
24
|
+
__exportStar(require("./avatars"), exports);
|
|
24
25
|
__exportStar(require("./activities"), exports);
|
|
25
26
|
var settings_1 = require("./settings");
|
|
26
27
|
Object.defineProperty(exports, "useUserSettings", { enumerable: true, get: function () { return settings_1.useUserSettings; } });
|
|
@@ -21,6 +21,7 @@ export declare class WorkoutRide extends IncyclistService {
|
|
|
21
21
|
protected currentLimits: ActiveWorkoutLimit;
|
|
22
22
|
protected updateInterval: NodeJS.Timeout;
|
|
23
23
|
protected currentStep: StepDefinition;
|
|
24
|
+
protected isFreeRide: boolean;
|
|
24
25
|
constructor();
|
|
25
26
|
init(): Observer;
|
|
26
27
|
start(paused?: boolean): Workout;
|
|
@@ -34,10 +35,13 @@ export declare class WorkoutRide extends IncyclistService {
|
|
|
34
35
|
getDashboardDisplayProperties(): WorkoutDisplayProperties;
|
|
35
36
|
getCurrentLimits(): ActiveWorkoutLimit;
|
|
36
37
|
inUse(): boolean;
|
|
38
|
+
appliesLimits(): boolean;
|
|
37
39
|
isActive(): boolean;
|
|
38
40
|
getWorkout(): Workout;
|
|
39
41
|
getObserver(): Observer;
|
|
40
42
|
protected update(startIfInitialized?: boolean): void;
|
|
43
|
+
protected startFreeRide(): Promise<void>;
|
|
44
|
+
protected stopFreeRide(): Promise<void>;
|
|
41
45
|
protected resetLimits(): Promise<void>;
|
|
42
46
|
protected resetTimes(): void;
|
|
43
47
|
protected setCurrentLimits(trainingTime?: number): void;
|
|
@@ -68,6 +68,7 @@ let WorkoutRide = (() => {
|
|
|
68
68
|
constructor() {
|
|
69
69
|
super('WorkoutRide');
|
|
70
70
|
this.state = 'idle';
|
|
71
|
+
this.isFreeRide = true;
|
|
71
72
|
}
|
|
72
73
|
init() {
|
|
73
74
|
var _a, _b;
|
|
@@ -282,6 +283,9 @@ let WorkoutRide = (() => {
|
|
|
282
283
|
inUse() {
|
|
283
284
|
return this.state !== 'idle' && this.state !== 'completed';
|
|
284
285
|
}
|
|
286
|
+
appliesLimits() {
|
|
287
|
+
return this.inUse() && !this.isFreeRide;
|
|
288
|
+
}
|
|
285
289
|
isActive() {
|
|
286
290
|
return this.state === 'active';
|
|
287
291
|
}
|
|
@@ -315,6 +319,12 @@ let WorkoutRide = (() => {
|
|
|
315
319
|
const prevStep = this.currentStep;
|
|
316
320
|
this.setCurrentLimits(time);
|
|
317
321
|
if (this.currentStep !== prevStep) {
|
|
322
|
+
if (!this.currentStep.power && prevStep.power) {
|
|
323
|
+
this.startFreeRide();
|
|
324
|
+
}
|
|
325
|
+
else if (this.currentStep.power && !prevStep.power) {
|
|
326
|
+
this.stopFreeRide();
|
|
327
|
+
}
|
|
318
328
|
this.emit('step-changed', this.getDashboardDisplayProperties());
|
|
319
329
|
}
|
|
320
330
|
else if (Math.round(time) !== prevTime) {
|
|
@@ -325,16 +335,31 @@ let WorkoutRide = (() => {
|
|
|
325
335
|
this.logError(err, 'update');
|
|
326
336
|
}
|
|
327
337
|
}
|
|
338
|
+
startFreeRide() {
|
|
339
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
340
|
+
if (this.settings.useErgMode) {
|
|
341
|
+
(0, devices_1.useDeviceRide)().resetCyclingMode(false);
|
|
342
|
+
}
|
|
343
|
+
this.resetLimits();
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
stopFreeRide() {
|
|
347
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
348
|
+
if (this.settings.useErgMode) {
|
|
349
|
+
(0, devices_1.useDeviceRide)().enforceERG();
|
|
350
|
+
}
|
|
351
|
+
this.resetLimits();
|
|
352
|
+
});
|
|
353
|
+
}
|
|
328
354
|
resetLimits() {
|
|
329
355
|
return __awaiter(this, void 0, void 0, function* () {
|
|
330
356
|
const rideService = (0, devices_1.useDeviceRide)();
|
|
331
|
-
rideService.resetCyclingMode();
|
|
332
357
|
const mode = rideService.getCyclingMode();
|
|
333
|
-
const data = rideService.getData();
|
|
334
358
|
const isERG = mode ? mode.constructor.supportsERGMode() : false;
|
|
335
359
|
if (!this.currentLimits || !mode)
|
|
336
360
|
return;
|
|
337
361
|
yield rideService.waitForUpdateFinish();
|
|
362
|
+
const data = rideService.getData();
|
|
338
363
|
if (isERG) {
|
|
339
364
|
rideService.sendUpdate({ targetPower: data.power });
|
|
340
365
|
}
|
|
@@ -374,6 +399,7 @@ let WorkoutRide = (() => {
|
|
|
374
399
|
request.maxHrm = ((_d = limits.hrm) === null || _d === void 0 ? void 0 : _d.max) ? Math.round(limits.hrm.max) : undefined;
|
|
375
400
|
this.currentLimits = Object.assign(Object.assign({}, request), { duration: limits.duration, remaining: limits.remaining });
|
|
376
401
|
}
|
|
402
|
+
this.isFreeRide = limits.power === undefined || limits.power === null;
|
|
377
403
|
this.logger.logEvent(Object.assign(Object.assign({ message: 'workout requests' }, this.currentLimits), { ftp }));
|
|
378
404
|
this.emit('request-update', this.currentLimits);
|
|
379
405
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "incyclist-services",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.16",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"gd-eventlog": "^0.1.26"
|
|
6
6
|
},
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"axios": "^1.6.1",
|
|
46
|
-
"incyclist-devices": "^2.2.
|
|
46
|
+
"incyclist-devices": "^2.2.3",
|
|
47
47
|
"promise.any": "^2.0.6",
|
|
48
48
|
"tcx-builder": "^1.1.1",
|
|
49
49
|
"uuid": "^9.0.0",
|