Consolidate original/GeoJSON download into one menu button

The file name doubling as a GeoJSON-only download link was easy to
miss as a link at all, and only ever covered half of what's actually
downloadable. One button next to delete now opens a small menu
(Original file / GeoJSON) instead -- costs a click when downloading
several files in a row, but both formats are reachable from one,
discoverable place. Only one menu open at a time, closed on any
outside click.

Verified in a real browser: opens with both items, closes on outside
click, no console errors.
This commit is contained in:
randy committed 2026-09-08 00:10:02 +02:00
1 parent 45567fc520
commit 1b4463a3c1
3 files changed
+149 -62

No files matched your search

+27 -24
View File
@@ -342,30 +342,33 @@ 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, so both forms need a way out of the app as an
actual file: the file name itself (`GET /api/gpx/file/<path>?download=geojson`)
is a real link, not a `<span>` — clicking it downloads the GeoJSON
rather than also toggling the row's checkbox, since a native
`<a>`/`<button>` nested inside a `<label>` claims its own click
instead of the label forwarding it to the associated control — and a
second button next to delete (`?download=original`) downloads the
untouched, as-uploaded file, since the name alone only covers half of
what's now downloadable and isn't an obvious click target regardless.
A directly-uploaded `.geojson` keeps its own name and both buttons
download the same (identical) content — redundant there, not wrong.
Both kinds of download require login, same as upload/move/delete/
create-folder — `GET /api/gpx/file` itself stays open to anyone
(viewing a file on the map isn't a write action), just not
`?download=...` on it. Since both links are real `<a href>`s (so
right-click/middle-click still do something sensible), a logged-out
click can't just be a plain navigation the way delete/upload already
aren't — `_download` (`gpx.js`) fetches instead, checks for a 401 the
same way `redirectIfUnauthorized` already does for every other write
action, and only then turns the response into a blob and clicks a
throwaway `<a download>` to trigger the actual save — a plain
navigation would otherwise land on a raw JSON error page instead of
being sent to log in. `Gpx` parses the GPX with REXML: `<wpt>`
point on. Plain text now, not a link — see below. The original is
never deleted or re-parsed once cached, just not re-shown as itself,
so both forms need a way out of the app as an actual file: one
download button, next to delete, opens a small menu ("Original file" /
"GeoJSON", `_downloadButton`/`_downloadMenuItem`) rather than the name
doubling as a GeoJSON-only download link the way it briefly did — that
was easy to miss as a link at all, and only ever covered half of
what's downloadable. Only one menu open at a time
(`_toggleDownloadMenu`/`_closeDownloadMenu`), closed by a
document-level click listener the same way an outside click closes a
`sidebar-flyout` dropdown, just scoped to this one control rather than
threaded through `onlyOneSidePanelOpen`. A directly-uploaded `.geojson`
keeps its own name and both menu items download the same (identical)
content — redundant there, not wrong. Both downloads (`GET
/api/gpx/file/<path>?download=original`/`?download=geojson`) require
login, same as upload/move/delete/create-folder, so the whole button —
not just its menu items — is left out of the DOM when logged out
rather than shown and failing on click (see "Authentication" below);
`GET /api/gpx/file` itself stays open to anyone otherwise (viewing a
file on the map isn't a write action), just not `?download=...` on it.
Each menu item is a real `<a href>` (so right-click/middle-click still
do something sensible), but a plain navigation would land a logged-out
click on a raw JSON error page instead of being sent to log in —
`_download` (`gpx.js`) fetches instead, checks for a 401 the same way
`redirectIfUnauthorized` already does for every other write action,
and only then turns the response into a blob and clicks a throwaway
`<a download>` to trigger the actual save. `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>`