incyclist-services 1.7.54 → 1.7.55

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.
@@ -30,8 +30,8 @@ class IncyclistXMLParser extends xml_1.XMLParser {
30
30
  async loadPoints(context) {
31
31
  const { data, fileInfo, route } = context;
32
32
  const gpxFile = { ...fileInfo };
33
- const xmlName = fileInfo.name;
34
- const fileName = data['gpx-file-path'] ?? xmlName.replace('xml', 'gpx');
33
+ const xmlBase = fileInfo.base;
34
+ const fileName = data['gpx-file-path'] ?? xmlBase.replace('.xml', '.gpx');
35
35
  if (fileName.startsWith('file') || fileName.startsWith('/') || fileName.startsWith('\\') || fileName.startsWith('.')) {
36
36
  gpxFile.type = 'file';
37
37
  gpxFile.filename = fileName;
@@ -41,10 +41,10 @@ class IncyclistXMLParser extends xml_1.XMLParser {
41
41
  gpxFile.url = fileName;
42
42
  }
43
43
  else if (fileInfo.type === 'url') {
44
- gpxFile.url = gpxFile.url.replace(xmlName, fileName);
44
+ gpxFile.url = gpxFile.url.replace(xmlBase, fileName);
45
45
  }
46
46
  else {
47
- gpxFile.filename = gpxFile.filename.replace(xmlName, fileName);
47
+ gpxFile.filename = gpxFile.filename.replace(xmlBase, fileName);
48
48
  }
49
49
  try {
50
50
  let points;
@@ -73,7 +73,7 @@ class BinaryReader {
73
73
  exports.BinaryReader = BinaryReader;
74
74
  const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
75
75
  if (info.type !== 'url') {
76
- return buildFromFile(info, referenced, scheme);
76
+ return buildFromFile(info, referenced);
77
77
  }
78
78
  if (referenced.url) {
79
79
  return referenced.url;
@@ -81,6 +81,9 @@ const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
81
81
  if (!referenced.file) {
82
82
  return;
83
83
  }
84
+ if (referenced.file && info.filename?.startsWith('content://')) {
85
+ return `${info.dir}${info.delimiter}${referenced.file}`;
86
+ }
84
87
  const targetFileName = referenced.file;
85
88
  const regex = /([\\/])/g;
86
89
  if (targetFileName.startsWith('http://') || targetFileName.startsWith('https://')) {
@@ -97,9 +100,9 @@ const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
97
100
  }
98
101
  };
99
102
  exports.getReferencedFileInfo = getReferencedFileInfo;
100
- const buildFromFile = (info, referenced, scheme = 'file') => {
103
+ const buildFromFile = (info, referenced) => {
101
104
  if (referenced.file) {
102
- const fileName = info.filename?.replace(info.name, referenced.file);
105
+ const fileName = info.filename?.replace(info.base, referenced.file);
103
106
  return `file:///${fileName}`;
104
107
  }
105
108
  return referenced.url;
@@ -107,9 +110,8 @@ const buildFromFile = (info, referenced, scheme = 'file') => {
107
110
  const buildAbsolutePathTarget = (fileName, info, scheme) => {
108
111
  const inputUrl = info.url;
109
112
  if (inputUrl.startsWith('incyclist:') || inputUrl.startsWith('file:')) {
110
- const target = {};
111
113
  const parts = inputUrl.split('://');
112
- const targetPath = parts[1].replace(info.name, fileName);
114
+ const targetPath = parts[1].replace(info.base, fileName);
113
115
  return `${scheme}://${targetPath}`;
114
116
  }
115
117
  };
@@ -95,7 +95,7 @@ let RouteLibraryScannerService = (() => {
95
95
  this.importProps = undefined;
96
96
  }
97
97
  getDisplayProps() {
98
- return this.importProps;
98
+ return { ...this.importProps };
99
99
  }
100
100
  importSingle(fileInfo) {
101
101
  const observer = new types_1.Observer();
@@ -125,8 +125,13 @@ let RouteLibraryScannerService = (() => {
125
125
  this.logError(err, 'scan', { uri: folderInfo.uri });
126
126
  observer.emit('error', err.message);
127
127
  });
128
- observer.on('scan-progress', (progress) => {
128
+ observer
129
+ .on('scan-progress', (progress) => {
129
130
  this.importProps.scanProgress = progress;
131
+ })
132
+ .on('scan-complete', () => {
133
+ console.log('# scan-complete');
134
+ this.importProps.phase = 'parsing';
130
135
  });
131
136
  return observer;
132
137
  }
@@ -143,9 +148,18 @@ let RouteLibraryScannerService = (() => {
143
148
  this.importProps.parseProgress = { parsed, total };
144
149
  });
145
150
  observer.on('parse-result', (route) => {
146
- this.importProps.routes.push(this.buildRouteDisplayItem(route));
151
+ const idx = this.importProps.routes.findIndex(r => r.id === route.controlFileUri);
152
+ if (idx !== -1) {
153
+ const observer = this.importProps.routes[idx].observer;
154
+ const displayProps = this.buildRouteDisplayItem(route, observer);
155
+ this.importProps.routes[idx] = displayProps;
156
+ if (observer) {
157
+ observer.emit('updated', displayProps);
158
+ }
159
+ }
147
160
  });
148
161
  observer.on('parse-complete', () => {
162
+ console.log('# parse-complete');
149
163
  this.importProps.phase = 'selecting';
150
164
  });
151
165
  return observer;
@@ -283,7 +297,7 @@ let RouteLibraryScannerService = (() => {
283
297
  const primaryFiles = files.filter((f) => {
284
298
  const name = typeof (f) === 'string' ? f : f.name;
285
299
  const ext = this.getExtension(name);
286
- return ext && parsers.isPrimaryExtension(ext);
300
+ return ext && ext !== 'gpx' && parsers.isPrimaryExtension(ext);
287
301
  }).map((f) => {
288
302
  if (typeof f === 'string') {
289
303
  return {
@@ -299,6 +313,7 @@ let RouteLibraryScannerService = (() => {
299
313
  if (!this.isCancelled) {
300
314
  const routeAnnouncement = await this.buildDiscoveredRoute(file, files, uri, folderName, parsers);
301
315
  discoveredCount.value++;
316
+ console.log('added', routeAnnouncement, this.scanResult.length);
302
317
  observer.emit('scan-result', routeAnnouncement);
303
318
  this.scanResult.push(routeAnnouncement);
304
319
  }
@@ -335,17 +350,29 @@ let RouteLibraryScannerService = (() => {
335
350
  const service = this.getRouteList();
336
351
  const targets = scannedRoutes.filter(r => !r.scanError);
337
352
  const total = targets.length;
353
+ targets.forEach(target => {
354
+ const file = this.buildFileInfo(target.controlFileUri, target.format);
355
+ this.importProps.routes.push({
356
+ format: target.format,
357
+ importable: false,
358
+ label: file.base,
359
+ id: target.controlFileUri,
360
+ alreadyImported: false,
361
+ observer: new types_1.Observer()
362
+ });
363
+ });
364
+ observer.emit('parse-start');
338
365
  for (let i = 0; i < targets.length; i++) {
339
366
  if (this.isCancelled)
340
367
  continue;
341
368
  const parsed = i + 1;
342
369
  const target = targets[i];
343
370
  observer.emit('parse-progress', { current: parsed, parsed, total, currentFolder: target.folderName });
344
- await this._parseTarget(target, service, observer);
371
+ await this._parseTarget(target, service, observer, i);
345
372
  }
346
373
  observer.emit('parse-complete');
347
374
  }
348
- async _parseTarget(target, service, observer) {
375
+ async _parseTarget(target, service, observer, idx) {
349
376
  if (service.existsBySourceUri(target.controlFileUri)) {
350
377
  observer.emit('parse-result', {
351
378
  alreadyImported: true,
@@ -465,37 +492,37 @@ let RouteLibraryScannerService = (() => {
465
492
  }
466
493
  }
467
494
  buildFileInfo(uri, ext) {
468
- const lastSlash = Math.max(uri.lastIndexOf('/'), uri.lastIndexOf('\\'));
469
- const dir = uri.slice(0, lastSlash);
470
- const base = uri.slice(lastSlash + 1);
471
- const name = base.slice(0, base.length - ext.length - 1);
495
+ const { dir, base, name } = this.getBindings().path.parse(uri);
472
496
  return {
473
- type: 'url',
497
+ type: 'file',
474
498
  url: uri,
475
499
  filename: uri,
476
500
  base,
477
501
  name,
478
502
  dir,
479
503
  ext,
480
- delimiter: '/'
504
+ delimiter: uri.startsWith('content://') ? '%2F' : '/'
481
505
  };
482
506
  }
483
- buildRouteDisplayItem(parsed) {
484
- const { route, alreadyImported, parseError, format } = parsed;
507
+ buildRouteDisplayItem(parsed, observer) {
508
+ const { route, alreadyImported, parseError, format, controlFileUri } = parsed;
485
509
  const descr = route?.description ?? {};
486
510
  const [C, U] = this.getUnitConversionShortcuts();
487
511
  const distance = descr.distance === undefined ? undefined : {
488
512
  value: C(descr.distance, 'distance', { digits: 1 }),
489
513
  unit: U('distance')
490
514
  };
515
+ const path = this.getBindings().path;
516
+ const info = path.parse(controlFileUri);
491
517
  return {
492
- id: route.description.id,
518
+ id: route?.description?.id ?? info?.base,
493
519
  distance,
494
- label: route.title,
520
+ label: route?.title ?? info?.base,
495
521
  alreadyImported,
496
522
  importable: parseError == null,
497
523
  format,
498
- errorReason: parseError
524
+ errorReason: parseError,
525
+ observer: observer ?? new types_1.Observer()
499
526
  };
500
527
  }
501
528
  getCompanionExts(parsers, primaryExt) {
@@ -206,6 +206,8 @@ let RoutesPageService = (() => {
206
206
  }
207
207
  }
208
208
  onImportClicked() {
209
+ if (this.showImportDialog)
210
+ return;
209
211
  try {
210
212
  this.showImportDialog = true;
211
213
  this.getRouteLibraryScanner().prepare();
@@ -217,7 +219,9 @@ let RoutesPageService = (() => {
217
219
  }
218
220
  importSingleRoute(fileInfo) {
219
221
  try {
220
- return this.getRouteLibraryScanner().importSingle(fileInfo);
222
+ const observer = this.getRouteLibraryScanner().importSingle(fileInfo);
223
+ this.updatePageDisplay();
224
+ return observer;
221
225
  }
222
226
  catch (err) {
223
227
  this.logError(err, 'importSingleRoute');
@@ -260,11 +264,11 @@ let RoutesPageService = (() => {
260
264
  }
261
265
  onImportClosed() {
262
266
  try {
267
+ this.showImportDialog = false;
263
268
  if (this.importObserver)
264
269
  this.importObserver.stop();
265
270
  this.getRouteLibraryScanner().done();
266
271
  this.importObserver = undefined;
267
- this.showImportDialog = false;
268
272
  this.getPageObserver().emit('import-closed');
269
273
  this.serviceState = this.getRouteList().search();
270
274
  this.updatePageDisplay();
@@ -27,8 +27,8 @@ export class IncyclistXMLParser extends XMLParser {
27
27
  async loadPoints(context) {
28
28
  const { data, fileInfo, route } = context;
29
29
  const gpxFile = { ...fileInfo };
30
- const xmlName = fileInfo.name;
31
- const fileName = data['gpx-file-path'] ?? xmlName.replace('xml', 'gpx');
30
+ const xmlBase = fileInfo.base;
31
+ const fileName = data['gpx-file-path'] ?? xmlBase.replace('.xml', '.gpx');
32
32
  if (fileName.startsWith('file') || fileName.startsWith('/') || fileName.startsWith('\\') || fileName.startsWith('.')) {
33
33
  gpxFile.type = 'file';
34
34
  gpxFile.filename = fileName;
@@ -38,10 +38,10 @@ export class IncyclistXMLParser extends XMLParser {
38
38
  gpxFile.url = fileName;
39
39
  }
40
40
  else if (fileInfo.type === 'url') {
41
- gpxFile.url = gpxFile.url.replace(xmlName, fileName);
41
+ gpxFile.url = gpxFile.url.replace(xmlBase, fileName);
42
42
  }
43
43
  else {
44
- gpxFile.filename = gpxFile.filename.replace(xmlName, fileName);
44
+ gpxFile.filename = gpxFile.filename.replace(xmlBase, fileName);
45
45
  }
46
46
  try {
47
47
  let points;
@@ -69,7 +69,7 @@ export class BinaryReader {
69
69
  }
70
70
  export const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
71
71
  if (info.type !== 'url') {
72
- return buildFromFile(info, referenced, scheme);
72
+ return buildFromFile(info, referenced);
73
73
  }
74
74
  if (referenced.url) {
75
75
  return referenced.url;
@@ -77,6 +77,9 @@ export const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
77
77
  if (!referenced.file) {
78
78
  return;
79
79
  }
80
+ if (referenced.file && info.filename?.startsWith('content://')) {
81
+ return `${info.dir}${info.delimiter}${referenced.file}`;
82
+ }
80
83
  const targetFileName = referenced.file;
81
84
  const regex = /([\\/])/g;
82
85
  if (targetFileName.startsWith('http://') || targetFileName.startsWith('https://')) {
@@ -92,9 +95,9 @@ export const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
92
95
  return `${scheme}:///${targetFileName}`;
93
96
  }
94
97
  };
95
- const buildFromFile = (info, referenced, scheme = 'file') => {
98
+ const buildFromFile = (info, referenced) => {
96
99
  if (referenced.file) {
97
- const fileName = info.filename?.replace(info.name, referenced.file);
100
+ const fileName = info.filename?.replace(info.base, referenced.file);
98
101
  return `file:///${fileName}`;
99
102
  }
100
103
  return referenced.url;
@@ -102,9 +105,8 @@ const buildFromFile = (info, referenced, scheme = 'file') => {
102
105
  const buildAbsolutePathTarget = (fileName, info, scheme) => {
103
106
  const inputUrl = info.url;
104
107
  if (inputUrl.startsWith('incyclist:') || inputUrl.startsWith('file:')) {
105
- const target = {};
106
108
  const parts = inputUrl.split('://');
107
- const targetPath = parts[1].replace(info.name, fileName);
109
+ const targetPath = parts[1].replace(info.base, fileName);
108
110
  return `${scheme}://${targetPath}`;
109
111
  }
110
112
  };
@@ -92,7 +92,7 @@ let RouteLibraryScannerService = (() => {
92
92
  this.importProps = undefined;
93
93
  }
94
94
  getDisplayProps() {
95
- return this.importProps;
95
+ return { ...this.importProps };
96
96
  }
97
97
  importSingle(fileInfo) {
98
98
  const observer = new Observer();
@@ -122,8 +122,13 @@ let RouteLibraryScannerService = (() => {
122
122
  this.logError(err, 'scan', { uri: folderInfo.uri });
123
123
  observer.emit('error', err.message);
124
124
  });
125
- observer.on('scan-progress', (progress) => {
125
+ observer
126
+ .on('scan-progress', (progress) => {
126
127
  this.importProps.scanProgress = progress;
128
+ })
129
+ .on('scan-complete', () => {
130
+ console.log('# scan-complete');
131
+ this.importProps.phase = 'parsing';
127
132
  });
128
133
  return observer;
129
134
  }
@@ -140,9 +145,18 @@ let RouteLibraryScannerService = (() => {
140
145
  this.importProps.parseProgress = { parsed, total };
141
146
  });
142
147
  observer.on('parse-result', (route) => {
143
- this.importProps.routes.push(this.buildRouteDisplayItem(route));
148
+ const idx = this.importProps.routes.findIndex(r => r.id === route.controlFileUri);
149
+ if (idx !== -1) {
150
+ const observer = this.importProps.routes[idx].observer;
151
+ const displayProps = this.buildRouteDisplayItem(route, observer);
152
+ this.importProps.routes[idx] = displayProps;
153
+ if (observer) {
154
+ observer.emit('updated', displayProps);
155
+ }
156
+ }
144
157
  });
145
158
  observer.on('parse-complete', () => {
159
+ console.log('# parse-complete');
146
160
  this.importProps.phase = 'selecting';
147
161
  });
148
162
  return observer;
@@ -280,7 +294,7 @@ let RouteLibraryScannerService = (() => {
280
294
  const primaryFiles = files.filter((f) => {
281
295
  const name = typeof (f) === 'string' ? f : f.name;
282
296
  const ext = this.getExtension(name);
283
- return ext && parsers.isPrimaryExtension(ext);
297
+ return ext && ext !== 'gpx' && parsers.isPrimaryExtension(ext);
284
298
  }).map((f) => {
285
299
  if (typeof f === 'string') {
286
300
  return {
@@ -296,6 +310,7 @@ let RouteLibraryScannerService = (() => {
296
310
  if (!this.isCancelled) {
297
311
  const routeAnnouncement = await this.buildDiscoveredRoute(file, files, uri, folderName, parsers);
298
312
  discoveredCount.value++;
313
+ console.log('added', routeAnnouncement, this.scanResult.length);
299
314
  observer.emit('scan-result', routeAnnouncement);
300
315
  this.scanResult.push(routeAnnouncement);
301
316
  }
@@ -332,17 +347,29 @@ let RouteLibraryScannerService = (() => {
332
347
  const service = this.getRouteList();
333
348
  const targets = scannedRoutes.filter(r => !r.scanError);
334
349
  const total = targets.length;
350
+ targets.forEach(target => {
351
+ const file = this.buildFileInfo(target.controlFileUri, target.format);
352
+ this.importProps.routes.push({
353
+ format: target.format,
354
+ importable: false,
355
+ label: file.base,
356
+ id: target.controlFileUri,
357
+ alreadyImported: false,
358
+ observer: new Observer()
359
+ });
360
+ });
361
+ observer.emit('parse-start');
335
362
  for (let i = 0; i < targets.length; i++) {
336
363
  if (this.isCancelled)
337
364
  continue;
338
365
  const parsed = i + 1;
339
366
  const target = targets[i];
340
367
  observer.emit('parse-progress', { current: parsed, parsed, total, currentFolder: target.folderName });
341
- await this._parseTarget(target, service, observer);
368
+ await this._parseTarget(target, service, observer, i);
342
369
  }
343
370
  observer.emit('parse-complete');
344
371
  }
345
- async _parseTarget(target, service, observer) {
372
+ async _parseTarget(target, service, observer, idx) {
346
373
  if (service.existsBySourceUri(target.controlFileUri)) {
347
374
  observer.emit('parse-result', {
348
375
  alreadyImported: true,
@@ -462,37 +489,37 @@ let RouteLibraryScannerService = (() => {
462
489
  }
463
490
  }
464
491
  buildFileInfo(uri, ext) {
465
- const lastSlash = Math.max(uri.lastIndexOf('/'), uri.lastIndexOf('\\'));
466
- const dir = uri.slice(0, lastSlash);
467
- const base = uri.slice(lastSlash + 1);
468
- const name = base.slice(0, base.length - ext.length - 1);
492
+ const { dir, base, name } = this.getBindings().path.parse(uri);
469
493
  return {
470
- type: 'url',
494
+ type: 'file',
471
495
  url: uri,
472
496
  filename: uri,
473
497
  base,
474
498
  name,
475
499
  dir,
476
500
  ext,
477
- delimiter: '/'
501
+ delimiter: uri.startsWith('content://') ? '%2F' : '/'
478
502
  };
479
503
  }
480
- buildRouteDisplayItem(parsed) {
481
- const { route, alreadyImported, parseError, format } = parsed;
504
+ buildRouteDisplayItem(parsed, observer) {
505
+ const { route, alreadyImported, parseError, format, controlFileUri } = parsed;
482
506
  const descr = route?.description ?? {};
483
507
  const [C, U] = this.getUnitConversionShortcuts();
484
508
  const distance = descr.distance === undefined ? undefined : {
485
509
  value: C(descr.distance, 'distance', { digits: 1 }),
486
510
  unit: U('distance')
487
511
  };
512
+ const path = this.getBindings().path;
513
+ const info = path.parse(controlFileUri);
488
514
  return {
489
- id: route.description.id,
515
+ id: route?.description?.id ?? info?.base,
490
516
  distance,
491
- label: route.title,
517
+ label: route?.title ?? info?.base,
492
518
  alreadyImported,
493
519
  importable: parseError == null,
494
520
  format,
495
- errorReason: parseError
521
+ errorReason: parseError,
522
+ observer: observer ?? new Observer()
496
523
  };
497
524
  }
498
525
  getCompanionExts(parsers, primaryExt) {
@@ -203,6 +203,8 @@ let RoutesPageService = (() => {
203
203
  }
204
204
  }
205
205
  onImportClicked() {
206
+ if (this.showImportDialog)
207
+ return;
206
208
  try {
207
209
  this.showImportDialog = true;
208
210
  this.getRouteLibraryScanner().prepare();
@@ -214,7 +216,9 @@ let RoutesPageService = (() => {
214
216
  }
215
217
  importSingleRoute(fileInfo) {
216
218
  try {
217
- return this.getRouteLibraryScanner().importSingle(fileInfo);
219
+ const observer = this.getRouteLibraryScanner().importSingle(fileInfo);
220
+ this.updatePageDisplay();
221
+ return observer;
218
222
  }
219
223
  catch (err) {
220
224
  this.logError(err, 'importSingleRoute');
@@ -257,11 +261,11 @@ let RoutesPageService = (() => {
257
261
  }
258
262
  onImportClosed() {
259
263
  try {
264
+ this.showImportDialog = false;
260
265
  if (this.importObserver)
261
266
  this.importObserver.stop();
262
267
  this.getRouteLibraryScanner().done();
263
268
  this.importObserver = undefined;
264
- this.showImportDialog = false;
265
269
  this.getPageObserver().emit('import-closed');
266
270
  this.serviceState = this.getRouteList().search();
267
271
  this.updatePageDisplay();
@@ -1,5 +1,6 @@
1
1
  import { ReadDirResult } from "../../api";
2
2
  import { FormattedNumber } from "../../i18n";
3
+ import { IObserver } from "../../types";
3
4
  import { Route } from "../base/model/route";
4
5
  export interface FolderInfo {
5
6
  uri: string;
@@ -21,6 +22,7 @@ export interface ParsedRoute {
21
22
  alreadyImported: boolean;
22
23
  parseError?: string;
23
24
  format: RouteFormat;
25
+ observer: IObserver;
24
26
  }
25
27
  export interface RouteDisplayItem {
26
28
  id: string;
@@ -30,6 +32,7 @@ export interface RouteDisplayItem {
30
32
  alreadyImported: boolean;
31
33
  importable: boolean;
32
34
  errorReason?: string;
35
+ observer: IObserver;
33
36
  }
34
37
  export interface ImportDisplayProps {
35
38
  phase: 'landing' | 'scanning' | 'parsing' | 'selecting' | 'ingesting' | 'complete' | 'result' | 'error';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-services",
3
- "version": "1.7.54",
3
+ "version": "1.7.55",
4
4
  "peerDependencies": {
5
5
  "gd-eventlog": "^0.1.27"
6
6
  },