incyclist-services 1.3.2 → 1.3.4

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.
@@ -1,15 +1,16 @@
1
1
  /// <reference types="node" />
2
2
  import { IncyclistService } from "../../base/service";
3
- import { Observer } from "../../base/types/observer";
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, ActivityDetails, ActivityLogRecord, ActivityRouteType, ScreenShotInfo } from "../base";
6
+ import { ActivitiesRepository, ActivityConverterFactory, ActivityDetails, ActivityLogRecord, ActivityRouteType, ScreenShotInfo } from "../base";
7
7
  import { FreeRideStartSettings } from "../../routes/list/types";
8
8
  import { RoutePoint } from "../../routes/base/types";
9
9
  import { ActivityState } from "./types";
10
10
  import { Route } from "../../routes/base/model/route";
11
11
  import { ActivityStatsCalculator } from "./stats";
12
12
  import { ActivityDuration } from "./duration";
13
+ import { ActivityUploadFactory } from "../upload";
13
14
  export declare class ActivityRideService extends IncyclistService {
14
15
  protected observer: Observer;
15
16
  protected activity: ActivityDetails;
@@ -26,6 +27,8 @@ export declare class ActivityRideService extends IncyclistService {
26
27
  routeDistance: number;
27
28
  speed: number;
28
29
  };
30
+ protected saveObserver: PromiseObserver<boolean>;
31
+ protected isSaveDone: boolean;
29
32
  protected statsCalculator: ActivityStatsCalculator;
30
33
  protected durationCalculator: ActivityDuration;
31
34
  protected deviceDataHandler: any;
@@ -47,13 +50,14 @@ export declare class ActivityRideService extends IncyclistService {
47
50
  init(id?: string): Observer;
48
51
  start(): void;
49
52
  stop(): void;
50
- private updateActivityTime;
51
53
  pause(autoResume?: boolean): void;
52
54
  resume(requester?: 'user' | 'system'): void;
53
55
  getDashboardDisplayProperties(): any[];
54
- getActivitySummaryDisplayProperties(): void;
56
+ getActivitySummaryDisplayProperties(): {};
55
57
  getActivity(): ActivityDetails;
56
- save(): Promise<void>;
58
+ setTitle(title: string): void;
59
+ delete(): Promise<void>;
60
+ save(): PromiseObserver<boolean>;
57
61
  getObserver(): Observer;
58
62
  addSceenshot(screenshot: ScreenShotInfo): void;
59
63
  onRouteUpdate(points: Array<RoutePoint>): void;
@@ -64,15 +68,19 @@ export declare class ActivityRideService extends IncyclistService {
64
68
  protected _save(): Promise<void>;
65
69
  protected getSaveInterval(): number;
66
70
  protected updateRepo(): Promise<void>;
71
+ protected convert(format: string): Promise<boolean>;
72
+ protected upload(format: string): Promise<boolean>;
67
73
  protected getRepo(): ActivitiesRepository;
68
74
  protected createLogRecord(): ActivityLogRecord;
69
75
  protected createActivity(requestedId?: string): ActivityDetails;
76
+ protected changeTitle(newTitle: string): void;
70
77
  protected updateActivityState(): void;
71
78
  protected getTotalDistance(): number;
72
79
  protected getTotalElevation(): number;
73
80
  protected update(): void;
74
81
  protected createFreeRide(settings: FreeRideStartSettings): Route;
75
82
  protected getMode(routeType: ActivityRouteType): "video" | "free ride" | "follow route";
83
+ protected updateActivityTime(): void;
76
84
  getRideProps(): any;
77
85
  protected logActivityUpdateMessage(): void;
78
86
  getBikeInterface(): string;
@@ -83,5 +91,8 @@ export declare class ActivityRideService extends IncyclistService {
83
91
  protected getRouteList(): import("../../routes").RouteListService;
84
92
  protected getDeviceRide(): import("../../devices").DeviceRideService;
85
93
  protected getDeviceConfiguration(): import("../../devices").DeviceConfigurationService;
94
+ protected getActivityUploadFactory(): ActivityUploadFactory;
95
+ protected getActivityConverterfactory(): ActivityConverterFactory;
96
+ protected getFileSystemBinding(): import("../../api/fs").IFileSystem;
86
97
  }
87
98
  export declare const useActivityRide: () => ActivityRideService;
@@ -63,6 +63,8 @@ const route_1 = require("../../routes/base/utils/route");
63
63
  const route_2 = require("../../routes/base/model/route");
64
64
  const stats_1 = require("./stats");
65
65
  const duration_1 = require("./duration");
66
+ const upload_1 = require("../upload");
67
+ const api_1 = require("../../api");
66
68
  const SAVE_INTERVAL = 5000;
67
69
  let ActivityRideService = (() => {
68
70
  let _classDecorators = [types_1.Singleton];
@@ -115,6 +117,7 @@ let ActivityRideService = (() => {
115
117
  this.state = 'active';
116
118
  this.tsStart = Date.now();
117
119
  this.tsPauseStart = undefined;
120
+ this.isSaveDone = false;
118
121
  this.logEvent({ message: 'activity started' });
119
122
  this.startWorker();
120
123
  this.emit('started');
@@ -132,13 +135,6 @@ let ActivityRideService = (() => {
132
135
  delete this.tsStart;
133
136
  (0, utils_1.waitNextTick)().then(() => { delete this.observer; });
134
137
  }
135
- updateActivityTime() {
136
- var _a;
137
- if (!this.activity)
138
- return;
139
- this.activity.timeTotal = (Date.now() - this.tsStart) / 1000;
140
- this.activity.time = this.activity.timeTotal - ((_a = this.activity.timePause) !== null && _a !== void 0 ? _a : 0);
141
- }
142
138
  pause(autoResume = false) {
143
139
  if (this.state !== 'active')
144
140
  return;
@@ -164,6 +160,7 @@ let ActivityRideService = (() => {
164
160
  this.logEvent({ message: 'activity resumed', requester });
165
161
  this.updateActivityTime();
166
162
  this._save();
163
+ this.isSaveDone = false;
167
164
  }
168
165
  getDashboardDisplayProperties() {
169
166
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
@@ -229,12 +226,91 @@ let ActivityRideService = (() => {
229
226
  }
230
227
  }
231
228
  getActivitySummaryDisplayProperties() {
229
+ var _a, _b;
230
+ try {
231
+ const route = (0, routes_1.useRouteList)().getSelected();
232
+ const showSave = this.activity !== undefined && !this.isSaveDone;
233
+ const showContinue = this.state !== 'completed';
234
+ const showMap = (_a = route === null || route === void 0 ? void 0 : route.description) === null || _a === void 0 ? void 0 : _a.hasGpx;
235
+ const preview = showMap ? undefined : (_b = route === null || route === void 0 ? void 0 : route.description) === null || _b === void 0 ? void 0 : _b.previewUrl;
236
+ const props = {
237
+ activity: this.activity,
238
+ showSave,
239
+ showContinue,
240
+ showMap,
241
+ preview
242
+ };
243
+ return props;
244
+ }
245
+ catch (err) {
246
+ this.logError(err, 'getActivitySummaryDisplayProperties()');
247
+ return {};
248
+ }
232
249
  }
233
250
  getActivity() {
234
251
  return this.activity;
235
252
  }
253
+ setTitle(title) {
254
+ this.activity.title = title;
255
+ this._save();
256
+ }
257
+ delete() {
258
+ return __awaiter(this, void 0, void 0, function* () {
259
+ if (this.state === 'idle' || !this.observer)
260
+ return;
261
+ if (this.state !== 'completed') {
262
+ this.stop();
263
+ yield new Promise(done => { this.observer.on('completed', done); });
264
+ }
265
+ this.getRepo().delete(this.activity.id);
266
+ this.state = 'idle';
267
+ });
268
+ }
236
269
  save() {
237
- return this._save();
270
+ console.log('~~~ ActivityRide.save', this.saveObserver !== undefined, this.isSaveDone);
271
+ if (this.saveObserver) {
272
+ return this.saveObserver;
273
+ }
274
+ const emit = (event, ...args) => {
275
+ if (this.saveObserver)
276
+ this.saveObserver.emit(event, ...args);
277
+ console.log('~~~ ActivityRide.emit', event, args, this.saveObserver !== undefined, this.isSaveDone);
278
+ };
279
+ const run = () => __awaiter(this, void 0, void 0, function* () {
280
+ let success = false;
281
+ try {
282
+ emit('start', success);
283
+ yield this._save();
284
+ let format = undefined;
285
+ let uploadSuccess = false;
286
+ let convertSuccess = yield this.convert('TCX');
287
+ if (convertSuccess) {
288
+ format = 'TCX';
289
+ this.convert('FIT');
290
+ }
291
+ else {
292
+ convertSuccess == (yield this.convert('FIT'));
293
+ if (convertSuccess)
294
+ format = 'FIT';
295
+ }
296
+ if (convertSuccess) {
297
+ uploadSuccess = yield this.upload(format);
298
+ }
299
+ success = convertSuccess && uploadSuccess;
300
+ this.isSaveDone = true;
301
+ }
302
+ catch (err) {
303
+ success = false;
304
+ }
305
+ emit('done', success);
306
+ (0, utils_1.waitNextTick)().then(() => {
307
+ delete this.saveObserver;
308
+ });
309
+ return success;
310
+ });
311
+ this.saveObserver = new observer_1.PromiseObserver(run());
312
+ console.log('~~~ ActivityRide.save returns', this.saveObserver !== undefined, this.isSaveDone);
313
+ return this.saveObserver;
238
314
  }
239
315
  getObserver() {
240
316
  if (this.state === 'idle')
@@ -267,6 +343,8 @@ let ActivityRideService = (() => {
267
343
  onDeviceData(data) {
268
344
  if (!this.current)
269
345
  return;
346
+ if (!data)
347
+ return;
270
348
  try {
271
349
  this.current.tsDeviceData = Date.now();
272
350
  const update = Object.assign({}, data);
@@ -332,6 +410,60 @@ let ActivityRideService = (() => {
332
410
  }
333
411
  });
334
412
  }
413
+ convert(format) {
414
+ return __awaiter(this, void 0, void 0, function* () {
415
+ const emit = (event, ...args) => {
416
+ if (this.saveObserver)
417
+ this.saveObserver.emit(event, ...args);
418
+ else {
419
+ try {
420
+ throw new Error('');
421
+ }
422
+ catch (err) {
423
+ console.log('~~~ WARN: emitting on non existing observer', event.args, err.stack);
424
+ }
425
+ }
426
+ };
427
+ const fs = this.getFileSystemBinding();
428
+ emit('convert.start', format);
429
+ try {
430
+ let data;
431
+ data = yield base_1.ActivityConverter.convert(this.activity, format);
432
+ emit('convert.done', format, true);
433
+ if (format.toLowerCase() === 'fit') {
434
+ const fileName = this.activity.fileName.replace('json', 'fit');
435
+ yield fs.writeFile(fileName, Buffer.from(data));
436
+ this.activity.fitFileName = fileName;
437
+ }
438
+ if (format.toLowerCase() === 'tcx') {
439
+ const fileName = this.activity.fileName.replace('json', 'tcx');
440
+ yield fs.writeFile(fileName, Buffer.from(data));
441
+ this.activity.tcxFileName = fileName;
442
+ }
443
+ yield this._save();
444
+ return true;
445
+ }
446
+ catch (err) {
447
+ emit('convert.error', format, err);
448
+ return false;
449
+ }
450
+ });
451
+ }
452
+ upload(format) {
453
+ return __awaiter(this, void 0, void 0, function* () {
454
+ const factory = this.getActivityUploadFactory();
455
+ this.saveObserver.emit('upload.start', format);
456
+ try {
457
+ yield factory.upload(this.activity, format.toLowerCase());
458
+ this.saveObserver.emit('upload.done', format, true);
459
+ return true;
460
+ }
461
+ catch (err) {
462
+ this.saveObserver.emit('upload.error', format, err);
463
+ return false;
464
+ }
465
+ });
466
+ }
335
467
  getRepo() {
336
468
  if (this.repo)
337
469
  return this.repo;
@@ -436,6 +568,11 @@ let ActivityRideService = (() => {
436
568
  };
437
569
  return activity;
438
570
  }
571
+ changeTitle(newTitle) {
572
+ if (!(newTitle === null || newTitle === void 0 ? void 0 : newTitle.length))
573
+ return;
574
+ this.activity.title = newTitle;
575
+ }
439
576
  updateActivityState() {
440
577
  var _a, _b;
441
578
  if (this.state !== 'active')
@@ -451,7 +588,7 @@ let ActivityRideService = (() => {
451
588
  this.current.routeDistance += distance;
452
589
  if (distance !== 0) {
453
590
  const prev = this.current.position;
454
- const position = (_b = (0, route_1.getNextPosition)(this.current.route, { distance, prev: this.current.position })) !== null && _b !== void 0 ? _b : (0, route_1.getNextPosition)(this.current.route, { routeDistance: this.current.routeDistance });
591
+ const position = (_b = (0, route_1.getNextPosition)(this.current.route, { routeDistance: this.current.routeDistance, prev: this.current.position })) !== null && _b !== void 0 ? _b : (0, route_1.getNextPosition)(this.current.route, { distance, prev: this.current.position });
455
592
  this.current.position = position !== null && position !== void 0 ? position : prev;
456
593
  if (this.activity.realityFactor > 0) {
457
594
  const gain = (0, route_1.getElevationGainAt)(this.current.route, this.current.routeDistance);
@@ -555,6 +692,13 @@ let ActivityRideService = (() => {
555
692
  return 'follow route';
556
693
  return 'free ride';
557
694
  }
695
+ updateActivityTime() {
696
+ var _a;
697
+ if (!this.activity)
698
+ return;
699
+ this.activity.timeTotal = (Date.now() - this.tsStart) / 1000;
700
+ this.activity.time = this.activity.timeTotal - ((_a = this.activity.timePause) !== null && _a !== void 0 ? _a : 0);
701
+ }
558
702
  getRideProps() {
559
703
  var _a;
560
704
  const { startPos, realityFactor, routeType } = this.activity;
@@ -613,6 +757,15 @@ let ActivityRideService = (() => {
613
757
  getDeviceConfiguration() {
614
758
  return (0, devices_1.useDeviceConfiguration)();
615
759
  }
760
+ getActivityUploadFactory() {
761
+ return new upload_1.ActivityUploadFactory();
762
+ }
763
+ getActivityConverterfactory() {
764
+ return new base_1.ActivityConverterFactory();
765
+ }
766
+ getFileSystemBinding() {
767
+ return (0, api_1.getBindings)().fs;
768
+ }
616
769
  };
617
770
  __setFunctionName(_classThis, "ActivityRideService");
618
771
  (() => {
@@ -0,0 +1,8 @@
1
+ import { ActivityDetails } from "../base";
2
+ import { IActivityUpload, UploaderInfo } from "./types";
3
+ export declare class ActivityUploadFactory {
4
+ protected uploaders: Array<UploaderInfo>;
5
+ constructor();
6
+ add(service: string, uploader: IActivityUpload): void;
7
+ upload(activity: ActivityDetails, format?: string): Promise<any[]>;
8
+ }
@@ -0,0 +1,98 @@
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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
46
+ if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
47
+ return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.ActivityUploadFactory = void 0;
51
+ const types_1 = require("../../base/types");
52
+ let ActivityUploadFactory = (() => {
53
+ let _classDecorators = [types_1.Singleton];
54
+ let _classDescriptor;
55
+ let _classExtraInitializers = [];
56
+ let _classThis;
57
+ var ActivityUploadFactory = _classThis = class {
58
+ constructor() {
59
+ this.uploaders = [];
60
+ }
61
+ add(service, uploader) {
62
+ const existing = this.uploaders.findIndex(ui => ui.service === service);
63
+ if (existing === -1)
64
+ this.uploaders.push({ service, uploader });
65
+ else
66
+ this.uploaders[existing] = { service, uploader };
67
+ }
68
+ upload(activity_1) {
69
+ return __awaiter(this, arguments, void 0, function* (activity, format = 'TCX') {
70
+ const uploads = [];
71
+ this.uploaders.forEach(ui => {
72
+ const { service, uploader } = ui;
73
+ if (uploader.isConnected()) {
74
+ const promise = uploader.upload(activity, format)
75
+ .then(success => ({ service, success }))
76
+ .catch(err => ({ service, success: false, error: err.message }));
77
+ uploads.push({ service, promise, status: undefined });
78
+ }
79
+ });
80
+ if (uploads.length > 0) {
81
+ const result = yield Promise.allSettled(uploads.map(us => us.promise));
82
+ }
83
+ else
84
+ return [];
85
+ });
86
+ }
87
+ };
88
+ __setFunctionName(_classThis, "ActivityUploadFactory");
89
+ (() => {
90
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
91
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
92
+ ActivityUploadFactory = _classThis = _classDescriptor.value;
93
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
94
+ __runInitializers(_classThis, _classExtraInitializers);
95
+ })();
96
+ return ActivityUploadFactory = _classThis;
97
+ })();
98
+ exports.ActivityUploadFactory = ActivityUploadFactory;
@@ -1,2 +1,4 @@
1
1
  export * from './velohero';
2
+ export * from './strava';
2
3
  export * from './types';
4
+ export * from './factory';
@@ -14,5 +14,13 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ const factory_1 = require("./factory");
18
+ const strava_1 = require("./strava");
19
+ const velohero_1 = require("./velohero");
17
20
  __exportStar(require("./velohero"), exports);
21
+ __exportStar(require("./strava"), exports);
18
22
  __exportStar(require("./types"), exports);
23
+ __exportStar(require("./factory"), exports);
24
+ const factory = new factory_1.ActivityUploadFactory();
25
+ factory.add('strava', new strava_1.StravaUpload());
26
+ factory.add('velohero', new velohero_1.VeloHeroUpload());
@@ -0,0 +1,24 @@
1
+ import { IncyclistService } from "../../base/service";
2
+ import { ActivityDetails } from "../base";
3
+ import { StravaApi, StravaConfig, StravaFormat } from "../../apps/base/api/strava";
4
+ import { IActivityUpload } from "./types";
5
+ export declare class StravaUpload extends IncyclistService implements IActivityUpload {
6
+ protected isInitialized: any;
7
+ protected api: StravaApi;
8
+ protected config: StravaConfig;
9
+ protected _isConnecting: any;
10
+ constructor();
11
+ init(): boolean;
12
+ isConnected(): boolean;
13
+ isConnecting(): boolean;
14
+ connect(accessToken: string, refreshToken: string, expiration?: Date): Promise<boolean>;
15
+ disconnect(): void;
16
+ upload(activity: ActivityDetails, format?: string): Promise<boolean>;
17
+ protected getStravaFormat(format: string): StravaFormat;
18
+ protected getCredentials(): any;
19
+ protected saveCredentials(): void;
20
+ protected updateConfig(config: StravaConfig): void;
21
+ protected getSecret(key: string): string;
22
+ protected getUserSettings(): import("../../settings").UserSettingsService;
23
+ protected getApi(): StravaApi;
24
+ }