perf(create-spot): cap reverse-geocode at 2.5s so publish can't stall before signing

This commit is contained in:
SkateSpots 2026-10-07 10:31:07 +07:00
parent f791e5375c
commit c08592144a
2 changed files with 21 additions and 6 deletions

View file

@ -1120,9 +1120,13 @@ session with **no other memory of this project**. So:
the form autosaves via **`useFormDraft`** (generalized from `useLabDraft`, same
resume-banner pattern). ⚠️ Leaflet's base CSS is **CDN-loaded in `index.html`**
(`src/main.tsx` just has a comment) — a new Leaflet map needs no CSS import.
Still open: the 60 m duplicate check still runs on submit with
`useSkateSpots(500)` (move it to pin-time + a targeted geohash query), and
publish still blocks on `reverseGeocodeRegions` before signing.
Follow-ups in the same pass: the 60 m duplicate check now runs **at pin-time**
(debounced, warning under the map) using the **same `useSkateSpots(2000)` cache
key as the map** (was a second `useSkateSpots(500)` query); and publish **caps
the reverse-geocode at `GEOCODE_BUDGET_MS` (2.5 s)** so a slow provider can't
stall signing (a miss falls back to the stats' read-time region derivation).
⚠️ A geohash-prefix nearby query is still impossible — relays only exact-match
`#g` — so the 2000-spot cache remains the mechanism.
- **Map pin rendering (perf, 2026-09-29):** the spot/tour/event/crew marker
effect in `SpotMap.tsx` used to `clearLayers()` + rebuild **every** marker
whenever the parent handed it fresh arrays, and added each marker with a

View file

@ -23,7 +23,7 @@ import { PhotoDropzone } from '@/components/PhotoDropzone';
import { TagInput } from '@/components/TagInput';
import { SpotLocationPicker, type LatLng } from '@/components/SpotLocationPicker';
import { SKATE_SPOT_KIND } from '@/lib/types';
import { reverseGeocodeRegions, pushRegionTags } from '@/lib/geocodeRegions';
import { reverseGeocodeRegions, pushRegionTags, type GeocodedRegions } from '@/lib/geocodeRegions';
import { spotCategories, MAX_TAGS, shelterOptions, DEFAULT_SHELTER, SHELTERED_VALUES, type ShelterType } from '@/lib/skateTags';
import { SPOT_TAG_CATEGORY_KEYS, SPOT_TAG_CATEGORY_DESC_KEYS, SHELTER_KEYS, SHELTER_DESC_KEYS } from '@/lib/labelKeys';
import { cn } from '@/lib/utils';
@ -68,6 +68,9 @@ type SpotDraft = {
const DRAFT_PREFIX = 'skatespots:spot-draft:';
/** How long to wait for a reverse-geocode before publishing without region tags. */
const GEOCODE_BUDGET_MS = 2500;
export default function CreateSpotPage() {
const { t } = useTranslation();
@ -386,8 +389,16 @@ export default function CreateSpotPage() {
// Reverse geocode to get structured city/country tags (best-effort; the
// helper never throws). These power Sasquatch's city/country search and
// the /stats region breakdown.
const geo = await reverseGeocodeRegions(lat, lng);
// the /stats region breakdown. Capped so a slow provider can't make
// "Publish" feel stuck — a miss is covered by the stats' read-time
// derivation of an untagged pin's coords.
const emptyRegions: GeocodedRegions = { city: '', country: '', state: '', countryCode: '' };
const geo = await Promise.race([
reverseGeocodeRegions(lat, lng),
new Promise<GeocodedRegions>((resolve) => {
window.setTimeout(() => resolve(emptyRegions), GEOCODE_BUDGET_MS);
}),
]);
let locationStr = location.trim();
if (!locationStr && geo.city) {
// Auto-populate an empty address from the geocode.