incyclist-services 1.7.71 → 1.7.73
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/devices/ride/service.js +7 -0
- package/lib/cjs/ride/display/service.js +4 -4
- package/lib/cjs/ride/page/service.js +18 -0
- package/lib/cjs/ride/route/RouteDisplayService.js +1 -1
- package/lib/cjs/routes/list/cards/RouteCard.js +11 -1
- package/lib/cjs/workouts/list/index.js +1 -0
- package/lib/cjs/workouts/list/pageService.js +98 -0
- package/lib/esm/activities/upload/trainingpeaks.js +123 -0
- package/lib/esm/apps/base/api/trainingpeaks/api.js +143 -0
- package/lib/esm/apps/base/api/trainingpeaks/index.js +1 -0
- package/lib/esm/apps/base/api/trainingpeaks/types.js +1 -0
- package/lib/esm/apps/trainingpeaks/TrainingPeaksAppConnection.js +189 -0
- package/lib/esm/apps/trainingpeaks/types.js +1 -0
- package/lib/esm/devices/ride/service.js +7 -0
- package/lib/esm/ride/display/service.js +4 -4
- package/lib/esm/ride/page/service.js +18 -0
- package/lib/esm/ride/route/RouteDisplayService.js +1 -1
- package/lib/esm/routes/list/cards/RouteCard.js +11 -1
- package/lib/esm/workouts/calendar/sync/trainingpeaks/provider.js +201 -0
- package/lib/esm/workouts/list/index.js +1 -0
- package/lib/esm/workouts/list/pageService.js +94 -0
- package/lib/types/activities/upload/trainingpeaks.d.ts +16 -0
- package/lib/types/apps/base/api/trainingpeaks/api.d.ts +30 -0
- package/lib/types/apps/base/api/trainingpeaks/index.d.ts +2 -0
- package/lib/types/apps/base/api/trainingpeaks/types.d.ts +55 -0
- package/lib/types/apps/trainingpeaks/TrainingPeaksAppConnection.d.ts +22 -0
- package/lib/types/apps/trainingpeaks/types.d.ts +6 -0
- package/lib/types/appstate/types.d.ts +1 -1
- package/lib/types/ride/page/service.d.ts +1 -0
- package/lib/types/ride/page/types.d.ts +2 -1
- package/lib/types/routes/list/cards/RouteCard.d.ts +1 -0
- package/lib/types/routes/list/cards/types.d.ts +1 -0
- package/lib/types/workouts/calendar/sync/trainingpeaks/provider.d.ts +22 -0
- package/lib/types/workouts/list/index.d.ts +1 -0
- package/lib/types/workouts/list/pageService.d.ts +18 -0
- package/package.json +2 -2
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { getBindings } from '../../api';
|
|
36
|
+
import { Injectable, Singleton } from '../../base/decorators';
|
|
37
|
+
import { valid } from '../../utils/valid';
|
|
38
|
+
import { TrainingPeaksApi } from '../base/api/trainingpeaks';
|
|
39
|
+
import { ConnectedAppService } from '../base/app';
|
|
40
|
+
let TrainingPeaksAppConnection = (() => {
|
|
41
|
+
let _classDecorators = [Singleton];
|
|
42
|
+
let _classDescriptor;
|
|
43
|
+
let _classExtraInitializers = [];
|
|
44
|
+
let _classThis;
|
|
45
|
+
let _classSuper = ConnectedAppService;
|
|
46
|
+
let _instanceExtraInitializers = [];
|
|
47
|
+
let _getSecretBindings_decorators;
|
|
48
|
+
var TrainingPeaksAppConnection = class extends _classSuper {
|
|
49
|
+
static { _classThis = this; }
|
|
50
|
+
static {
|
|
51
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
52
|
+
_getSecretBindings_decorators = [Injectable];
|
|
53
|
+
__esDecorate(this, null, _getSecretBindings_decorators, { kind: "method", name: "getSecretBindings", static: false, private: false, access: { has: obj => "getSecretBindings" in obj, get: obj => obj.getSecretBindings }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
54
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
55
|
+
TrainingPeaksAppConnection = _classThis = _classDescriptor.value;
|
|
56
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
57
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
58
|
+
}
|
|
59
|
+
config = __runInitializers(this, _instanceExtraInitializers);
|
|
60
|
+
api;
|
|
61
|
+
tokenUpdateHandler = this.updateConfig.bind(this);
|
|
62
|
+
constructor() {
|
|
63
|
+
super('TrainingPeaksAppConnection', 'trainingpeaks');
|
|
64
|
+
}
|
|
65
|
+
async connect(credentials) {
|
|
66
|
+
this.ensureInitialized();
|
|
67
|
+
this.logEvent({ message: 'Connect with TrainingPeaks' });
|
|
68
|
+
try {
|
|
69
|
+
const isConnected = this.isConnected();
|
|
70
|
+
const config = this.buildConfigFromCredentials(credentials);
|
|
71
|
+
if (isConnected) {
|
|
72
|
+
this.getApi().update(config);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.initApi(config);
|
|
76
|
+
this.saveCredentials(config);
|
|
77
|
+
}
|
|
78
|
+
this.logEvent({ message: 'Connect with TrainingPeaks success' });
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
this.logError(err, 'connect');
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
isConnecting() {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
disconnect() {
|
|
90
|
+
try {
|
|
91
|
+
this.config = undefined;
|
|
92
|
+
this.getApi().init(undefined);
|
|
93
|
+
this.saveCredentials();
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
this.logError(err, 'disconnect');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
isConnected() {
|
|
100
|
+
if (!this.isInitialized) {
|
|
101
|
+
this.init();
|
|
102
|
+
}
|
|
103
|
+
return valid(this.config);
|
|
104
|
+
}
|
|
105
|
+
getCredentials() {
|
|
106
|
+
try {
|
|
107
|
+
return this.getUserSettings().get('user.auth.trainingpeaks', null);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
getApi() {
|
|
114
|
+
if (!this.api)
|
|
115
|
+
this.api = new TrainingPeaksApi();
|
|
116
|
+
return this.api;
|
|
117
|
+
}
|
|
118
|
+
initAuth() {
|
|
119
|
+
let isInitialized = false;
|
|
120
|
+
try {
|
|
121
|
+
const auth = this.getCredentials();
|
|
122
|
+
const clientId = this.getSecret('TRAININGPEAKS_CLIENT_ID');
|
|
123
|
+
const clientSecret = this.getSecret('TRAININGPEAKS_CLIENT_SECRET');
|
|
124
|
+
if (clientId && clientSecret && auth) {
|
|
125
|
+
this.logEvent({ message: 'TrainingPeaks init done', hasCredentials: true });
|
|
126
|
+
this.config = this.buildConfigFromCredentials(auth);
|
|
127
|
+
this.initApi(this.config);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
this.logEvent({ message: 'TrainingPeaks init done', hasCredentials: false });
|
|
131
|
+
}
|
|
132
|
+
isInitialized = true;
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
this.logEvent({ message: 'error', error: err.message, fn: 'init', stack: err.stack });
|
|
136
|
+
isInitialized = false;
|
|
137
|
+
delete this.config;
|
|
138
|
+
}
|
|
139
|
+
return isInitialized;
|
|
140
|
+
}
|
|
141
|
+
updateConfig(config) {
|
|
142
|
+
const { accessToken, refreshToken, expiration } = config;
|
|
143
|
+
this.config = { ...this.config, accessToken, refreshToken, expiration };
|
|
144
|
+
this.saveCredentials();
|
|
145
|
+
}
|
|
146
|
+
saveCredentials(config) {
|
|
147
|
+
if (config)
|
|
148
|
+
this.config = config;
|
|
149
|
+
try {
|
|
150
|
+
if (!this.isConnected()) {
|
|
151
|
+
this.logEvent({ message: 'TrainingPeaks delete credentials' });
|
|
152
|
+
this.getUserSettings().set('user.auth.trainingpeaks', null);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.logEvent({ message: 'TrainingPeaks save credentials' });
|
|
156
|
+
this.getUserSettings().set('user.auth.trainingpeaks', {
|
|
157
|
+
accesstoken: this.config.accessToken,
|
|
158
|
+
refreshtoken: this.config.refreshToken,
|
|
159
|
+
expiration: this.config.expiration?.valueOf().toString()
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
this.logEvent({ message: 'error', fn: 'saveCredentials', error: err.message, stack: err.stack });
|
|
164
|
+
}
|
|
165
|
+
this.logEvent({ message: 'TrainingPeaks save credentials done' });
|
|
166
|
+
}
|
|
167
|
+
buildConfigFromCredentials(credentials) {
|
|
168
|
+
return {
|
|
169
|
+
clientId: this.getSecret('TRAININGPEAKS_CLIENT_ID'),
|
|
170
|
+
clientSecret: this.getSecret('TRAININGPEAKS_CLIENT_SECRET'),
|
|
171
|
+
accessToken: credentials?.accesstoken,
|
|
172
|
+
refreshToken: credentials?.refreshtoken,
|
|
173
|
+
expiration: credentials?.expiration ? new Date(Number(credentials.expiration)) : undefined
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
initApi(config) {
|
|
177
|
+
const observer = this.getApi().init(config);
|
|
178
|
+
observer.on('token.updated', this.tokenUpdateHandler);
|
|
179
|
+
}
|
|
180
|
+
getSecret(key) {
|
|
181
|
+
return this.getSecretBindings()?.getSecret(key);
|
|
182
|
+
}
|
|
183
|
+
getSecretBindings() {
|
|
184
|
+
return getBindings()?.secret;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
return TrainingPeaksAppConnection = _classThis;
|
|
188
|
+
})();
|
|
189
|
+
export { TrainingPeaksAppConnection };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1356,8 +1356,15 @@ let DeviceRideService = (() => {
|
|
|
1356
1356
|
canEnforceSimulator() {
|
|
1357
1357
|
const selected = getRouteList().getSelected();
|
|
1358
1358
|
const personalApiKey = this.getGoogleMaps().hasPersonalApiKey();
|
|
1359
|
+
if (!selected?.description)
|
|
1360
|
+
return false;
|
|
1359
1361
|
if (selected?.description?.hasVideo || personalApiKey)
|
|
1360
1362
|
return true;
|
|
1363
|
+
if (!selected.description.hasVideo) {
|
|
1364
|
+
const rideView = useUserSettings().getValue('preferences.rideView', null);
|
|
1365
|
+
if (rideView === 'map')
|
|
1366
|
+
return true;
|
|
1367
|
+
}
|
|
1361
1368
|
return useUserSettings().isNewUser();
|
|
1362
1369
|
}
|
|
1363
1370
|
logEvent(event) {
|
|
@@ -374,7 +374,7 @@ let RideDisplayService = (() => {
|
|
|
374
374
|
return { ...props, ...childProps };
|
|
375
375
|
}
|
|
376
376
|
catch (err) {
|
|
377
|
-
this.logError(err, 'getDisplayProperties');
|
|
377
|
+
this.logError(err, 'getDisplayProperties', { state: this.state });
|
|
378
378
|
return { state: this.state };
|
|
379
379
|
}
|
|
380
380
|
}
|
|
@@ -422,7 +422,7 @@ let RideDisplayService = (() => {
|
|
|
422
422
|
async stopRide(props = {}) {
|
|
423
423
|
try {
|
|
424
424
|
if (this.state !== 'Starting' && this.state !== 'Idle') {
|
|
425
|
-
this.logEvent({ message: "activity stopped", activity: this.activity?.id });
|
|
425
|
+
this.logEvent({ message: "activity stopped", activity: this.activity?.id, lastState: this.state });
|
|
426
426
|
}
|
|
427
427
|
const prevState = this.state;
|
|
428
428
|
this.state = prevState === 'Starting' ? 'Closing' : 'Finished';
|
|
@@ -446,7 +446,6 @@ let RideDisplayService = (() => {
|
|
|
446
446
|
}
|
|
447
447
|
if (prevState !== 'Starting')
|
|
448
448
|
this.enableScreensaver();
|
|
449
|
-
this.state = 'Idle';
|
|
450
449
|
}
|
|
451
450
|
catch (err) {
|
|
452
451
|
this.logError(err, 'stopRide');
|
|
@@ -455,9 +454,10 @@ let RideDisplayService = (() => {
|
|
|
455
454
|
}
|
|
456
455
|
async closePrevRide() {
|
|
457
456
|
try {
|
|
458
|
-
if (this.observer || (this.state !== 'Idle')) {
|
|
457
|
+
if (this.observer || (this.state !== 'Idle' && this.state !== 'Finished')) {
|
|
459
458
|
await this.stopRide({ noStateUpdates: true });
|
|
460
459
|
}
|
|
460
|
+
this.state = 'Idle';
|
|
461
461
|
}
|
|
462
462
|
catch (err) {
|
|
463
463
|
this.logError(err, 'init');
|
|
@@ -193,6 +193,12 @@ let RidePageService = (() => {
|
|
|
193
193
|
}
|
|
194
194
|
onMenuClose() {
|
|
195
195
|
try {
|
|
196
|
+
const state = this.getRideDisplay().getState();
|
|
197
|
+
if (state === 'Finished' || this.menuProps.finished) {
|
|
198
|
+
this.moveToPreviousPage();
|
|
199
|
+
this.closePage();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
196
202
|
this.menuProps = null;
|
|
197
203
|
this.updatePageDisplay();
|
|
198
204
|
}
|
|
@@ -220,6 +226,15 @@ let RidePageService = (() => {
|
|
|
220
226
|
this.logError(err, 'onResume');
|
|
221
227
|
}
|
|
222
228
|
}
|
|
229
|
+
onFinished() {
|
|
230
|
+
try {
|
|
231
|
+
this.menuProps = { showResume: false, finished: true };
|
|
232
|
+
this.updatePageDisplay();
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
this.logError(err, 'onResume');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
223
238
|
onEndRide() {
|
|
224
239
|
try {
|
|
225
240
|
this.getRideDisplay().stop();
|
|
@@ -333,6 +348,9 @@ let RidePageService = (() => {
|
|
|
333
348
|
case 'Paused':
|
|
334
349
|
this.menuProps = { showResume: true };
|
|
335
350
|
break;
|
|
351
|
+
case 'Finished':
|
|
352
|
+
this.menuProps = { showResume: false, finished: true };
|
|
353
|
+
break;
|
|
336
354
|
case 'Active':
|
|
337
355
|
this.menuProps = null;
|
|
338
356
|
break;
|
|
@@ -270,7 +270,7 @@ let RouteDisplayService = (() => {
|
|
|
270
270
|
const mode = this.getDeviceRide().getCyclingMode();
|
|
271
271
|
const isSIM = mode?.isSIM();
|
|
272
272
|
const realityFactor = this.startSettings?.realityFactor ?? 100;
|
|
273
|
-
const targetSlope = (this.position
|
|
273
|
+
const targetSlope = (this.position?.slope ?? 0) * realityFactor / 100;
|
|
274
274
|
if (props?.limits && !isSIM) {
|
|
275
275
|
delete this.prevRequestSlope;
|
|
276
276
|
return { ...props?.limits, slope: targetSlope };
|
|
@@ -50,6 +50,7 @@ import { useOnlineStatusMonitoring } from "../../../monitoring";
|
|
|
50
50
|
import { distanceBetween } from "../../../utils/geo";
|
|
51
51
|
import { getUnitConversionShortcuts } from "../../../i18n";
|
|
52
52
|
import { Injectable } from "../../../base/decorators";
|
|
53
|
+
import { useAppState } from "../../../appstate";
|
|
53
54
|
export const DEFAULT_TITLE = 'Import Route';
|
|
54
55
|
export const DEFAULT_FILTERS = [
|
|
55
56
|
{ name: 'Routes', extensions: ['gpx', 'epm', 'xml', 'rlv', 'pgmf'] },
|
|
@@ -76,13 +77,16 @@ let RouteCard = (() => {
|
|
|
76
77
|
let _instanceExtraInitializers = [];
|
|
77
78
|
let _getBindings_decorators;
|
|
78
79
|
let _getOnlineStatusMonitoring_decorators;
|
|
80
|
+
let _getAppState_decorators;
|
|
79
81
|
return class RouteCard extends _classSuper {
|
|
80
82
|
static {
|
|
81
83
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
82
84
|
_getBindings_decorators = [Injectable];
|
|
83
85
|
_getOnlineStatusMonitoring_decorators = [Injectable];
|
|
86
|
+
_getAppState_decorators = [Injectable];
|
|
84
87
|
__esDecorate(this, null, _getBindings_decorators, { kind: "method", name: "getBindings", static: false, private: false, access: { has: obj => "getBindings" in obj, get: obj => obj.getBindings }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
85
88
|
__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);
|
|
89
|
+
__esDecorate(this, null, _getAppState_decorators, { kind: "method", name: "getAppState", static: false, private: false, access: { has: obj => "getAppState" in obj, get: obj => obj.getAppState }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
86
90
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
87
91
|
}
|
|
88
92
|
downloadObserver = __runInitializers(this, _instanceExtraInitializers);
|
|
@@ -367,11 +371,14 @@ let RouteCard = (() => {
|
|
|
367
371
|
}
|
|
368
372
|
let showLoopOverwrite, showNextOverwrite;
|
|
369
373
|
let hasWorkout = true;
|
|
374
|
+
let showWorkoutOption = false;
|
|
370
375
|
try {
|
|
371
376
|
showLoopOverwrite = this.route?.description?.isLoop;
|
|
372
377
|
const next = getNextVideoId(this.route);
|
|
373
378
|
showNextOverwrite = !!next;
|
|
374
379
|
hasWorkout = valid(workouts.getSelected());
|
|
380
|
+
const workoutsEnabled = this.isMobile() ? this.getAppState().hasFeature('MOBILE_WORKOUTS') : true;
|
|
381
|
+
showWorkoutOption = workoutsEnabled && !hasWorkout;
|
|
375
382
|
if (showNextOverwrite) {
|
|
376
383
|
const card = getRouteList().getCard(next);
|
|
377
384
|
const route = card?.getData();
|
|
@@ -389,7 +396,7 @@ let RouteCard = (() => {
|
|
|
389
396
|
const totalElevation = { value: C(this.route.description.elevation, 'elevation', { digits: 0 }), unit: uEl };
|
|
390
397
|
const xScale = { value: C(1, 'distance'), unit: uDist };
|
|
391
398
|
const yScale = { value: C(1, 'elevation'), unit: uEl };
|
|
392
|
-
return { settings: uiSettings, totalDistance, totalElevation, showLoopOverwrite, showNextOverwrite, hasWorkout, canStart, videoChecking, videoMissing,
|
|
399
|
+
return { settings: uiSettings, totalDistance, totalElevation, showLoopOverwrite, showNextOverwrite, hasWorkout, showWorkoutOption, canStart, videoChecking, videoMissing,
|
|
393
400
|
xScale, yScale,
|
|
394
401
|
updateStartPos: this.updateStartPos.bind(this),
|
|
395
402
|
updateMarkers: this.updateMarkers.bind(this)
|
|
@@ -906,6 +913,9 @@ let RouteCard = (() => {
|
|
|
906
913
|
getOnlineStatusMonitoring() {
|
|
907
914
|
return useOnlineStatusMonitoring();
|
|
908
915
|
}
|
|
916
|
+
getAppState() {
|
|
917
|
+
return useAppState();
|
|
918
|
+
}
|
|
909
919
|
getRouteDownload() {
|
|
910
920
|
return useRouteDownload();
|
|
911
921
|
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { useAppsService } from '../../../../apps';
|
|
36
|
+
import { TrainingPeaksAppConnection } from '../../../../apps/trainingpeaks/TrainingPeaksAppConnection';
|
|
37
|
+
import { Injectable } from '../../../../base/decorators';
|
|
38
|
+
import { IncyclistService } from '../../../../base/service';
|
|
39
|
+
import { Observer } from '../../../../base/types';
|
|
40
|
+
import { getFirstDayOfCurrentWeek, waitNextTick } from '../../../../utils';
|
|
41
|
+
import { ZwoParser } from '../../../base/parsers/zwo/zwo';
|
|
42
|
+
import { WorkoutSyncFactory } from '../factory';
|
|
43
|
+
const SYNC_DAYS = 30;
|
|
44
|
+
let TrainingPeaksCalendarSyncProvider = (() => {
|
|
45
|
+
let _classSuper = IncyclistService;
|
|
46
|
+
let _instanceExtraInitializers = [];
|
|
47
|
+
let _getApi_decorators;
|
|
48
|
+
let _getConnection_decorators;
|
|
49
|
+
let _getAppsService_decorators;
|
|
50
|
+
return class TrainingPeaksCalendarSyncProvider extends _classSuper {
|
|
51
|
+
static {
|
|
52
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
53
|
+
_getApi_decorators = [Injectable];
|
|
54
|
+
_getConnection_decorators = [Injectable];
|
|
55
|
+
_getAppsService_decorators = [Injectable];
|
|
56
|
+
__esDecorate(this, null, _getApi_decorators, { kind: "method", name: "getApi", static: false, private: false, access: { has: obj => "getApi" in obj, get: obj => obj.getApi }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
57
|
+
__esDecorate(this, null, _getConnection_decorators, { kind: "method", name: "getConnection", static: false, private: false, access: { has: obj => "getConnection" in obj, get: obj => obj.getConnection }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
58
|
+
__esDecorate(this, null, _getAppsService_decorators, { kind: "method", name: "getAppsService", static: false, private: false, access: { has: obj => "getAppsService" in obj, get: obj => obj.getAppsService }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
59
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
60
|
+
}
|
|
61
|
+
lastSyncTS = __runInitializers(this, _instanceExtraInitializers);
|
|
62
|
+
observer;
|
|
63
|
+
stopRequested;
|
|
64
|
+
workouts;
|
|
65
|
+
constructor() {
|
|
66
|
+
super('TrainingPeaksCalendarSync');
|
|
67
|
+
this.lastSyncTS = 0;
|
|
68
|
+
this.workouts = [];
|
|
69
|
+
}
|
|
70
|
+
sync() {
|
|
71
|
+
if (this.observer)
|
|
72
|
+
return this.observer;
|
|
73
|
+
this.observer = new Observer();
|
|
74
|
+
const onDone = () => {
|
|
75
|
+
waitNextTick().then(() => {
|
|
76
|
+
this.stopRequested = false;
|
|
77
|
+
delete this.observer;
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
this.observer.once('done', onDone);
|
|
81
|
+
this.observer.once('loaded', onDone);
|
|
82
|
+
this.observer.once('stop', () => { this.stopSync(); });
|
|
83
|
+
this.loadWorkouts();
|
|
84
|
+
return this.observer;
|
|
85
|
+
}
|
|
86
|
+
isConnected() {
|
|
87
|
+
try {
|
|
88
|
+
if (this.getApi().isAuthenticated())
|
|
89
|
+
return true;
|
|
90
|
+
return this.getConnection().isConnected();
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
reset() {
|
|
97
|
+
this.lastSyncTS = 0;
|
|
98
|
+
this.workouts = [];
|
|
99
|
+
}
|
|
100
|
+
stopSync() {
|
|
101
|
+
this.stopRequested = true;
|
|
102
|
+
}
|
|
103
|
+
async loadWorkouts() {
|
|
104
|
+
const isFirst = this.lastSyncTS === 0;
|
|
105
|
+
const event = isFirst ? 'loaded' : 'done';
|
|
106
|
+
try {
|
|
107
|
+
if (!this.isConnected()) {
|
|
108
|
+
this.observer.emit(event, [], 'trainingpeaks');
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
if (!this.getAppsService().isEnabled('trainingpeaks', 'WorkoutDownload')) {
|
|
112
|
+
await waitNextTick();
|
|
113
|
+
this.observer.emit(event, [], 'trainingpeaks');
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const oldest = getFirstDayOfCurrentWeek();
|
|
117
|
+
const newest = new Date(oldest.valueOf() + SYNC_DAYS * 24 * 60 * 60 * 1000);
|
|
118
|
+
const all = await this.getApi().getCalendarWorkouts({ oldest, newest }) ?? [];
|
|
119
|
+
const bike = all.filter(w => w.WorkoutType === 'Bike');
|
|
120
|
+
await this.parseWorkouts(bike);
|
|
121
|
+
this.observer.emit(event, this.workouts, 'trainingpeaks');
|
|
122
|
+
this.lastSyncTS = Date.now();
|
|
123
|
+
return this.workouts;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
this.observer.emit(event, [], 'trainingpeaks');
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async parseWorkouts(workouts) {
|
|
131
|
+
const zwoParser = new ZwoParser();
|
|
132
|
+
const promises = [];
|
|
133
|
+
const transform = async (w) => {
|
|
134
|
+
try {
|
|
135
|
+
const day = new Date(w.WorkoutDay);
|
|
136
|
+
const updated = w.LastModifiedDate ? new Date(w.LastModifiedDate) : undefined;
|
|
137
|
+
if (!w.WorkoutFileFormats?.includes('zwo')) {
|
|
138
|
+
this.logEvent({
|
|
139
|
+
message: 'workout skipped - no ZWO format available',
|
|
140
|
+
workoutId: w.Id,
|
|
141
|
+
title: w.Title,
|
|
142
|
+
availableFormats: w.WorkoutFileFormats ?? []
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const fileContent = await this.getApi().getWorkoutFile(w.Id, 'zwo');
|
|
147
|
+
const workout = await zwoParser.fromStr(fileContent, w.Title);
|
|
148
|
+
const entry = {
|
|
149
|
+
day,
|
|
150
|
+
updated,
|
|
151
|
+
workoutId: String(w.Id),
|
|
152
|
+
workout
|
|
153
|
+
};
|
|
154
|
+
const existingIdx = this.workouts.findIndex(e => e.workoutId === String(w.Id));
|
|
155
|
+
const isExisting = existingIdx !== -1;
|
|
156
|
+
if (this.lastSyncTS > 0) {
|
|
157
|
+
if (!isExisting) {
|
|
158
|
+
this.observer.emit('added', entry, 'trainingpeaks');
|
|
159
|
+
}
|
|
160
|
+
else if (updated && updated.valueOf() > this.lastSyncTS) {
|
|
161
|
+
this.observer.emit('updated', entry, 'trainingpeaks');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (isExisting) {
|
|
165
|
+
this.workouts[existingIdx] = entry;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
this.workouts.push(entry);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
this.logEvent({ message: 'error parsing workout', error: err.message, workoutId: w.Id, title: w.Title });
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
if (this.lastSyncTS > 0) {
|
|
176
|
+
const deleted = this.workouts.filter(e => !workouts.some(w => String(w.Id) === e.workoutId));
|
|
177
|
+
for (const e of deleted) {
|
|
178
|
+
this.observer.emit('deleted', e, 'trainingpeaks');
|
|
179
|
+
}
|
|
180
|
+
this.workouts = this.workouts.filter(e => workouts.some(w => String(w.Id) === e.workoutId));
|
|
181
|
+
}
|
|
182
|
+
for (const w of workouts) {
|
|
183
|
+
promises.push(transform(w));
|
|
184
|
+
}
|
|
185
|
+
await Promise.all(promises);
|
|
186
|
+
return this.workouts;
|
|
187
|
+
}
|
|
188
|
+
getApi() {
|
|
189
|
+
return this.getConnection().getApi();
|
|
190
|
+
}
|
|
191
|
+
getConnection() {
|
|
192
|
+
return new TrainingPeaksAppConnection();
|
|
193
|
+
}
|
|
194
|
+
getAppsService() {
|
|
195
|
+
return useAppsService();
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
})();
|
|
199
|
+
export { TrainingPeaksCalendarSyncProvider };
|
|
200
|
+
const factory = new WorkoutSyncFactory();
|
|
201
|
+
factory.add('trainingpeaks', new TrainingPeaksCalendarSyncProvider());
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { Singleton } from "../../base/types";
|
|
36
|
+
import { IncyclistPageService } from "../../base/pages";
|
|
37
|
+
import { Injectable } from "../../base/decorators";
|
|
38
|
+
import { useAppState } from "../../appstate";
|
|
39
|
+
let WorkoutListPageService = (() => {
|
|
40
|
+
let _classDecorators = [Singleton];
|
|
41
|
+
let _classDescriptor;
|
|
42
|
+
let _classExtraInitializers = [];
|
|
43
|
+
let _classThis;
|
|
44
|
+
let _classSuper = IncyclistPageService;
|
|
45
|
+
let _instanceExtraInitializers = [];
|
|
46
|
+
let _getAppState_decorators;
|
|
47
|
+
var WorkoutListPageService = class extends _classSuper {
|
|
48
|
+
static { _classThis = this; }
|
|
49
|
+
static {
|
|
50
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
51
|
+
_getAppState_decorators = [Injectable];
|
|
52
|
+
__esDecorate(this, null, _getAppState_decorators, { kind: "method", name: "getAppState", static: false, private: false, access: { has: obj => "getAppState" in obj, get: obj => obj.getAppState }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
53
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
54
|
+
WorkoutListPageService = _classThis = _classDescriptor.value;
|
|
55
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
56
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
57
|
+
}
|
|
58
|
+
constructor() {
|
|
59
|
+
super('WorkoutListPage');
|
|
60
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
61
|
+
}
|
|
62
|
+
openPage() {
|
|
63
|
+
try {
|
|
64
|
+
super.openPage();
|
|
65
|
+
this.logEvent({ message: 'page shown', page: 'Workouts' });
|
|
66
|
+
return this.getPageObserver();
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
this.logError(err, 'openPage');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
closePage() {
|
|
73
|
+
try {
|
|
74
|
+
this.logEvent({ message: 'page closed', page: 'Workouts' });
|
|
75
|
+
super.closePage();
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
this.logError(err, 'closePage');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
getPageDisplayProps() {
|
|
82
|
+
if (!this.getAppState().hasFeature('MOBILE_WORKOUTS')) {
|
|
83
|
+
return { pageType: 'placeholder' };
|
|
84
|
+
}
|
|
85
|
+
return { pageType: 'list' };
|
|
86
|
+
}
|
|
87
|
+
getAppState() {
|
|
88
|
+
return useAppState();
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
return WorkoutListPageService = _classThis;
|
|
92
|
+
})();
|
|
93
|
+
export { WorkoutListPageService };
|
|
94
|
+
export const getWorkoutListPageService = () => new WorkoutListPageService();
|