fix(spots): editing a spot no longer drops its city/country tags
EditSpotPage rebuilds eventTags from scratch, so saving a spot replaced the addressable event WITHOUT its city/country/country_code — silently losing the region tags it had. It also meant re-saving a legacy spot could never fix a missing tag. Both edit pages (and both create pages) now go through the shared reverseGeocodeRegions()/pushRegionTags() helper, so region tags are re-derived on every save. Re-saving a legacy spot now tags it. Docs: AGENTS.md (any page that republishes an addressable event must re-derive or preserve its region tags).
This commit is contained in:
parent
6d2492d847
commit
cc4e1cd5ca
2 changed files with 19 additions and 2 deletions
13
AGENTS.md
13
AGENTS.md
|
|
@ -867,14 +867,23 @@ session with **no other memory of this project**. So:
|
||||||
the stats can't know about reports without querying them).
|
the stats can't know about reports without querying them).
|
||||||
- **Reverse geocoding hits Nominatim — disclosed (2026-10-01).**
|
- **Reverse geocoding hits Nominatim — disclosed (2026-10-01).**
|
||||||
`src/lib/geocodeRegions.ts` (`reverseGeocodeRegions` / `pushRegionTags`) is the
|
`src/lib/geocodeRegions.ts` (`reverseGeocodeRegions` / `pushRegionTags`) is the
|
||||||
**single source of truth**, used by **spot create, shop create and shop edit**;
|
**single source of truth**, used by **spot create, spot edit, shop create and
|
||||||
it fills `city`/`country`/`country_code` via
|
shop edit**; it fills `city`/`country`/`country_code` via
|
||||||
`nominatim.openstreetmap.org/reverse?lat=…&lon=…` (`Accept-Language: en`) and
|
`nominatim.openstreetmap.org/reverse?lat=…&lon=…` (`Accept-Language: en`) and
|
||||||
**never throws** (a geocoder outage can't block a publish). Nominatim **sees the
|
**never throws** (a geocoder outage can't block a publish). Nominatim **sees the
|
||||||
creator's IP** and was missing from `/privacy`'s "servers that see your IP"
|
creator's IP** and was missing from `/privacy`'s "servers that see your IP"
|
||||||
list — now disclosed as "Spot/shop geocoding". Coordinates are the **pin you
|
list — now disclosed as "Spot/shop geocoding". Coordinates are the **pin you
|
||||||
placed**, never the device's live GPS. ⚠️ Any new outbound call that receives
|
placed**, never the device's live GPS. ⚠️ Any new outbound call that receives
|
||||||
coordinates must be added to `/privacy` in the same change.
|
coordinates must be added to `/privacy` in the same change.
|
||||||
|
⚠️ **`EditSpotPage` used to DROP region tags (fixed 2026-10-01):** like
|
||||||
|
`EditShopPage` it rebuilds `eventTags` from scratch, so any `city`/`country`/
|
||||||
|
`country_code` the spot already had were silently lost on save (the new
|
||||||
|
addressable event replaced the old one without them). Both edit pages now
|
||||||
|
`reverseGeocodeRegions()` on save. **Any page that republishes an addressable
|
||||||
|
event must re-derive or preserve its region tags** — check this whenever a new
|
||||||
|
editor is added. Related: spots created before the tags existed stay
|
||||||
|
"Uncategorised" in `/stats` until they're re-saved (only the author can update
|
||||||
|
an addressable event).
|
||||||
- **NIP-05 names — `you@skate-spots.org` (2026-09-28):** SkateSpots hands out
|
- **NIP-05 names — `you@skate-spots.org` (2026-09-28):** SkateSpots hands out
|
||||||
free handles so a new skater has a human name from minute one. The registry is
|
free handles so a new skater has a human name from minute one. The registry is
|
||||||
the Netlify Function **`netlify/functions/nip05.mjs`**
|
the Netlify Function **`netlify/functions/nip05.mjs`**
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import { LoginArea } from '@/components/auth/LoginArea';
|
||||||
import { PhotoDropzone } from '@/components/PhotoDropzone';
|
import { PhotoDropzone } from '@/components/PhotoDropzone';
|
||||||
import { TagInput } from '@/components/TagInput';
|
import { TagInput } from '@/components/TagInput';
|
||||||
import { SKATE_SPOT_KIND } from '@/lib/types';
|
import { SKATE_SPOT_KIND } from '@/lib/types';
|
||||||
|
import { reverseGeocodeRegions, pushRegionTags } from '@/lib/geocodeRegions';
|
||||||
import { spotCategories, MAX_TAGS, shelterOptions, DEFAULT_SHELTER, SHELTERED_VALUES, type ShelterType } from '@/lib/skateTags';
|
import { spotCategories, MAX_TAGS, shelterOptions, DEFAULT_SHELTER, SHELTERED_VALUES, type ShelterType } from '@/lib/skateTags';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
|
|
@ -232,6 +233,12 @@ export default function EditSpotPage() {
|
||||||
// Generate geohash from coordinates (7 chars = ~153m precision)
|
// Generate geohash from coordinates (7 chars = ~153m precision)
|
||||||
const geohash = ngeohash.encode(lat, lng, 7);
|
const geohash = ngeohash.encode(lat, lng, 7);
|
||||||
|
|
||||||
|
// Best-effort reverse geocode → city/country/country_code. Re-run on edit
|
||||||
|
// so moving a spot updates its region, AND so a legacy spot that has no
|
||||||
|
// tags gets them the next time it's saved. (This page rebuilds tags from
|
||||||
|
// scratch, so without this it silently DROPPED any existing region tags.)
|
||||||
|
const geo = await reverseGeocodeRegions(lat, lng);
|
||||||
|
|
||||||
// Build event tags - use the same identifier to update the addressable event
|
// Build event tags - use the same identifier to update the addressable event
|
||||||
const eventTags: string[][] = [
|
const eventTags: string[][] = [
|
||||||
['d', spot.identifier],
|
['d', spot.identifier],
|
||||||
|
|
@ -243,6 +250,7 @@ export default function EditSpotPage() {
|
||||||
['lon', lng.toString()],
|
['lon', lng.toString()],
|
||||||
['alt', `Skate spot: ${title.trim()}`],
|
['alt', `Skate spot: ${title.trim()}`],
|
||||||
];
|
];
|
||||||
|
pushRegionTags(eventTags, geo);
|
||||||
|
|
||||||
if (location.trim()) {
|
if (location.trim()) {
|
||||||
eventTags.push(['location', location.trim()]);
|
eventTags.push(['location', location.trim()]);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue