From cc4e1cd5ca42d45001494307088af3fb251b23dc Mon Sep 17 00:00:00 2001 From: SkateSpots Date: Thu, 1 Oct 2026 13:37:54 +0700 Subject: [PATCH] fix(spots): editing a spot no longer drops its city/country tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- AGENTS.md | 13 +++++++++++-- src/pages/EditSpotPage.tsx | 8 ++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3a8b694..85f349b 100644 --- a/AGENTS.md +++ b/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). - **Reverse geocoding hits Nominatim — disclosed (2026-10-01).** `src/lib/geocodeRegions.ts` (`reverseGeocodeRegions` / `pushRegionTags`) is the - **single source of truth**, used by **spot create, shop create and shop edit**; - it fills `city`/`country`/`country_code` via + **single source of truth**, used by **spot create, spot edit, shop create and + shop edit**; it fills `city`/`country`/`country_code` via `nominatim.openstreetmap.org/reverse?lat=…&lon=…` (`Accept-Language: en`) and **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" 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 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 free handles so a new skater has a human name from minute one. The registry is the Netlify Function **`netlify/functions/nip05.mjs`** diff --git a/src/pages/EditSpotPage.tsx b/src/pages/EditSpotPage.tsx index f1d053b..d009764 100644 --- a/src/pages/EditSpotPage.tsx +++ b/src/pages/EditSpotPage.tsx @@ -17,6 +17,7 @@ import { LoginArea } from '@/components/auth/LoginArea'; import { PhotoDropzone } from '@/components/PhotoDropzone'; import { TagInput } from '@/components/TagInput'; 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 { cn } from '@/lib/utils'; import { @@ -232,6 +233,12 @@ export default function EditSpotPage() { // Generate geohash from coordinates (7 chars = ~153m precision) 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 const eventTags: string[][] = [ ['d', spot.identifier], @@ -243,6 +250,7 @@ export default function EditSpotPage() { ['lon', lng.toString()], ['alt', `Skate spot: ${title.trim()}`], ]; + pushRegionTags(eventTags, geo); if (location.trim()) { eventTags.push(['location', location.trim()]);