incyclist-services 1.8.5 → 1.8.6
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/routes/library/errors.js +10 -1
- package/lib/cjs/routes/library/service.js +96 -65
- package/lib/cjs/routes/list/service.js +10 -3
- package/lib/esm/routes/library/errors.js +8 -0
- package/lib/esm/routes/library/service.js +97 -66
- package/lib/esm/routes/list/service.js +10 -3
- package/lib/types/routes/library/errors.d.ts +4 -0
- package/lib/types/routes/library/service.d.ts +2 -1
- package/lib/types/routes/library/types.d.ts +4 -0
- package/lib/types/routes/list/service.d.ts +2 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toRouteImportError = exports.mapErrorToImportCode = exports.RouteImportError = void 0;
|
|
3
|
+
exports.toRouteImportError = exports.mapErrorToImportCode = exports.DuplicateRouteError = exports.RouteImportError = void 0;
|
|
4
4
|
class RouteImportError extends Error {
|
|
5
5
|
code;
|
|
6
6
|
missingExt;
|
|
@@ -18,6 +18,15 @@ class RouteImportError extends Error {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
exports.RouteImportError = RouteImportError;
|
|
21
|
+
class DuplicateRouteError extends Error {
|
|
22
|
+
duplicateOf;
|
|
23
|
+
constructor(duplicateOf) {
|
|
24
|
+
super(`Duplicate of ${duplicateOf}`);
|
|
25
|
+
this.name = 'DuplicateRouteError';
|
|
26
|
+
this.duplicateOf = duplicateOf;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.DuplicateRouteError = DuplicateRouteError;
|
|
21
30
|
const mapErrorToImportCode = (err, scope) => {
|
|
22
31
|
if (err instanceof RouteImportError)
|
|
23
32
|
return err.code;
|
|
@@ -95,7 +95,6 @@ let RouteLibraryScannerService = (() => {
|
|
|
95
95
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
96
96
|
}
|
|
97
97
|
isCancelled = (__runInitializers(this, _instanceExtraInitializers), false);
|
|
98
|
-
scanResult = [];
|
|
99
98
|
importProps;
|
|
100
99
|
currentScope;
|
|
101
100
|
constructor() {
|
|
@@ -110,7 +109,6 @@ let RouteLibraryScannerService = (() => {
|
|
|
110
109
|
}
|
|
111
110
|
done() {
|
|
112
111
|
this.importProps = undefined;
|
|
113
|
-
this.scanResult = [];
|
|
114
112
|
}
|
|
115
113
|
getDisplayProps() {
|
|
116
114
|
const importProps = this.importProps ?? { phase: 'landing', routes: [], hasICloudDownloadFailures: false };
|
|
@@ -156,7 +154,8 @@ let RouteLibraryScannerService = (() => {
|
|
|
156
154
|
this.isCancelled = false;
|
|
157
155
|
if (!this.importProps)
|
|
158
156
|
this.prepare();
|
|
159
|
-
this.importProps
|
|
157
|
+
const session = this.importProps;
|
|
158
|
+
session.phase = 'ingesting';
|
|
160
159
|
const list = this.getRouteList();
|
|
161
160
|
list.pauseListUpdates();
|
|
162
161
|
this._importFiles(files ?? [], observer)
|
|
@@ -169,15 +168,16 @@ let RouteLibraryScannerService = (() => {
|
|
|
169
168
|
list.emitLists('updated', { source: 'system' });
|
|
170
169
|
});
|
|
171
170
|
observer.on('ingest-progress', (progress) => {
|
|
172
|
-
if (this.importProps)
|
|
173
|
-
|
|
171
|
+
if (this.importProps !== session)
|
|
172
|
+
return;
|
|
173
|
+
session.ingestProgress = progress;
|
|
174
174
|
});
|
|
175
175
|
observer.on('ingest-complete', (status) => {
|
|
176
|
-
if (
|
|
176
|
+
if (this.importProps !== session)
|
|
177
177
|
return;
|
|
178
178
|
const { imported, skipped, errors, failedRoutes } = status;
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
session.phase = 'complete';
|
|
180
|
+
session.completionSummary = { imported, skipped, errors, failedRoutes };
|
|
181
181
|
});
|
|
182
182
|
return observer;
|
|
183
183
|
}
|
|
@@ -203,9 +203,9 @@ let RouteLibraryScannerService = (() => {
|
|
|
203
203
|
this.prepare();
|
|
204
204
|
this.logEvent({ message: 'import route library', folder: folderInfo.displayName });
|
|
205
205
|
this.isCancelled = false;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
const session = this.importProps;
|
|
207
|
+
session.phase = 'scanning';
|
|
208
|
+
session.scanProgress = { scannedFolders: 0, failedFolders: 0 };
|
|
209
209
|
const observer = new types_1.Observer();
|
|
210
210
|
this._scan(folderInfo, observer).catch(err => {
|
|
211
211
|
this.logError(err, 'scan', { folder: folderInfo.displayName });
|
|
@@ -213,12 +213,16 @@ let RouteLibraryScannerService = (() => {
|
|
|
213
213
|
});
|
|
214
214
|
observer
|
|
215
215
|
.on('scan-progress', (progress) => {
|
|
216
|
-
this.importProps
|
|
216
|
+
if (this.importProps !== session)
|
|
217
|
+
return;
|
|
218
|
+
session.scanProgress = progress;
|
|
217
219
|
})
|
|
218
220
|
.on('scan-complete', () => {
|
|
219
|
-
|
|
221
|
+
if (this.importProps !== session)
|
|
222
|
+
return;
|
|
223
|
+
const { scannedFolders: cntScanned, failedFolders: cntFailed } = session.scanProgress;
|
|
220
224
|
this.logEvent({ message: 'import route library: scan success', folder: folderInfo.displayName, cntScanned, cntFailed });
|
|
221
|
-
|
|
225
|
+
session.phase = 'parsing';
|
|
222
226
|
});
|
|
223
227
|
return observer;
|
|
224
228
|
}
|
|
@@ -226,35 +230,42 @@ let RouteLibraryScannerService = (() => {
|
|
|
226
230
|
const observer = new types_1.Observer();
|
|
227
231
|
if (!this.importProps)
|
|
228
232
|
this.prepare();
|
|
229
|
-
|
|
230
|
-
this.
|
|
231
|
-
this.logError(err, 'parse');
|
|
232
|
-
observer.emit('error', err.message);
|
|
233
|
-
});
|
|
233
|
+
const session = this.importProps;
|
|
234
|
+
this.logEvent({ message: 'import route library: parse start', folder: scannedRoutes?.[0]?.folderName });
|
|
234
235
|
observer.on('parse-progress', (progress) => {
|
|
236
|
+
if (this.importProps !== session)
|
|
237
|
+
return;
|
|
235
238
|
const { parsed, total } = progress;
|
|
236
|
-
|
|
239
|
+
session.parseProgress = { parsed, total };
|
|
237
240
|
});
|
|
238
241
|
observer.on('parse-result', (route) => {
|
|
239
|
-
|
|
242
|
+
if (this.importProps !== session)
|
|
243
|
+
return;
|
|
244
|
+
const idx = session.routes.findIndex(r => r.fileUri === route.controlFileUri);
|
|
240
245
|
if (idx !== -1) {
|
|
241
|
-
const observer =
|
|
242
|
-
const displayProps = this.buildRouteDisplayItem(route, observer);
|
|
243
|
-
|
|
246
|
+
const observer = session.routes[idx].observer;
|
|
247
|
+
const displayProps = this.buildRouteDisplayItem(route, session, observer);
|
|
248
|
+
session.routes[idx] = displayProps;
|
|
244
249
|
if (observer) {
|
|
245
250
|
observer.emit('updated', displayProps);
|
|
246
251
|
}
|
|
247
252
|
}
|
|
248
253
|
});
|
|
249
254
|
observer.on('parse-complete', () => {
|
|
255
|
+
if (this.importProps !== session)
|
|
256
|
+
return;
|
|
250
257
|
const parseSumamry = {
|
|
251
|
-
cntTotal:
|
|
252
|
-
cntSuccess:
|
|
253
|
-
cntError:
|
|
254
|
-
cntExisting:
|
|
258
|
+
cntTotal: session.routes.length,
|
|
259
|
+
cntSuccess: session.routes.filter(r => !r.errorReason).length,
|
|
260
|
+
cntError: session.routes.filter(r => r.errorReason != null).length,
|
|
261
|
+
cntExisting: session.routes.filter(r => r.alreadyImported).length,
|
|
255
262
|
};
|
|
256
|
-
this.logEvent({ message: 'import route library: parse completed', folder: scannedRoutes?.[0]
|
|
257
|
-
|
|
263
|
+
this.logEvent({ message: 'import route library: parse completed', folder: scannedRoutes?.[0]?.folderName, parseSumamry });
|
|
264
|
+
session.phase = 'selecting';
|
|
265
|
+
});
|
|
266
|
+
this._parse(scannedRoutes, observer, session).catch(err => {
|
|
267
|
+
this.logError(err, 'parse');
|
|
268
|
+
observer.emit('error', err.message);
|
|
258
269
|
});
|
|
259
270
|
return observer;
|
|
260
271
|
}
|
|
@@ -262,9 +273,10 @@ let RouteLibraryScannerService = (() => {
|
|
|
262
273
|
const observer = new types_1.Observer();
|
|
263
274
|
if (!this.importProps)
|
|
264
275
|
this.prepare();
|
|
276
|
+
const session = this.importProps;
|
|
265
277
|
const list = this.getRouteList();
|
|
266
278
|
list.pauseListUpdates();
|
|
267
|
-
|
|
279
|
+
session.phase = 'ingesting';
|
|
268
280
|
this._ingest(routes, observer)
|
|
269
281
|
.catch(err => {
|
|
270
282
|
this.logError(err, 'ingest');
|
|
@@ -275,13 +287,17 @@ let RouteLibraryScannerService = (() => {
|
|
|
275
287
|
list.emitLists('updated', { source: 'system' });
|
|
276
288
|
});
|
|
277
289
|
observer.on('ingest-progress', (progress) => {
|
|
290
|
+
if (this.importProps !== session)
|
|
291
|
+
return;
|
|
278
292
|
const { current, total, currentName } = progress;
|
|
279
|
-
|
|
293
|
+
session.ingestProgress = { current, total, currentName };
|
|
280
294
|
});
|
|
281
295
|
observer.on('ingest-complete', (status) => {
|
|
282
|
-
this.importProps
|
|
296
|
+
if (this.importProps !== session)
|
|
297
|
+
return;
|
|
298
|
+
session.phase = 'complete';
|
|
283
299
|
const { imported, skipped, errors, failedRoutes } = status;
|
|
284
|
-
|
|
300
|
+
session.completionSummary = { imported, skipped, errors, failedRoutes };
|
|
285
301
|
});
|
|
286
302
|
return observer;
|
|
287
303
|
}
|
|
@@ -403,8 +419,8 @@ let RouteLibraryScannerService = (() => {
|
|
|
403
419
|
await this.scanFolder(folderInfo.uri, folderInfo.displayName, ctx);
|
|
404
420
|
await this.upsertImportHistory(folderInfo, ctx.discoveredCount.value);
|
|
405
421
|
const { scannedFolders, failedFolders } = ctx.progress;
|
|
406
|
-
this.logEvent({ message: 'video scan result', scannedFolders, failedFolders, files:
|
|
407
|
-
observer.emit('scan-complete',
|
|
422
|
+
this.logEvent({ message: 'video scan result', scannedFolders, failedFolders, files: ctx.results.length });
|
|
423
|
+
observer.emit('scan-complete', ctx.results);
|
|
408
424
|
}
|
|
409
425
|
createScanContext(observer, parsers, recursive) {
|
|
410
426
|
return {
|
|
@@ -412,7 +428,8 @@ let RouteLibraryScannerService = (() => {
|
|
|
412
428
|
parsers,
|
|
413
429
|
recursive,
|
|
414
430
|
progress: { scannedFolders: 0, failedFolders: 0 },
|
|
415
|
-
discoveredCount: { value: 0 }
|
|
431
|
+
discoveredCount: { value: 0 },
|
|
432
|
+
results: []
|
|
416
433
|
};
|
|
417
434
|
}
|
|
418
435
|
async scanFolder(uri, folderName, ctx) {
|
|
@@ -449,7 +466,7 @@ let RouteLibraryScannerService = (() => {
|
|
|
449
466
|
const routeAnnouncement = await this.buildDiscoveredRoute(file, files, uri, folderName, ctx.parsers);
|
|
450
467
|
ctx.discoveredCount.value++;
|
|
451
468
|
ctx.observer.emit('scan-result', routeAnnouncement);
|
|
452
|
-
|
|
469
|
+
ctx.results.push(routeAnnouncement);
|
|
453
470
|
}
|
|
454
471
|
}
|
|
455
472
|
}
|
|
@@ -600,29 +617,30 @@ let RouteLibraryScannerService = (() => {
|
|
|
600
617
|
scanError: skipReason
|
|
601
618
|
};
|
|
602
619
|
}
|
|
603
|
-
async _parse(scannedRoutes, observer) {
|
|
620
|
+
async _parse(scannedRoutes, observer, session) {
|
|
604
621
|
const service = this.getRouteList();
|
|
605
622
|
const targets = scannedRoutes.filter(r => !r.scanError);
|
|
606
623
|
const total = targets.length;
|
|
607
624
|
targets.forEach(target => {
|
|
608
625
|
const file = this.buildFileInfo(target.controlFileUri, target.format);
|
|
609
|
-
|
|
626
|
+
session.routes.push({
|
|
610
627
|
format: target.format,
|
|
611
628
|
parseState: 'waiting',
|
|
612
629
|
importable: false,
|
|
613
630
|
label: file.base,
|
|
614
631
|
folder: target.folderName,
|
|
615
632
|
id: target.controlFileUri,
|
|
633
|
+
fileUri: target.controlFileUri,
|
|
616
634
|
alreadyImported: false,
|
|
617
635
|
observer: new types_1.Observer()
|
|
618
636
|
});
|
|
619
637
|
});
|
|
620
638
|
observer.emit('parse-start');
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
639
|
+
await targets.reduce(async (previous, target, i) => {
|
|
640
|
+
await previous;
|
|
641
|
+
if (this.isCancelled || this.importProps !== session)
|
|
642
|
+
return;
|
|
624
643
|
const parsed = i + 1;
|
|
625
|
-
const target = targets[i];
|
|
626
644
|
observer.emit('parse-progress', { current: parsed, parsed, total, currentFolder: target.folderName });
|
|
627
645
|
let fileName = target.controlFileUri;
|
|
628
646
|
try {
|
|
@@ -631,15 +649,15 @@ let RouteLibraryScannerService = (() => {
|
|
|
631
649
|
fileName = info.base;
|
|
632
650
|
}
|
|
633
651
|
catch { }
|
|
634
|
-
await this._parseTarget(target, service, observer);
|
|
652
|
+
await this._parseTarget(target, service, observer, session);
|
|
635
653
|
this.logEvent({ message: 'parsing route done', fileName });
|
|
636
|
-
}
|
|
654
|
+
}, Promise.resolve());
|
|
637
655
|
observer.emit('parse-complete');
|
|
638
656
|
}
|
|
639
|
-
async _parseTarget(target, service, observer) {
|
|
657
|
+
async _parseTarget(target, service, observer, session) {
|
|
640
658
|
let result;
|
|
641
659
|
const file = this.buildFileInfo(target.controlFileUri, target.format);
|
|
642
|
-
const importProps =
|
|
660
|
+
const importProps = session.routes.find(r => r.fileUri === target.controlFileUri) ?? {};
|
|
643
661
|
const scope = (0, externalFiles_1.useExternalFileService)().beginScope({ isCancelled: () => this.isCancelled });
|
|
644
662
|
this.currentScope = scope;
|
|
645
663
|
try {
|
|
@@ -658,10 +676,10 @@ let RouteLibraryScannerService = (() => {
|
|
|
658
676
|
}
|
|
659
677
|
importProps.parseState = 'parsed';
|
|
660
678
|
const route = new route_1.Route(result.data, result.details);
|
|
661
|
-
const existing =
|
|
679
|
+
const existing = session.routes.find(ri => ri.id === route.description.id);
|
|
662
680
|
if (existing) {
|
|
663
681
|
result = undefined;
|
|
664
|
-
throw new
|
|
682
|
+
throw new errors_1.DuplicateRouteError(existing.label);
|
|
665
683
|
}
|
|
666
684
|
if (result.data.hasVideo) {
|
|
667
685
|
this.validateVideoUrl(route, target.folderUri, target.files);
|
|
@@ -681,20 +699,31 @@ let RouteLibraryScannerService = (() => {
|
|
|
681
699
|
}
|
|
682
700
|
catch (err) {
|
|
683
701
|
importProps.parseState = 'parsed';
|
|
684
|
-
const parsed =
|
|
685
|
-
|
|
686
|
-
route: result ? new route_1.Route(result.data, result.details) : undefined,
|
|
687
|
-
folderUri: target.folderUri,
|
|
688
|
-
folderName: target.folderName,
|
|
689
|
-
controlFileUri: target.controlFileUri,
|
|
690
|
-
format: target.format,
|
|
691
|
-
parseError: scope.lastFailure ? this.getReadFailureMessage(scope) : (err?.message ?? String(err)),
|
|
692
|
-
parseErrorCode: (0, errors_1.mapErrorToImportCode)(err, scope)
|
|
693
|
-
};
|
|
694
|
-
this.logEvent({ message: 'could not parse route file', file: file.base, reason: err.message, stack: err.stack });
|
|
702
|
+
const parsed = this.buildParseFailure(target, err, scope, result);
|
|
703
|
+
this.logParseFailure(file.base, err);
|
|
695
704
|
observer.emit('parse-result', parsed);
|
|
696
705
|
}
|
|
697
706
|
}
|
|
707
|
+
buildParseFailure(target, err, scope, result) {
|
|
708
|
+
const duplicateOf = err instanceof errors_1.DuplicateRouteError ? err.duplicateOf : undefined;
|
|
709
|
+
return {
|
|
710
|
+
alreadyImported: false,
|
|
711
|
+
route: result ? new route_1.Route(result.data, result.details) : undefined,
|
|
712
|
+
folderUri: target.folderUri,
|
|
713
|
+
folderName: target.folderName,
|
|
714
|
+
controlFileUri: target.controlFileUri,
|
|
715
|
+
format: target.format,
|
|
716
|
+
parseError: scope.lastFailure ? this.getReadFailureMessage(scope) : (err?.message ?? String(err)),
|
|
717
|
+
parseErrorCode: duplicateOf ? undefined : (0, errors_1.mapErrorToImportCode)(err, scope),
|
|
718
|
+
duplicateOf
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
logParseFailure(fileName, err) {
|
|
722
|
+
if (err instanceof errors_1.DuplicateRouteError)
|
|
723
|
+
this.logEvent({ message: 'route is a duplicate in this import', file: fileName, duplicateOf: err.duplicateOf });
|
|
724
|
+
else
|
|
725
|
+
this.logEvent({ message: 'could not parse route file', file: fileName, reason: err?.message, stack: err?.stack });
|
|
726
|
+
}
|
|
698
727
|
getReadFailureMessage(scope) {
|
|
699
728
|
const name = this.getFileName(scope.failedFile);
|
|
700
729
|
switch (scope.lastFailure?.reason) {
|
|
@@ -910,10 +939,10 @@ let RouteLibraryScannerService = (() => {
|
|
|
910
939
|
delimiter: uri?.startsWith('content://') ? '%2F' : '/'
|
|
911
940
|
};
|
|
912
941
|
}
|
|
913
|
-
getImportProps(parsed) {
|
|
914
|
-
return
|
|
942
|
+
getImportProps(parsed, session) {
|
|
943
|
+
return session.routes.find(r => r.fileUri === parsed.controlFileUri);
|
|
915
944
|
}
|
|
916
|
-
buildRouteDisplayItem(parsed, observer) {
|
|
945
|
+
buildRouteDisplayItem(parsed, session, observer) {
|
|
917
946
|
const { route, alreadyImported, parseError, format, controlFileUri, folderName } = parsed;
|
|
918
947
|
const descr = route?.description ?? {};
|
|
919
948
|
const [C, U] = this.getUnitConversionShortcuts();
|
|
@@ -923,9 +952,11 @@ let RouteLibraryScannerService = (() => {
|
|
|
923
952
|
};
|
|
924
953
|
const path = this.getBindings().path;
|
|
925
954
|
const info = path.parse(controlFileUri);
|
|
926
|
-
const importProps = this.getImportProps(parsed);
|
|
955
|
+
const importProps = this.getImportProps(parsed, session);
|
|
927
956
|
return {
|
|
928
957
|
id: route?.description?.id ?? info?.base,
|
|
958
|
+
fileUri: controlFileUri,
|
|
959
|
+
duplicateOf: parsed.duplicateOf,
|
|
929
960
|
distance,
|
|
930
961
|
label: route?.title ?? info?.base,
|
|
931
962
|
folder: folderName,
|
|
@@ -215,7 +215,7 @@ let RouteListService = (() => {
|
|
|
215
215
|
}
|
|
216
216
|
if (this.initialized && !hasLists)
|
|
217
217
|
emitLoadedEvent();
|
|
218
|
-
this.
|
|
218
|
+
this.subscribeStats();
|
|
219
219
|
this.emit('opened', this.observer, this.stats === undefined);
|
|
220
220
|
const unitsObserver = this.getUserSettings().requestNotifyOnChange('routeList', 'preferences.units');
|
|
221
221
|
unitsObserver.on('changed', this.redrawCards.bind(this));
|
|
@@ -336,7 +336,7 @@ let RouteListService = (() => {
|
|
|
336
336
|
this.setListTop('tiles', 0);
|
|
337
337
|
}
|
|
338
338
|
const res = this.searchRepo(filters);
|
|
339
|
-
this.
|
|
339
|
+
this.subscribeStats();
|
|
340
340
|
this.emit('opened', this.observer, this.stats === undefined);
|
|
341
341
|
this.getAppState().setPersistedState('page', 'search');
|
|
342
342
|
return res;
|
|
@@ -1132,8 +1132,10 @@ let RouteListService = (() => {
|
|
|
1132
1132
|
async performSync(service) {
|
|
1133
1133
|
return new Promise(done => {
|
|
1134
1134
|
const observer = this.getRouteSyncFactory().sync(service);
|
|
1135
|
-
if (!observer)
|
|
1135
|
+
if (!observer) {
|
|
1136
1136
|
done();
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1137
1139
|
this.observer.emit('sync-start');
|
|
1138
1140
|
this.syncInfo.observer = observer;
|
|
1139
1141
|
const onAdded = this.onSyncRouteAdded.bind(this);
|
|
@@ -1620,6 +1622,11 @@ let RouteListService = (() => {
|
|
|
1620
1622
|
this.emitLists('updated');
|
|
1621
1623
|
});
|
|
1622
1624
|
}
|
|
1625
|
+
onStatsUpdateHandler = (stats) => this.onRouteStatsUpdate(stats);
|
|
1626
|
+
subscribeStats() {
|
|
1627
|
+
this.observer.off('stats-update', this.onStatsUpdateHandler);
|
|
1628
|
+
this.observer.on('stats-update', this.onStatsUpdateHandler);
|
|
1629
|
+
}
|
|
1623
1630
|
onRouteStatsUpdate(stats) {
|
|
1624
1631
|
this.stats = stats;
|
|
1625
1632
|
for (const stat of stats) {
|
|
@@ -14,6 +14,14 @@ export class RouteImportError extends Error {
|
|
|
14
14
|
return failure;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
export class DuplicateRouteError extends Error {
|
|
18
|
+
duplicateOf;
|
|
19
|
+
constructor(duplicateOf) {
|
|
20
|
+
super(`Duplicate of ${duplicateOf}`);
|
|
21
|
+
this.name = 'DuplicateRouteError';
|
|
22
|
+
this.duplicateOf = duplicateOf;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
17
25
|
export const mapErrorToImportCode = (err, scope) => {
|
|
18
26
|
if (err instanceof RouteImportError)
|
|
19
27
|
return err.code;
|
|
@@ -41,7 +41,7 @@ import { Observer } from '../../base/types';
|
|
|
41
41
|
import { RouteParser, useParsers } from '../base/parsers';
|
|
42
42
|
import { useRouteList } from '../list/service';
|
|
43
43
|
import { waitNextTick } from '../../utils';
|
|
44
|
-
import { RouteImportError, mapErrorToImportCode, toRouteImportError } from './errors';
|
|
44
|
+
import { DuplicateRouteError, RouteImportError, mapErrorToImportCode, toRouteImportError } from './errors';
|
|
45
45
|
import { useRoutesDbLoader } from '../list/loaders/db';
|
|
46
46
|
import { Route } from '../base/model/route';
|
|
47
47
|
import { sleep } from '../../utils/sleep';
|
|
@@ -92,7 +92,6 @@ let RouteLibraryScannerService = (() => {
|
|
|
92
92
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
93
93
|
}
|
|
94
94
|
isCancelled = (__runInitializers(this, _instanceExtraInitializers), false);
|
|
95
|
-
scanResult = [];
|
|
96
95
|
importProps;
|
|
97
96
|
currentScope;
|
|
98
97
|
constructor() {
|
|
@@ -107,7 +106,6 @@ let RouteLibraryScannerService = (() => {
|
|
|
107
106
|
}
|
|
108
107
|
done() {
|
|
109
108
|
this.importProps = undefined;
|
|
110
|
-
this.scanResult = [];
|
|
111
109
|
}
|
|
112
110
|
getDisplayProps() {
|
|
113
111
|
const importProps = this.importProps ?? { phase: 'landing', routes: [], hasICloudDownloadFailures: false };
|
|
@@ -153,7 +151,8 @@ let RouteLibraryScannerService = (() => {
|
|
|
153
151
|
this.isCancelled = false;
|
|
154
152
|
if (!this.importProps)
|
|
155
153
|
this.prepare();
|
|
156
|
-
this.importProps
|
|
154
|
+
const session = this.importProps;
|
|
155
|
+
session.phase = 'ingesting';
|
|
157
156
|
const list = this.getRouteList();
|
|
158
157
|
list.pauseListUpdates();
|
|
159
158
|
this._importFiles(files ?? [], observer)
|
|
@@ -166,15 +165,16 @@ let RouteLibraryScannerService = (() => {
|
|
|
166
165
|
list.emitLists('updated', { source: 'system' });
|
|
167
166
|
});
|
|
168
167
|
observer.on('ingest-progress', (progress) => {
|
|
169
|
-
if (this.importProps)
|
|
170
|
-
|
|
168
|
+
if (this.importProps !== session)
|
|
169
|
+
return;
|
|
170
|
+
session.ingestProgress = progress;
|
|
171
171
|
});
|
|
172
172
|
observer.on('ingest-complete', (status) => {
|
|
173
|
-
if (
|
|
173
|
+
if (this.importProps !== session)
|
|
174
174
|
return;
|
|
175
175
|
const { imported, skipped, errors, failedRoutes } = status;
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
session.phase = 'complete';
|
|
177
|
+
session.completionSummary = { imported, skipped, errors, failedRoutes };
|
|
178
178
|
});
|
|
179
179
|
return observer;
|
|
180
180
|
}
|
|
@@ -200,9 +200,9 @@ let RouteLibraryScannerService = (() => {
|
|
|
200
200
|
this.prepare();
|
|
201
201
|
this.logEvent({ message: 'import route library', folder: folderInfo.displayName });
|
|
202
202
|
this.isCancelled = false;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
const session = this.importProps;
|
|
204
|
+
session.phase = 'scanning';
|
|
205
|
+
session.scanProgress = { scannedFolders: 0, failedFolders: 0 };
|
|
206
206
|
const observer = new Observer();
|
|
207
207
|
this._scan(folderInfo, observer).catch(err => {
|
|
208
208
|
this.logError(err, 'scan', { folder: folderInfo.displayName });
|
|
@@ -210,12 +210,16 @@ let RouteLibraryScannerService = (() => {
|
|
|
210
210
|
});
|
|
211
211
|
observer
|
|
212
212
|
.on('scan-progress', (progress) => {
|
|
213
|
-
this.importProps
|
|
213
|
+
if (this.importProps !== session)
|
|
214
|
+
return;
|
|
215
|
+
session.scanProgress = progress;
|
|
214
216
|
})
|
|
215
217
|
.on('scan-complete', () => {
|
|
216
|
-
|
|
218
|
+
if (this.importProps !== session)
|
|
219
|
+
return;
|
|
220
|
+
const { scannedFolders: cntScanned, failedFolders: cntFailed } = session.scanProgress;
|
|
217
221
|
this.logEvent({ message: 'import route library: scan success', folder: folderInfo.displayName, cntScanned, cntFailed });
|
|
218
|
-
|
|
222
|
+
session.phase = 'parsing';
|
|
219
223
|
});
|
|
220
224
|
return observer;
|
|
221
225
|
}
|
|
@@ -223,35 +227,42 @@ let RouteLibraryScannerService = (() => {
|
|
|
223
227
|
const observer = new Observer();
|
|
224
228
|
if (!this.importProps)
|
|
225
229
|
this.prepare();
|
|
226
|
-
|
|
227
|
-
this.
|
|
228
|
-
this.logError(err, 'parse');
|
|
229
|
-
observer.emit('error', err.message);
|
|
230
|
-
});
|
|
230
|
+
const session = this.importProps;
|
|
231
|
+
this.logEvent({ message: 'import route library: parse start', folder: scannedRoutes?.[0]?.folderName });
|
|
231
232
|
observer.on('parse-progress', (progress) => {
|
|
233
|
+
if (this.importProps !== session)
|
|
234
|
+
return;
|
|
232
235
|
const { parsed, total } = progress;
|
|
233
|
-
|
|
236
|
+
session.parseProgress = { parsed, total };
|
|
234
237
|
});
|
|
235
238
|
observer.on('parse-result', (route) => {
|
|
236
|
-
|
|
239
|
+
if (this.importProps !== session)
|
|
240
|
+
return;
|
|
241
|
+
const idx = session.routes.findIndex(r => r.fileUri === route.controlFileUri);
|
|
237
242
|
if (idx !== -1) {
|
|
238
|
-
const observer =
|
|
239
|
-
const displayProps = this.buildRouteDisplayItem(route, observer);
|
|
240
|
-
|
|
243
|
+
const observer = session.routes[idx].observer;
|
|
244
|
+
const displayProps = this.buildRouteDisplayItem(route, session, observer);
|
|
245
|
+
session.routes[idx] = displayProps;
|
|
241
246
|
if (observer) {
|
|
242
247
|
observer.emit('updated', displayProps);
|
|
243
248
|
}
|
|
244
249
|
}
|
|
245
250
|
});
|
|
246
251
|
observer.on('parse-complete', () => {
|
|
252
|
+
if (this.importProps !== session)
|
|
253
|
+
return;
|
|
247
254
|
const parseSumamry = {
|
|
248
|
-
cntTotal:
|
|
249
|
-
cntSuccess:
|
|
250
|
-
cntError:
|
|
251
|
-
cntExisting:
|
|
255
|
+
cntTotal: session.routes.length,
|
|
256
|
+
cntSuccess: session.routes.filter(r => !r.errorReason).length,
|
|
257
|
+
cntError: session.routes.filter(r => r.errorReason != null).length,
|
|
258
|
+
cntExisting: session.routes.filter(r => r.alreadyImported).length,
|
|
252
259
|
};
|
|
253
|
-
this.logEvent({ message: 'import route library: parse completed', folder: scannedRoutes?.[0]
|
|
254
|
-
|
|
260
|
+
this.logEvent({ message: 'import route library: parse completed', folder: scannedRoutes?.[0]?.folderName, parseSumamry });
|
|
261
|
+
session.phase = 'selecting';
|
|
262
|
+
});
|
|
263
|
+
this._parse(scannedRoutes, observer, session).catch(err => {
|
|
264
|
+
this.logError(err, 'parse');
|
|
265
|
+
observer.emit('error', err.message);
|
|
255
266
|
});
|
|
256
267
|
return observer;
|
|
257
268
|
}
|
|
@@ -259,9 +270,10 @@ let RouteLibraryScannerService = (() => {
|
|
|
259
270
|
const observer = new Observer();
|
|
260
271
|
if (!this.importProps)
|
|
261
272
|
this.prepare();
|
|
273
|
+
const session = this.importProps;
|
|
262
274
|
const list = this.getRouteList();
|
|
263
275
|
list.pauseListUpdates();
|
|
264
|
-
|
|
276
|
+
session.phase = 'ingesting';
|
|
265
277
|
this._ingest(routes, observer)
|
|
266
278
|
.catch(err => {
|
|
267
279
|
this.logError(err, 'ingest');
|
|
@@ -272,13 +284,17 @@ let RouteLibraryScannerService = (() => {
|
|
|
272
284
|
list.emitLists('updated', { source: 'system' });
|
|
273
285
|
});
|
|
274
286
|
observer.on('ingest-progress', (progress) => {
|
|
287
|
+
if (this.importProps !== session)
|
|
288
|
+
return;
|
|
275
289
|
const { current, total, currentName } = progress;
|
|
276
|
-
|
|
290
|
+
session.ingestProgress = { current, total, currentName };
|
|
277
291
|
});
|
|
278
292
|
observer.on('ingest-complete', (status) => {
|
|
279
|
-
this.importProps
|
|
293
|
+
if (this.importProps !== session)
|
|
294
|
+
return;
|
|
295
|
+
session.phase = 'complete';
|
|
280
296
|
const { imported, skipped, errors, failedRoutes } = status;
|
|
281
|
-
|
|
297
|
+
session.completionSummary = { imported, skipped, errors, failedRoutes };
|
|
282
298
|
});
|
|
283
299
|
return observer;
|
|
284
300
|
}
|
|
@@ -400,8 +416,8 @@ let RouteLibraryScannerService = (() => {
|
|
|
400
416
|
await this.scanFolder(folderInfo.uri, folderInfo.displayName, ctx);
|
|
401
417
|
await this.upsertImportHistory(folderInfo, ctx.discoveredCount.value);
|
|
402
418
|
const { scannedFolders, failedFolders } = ctx.progress;
|
|
403
|
-
this.logEvent({ message: 'video scan result', scannedFolders, failedFolders, files:
|
|
404
|
-
observer.emit('scan-complete',
|
|
419
|
+
this.logEvent({ message: 'video scan result', scannedFolders, failedFolders, files: ctx.results.length });
|
|
420
|
+
observer.emit('scan-complete', ctx.results);
|
|
405
421
|
}
|
|
406
422
|
createScanContext(observer, parsers, recursive) {
|
|
407
423
|
return {
|
|
@@ -409,7 +425,8 @@ let RouteLibraryScannerService = (() => {
|
|
|
409
425
|
parsers,
|
|
410
426
|
recursive,
|
|
411
427
|
progress: { scannedFolders: 0, failedFolders: 0 },
|
|
412
|
-
discoveredCount: { value: 0 }
|
|
428
|
+
discoveredCount: { value: 0 },
|
|
429
|
+
results: []
|
|
413
430
|
};
|
|
414
431
|
}
|
|
415
432
|
async scanFolder(uri, folderName, ctx) {
|
|
@@ -446,7 +463,7 @@ let RouteLibraryScannerService = (() => {
|
|
|
446
463
|
const routeAnnouncement = await this.buildDiscoveredRoute(file, files, uri, folderName, ctx.parsers);
|
|
447
464
|
ctx.discoveredCount.value++;
|
|
448
465
|
ctx.observer.emit('scan-result', routeAnnouncement);
|
|
449
|
-
|
|
466
|
+
ctx.results.push(routeAnnouncement);
|
|
450
467
|
}
|
|
451
468
|
}
|
|
452
469
|
}
|
|
@@ -597,29 +614,30 @@ let RouteLibraryScannerService = (() => {
|
|
|
597
614
|
scanError: skipReason
|
|
598
615
|
};
|
|
599
616
|
}
|
|
600
|
-
async _parse(scannedRoutes, observer) {
|
|
617
|
+
async _parse(scannedRoutes, observer, session) {
|
|
601
618
|
const service = this.getRouteList();
|
|
602
619
|
const targets = scannedRoutes.filter(r => !r.scanError);
|
|
603
620
|
const total = targets.length;
|
|
604
621
|
targets.forEach(target => {
|
|
605
622
|
const file = this.buildFileInfo(target.controlFileUri, target.format);
|
|
606
|
-
|
|
623
|
+
session.routes.push({
|
|
607
624
|
format: target.format,
|
|
608
625
|
parseState: 'waiting',
|
|
609
626
|
importable: false,
|
|
610
627
|
label: file.base,
|
|
611
628
|
folder: target.folderName,
|
|
612
629
|
id: target.controlFileUri,
|
|
630
|
+
fileUri: target.controlFileUri,
|
|
613
631
|
alreadyImported: false,
|
|
614
632
|
observer: new Observer()
|
|
615
633
|
});
|
|
616
634
|
});
|
|
617
635
|
observer.emit('parse-start');
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
636
|
+
await targets.reduce(async (previous, target, i) => {
|
|
637
|
+
await previous;
|
|
638
|
+
if (this.isCancelled || this.importProps !== session)
|
|
639
|
+
return;
|
|
621
640
|
const parsed = i + 1;
|
|
622
|
-
const target = targets[i];
|
|
623
641
|
observer.emit('parse-progress', { current: parsed, parsed, total, currentFolder: target.folderName });
|
|
624
642
|
let fileName = target.controlFileUri;
|
|
625
643
|
try {
|
|
@@ -628,15 +646,15 @@ let RouteLibraryScannerService = (() => {
|
|
|
628
646
|
fileName = info.base;
|
|
629
647
|
}
|
|
630
648
|
catch { }
|
|
631
|
-
await this._parseTarget(target, service, observer);
|
|
649
|
+
await this._parseTarget(target, service, observer, session);
|
|
632
650
|
this.logEvent({ message: 'parsing route done', fileName });
|
|
633
|
-
}
|
|
651
|
+
}, Promise.resolve());
|
|
634
652
|
observer.emit('parse-complete');
|
|
635
653
|
}
|
|
636
|
-
async _parseTarget(target, service, observer) {
|
|
654
|
+
async _parseTarget(target, service, observer, session) {
|
|
637
655
|
let result;
|
|
638
656
|
const file = this.buildFileInfo(target.controlFileUri, target.format);
|
|
639
|
-
const importProps =
|
|
657
|
+
const importProps = session.routes.find(r => r.fileUri === target.controlFileUri) ?? {};
|
|
640
658
|
const scope = useExternalFileService().beginScope({ isCancelled: () => this.isCancelled });
|
|
641
659
|
this.currentScope = scope;
|
|
642
660
|
try {
|
|
@@ -655,10 +673,10 @@ let RouteLibraryScannerService = (() => {
|
|
|
655
673
|
}
|
|
656
674
|
importProps.parseState = 'parsed';
|
|
657
675
|
const route = new Route(result.data, result.details);
|
|
658
|
-
const existing =
|
|
676
|
+
const existing = session.routes.find(ri => ri.id === route.description.id);
|
|
659
677
|
if (existing) {
|
|
660
678
|
result = undefined;
|
|
661
|
-
throw new
|
|
679
|
+
throw new DuplicateRouteError(existing.label);
|
|
662
680
|
}
|
|
663
681
|
if (result.data.hasVideo) {
|
|
664
682
|
this.validateVideoUrl(route, target.folderUri, target.files);
|
|
@@ -678,20 +696,31 @@ let RouteLibraryScannerService = (() => {
|
|
|
678
696
|
}
|
|
679
697
|
catch (err) {
|
|
680
698
|
importProps.parseState = 'parsed';
|
|
681
|
-
const parsed =
|
|
682
|
-
|
|
683
|
-
route: result ? new Route(result.data, result.details) : undefined,
|
|
684
|
-
folderUri: target.folderUri,
|
|
685
|
-
folderName: target.folderName,
|
|
686
|
-
controlFileUri: target.controlFileUri,
|
|
687
|
-
format: target.format,
|
|
688
|
-
parseError: scope.lastFailure ? this.getReadFailureMessage(scope) : (err?.message ?? String(err)),
|
|
689
|
-
parseErrorCode: mapErrorToImportCode(err, scope)
|
|
690
|
-
};
|
|
691
|
-
this.logEvent({ message: 'could not parse route file', file: file.base, reason: err.message, stack: err.stack });
|
|
699
|
+
const parsed = this.buildParseFailure(target, err, scope, result);
|
|
700
|
+
this.logParseFailure(file.base, err);
|
|
692
701
|
observer.emit('parse-result', parsed);
|
|
693
702
|
}
|
|
694
703
|
}
|
|
704
|
+
buildParseFailure(target, err, scope, result) {
|
|
705
|
+
const duplicateOf = err instanceof DuplicateRouteError ? err.duplicateOf : undefined;
|
|
706
|
+
return {
|
|
707
|
+
alreadyImported: false,
|
|
708
|
+
route: result ? new Route(result.data, result.details) : undefined,
|
|
709
|
+
folderUri: target.folderUri,
|
|
710
|
+
folderName: target.folderName,
|
|
711
|
+
controlFileUri: target.controlFileUri,
|
|
712
|
+
format: target.format,
|
|
713
|
+
parseError: scope.lastFailure ? this.getReadFailureMessage(scope) : (err?.message ?? String(err)),
|
|
714
|
+
parseErrorCode: duplicateOf ? undefined : mapErrorToImportCode(err, scope),
|
|
715
|
+
duplicateOf
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
logParseFailure(fileName, err) {
|
|
719
|
+
if (err instanceof DuplicateRouteError)
|
|
720
|
+
this.logEvent({ message: 'route is a duplicate in this import', file: fileName, duplicateOf: err.duplicateOf });
|
|
721
|
+
else
|
|
722
|
+
this.logEvent({ message: 'could not parse route file', file: fileName, reason: err?.message, stack: err?.stack });
|
|
723
|
+
}
|
|
695
724
|
getReadFailureMessage(scope) {
|
|
696
725
|
const name = this.getFileName(scope.failedFile);
|
|
697
726
|
switch (scope.lastFailure?.reason) {
|
|
@@ -907,10 +936,10 @@ let RouteLibraryScannerService = (() => {
|
|
|
907
936
|
delimiter: uri?.startsWith('content://') ? '%2F' : '/'
|
|
908
937
|
};
|
|
909
938
|
}
|
|
910
|
-
getImportProps(parsed) {
|
|
911
|
-
return
|
|
939
|
+
getImportProps(parsed, session) {
|
|
940
|
+
return session.routes.find(r => r.fileUri === parsed.controlFileUri);
|
|
912
941
|
}
|
|
913
|
-
buildRouteDisplayItem(parsed, observer) {
|
|
942
|
+
buildRouteDisplayItem(parsed, session, observer) {
|
|
914
943
|
const { route, alreadyImported, parseError, format, controlFileUri, folderName } = parsed;
|
|
915
944
|
const descr = route?.description ?? {};
|
|
916
945
|
const [C, U] = this.getUnitConversionShortcuts();
|
|
@@ -920,9 +949,11 @@ let RouteLibraryScannerService = (() => {
|
|
|
920
949
|
};
|
|
921
950
|
const path = this.getBindings().path;
|
|
922
951
|
const info = path.parse(controlFileUri);
|
|
923
|
-
const importProps = this.getImportProps(parsed);
|
|
952
|
+
const importProps = this.getImportProps(parsed, session);
|
|
924
953
|
return {
|
|
925
954
|
id: route?.description?.id ?? info?.base,
|
|
955
|
+
fileUri: controlFileUri,
|
|
956
|
+
duplicateOf: parsed.duplicateOf,
|
|
926
957
|
distance,
|
|
927
958
|
label: route?.title ?? info?.base,
|
|
928
959
|
folder: folderName,
|
|
@@ -209,7 +209,7 @@ let RouteListService = (() => {
|
|
|
209
209
|
}
|
|
210
210
|
if (this.initialized && !hasLists)
|
|
211
211
|
emitLoadedEvent();
|
|
212
|
-
this.
|
|
212
|
+
this.subscribeStats();
|
|
213
213
|
this.emit('opened', this.observer, this.stats === undefined);
|
|
214
214
|
const unitsObserver = this.getUserSettings().requestNotifyOnChange('routeList', 'preferences.units');
|
|
215
215
|
unitsObserver.on('changed', this.redrawCards.bind(this));
|
|
@@ -330,7 +330,7 @@ let RouteListService = (() => {
|
|
|
330
330
|
this.setListTop('tiles', 0);
|
|
331
331
|
}
|
|
332
332
|
const res = this.searchRepo(filters);
|
|
333
|
-
this.
|
|
333
|
+
this.subscribeStats();
|
|
334
334
|
this.emit('opened', this.observer, this.stats === undefined);
|
|
335
335
|
this.getAppState().setPersistedState('page', 'search');
|
|
336
336
|
return res;
|
|
@@ -1126,8 +1126,10 @@ let RouteListService = (() => {
|
|
|
1126
1126
|
async performSync(service) {
|
|
1127
1127
|
return new Promise(done => {
|
|
1128
1128
|
const observer = this.getRouteSyncFactory().sync(service);
|
|
1129
|
-
if (!observer)
|
|
1129
|
+
if (!observer) {
|
|
1130
1130
|
done();
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1131
1133
|
this.observer.emit('sync-start');
|
|
1132
1134
|
this.syncInfo.observer = observer;
|
|
1133
1135
|
const onAdded = this.onSyncRouteAdded.bind(this);
|
|
@@ -1614,6 +1616,11 @@ let RouteListService = (() => {
|
|
|
1614
1616
|
this.emitLists('updated');
|
|
1615
1617
|
});
|
|
1616
1618
|
}
|
|
1619
|
+
onStatsUpdateHandler = (stats) => this.onRouteStatsUpdate(stats);
|
|
1620
|
+
subscribeStats() {
|
|
1621
|
+
this.observer.off('stats-update', this.onStatsUpdateHandler);
|
|
1622
|
+
this.observer.on('stats-update', this.onStatsUpdateHandler);
|
|
1623
|
+
}
|
|
1617
1624
|
onRouteStatsUpdate(stats) {
|
|
1618
1625
|
this.stats = stats;
|
|
1619
1626
|
for (const stat of stats) {
|
|
@@ -8,5 +8,9 @@ export declare class RouteImportError extends Error {
|
|
|
8
8
|
});
|
|
9
9
|
toFailure(): RouteImportFailure;
|
|
10
10
|
}
|
|
11
|
+
export declare class DuplicateRouteError extends Error {
|
|
12
|
+
readonly duplicateOf: string;
|
|
13
|
+
constructor(duplicateOf: string);
|
|
14
|
+
}
|
|
11
15
|
export declare const mapErrorToImportCode: (err: unknown, scope?: ExternalFileScope) => RouteImportErrorCode;
|
|
12
16
|
export declare const toRouteImportError: (err: unknown, scope?: ExternalFileScope, message?: string) => RouteImportError;
|
|
@@ -7,7 +7,6 @@ import { FolderInfo, ImportDisplayProps, ParsedRoute, ScannedRoute } from './typ
|
|
|
7
7
|
import { Route } from '../base/model/route';
|
|
8
8
|
export declare class RouteLibraryScannerService extends IncyclistService {
|
|
9
9
|
private isCancelled;
|
|
10
|
-
private scanResult;
|
|
11
10
|
private importProps;
|
|
12
11
|
private currentScope;
|
|
13
12
|
constructor();
|
|
@@ -51,6 +50,8 @@ export declare class RouteLibraryScannerService extends IncyclistService {
|
|
|
51
50
|
private buildDiscoveredRoute;
|
|
52
51
|
private _parse;
|
|
53
52
|
private _parseTarget;
|
|
53
|
+
private buildParseFailure;
|
|
54
|
+
private logParseFailure;
|
|
54
55
|
private getReadFailureMessage;
|
|
55
56
|
private getFileName;
|
|
56
57
|
private validateVideoUrl;
|
|
@@ -32,12 +32,14 @@ export interface ParsedRoute {
|
|
|
32
32
|
alreadyImported: boolean;
|
|
33
33
|
parseError?: string;
|
|
34
34
|
parseErrorCode?: RouteImportErrorCode;
|
|
35
|
+
duplicateOf?: string;
|
|
35
36
|
format: RouteFormat;
|
|
36
37
|
observer?: IObserver;
|
|
37
38
|
}
|
|
38
39
|
export type ParseState = 'waiting' | 'parsing' | 'parsed';
|
|
39
40
|
export interface RouteDisplayItem {
|
|
40
41
|
id: string;
|
|
42
|
+
fileUri?: string;
|
|
41
43
|
label: string;
|
|
42
44
|
folder?: string;
|
|
43
45
|
distance?: FormattedNumber;
|
|
@@ -47,6 +49,7 @@ export interface RouteDisplayItem {
|
|
|
47
49
|
importable: boolean;
|
|
48
50
|
errorReason?: string;
|
|
49
51
|
errorCode?: RouteImportErrorCode;
|
|
52
|
+
duplicateOf?: string;
|
|
50
53
|
observer: IObserver;
|
|
51
54
|
}
|
|
52
55
|
export interface ImportDisplayProps {
|
|
@@ -102,6 +105,7 @@ export interface ScanContext {
|
|
|
102
105
|
discoveredCount: {
|
|
103
106
|
value: number;
|
|
104
107
|
};
|
|
108
|
+
results: ScannedRoute[];
|
|
105
109
|
recursive: boolean;
|
|
106
110
|
}
|
|
107
111
|
export interface ImportedLibrary {
|
|
@@ -212,6 +212,8 @@ export declare class RouteListService extends IncyclistService implements IRoute
|
|
|
212
212
|
private sortCardsBySuggestion;
|
|
213
213
|
protected isSameCardSet(a: Array<RouteCard>, b: Array<RouteCard>): boolean;
|
|
214
214
|
protected handleConfigChanges(): void;
|
|
215
|
+
protected onStatsUpdateHandler: (stats: ActiveRideCount[]) => void;
|
|
216
|
+
protected subscribeStats(): void;
|
|
215
217
|
protected onRouteStatsUpdate(stats: ActiveRideCount[]): void;
|
|
216
218
|
protected isMobile(): boolean;
|
|
217
219
|
protected getUserSettings(): import("../../settings").UserSettingsService;
|