Badge the exception (not yet cached) instead of the near-universal case

GpxStore#save converts eagerly, so "cached" is true for nearly every
file in the tree already — badging that was just noise. Flipped to
PENDING_ICON, shown only for the rare case: a .gpx/.fit dropped
straight into data/gpx/ on the filesystem, bypassing the app, and not
yet opened.

Also add an explicit download button next to delete (DOWNLOAD_ICON) —
the file name itself has been a download link since the last commit,
but isn't obviously clickable at a glance.
This commit is contained in:
randy committed 2026-09-07 22:59:02 +02:00
1 parent cd92dadf6a
commit 12635a48c3
4 files changed
+73 -26

No files matched your search

+19 -14
View File
@@ -282,26 +282,31 @@ multi-second hang on a checkbox click reads as broken in a way the
same wait during an upload doesn't. A file that fails to parse still
saves fine either way; the error just surfaces on first view instead.
A `.geojson` upload skips this path entirely — `geojson` reads and
returns it directly, no cache file, no `Gpx`/`Fit` involved; the file
tree's cached-file icon (`GEOJSON_ICON`, `public/js/icons.js`) always
shows for one, since there's no conversion step it could ever be
waiting on.
returns it directly, no cache file, no `Gpx`/`Fit` involved.
`GpxStore#save` converting eagerly means `cached` is `true` for
almost everything in the tree — the one exception is a `.gpx`/`.fit`
dropped straight into `data/gpx/` on the filesystem, bypassing the
app, and not yet opened. Badging the common case would just be noise,
so the file tree's icon (`PENDING_ICON`, `public/js/icons.js`) marks
that exception instead of the (near-universal) ready state.
+
Once a `.gpx`/`.fit` is cached, the tree shows it under its converted
name instead — `"ride.gpx"` reads as `"ride.gpx.geojson"`, matching
the real cache file sitting next to it on disk — since the GeoJSON,
not the original, is what's actually rendered on the map from that
point on. The original is never deleted or re-parsed once cached, just
not re-shown as itself; the tree's file name is a real link
(`GET /api/gpx/file/<path>?download=1`), the only way to get that
converted form out of the app as an actual file, since the cache is
otherwise an implementation detail with no name of its own to expose.
It's an `<a>`, not a `<span>`, specifically so clicking it downloads
instead of also toggling the row's checkbox — a native `<a>`/`<button>`
nested inside a `<label>` claims its own click instead of the label
forwarding it to the associated control, so the two behaviors don't
fight over the same click. A directly-uploaded `.geojson` keeps its
own name (nothing to convert) and downloads unchanged. `Gpx` parses the GPX with REXML: `<wpt>`
not re-shown as itself. Two ways to get that converted form out of the
app as an actual file (`GET /api/gpx/file/<path>?download=1`) — the
cache is otherwise an implementation detail with no name of its own to
expose: the file name itself is a real link (an `<a>`, not a `<span>`,
specifically so clicking it downloads instead of also toggling the
row's checkbox — a native `<a>`/`<button>` nested inside a `<label>`
claims its own click instead of the label forwarding it to the
associated control, so the two behaviors don't fight over the same
click), and a dedicated download button sits next to delete for anyone
who wouldn't guess the name itself is clickable. A directly-uploaded
`.geojson` keeps its own name (nothing to convert) and downloads
unchanged either way. `Gpx` parses the GPX with REXML: `<wpt>`
waypoints become `Point` features (with `name`, `desc` and `time`
carried through as properties), each `<trk>` becomes one
`MultiLineString` feature (one line per `<trkseg>`), and each `<rte>`