@signalk/tracks-plugin 3.3.0 → 3.4.0

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/dist/types.d.ts CHANGED
@@ -55,6 +55,11 @@ export interface ImportedTrack {
55
55
  /** Segments as posted; a point is either timed throughout or not at all. */
56
56
  segments: ImportedPoint[][];
57
57
  }
58
+ /** An imported track as a store gives it back. */
59
+ export interface StoredImport extends ImportedTrack {
60
+ /** When the store kept it, in ms since the epoch. */
61
+ importedAt: number;
62
+ }
58
63
  /** Which imported tracks a query can see. Every field narrows; none means all. */
59
64
  export interface ImportFilter {
60
65
  /** Only imports naming one of these contexts; contextless ones drop out. */
@@ -65,9 +65,11 @@ be stored fails alone and recording carries on. It is admitted outside the limit
65
65
  above, so a large import neither pauses recording nor is dropped. Once recording
66
66
  writes have failed, imports are refused too. Retention never removes an imported
67
67
  track: it stays until it is deleted. A timed track imported for the own vessel is
68
- the exception: it is written into the recording, in place of what was recorded
69
- over the same time, which goes to the recycle bin first, and from then on it is
70
- part of the recording like any position.
68
+ the exception: it is written into the recording, by default only where the
69
+ recording and the history provider have nothing for at least five minutes (an
70
+ import that cannot read the provider is refused, since an outage would look like
71
+ one long gap), or, when asked, in place of what was recorded over the same time, which goes to the
72
+ recycle bin first. From then on it is part of the recording like any position.
71
73
 
72
74
  Deleting a track, or part of one, moves its points into recycle bin tables in the
73
75
  same database, and restoring moves them back; both are written as imports are.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalk/tracks-plugin",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "Plugin to keep track of tracks of vessels",
5
5
  "type": "module",
6
6
  "exports": {
package/public/import.js CHANGED
@@ -10,12 +10,26 @@ import { refusal } from './bin.js'
10
10
  /** Relative, because the server mounts this page under /@signalk/tracks-plugin. */
11
11
  const IMPORT_URL = '../../plugins/tracks/imports'
12
12
 
13
+ const points = (count) => `${count} point${count === 1 ? '' : 's'}`
14
+
13
15
  /** What an import came to, in words. */
14
- export function imported({ ids, skippedPoints }) {
15
- const count = `Imported ${ids.length} track${ids.length === 1 ? '' : 's'}.`
16
- return skippedPoints > 0
17
- ? `${count} ${skippedPoints} point${skippedPoints === 1 ? '' : 's'} without a time left out.`
18
- : count
16
+ export function imported({ ids, skippedPoints, overlappingPoints = 0 }) {
17
+ return [
18
+ `Imported ${ids.length} track${ids.length === 1 ? '' : 's'}.`,
19
+ ...(skippedPoints > 0 ? [`${points(skippedPoints)} without a time left out.`] : []),
20
+ ...(overlappingPoints > 0 ? [`${points(overlappingPoints)} left out where your vessel already has a track.`] : []),
21
+ ].join(' ')
22
+ }
23
+
24
+ /**
25
+ * The upload's query. Only the own vessel's tracks go into a recording, so
26
+ * replacing is offered for those alone; the route keeps by default.
27
+ */
28
+ export function importQuery({ self, replace }) {
29
+ if (!self) {
30
+ return ''
31
+ }
32
+ return replace ? '?self=true&onOverlap=replace' : '?self=true'
19
33
  }
20
34
 
21
35
  async function messageOf(response) {
@@ -31,6 +45,13 @@ export function start() {
31
45
  const form = document.getElementById('import-form')
32
46
  const status = document.getElementById('import-status')
33
47
  const button = form.querySelector('button[type="submit"]')
48
+ const { self, replace } = form.elements
49
+ // Only the own vessel has a recording to replace.
50
+ const allowReplace = () => {
51
+ replace.disabled = !self.checked
52
+ }
53
+ allowReplace()
54
+ self.addEventListener('change', allowReplace)
34
55
 
35
56
  function show(message, state) {
36
57
  // A failed upload can be tried again; a successful one reloads the page.
@@ -58,7 +79,7 @@ export function start() {
58
79
  show('Choose a GPX file first.', 'error')
59
80
  return
60
81
  }
61
- const query = form.elements.self.checked ? '?self=true' : ''
82
+ const query = importQuery({ self: self.checked, replace: replace.checked })
62
83
  button.disabled = true
63
84
  show('Importing…')
64
85
  let response
package/public/index.html CHANGED
@@ -31,6 +31,10 @@
31
31
  <form id="import-form">
32
32
  <label>GPX file <input type="file" name="file" accept=".gpx,application/gpx+xml" required /></label>
33
33
  <label><input type="checkbox" name="self" /> Tracks of my own vessel</label>
34
+ <label
35
+ ><input type="checkbox" name="replace" /> Replace what my vessel recorded over the same time (it goes to the
36
+ recycle bin)</label
37
+ >
34
38
  <button type="submit">Import</button>
35
39
  </form>
36
40
  <p id="import-status" role="status" hidden></p>