incyclist-services 1.7.139 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/activities/active-rides/service.js +1 -1
- package/lib/cjs/activities/page/service.js +211 -7
- package/lib/cjs/activities/ride/service.js +17 -3
- package/lib/cjs/api/bindings/index.js +1 -0
- package/lib/cjs/fileaccess/externalFiles.js +39 -32
- package/lib/cjs/fileaccess/service.js +42 -28
- package/lib/cjs/fileaccess/utils.js +6 -2
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/ride/page/service.js +168 -8
- package/lib/cjs/routes/download/service.js +8 -0
- package/lib/cjs/routes/index.js +1 -0
- package/lib/cjs/routes/library/service.js +118 -22
- package/lib/cjs/routes/list/cards/RouteCard.js +23 -5
- package/lib/cjs/routes/list/service.js +124 -63
- package/lib/cjs/routes/page/service.js +318 -12
- package/lib/cjs/routes/previews/store.js +9 -10
- package/lib/cjs/routes/video-availability/journal.js +2 -2
- package/lib/cjs/routes/video-availability/pageActions.js +19 -3
- package/lib/cjs/routes/video-availability/service.js +29 -20
- package/lib/esm/activities/active-rides/service.js +1 -1
- package/lib/esm/activities/page/service.js +211 -7
- package/lib/esm/activities/ride/service.js +17 -3
- package/lib/esm/api/bindings/index.js +1 -0
- package/lib/esm/fileaccess/externalFiles.js +39 -32
- package/lib/esm/fileaccess/service.js +42 -28
- package/lib/esm/fileaccess/utils.js +6 -2
- package/lib/esm/index.js +1 -0
- package/lib/esm/ride/page/service.js +168 -8
- package/lib/esm/routes/download/service.js +8 -0
- package/lib/esm/routes/index.js +1 -0
- package/lib/esm/routes/library/service.js +118 -22
- package/lib/esm/routes/list/cards/RouteCard.js +23 -5
- package/lib/esm/routes/list/service.js +124 -63
- package/lib/esm/routes/page/service.js +318 -12
- package/lib/esm/routes/previews/store.js +9 -10
- package/lib/esm/routes/video-availability/journal.js +2 -2
- package/lib/esm/routes/video-availability/pageActions.js +19 -3
- package/lib/esm/routes/video-availability/service.js +29 -20
- package/lib/types/activities/active-rides/types.d.ts +1 -0
- package/lib/types/activities/page/service.d.ts +30 -2
- package/lib/types/activities/page/types.d.ts +22 -2
- package/lib/types/activities/ride/service.d.ts +2 -0
- package/lib/types/api/bindings/index.d.ts +2 -0
- package/lib/types/api/types.d.ts +1 -0
- package/lib/types/api/ui/index.d.ts +6 -1
- package/lib/types/fileaccess/externalFiles.d.ts +2 -0
- package/lib/types/fileaccess/service.d.ts +4 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/ride/page/service.d.ts +27 -0
- package/lib/types/ride/page/types.d.ts +5 -0
- package/lib/types/routes/base/types/index.d.ts +1 -0
- package/lib/types/routes/download/service.d.ts +1 -0
- package/lib/types/routes/index.d.ts +1 -0
- package/lib/types/routes/library/service.d.ts +7 -0
- package/lib/types/routes/library/types.d.ts +8 -0
- package/lib/types/routes/list/cards/RouteCard.d.ts +4 -0
- package/lib/types/routes/list/cards/types.d.ts +2 -0
- package/lib/types/routes/list/service.d.ts +8 -5
- package/lib/types/routes/page/service.d.ts +46 -1
- package/lib/types/routes/page/types.d.ts +59 -1
- package/lib/types/routes/types.d.ts +1 -0
- package/lib/types/routes/video-availability/pageActions.d.ts +2 -1
- package/lib/types/routes/video-availability/service.d.ts +3 -1
- package/lib/types/types.d.ts +1 -0
- package/package.json +5 -5
|
@@ -720,7 +720,7 @@ let ActiveRidesService = (() => {
|
|
|
720
720
|
if (!this.current || !this.isOnline)
|
|
721
721
|
return;
|
|
722
722
|
const topic = `incyclist/activity/${this.session}/${this.getRouteHash()}/update`;
|
|
723
|
-
this.getMessageQueue().sendMessage(topic, payload);
|
|
723
|
+
this.getMessageQueue().sendMessage(topic, { ...payload, ts: Date.now() });
|
|
724
724
|
}
|
|
725
725
|
async onActivityUpdateEvent(session, payload) {
|
|
726
726
|
try {
|
|
@@ -42,6 +42,9 @@ const list_1 = require("../list");
|
|
|
42
42
|
const sleep_1 = require("../../utils/sleep");
|
|
43
43
|
const workouts_1 = require("../../workouts");
|
|
44
44
|
const devices_1 = require("../../devices");
|
|
45
|
+
const service_1 = require("../../fileaccess/service");
|
|
46
|
+
const service_2 = require("../../routes/video-availability/service");
|
|
47
|
+
const pageActions_1 = require("../../routes/video-availability/pageActions");
|
|
45
48
|
let ActivitiesPageService = (() => {
|
|
46
49
|
let _classDecorators = [decorators_1.Singleton];
|
|
47
50
|
let _classDescriptor;
|
|
@@ -52,6 +55,9 @@ let ActivitiesPageService = (() => {
|
|
|
52
55
|
let _getActivityList_decorators;
|
|
53
56
|
let _getWorkoutList_decorators;
|
|
54
57
|
let _getDevicePairing_decorators;
|
|
58
|
+
let _getFolderAccess_decorators;
|
|
59
|
+
let _getAvailability_decorators;
|
|
60
|
+
let _getVideoActions_decorators;
|
|
55
61
|
var ActivitiesPageService = class extends _classSuper {
|
|
56
62
|
static { _classThis = this; }
|
|
57
63
|
static {
|
|
@@ -59,9 +65,15 @@ let ActivitiesPageService = (() => {
|
|
|
59
65
|
_getActivityList_decorators = [decorators_1.Injectable];
|
|
60
66
|
_getWorkoutList_decorators = [decorators_1.Injectable];
|
|
61
67
|
_getDevicePairing_decorators = [decorators_1.Injectable];
|
|
68
|
+
_getFolderAccess_decorators = [decorators_1.Injectable];
|
|
69
|
+
_getAvailability_decorators = [decorators_1.Injectable];
|
|
70
|
+
_getVideoActions_decorators = [decorators_1.Injectable];
|
|
62
71
|
__esDecorate(this, null, _getActivityList_decorators, { kind: "method", name: "getActivityList", static: false, private: false, access: { has: obj => "getActivityList" in obj, get: obj => obj.getActivityList }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
63
72
|
__esDecorate(this, null, _getWorkoutList_decorators, { kind: "method", name: "getWorkoutList", static: false, private: false, access: { has: obj => "getWorkoutList" in obj, get: obj => obj.getWorkoutList }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
64
73
|
__esDecorate(this, null, _getDevicePairing_decorators, { kind: "method", name: "getDevicePairing", static: false, private: false, access: { has: obj => "getDevicePairing" in obj, get: obj => obj.getDevicePairing }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
74
|
+
__esDecorate(this, null, _getFolderAccess_decorators, { kind: "method", name: "getFolderAccess", static: false, private: false, access: { has: obj => "getFolderAccess" in obj, get: obj => obj.getFolderAccess }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
75
|
+
__esDecorate(this, null, _getAvailability_decorators, { kind: "method", name: "getAvailability", static: false, private: false, access: { has: obj => "getAvailability" in obj, get: obj => obj.getAvailability }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
76
|
+
__esDecorate(this, null, _getVideoActions_decorators, { kind: "method", name: "getVideoActions", static: false, private: false, access: { has: obj => "getVideoActions" in obj, get: obj => obj.getVideoActions }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
65
77
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
66
78
|
ActivitiesPageService = _classThis = _classDescriptor.value;
|
|
67
79
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
@@ -71,6 +83,9 @@ let ActivitiesPageService = (() => {
|
|
|
71
83
|
listState;
|
|
72
84
|
detailActivityId;
|
|
73
85
|
isOpen = false;
|
|
86
|
+
rideAgainRoute;
|
|
87
|
+
rideAgainCheck = null;
|
|
88
|
+
rideAgainVideoUpdateHandler = this.onRideAgainVideoUpdate.bind(this);
|
|
74
89
|
constructor() {
|
|
75
90
|
super('ActivitiesPage');
|
|
76
91
|
}
|
|
@@ -78,6 +93,7 @@ let ActivitiesPageService = (() => {
|
|
|
78
93
|
if (this.isOpen) {
|
|
79
94
|
this.getActivityList().closeList();
|
|
80
95
|
this.stopEventListener();
|
|
96
|
+
this.closeRideAgainCheck();
|
|
81
97
|
}
|
|
82
98
|
try {
|
|
83
99
|
this.logEvent({ message: 'page shown', page: 'Activities' });
|
|
@@ -102,6 +118,7 @@ let ActivitiesPageService = (() => {
|
|
|
102
118
|
this.logEvent({ message: 'page closed', page: 'Activities' });
|
|
103
119
|
this.getActivityList().closeList();
|
|
104
120
|
this.stopEventListener();
|
|
121
|
+
this.closeRideAgainCheck();
|
|
105
122
|
this.isOpen = false;
|
|
106
123
|
super.closePage();
|
|
107
124
|
}
|
|
@@ -121,6 +138,7 @@ let ActivitiesPageService = (() => {
|
|
|
121
138
|
resumePage() {
|
|
122
139
|
try {
|
|
123
140
|
this.startEventListener();
|
|
141
|
+
this.getAvailability().onForeground().catch(err => this.logError(err, 'resumePage'));
|
|
124
142
|
return super.resumePage();
|
|
125
143
|
}
|
|
126
144
|
catch (err) {
|
|
@@ -131,7 +149,8 @@ let ActivitiesPageService = (() => {
|
|
|
131
149
|
const props = {
|
|
132
150
|
loading: this.listState.loading,
|
|
133
151
|
activities: this.listState.activities,
|
|
134
|
-
detailActivityId: this.detailActivityId
|
|
152
|
+
detailActivityId: this.detailActivityId,
|
|
153
|
+
rideAgainCheck: this.rideAgainCheck
|
|
135
154
|
};
|
|
136
155
|
return props;
|
|
137
156
|
}
|
|
@@ -165,16 +184,128 @@ let ActivitiesPageService = (() => {
|
|
|
165
184
|
return { activityId, attachedWorkout: null };
|
|
166
185
|
}
|
|
167
186
|
}
|
|
168
|
-
onRideAgain(route) {
|
|
187
|
+
async onRideAgain(route) {
|
|
169
188
|
try {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
189
|
+
if (!route?.id || !this.getVideoActions().isSupported()) {
|
|
190
|
+
this.navigateToRide(route);
|
|
191
|
+
return 'started';
|
|
192
|
+
}
|
|
193
|
+
let status;
|
|
194
|
+
try {
|
|
195
|
+
await this.getFolderAccess().activateAll();
|
|
196
|
+
status = await this.getAvailability().refresh(route.id);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
this.logError(err, 'onRideAgain');
|
|
200
|
+
this.navigateToRide(route);
|
|
201
|
+
return 'started';
|
|
202
|
+
}
|
|
203
|
+
if (status.state === 'ready' || status.state === 'unknown') {
|
|
204
|
+
this.closeRideAgainCheck();
|
|
205
|
+
this.navigateToRide(route);
|
|
206
|
+
return 'started';
|
|
207
|
+
}
|
|
208
|
+
this.openRideAgainCheck(route);
|
|
209
|
+
return 'blocked';
|
|
175
210
|
}
|
|
176
211
|
catch (err) {
|
|
177
212
|
this.logError(err, 'onRideAgain');
|
|
213
|
+
return 'started';
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
onRideAgainCheckClosed() {
|
|
217
|
+
try {
|
|
218
|
+
this.closeRideAgainCheck();
|
|
219
|
+
this.updatePageDisplay();
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
this.logError(err, 'onRideAgainCheckClosed');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
onRideAgainCheckStart() {
|
|
226
|
+
const route = this.rideAgainRoute;
|
|
227
|
+
if (!route)
|
|
228
|
+
return Promise.resolve('blocked');
|
|
229
|
+
return this.onRideAgain(route);
|
|
230
|
+
}
|
|
231
|
+
onVideoDownloadPressed(routeId) {
|
|
232
|
+
try {
|
|
233
|
+
this.getVideoActions().onVideoDownloadPressed(routeId);
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
this.logError(err, 'onVideoDownloadPressed');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
onVideoDownloadConfirmed(routeId, choice) {
|
|
240
|
+
try {
|
|
241
|
+
this.getVideoActions().onVideoDownloadConfirmed(routeId, choice);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
this.logError(err, 'onVideoDownloadConfirmed');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
onVideoDownloadDismissed(routeId) {
|
|
248
|
+
try {
|
|
249
|
+
this.getVideoActions().onVideoDownloadDismissed(routeId);
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
this.logError(err, 'onVideoDownloadDismissed');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
onVideoStop(routeId) {
|
|
256
|
+
try {
|
|
257
|
+
this.getVideoActions().onVideoStop(routeId);
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
this.logError(err, 'onVideoStop');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
onVideoRetry(routeId) {
|
|
264
|
+
try {
|
|
265
|
+
this.getVideoActions().onVideoRetry(routeId);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
this.logError(err, 'onVideoRetry');
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
onVideoKeepInstead(routeId) {
|
|
272
|
+
try {
|
|
273
|
+
this.getVideoActions().onVideoKeepInstead(routeId);
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
this.logError(err, 'onVideoKeepInstead');
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
onVideoRemovePressed(routeId) {
|
|
280
|
+
try {
|
|
281
|
+
this.getVideoActions().onVideoRemovePressed(routeId);
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
this.logError(err, 'onVideoRemovePressed');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
onVideoRemoveConfirmed(routeId) {
|
|
288
|
+
try {
|
|
289
|
+
this.getVideoActions().onVideoRemoveConfirmed(routeId);
|
|
290
|
+
}
|
|
291
|
+
catch (err) {
|
|
292
|
+
this.logError(err, 'onVideoRemoveConfirmed');
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
onVideoRemoveDismissed(routeId) {
|
|
296
|
+
try {
|
|
297
|
+
this.getVideoActions().onVideoRemoveDismissed(routeId);
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
this.logError(err, 'onVideoRemoveDismissed');
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async onConfirmAccess(routeId) {
|
|
304
|
+
try {
|
|
305
|
+
await this.getVideoActions().onConfirmAccess(routeId);
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
this.logError(err, 'onConfirmAccess');
|
|
178
309
|
}
|
|
179
310
|
}
|
|
180
311
|
onClearWorkoutSelection() {
|
|
@@ -198,6 +329,70 @@ let ActivitiesPageService = (() => {
|
|
|
198
329
|
updatePageDisplay() {
|
|
199
330
|
this.getPageObserver()?.emit('page-update');
|
|
200
331
|
}
|
|
332
|
+
navigateToRide(route) {
|
|
333
|
+
const pairing = this.getDevicePairing();
|
|
334
|
+
const readyToStart = pairing.isReadyToStart();
|
|
335
|
+
this.logEvent({ message: 'Attempting to start a ride again', id: route?.id, title: route?.title, readyToStart });
|
|
336
|
+
const next = readyToStart ? '/rideDeviceOK' : '/pairingStart';
|
|
337
|
+
this.moveTo(next);
|
|
338
|
+
}
|
|
339
|
+
openRideAgainCheck(route) {
|
|
340
|
+
this.rideAgainRoute = route;
|
|
341
|
+
this.rideAgainCheck = {
|
|
342
|
+
routeId: route.id,
|
|
343
|
+
routeTitle: route.title ?? '',
|
|
344
|
+
video: this.getVideoActions().getDisplayProps(route.id),
|
|
345
|
+
downloadedWhileOpen: false
|
|
346
|
+
};
|
|
347
|
+
this.subscribeToRideAgainVideoUpdates();
|
|
348
|
+
this.updatePageDisplay();
|
|
349
|
+
}
|
|
350
|
+
closeRideAgainCheck() {
|
|
351
|
+
if (!this.rideAgainCheck && !this.rideAgainRoute)
|
|
352
|
+
return;
|
|
353
|
+
this.unsubscribeFromRideAgainVideoUpdates();
|
|
354
|
+
this.rideAgainRoute = undefined;
|
|
355
|
+
this.rideAgainCheck = null;
|
|
356
|
+
}
|
|
357
|
+
subscribeToRideAgainVideoUpdates() {
|
|
358
|
+
try {
|
|
359
|
+
this.unsubscribeFromRideAgainVideoUpdates();
|
|
360
|
+
this.getAvailability().on('route-video-update', this.rideAgainVideoUpdateHandler);
|
|
361
|
+
this.getVideoActions().on('route-video-actions-update', this.rideAgainVideoUpdateHandler);
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
this.logError(err, 'subscribeToRideAgainVideoUpdates');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
unsubscribeFromRideAgainVideoUpdates() {
|
|
368
|
+
try {
|
|
369
|
+
this.getAvailability().off('route-video-update', this.rideAgainVideoUpdateHandler);
|
|
370
|
+
this.getVideoActions().off('route-video-actions-update', this.rideAgainVideoUpdateHandler);
|
|
371
|
+
}
|
|
372
|
+
catch (err) {
|
|
373
|
+
this.logError(err, 'unsubscribeFromRideAgainVideoUpdates');
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
onRideAgainVideoUpdate(routeId) {
|
|
377
|
+
try {
|
|
378
|
+
const route = this.rideAgainRoute;
|
|
379
|
+
if (!route || !this.rideAgainCheck)
|
|
380
|
+
return;
|
|
381
|
+
if (routeId && routeId !== route.id)
|
|
382
|
+
return;
|
|
383
|
+
const status = this.getAvailability().getStatus(route.id);
|
|
384
|
+
const nowReady = status.state === 'ready' || status.state === 'unknown';
|
|
385
|
+
this.rideAgainCheck = {
|
|
386
|
+
...this.rideAgainCheck,
|
|
387
|
+
video: this.getVideoActions().getDisplayProps(route.id),
|
|
388
|
+
downloadedWhileOpen: this.rideAgainCheck.downloadedWhileOpen || nowReady
|
|
389
|
+
};
|
|
390
|
+
this.updatePageDisplay();
|
|
391
|
+
}
|
|
392
|
+
catch (err) {
|
|
393
|
+
this.logError(err, 'onRideAgainVideoUpdate');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
201
396
|
onStateUpdate(state) {
|
|
202
397
|
if (state)
|
|
203
398
|
this.listState = state;
|
|
@@ -226,6 +421,15 @@ let ActivitiesPageService = (() => {
|
|
|
226
421
|
getDevicePairing() {
|
|
227
422
|
return (0, devices_1.useDevicePairing)();
|
|
228
423
|
}
|
|
424
|
+
getFolderAccess() {
|
|
425
|
+
return (0, service_1.useFolderAccess)();
|
|
426
|
+
}
|
|
427
|
+
getAvailability() {
|
|
428
|
+
return (0, service_2.useRouteVideoAvailability)();
|
|
429
|
+
}
|
|
430
|
+
getVideoActions() {
|
|
431
|
+
return (0, pageActions_1.useRouteVideoPageActions)();
|
|
432
|
+
}
|
|
229
433
|
};
|
|
230
434
|
return ActivitiesPageService = _classThis;
|
|
231
435
|
})();
|
|
@@ -101,6 +101,7 @@ let ActivityRideService = (() => {
|
|
|
101
101
|
isSaveDone;
|
|
102
102
|
isDonateShown;
|
|
103
103
|
isSummaryShown;
|
|
104
|
+
stopPromise;
|
|
104
105
|
statsCalculator;
|
|
105
106
|
durationCalculator;
|
|
106
107
|
deviceDataHandler = this.onDeviceData.bind(this);
|
|
@@ -183,6 +184,17 @@ let ActivityRideService = (() => {
|
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
async stop() {
|
|
187
|
+
if (this.stopPromise !== undefined)
|
|
188
|
+
return this.stopPromise;
|
|
189
|
+
this.stopPromise = this.doStop();
|
|
190
|
+
try {
|
|
191
|
+
await this.stopPromise;
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
delete this.stopPromise;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async doStop() {
|
|
186
198
|
this.stopWorker();
|
|
187
199
|
if (!this.observer || !this.activity)
|
|
188
200
|
return;
|
|
@@ -206,7 +218,9 @@ let ActivityRideService = (() => {
|
|
|
206
218
|
delete this.observer;
|
|
207
219
|
}
|
|
208
220
|
async cleanup() {
|
|
209
|
-
if (this.
|
|
221
|
+
if (this.stopPromise !== undefined)
|
|
222
|
+
await this.stopPromise;
|
|
223
|
+
else if (this.state !== 'idle' && this.state !== 'completed')
|
|
210
224
|
await this.stop();
|
|
211
225
|
delete this.activity;
|
|
212
226
|
}
|
|
@@ -1161,7 +1175,7 @@ let ActivityRideService = (() => {
|
|
|
1161
1175
|
getTotalDistance() {
|
|
1162
1176
|
try {
|
|
1163
1177
|
const route = this.current.route;
|
|
1164
|
-
if (!route)
|
|
1178
|
+
if (!route || !this.activity)
|
|
1165
1179
|
return 0;
|
|
1166
1180
|
const isLoop = (0, route_1.checkIsLoop)(route);
|
|
1167
1181
|
const totalRouteDistance = this.current?.endPos ?? route.distance ?? route.points?.at(-1)?.routeDistance;
|
|
@@ -1181,7 +1195,7 @@ let ActivityRideService = (() => {
|
|
|
1181
1195
|
getTotalElevation() {
|
|
1182
1196
|
try {
|
|
1183
1197
|
const route = this.current.route;
|
|
1184
|
-
if (!route)
|
|
1198
|
+
if (!route || !this.activity)
|
|
1185
1199
|
return 0;
|
|
1186
1200
|
const isLoop = (0, route_1.checkIsLoop)(route);
|
|
1187
1201
|
const totalRouteDistance = this.current?.endPos ?? route.distance ?? route.points?.at(-1)?.routeDistance;
|
|
@@ -117,20 +117,9 @@ let ExternalFileService = (() => {
|
|
|
117
117
|
async ensureLocal(path, opts) {
|
|
118
118
|
const start = Date.now();
|
|
119
119
|
const binding = this.getBindings()?.fileAccess;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return { ok: true, downloaded: false, waitedMs: 0 };
|
|
124
|
-
let location;
|
|
125
|
-
try {
|
|
126
|
-
location = binding.classifyLocation(path);
|
|
127
|
-
}
|
|
128
|
-
catch (err) {
|
|
129
|
-
this.logError(err, 'ensureLocal:classifyLocation');
|
|
130
|
-
return { ok: true, downloaded: false, waitedMs: 0 };
|
|
131
|
-
}
|
|
132
|
-
if (location !== 'icloud')
|
|
133
|
-
return { ok: true, downloaded: false, waitedMs: 0 };
|
|
120
|
+
const skip = this.resolveSkip(path, binding);
|
|
121
|
+
if (skip)
|
|
122
|
+
return skip;
|
|
134
123
|
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
135
124
|
const isCancelled = opts?.isCancelled ?? (() => false);
|
|
136
125
|
try {
|
|
@@ -139,7 +128,7 @@ let ExternalFileService = (() => {
|
|
|
139
128
|
return { ok: false, reason: 'access-lost' };
|
|
140
129
|
if (probe.state === 'not-found')
|
|
141
130
|
return { ok: false, reason: 'not-found' };
|
|
142
|
-
|
|
131
|
+
const availability = await binding.getAvailability(path);
|
|
143
132
|
if (this.isLocallyAvailable(availability))
|
|
144
133
|
return { ok: true, downloaded: false, waitedMs: Date.now() - start };
|
|
145
134
|
if (availability.downloadError)
|
|
@@ -149,29 +138,47 @@ let ExternalFileService = (() => {
|
|
|
149
138
|
if (isCancelled())
|
|
150
139
|
return { ok: false, reason: 'cancelled' };
|
|
151
140
|
await binding.startDownload(path);
|
|
152
|
-
|
|
153
|
-
for (;;) {
|
|
154
|
-
const elapsed = Date.now() - start;
|
|
155
|
-
if (elapsed >= timeoutMs)
|
|
156
|
-
return { ok: false, reason: 'timeout' };
|
|
157
|
-
if (isCancelled())
|
|
158
|
-
return { ok: false, reason: 'cancelled' };
|
|
159
|
-
if (this.isOffline())
|
|
160
|
-
return { ok: false, reason: 'offline' };
|
|
161
|
-
await (0, sleep_1.sleep)(Math.max(0, Math.min(interval, timeoutMs - elapsed)));
|
|
162
|
-
interval = BACKOFF_POLL_INTERVAL_MS;
|
|
163
|
-
availability = await binding.getAvailability(path);
|
|
164
|
-
if (this.isLocallyAvailable(availability))
|
|
165
|
-
return { ok: true, downloaded: true, waitedMs: Date.now() - start };
|
|
166
|
-
if (availability.downloadError)
|
|
167
|
-
return this.downloadFailedResult(availability.downloadError);
|
|
168
|
-
}
|
|
141
|
+
return await this.pollUntilAvailable(binding, path, start, timeoutMs, isCancelled);
|
|
169
142
|
}
|
|
170
143
|
catch (err) {
|
|
171
144
|
this.logError(err, 'ensureLocal');
|
|
172
145
|
return { ok: false, reason: 'download-failed' };
|
|
173
146
|
}
|
|
174
147
|
}
|
|
148
|
+
resolveSkip(path, binding) {
|
|
149
|
+
if (!path || !binding?.isSupported())
|
|
150
|
+
return { ok: true, downloaded: false, waitedMs: 0 };
|
|
151
|
+
if (path.startsWith('http://') || path.startsWith('https://') || path.startsWith('content://'))
|
|
152
|
+
return { ok: true, downloaded: false, waitedMs: 0 };
|
|
153
|
+
let location;
|
|
154
|
+
try {
|
|
155
|
+
location = binding.classifyLocation(path);
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
this.logError(err, 'ensureLocal:classifyLocation');
|
|
159
|
+
return { ok: true, downloaded: false, waitedMs: 0 };
|
|
160
|
+
}
|
|
161
|
+
return location !== 'icloud' ? { ok: true, downloaded: false, waitedMs: 0 } : undefined;
|
|
162
|
+
}
|
|
163
|
+
async pollUntilAvailable(binding, path, start, timeoutMs, isCancelled) {
|
|
164
|
+
let interval = INITIAL_POLL_INTERVAL_MS;
|
|
165
|
+
for (;;) {
|
|
166
|
+
const elapsed = Date.now() - start;
|
|
167
|
+
if (elapsed >= timeoutMs)
|
|
168
|
+
return { ok: false, reason: 'timeout' };
|
|
169
|
+
if (isCancelled())
|
|
170
|
+
return { ok: false, reason: 'cancelled' };
|
|
171
|
+
if (this.isOffline())
|
|
172
|
+
return { ok: false, reason: 'offline' };
|
|
173
|
+
await (0, sleep_1.sleep)(Math.max(0, Math.min(interval, timeoutMs - elapsed)));
|
|
174
|
+
interval = BACKOFF_POLL_INTERVAL_MS;
|
|
175
|
+
const availability = await binding.getAvailability(path);
|
|
176
|
+
if (this.isLocallyAvailable(availability))
|
|
177
|
+
return { ok: true, downloaded: true, waitedMs: Date.now() - start };
|
|
178
|
+
if (availability.downloadError)
|
|
179
|
+
return this.downloadFailedResult(availability.downloadError);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
175
182
|
isLocallyAvailable(availability) {
|
|
176
183
|
return !availability.isUbiquitous || availability.downloadStatus === 'current' || availability.downloadStatus === 'downloaded';
|
|
177
184
|
}
|
|
@@ -104,20 +104,7 @@ let FolderAccessService = (() => {
|
|
|
104
104
|
const id = existing?.id ?? (0, uuid_1.v4)();
|
|
105
105
|
await this.releaseScope(id);
|
|
106
106
|
const location = this.classify(canonical);
|
|
107
|
-
|
|
108
|
-
let source = 'pick';
|
|
109
|
-
let token = grant;
|
|
110
|
-
if (!activation.ok) {
|
|
111
|
-
const captured = await this.tryCapture(canonical);
|
|
112
|
-
if (captured) {
|
|
113
|
-
const capturedActivation = await this.tryActivate(id, captured);
|
|
114
|
-
if (capturedActivation.ok) {
|
|
115
|
-
activation = capturedActivation;
|
|
116
|
-
source = 'captured';
|
|
117
|
-
token = captured;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
107
|
+
const { activation, source, token } = await this.activateWithFallback(id, canonical, grant);
|
|
121
108
|
const record = {
|
|
122
109
|
id,
|
|
123
110
|
folder: canonical,
|
|
@@ -130,20 +117,13 @@ let FolderAccessService = (() => {
|
|
|
130
117
|
lastError: activation.ok ? undefined : (activation.error ?? grantError ?? 'no grant')
|
|
131
118
|
};
|
|
132
119
|
this.upsert(record);
|
|
133
|
-
this.activation[id] = activation.ok
|
|
134
|
-
if (activation.ok)
|
|
120
|
+
this.activation[id] = this.activationStatus(activation.ok, !!token);
|
|
121
|
+
if (activation.ok)
|
|
135
122
|
this.resolvedPaths[id] = activation.resolvedPath ?? canonical;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
this.logEvent({
|
|
143
|
-
message: 'folder grant unverified',
|
|
144
|
-
location, name: this.folderName(canonical), error: record.lastError
|
|
145
|
-
});
|
|
146
|
-
}
|
|
123
|
+
if (activation.ok)
|
|
124
|
+
this.logGrantActivated(source, location, canonical);
|
|
125
|
+
else
|
|
126
|
+
this.logGrantUnverified(location, canonical, record.lastError);
|
|
147
127
|
await this.save(record);
|
|
148
128
|
this.rebuildCoverage();
|
|
149
129
|
this.emitAccessChanged(canonical);
|
|
@@ -152,6 +132,40 @@ let FolderAccessService = (() => {
|
|
|
152
132
|
this.logError(err, 'registerGrant');
|
|
153
133
|
}
|
|
154
134
|
}
|
|
135
|
+
async activateWithFallback(id, canonical, grant) {
|
|
136
|
+
let activation = await this.tryActivate(id, grant);
|
|
137
|
+
let source = 'pick';
|
|
138
|
+
let token = grant;
|
|
139
|
+
if (!activation.ok) {
|
|
140
|
+
const captured = await this.tryCapture(canonical);
|
|
141
|
+
if (captured) {
|
|
142
|
+
const capturedActivation = await this.tryActivate(id, captured);
|
|
143
|
+
if (capturedActivation.ok) {
|
|
144
|
+
activation = capturedActivation;
|
|
145
|
+
source = 'captured';
|
|
146
|
+
token = captured;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { activation, source, token };
|
|
151
|
+
}
|
|
152
|
+
activationStatus(ok, hasToken) {
|
|
153
|
+
if (ok)
|
|
154
|
+
return 'active';
|
|
155
|
+
return hasToken ? 'failed' : 'no-grant';
|
|
156
|
+
}
|
|
157
|
+
logGrantActivated(source, location, canonical) {
|
|
158
|
+
this.logEvent({
|
|
159
|
+
message: source === 'captured' ? 'folder grant captured' : 'folder grant stored',
|
|
160
|
+
location, name: this.folderName(canonical)
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
logGrantUnverified(location, canonical, lastError) {
|
|
164
|
+
this.logEvent({
|
|
165
|
+
message: 'folder grant unverified',
|
|
166
|
+
location, name: this.folderName(canonical), error: lastError
|
|
167
|
+
});
|
|
168
|
+
}
|
|
155
169
|
isCovered(path) {
|
|
156
170
|
if (!this.isSupported())
|
|
157
171
|
return false;
|
|
@@ -434,7 +448,7 @@ let FolderAccessService = (() => {
|
|
|
434
448
|
folderName(folder) {
|
|
435
449
|
if (!folder)
|
|
436
450
|
return undefined;
|
|
437
|
-
return folder.split('/').
|
|
451
|
+
return folder.split('/').findLast(s => s.length > 0);
|
|
438
452
|
}
|
|
439
453
|
logAccessState(path, location, state, transient, errno) {
|
|
440
454
|
this.logEvent({ message: 'access state', location, name: this.folderName(path), state, transient, errno });
|
|
@@ -36,8 +36,12 @@ const canonicalPath = (url) => {
|
|
|
36
36
|
if (/^\/[A-Za-z]:/.exec(path))
|
|
37
37
|
path = path.substring(1);
|
|
38
38
|
path = path.replace(/\/{2,}/g, SEPARATOR);
|
|
39
|
-
if (path.length > 1)
|
|
40
|
-
|
|
39
|
+
if (path.length > 1) {
|
|
40
|
+
let end = path.length;
|
|
41
|
+
while (end > 1 && path[end - 1] === SEPARATOR)
|
|
42
|
+
end--;
|
|
43
|
+
path = path.slice(0, end);
|
|
44
|
+
}
|
|
41
45
|
if (!path)
|
|
42
46
|
return undefined;
|
|
43
47
|
if (path === '/private/var')
|
package/lib/cjs/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __exportStar(require("./avatars"), exports);
|
|
|
24
24
|
__exportStar(require("./base/types"), exports);
|
|
25
25
|
__exportStar(require("./coaches"), exports);
|
|
26
26
|
__exportStar(require("./devices"), exports);
|
|
27
|
+
__exportStar(require("./fileaccess"), exports);
|
|
27
28
|
__exportStar(require("./i18n"), exports);
|
|
28
29
|
__exportStar(require("./maps"), exports);
|
|
29
30
|
__exportStar(require("./monitoring"), exports);
|