Consolidate note-drawing into Measure; add edit/delete
Measure already draws distance lines and area polygons by hand — a second click-to-draw UI in the notes tool (Point/Area mode, from the previous commit) duplicated that. Replaced it with a "Save as note" button on the Measure panel that saves whatever shape is currently drawn there; NotesControl goes back to being just a one-click point pin. Both share a single NoteSaveForm (notes.js) for the note-text + target-layer-file UI instead of two copies of it. gpx.js: the "lines" source now also renders a note-flagged LineString (dashed, popup-on-click) alongside the Polygon fill/outline from before — the plain track-line layer excludes category:"Note" features so a saved distance note doesn't also get the track hover-summary/ profile-panel treatment. Notes are now individually editable/deletable from their own popup (logged-in only): Edit swaps the popup content for an inline textarea, Delete confirms and removes. Both target the feature by a new client-generated properties.id rather than array position (new GpxStore#update_feature/#delete_feature, PUT/DELETE /api/gpx/notes), since neither array index nor the note's own text (the thing being edited) is a stable identifier. Verified live: Measure's Save button (disabled/enabled state, saves the exact on-screen shape), the popup's Edit/Delete buttons and inline edit form. Full test suite: 115 runs, 0 failures (new coverage for update_feature/delete_feature and the PUT/DELETE routes).
This commit is contained in:
1 parent
db58468187
commit
aafb4cb4a0
9 files changed
+499
-239
No files matched your search
+53
-2
@@ -230,6 +230,57 @@ class AppTest < Minitest::Test
|
||||
assert_equal 404, response.status
|
||||
end
|
||||
|
||||
def test_notes_update_requires_login
|
||||
assert_equal 401, request.put("/api/gpx/notes", params: { feature: sample_note_feature, path: "x.geojson", id: "a" }).status
|
||||
end
|
||||
|
||||
def test_notes_update_edits_the_matching_note
|
||||
created = json_body(request.post("/api/gpx/notes", params: { feature: sample_note_feature("Original", id: "a"), name: "My Notes" }, **logged_in_env))
|
||||
|
||||
response = request.put(
|
||||
"/api/gpx/notes",
|
||||
params: { feature: sample_note_feature("Edited", id: "a"), path: created["path"], id: "a" },
|
||||
**logged_in_env,
|
||||
)
|
||||
assert_equal 200, response.status
|
||||
|
||||
features = json_body(request.get("/api/gpx/file/#{Rack::Utils.escape_path(created["path"])}"))["features"]
|
||||
assert_equal ["Edited"], features.map { |f| f["properties"]["name"] }
|
||||
end
|
||||
|
||||
def test_notes_update_missing_note_is_404
|
||||
created = json_body(request.post("/api/gpx/notes", params: { feature: sample_note_feature("Original", id: "a"), name: "My Notes" }, **logged_in_env))
|
||||
|
||||
response = request.put(
|
||||
"/api/gpx/notes",
|
||||
params: { feature: sample_note_feature("Edited", id: "nope"), path: created["path"], id: "nope" },
|
||||
**logged_in_env,
|
||||
)
|
||||
assert_equal 404, response.status
|
||||
end
|
||||
|
||||
def test_notes_delete_requires_login
|
||||
assert_equal 401, request.delete("/api/gpx/notes", params: { path: "x.geojson", id: "a" }).status
|
||||
end
|
||||
|
||||
def test_notes_delete_removes_the_matching_note
|
||||
created = json_body(request.post("/api/gpx/notes", params: { feature: sample_note_feature("Turning spot", id: "a"), name: "My Notes" }, **logged_in_env))
|
||||
request.post("/api/gpx/notes", params: { feature: sample_note_feature("Narrow gate", id: "b"), path: created["path"] }, **logged_in_env)
|
||||
|
||||
response = request.delete("/api/gpx/notes", params: { path: created["path"], id: "a" }, **logged_in_env)
|
||||
assert_equal 200, response.status
|
||||
|
||||
features = json_body(request.get("/api/gpx/file/#{Rack::Utils.escape_path(created["path"])}"))["features"]
|
||||
assert_equal ["Narrow gate"], features.map { |f| f["properties"]["name"] }
|
||||
end
|
||||
|
||||
def test_notes_delete_missing_note_is_404
|
||||
created = json_body(request.post("/api/gpx/notes", params: { feature: sample_note_feature("Turning spot", id: "a"), name: "My Notes" }, **logged_in_env))
|
||||
|
||||
response = request.delete("/api/gpx/notes", params: { path: created["path"], id: "nope" }, **logged_in_env)
|
||||
assert_equal 404, response.status
|
||||
end
|
||||
|
||||
# --- LiveTrack webhook ---
|
||||
|
||||
def test_livetrack_webhook_requires_the_configured_secret
|
||||
@@ -268,10 +319,10 @@ class AppTest < Minitest::Test
|
||||
|
||||
private
|
||||
|
||||
def sample_note_feature(name = "Turning spot")
|
||||
def sample_note_feature(name = "Turning spot", id: nil)
|
||||
JSON.generate(
|
||||
"type" => "Feature",
|
||||
"properties" => { "name" => name, "category" => "Note" },
|
||||
"properties" => { "name" => name, "category" => "Note", "id" => id },
|
||||
"geometry" => { "type" => "Point", "coordinates" => [17.1, 48.1] },
|
||||
)
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user