Commit graph

141 commits

Author SHA1 Message Date
SkateSpots
efd7e9608d feat(create-spot): pin map, progressive disclosure, mandatory photo+description, inline errors, draft autosave 2026-10-07 10:29:01 +07:00
SkateSpots
24322b3b1c feat(map): tap drops a pin + confirm card instead of jumping into create-spot 2026-10-07 10:26:36 +07:00
SkateSpots
1078bf9a8e i18n(th): batch 5 — routing/directions + Sasquatch assistant (UI + FAQ) 2026-10-06 13:54:07 +07:00
SkateSpots
59cf47d3e6 i18n(th): batch 4 — shared labels, fees & receipts, physical cards 2026-10-06 13:51:23 +07:00
SkateSpots
185924649c i18n(th): batch 3 — spot detail/forms (spotUi) + DIY hub (diyUi) 2026-10-06 13:49:19 +07:00
SkateSpots
b8c306125c i18n(th): batch 2 — map (mapUi + mapPage) + AGENTS note 2026-10-06 13:35:20 +07:00
SkateSpots
39572998a0 feat(clink): real bech32+TLV decode/validation (lib/clink.ts), spec-correct raw QR/copy, show decoded pricing; validate scanner 2026-10-06 11:16:20 +07:00
SkateSpots
9ca5f2a604 fix(audit UI/UX): 44px slabs, missing grid base cols, aria-labels, dark weather panel, contrast, sheet width, stretched-link SpotCard 2026-10-05 21:47:29 +07:00
SkateSpots
5728c226d5 fix(audit P1): comment cache key, ticket verify fails closed, featured amount check, no empty-object persist, sticky sub-bars 2026-10-05 21:07:07 +07:00
SkateSpots
a9dcb5719c feat(supporters): per-row dates + 'updated', dedicated /donate page, extract shared support cards 2026-10-05 19:54:49 +07:00
SkateSpots
4ffad80728 feat(supporters): ways-to-support trio (purpose per rail, scrolls to each) 2026-10-05 19:44:31 +07:00
SkateSpots
1691d49930 feat(supporters): add silent-payments donation card (BIP-353 name + QR) 2026-10-05 19:40:38 +07:00
SkateSpots
cc1883cf82 feat(supporters): top 21, extravagant auto tiers + tier ladder with supporter avatars 2026-10-05 19:02:25 +07:00
SkateSpots
66d83123cd feat(supporters): Dec-2025 cutoff + collapse list to top 10 (show all) 2026-10-05 18:57:53 +07:00
SkateSpots
24e1369f6c feat(supporters): unified ranked wall, verifiable zaps, tier legend, impact link, pay-for detail, anonymous flag, map chip, OG meta 2026-10-05 18:55:16 +07:00
SkateSpots
87aca51248 feat(supporters): pay-for list + impact stats; scope zaps to the project 2026-10-05 18:46:49 +07:00
SkateSpots
b88379a390 feat(supporters): add tiers/badges and a unified top-zappers block 2026-10-05 18:00:19 +07:00
SkateSpots
1bdaa57aa3 feat(supporters): on-chain verify chips, real avatars/names, fiat total, footer count 2026-10-05 17:56:10 +07:00
SkateSpots
918c289a71 feat(supporters): present Angor as a donation option for small amounts 2026-10-05 17:45:08 +07:00
SkateSpots
3f94d9be30 feat(supporters): show Angor supporters with amounts on /supporters 2026-10-05 17:37:00 +07:00
SkateSpots
b247be13b0 DIY: parametric 2D deck designer with 1:1 printable template
New /diy/deck-designer tool. src/lib/deckDesign.ts holds the pure geometry
(outline, waist, rounded ends, kicks, concave) and the exporters; the component
renders a live top view, side profile and concave section with presets, saved
locally.

Exports: true-size SVG, a tiled 1:1 PDF (A4/Letter, split across sheets with
corner registration crosses), and an R12 DXF (outline polyline + 3/16" truck
holes + centreline, $INSUNITS=4). Truck patterns: New school 53.975x41.275 mm
and Old school 63.5x41.275 mm.

Deliberately 2D: this is the outline a builder traces and cuts, not a SK8CAD
clone (SK8CAD is proprietary; only its JSCAD engine is MIT, and we copied
nothing).
2026-10-05 11:16:34 +07:00
SkateSpots
b90fef1d2e DIY: drop the category chips; add the deck cost calculator
- Removed the sticky category-chip row from /diy (sections still group the cards).
- New in-app tool at /diy/deck-cost (components/DeckCostCalculator.tsx): a
  self-contained pricing model — labour rate + overhead, equipment amortisation,
  operation expenses, materials (with order/unload time), process time,
  packaging, profit margin and scrap → cost per board and a fair selling price
  (no-packaging / with-packaging). State persists to localStorage; reset to the
  worked example. The "Deck cost & pricing" card now links to it instead of
  showing "Coming soon".
- Smoke test walks /diy/deck-cost.
2026-10-05 10:08:01 +07:00
SkateSpots
30d46ddfed DIY: use internal links, mark the rest "coming soon"
Reworked the DIY hub per feedback: point at our own app sections where we have
relevant content, and render the topics we don't cover yet as unclickable cards
with a "Coming soon" badge — no external links.

- Live internal cards: Map your DIY spot (/create-spot), Buy parts & gear
  (/marketplace), plus the "Do it with people" actions (Builder, crews, spot,
  listing, coaching).
- Coming-soon cards: deck building, SK8CAD, cost/pricing, ledge/box, quarterpipe,
  flatbar, concrete basics, coping, regrip, bearings/trucks, 3D-printed molds,
  mold tools.
- Dropped external URLs/sanitizeUrl from the page; updated the footer copy.
2026-10-05 10:03:11 +07:00
SkateSpots
84fde55ddd Add the DIY section (resources for boards, ramps & concrete)
New /diy hub: curated resources for making your own gear, with cross-links into
the rest of the app.

- src/lib/diyGuides.ts: seed data — categories (Boards, Ramps & wooden
  obstacles, Concrete & DIY spots, Repairs & tuning, Tools & plans), resource
  cards (Open Source Skateboards / SK8CAD / Instructables / YouTube), and an
  actions block (find a Builder, find a DIY crew, map a DIY spot, sell builds,
  find a coach).
- src/pages/DiyPage.tsx renders it (category chips + card grid + actions);
  external links go through sanitizeUrl and credit the source.
- Route /diy (lazyWithRetry) + a nav-catalog entry (id diy) so it appears in the
  desktop "More" menu and the mobile burger; also linked from the site footer
  and the map links strip. Smoke test walks /diy.
2026-10-05 09:55:26 +07:00
SkateSpots
b37d627138 Add a slim, dismissible site-links strip to the map
The desktop footer is hidden on full-bleed map routes, leaving the
Newsletter/License/Privacy/Collaborators links (and the "not sponsored by
OpenSats" note) unreachable from the homepage. MapFooterStrip overlays them
inside the map container without stealing map height:

- Desktop: a slim full-width bottom bar.
- Mobile: a tiny collapsed "Links" chip that expands to a compact, scrollable
  row.
- Dismissible (useMapFooterHidden, device-local, same-tab synced) and
  restorable from Settings -> Appearance.
- The map's OSM attribution moved up (md:bottom-7) so the bar doesn't cover it.
2026-10-05 09:15:51 +07:00
SkateSpots
084c002c45 Fix mobile grid overflow + add a Top Shops leaderboard
The /lab profile card and /stats Top Mappers were overflowing off the right
edge: Tailwind `grid md:grid-cols-2` (no base column) renders one implicit
`auto` track on mobile that can exceed the container when a name is long, so the
whole card got pushed off-screen. Added the base `grid-cols-1` repo-wide (plus
`min-w-0` on truncating flex/grid children).

/stats also gained a "Top Shops — most shops added" leaderboard (kind 30017)
beside Top Mappers (spots): `topShops` in useGrowthStats, a generalized
MapperRow (count + unit), and EN/ES copy. PERSIST_BUSTER → v6 (new persisted
field).
2026-10-04 18:01:49 +07:00
SkateSpots
fb42f4f0d1 Add Cashu escrow settings + a "require escrow" listing flag
- useEscrowPrefs (device-local): default lock window, preferred escrow mint,
  require-escrow default for new listings.
- EscrowSettingsCard on the Wallet (native-only, beside EscrowKeyCard).
- EscrowRequestDialog now defaults its lock window + funding mint from the prefs.
- Listings gain an optional ['escrow','required'] tag: a toggle on
  CreateListingPage (defaulted from the pref) and a 🔒 badge on the card.
2026-10-04 17:54:42 +07:00
SkateSpots
d82ecf4adf Sasquatch: restore the pulse, hide by dragging left, cleaner Go Pro
- Restore the always-on blink/pulse (still motion-reduce:hidden for a11y).
- Drag the bubble off the left edge to hide it; a tiny left-edge tab brings it
  back. (The ✕/panel/Settings Hide paths remain.)
- Replaced the cramped inline "Go Pro" header pill with a clean full-width
  strip at the top of the panel body.
2026-10-04 17:54:42 +07:00
SkateSpots
0e405eaa00 Polish Sasquatch bubble; full mint list; link shops to events
Sasquatch (the look regressed): I over-corrected last pass by shrinking the
bubble on the map and bolting an always-visible ✕ over the avatar on mobile.
Restored the full-size bubble + glow on every route (the map only loses the
pulse), moved Hide to a desktop-hover ✕ and an EyeOff "Hide assistant" button
in the panel header (Settings → Appearance still re-enables it).

Mints: the add-mint dialog only offered a hand-picked "Recommended" shortlist.
It now also lists the full searchable MintRadar directory (useMintDirectory)
below the recommendations.

Events: CreateEventPage could only link a skate spot. Added an optional
"Link to a Shop" picker (useSkateShops, search, location autofill) that writes
['a','30017:…'] + ['venue-name', …].
2026-10-04 17:45:42 +07:00
SkateSpots
c8778aa3c9 Wallet: fix Manage chip overflow + plainer escrow copy
- The wallet status bar (Cashu/WebLN/NWC pills + Manage) was a rigid
  justify-between row that pushed the Manage chip off the right edge on phones.
  It now wraps, with Manage right-aligned (ml-auto), so it stays on screen.
- The escrow card copy was jargon ("advertise a public key ... if the deal falls
  through"). Reworded seller-facing: "turn on escrow ... a buyer can lock their
  sats to you ... you release them when you deliver — if you don't, the buyer can
  reclaim them after the agreed time." EN/ES.
2026-10-04 17:36:41 +07:00
SkateSpots
4cad201716 Add a Settings → Pro & Support hub
One discoverable home for every paid extra, so the pricing model is legible
instead of scattered across the Sasquatch panel and each offer form:

- components/ProSupportScreen.tsx (Settings ?section=pro + a menu row): honest
  "prepaid, not a subscription" framing; Sasquatch Pro status (days left) +
  PRO_TIERS with per-day sats and a Go Pro / Extend button; Featured placement
  (PLATFORM_FEES x DURATION_TIERS with per-day + best-value); your currently
  featured Lab listings (featuredUntil); and the free paths (PoW listing, free
  AI FAQ).
- Distinct naming in copy: Sasquatch Pro (AI) vs Featured placement (visibility).
- Smoke test now walks /settings?section=pro.
2026-10-04 14:28:13 +07:00
SkateSpots
8cdc41cc15 Quieter Sasquatch bubble: no perpetual pulse, hideable, map-aware
Addresses "the floating Sasquatch flashing is annoying":

- Pulse is now state-driven: the animate-ping halo shows only when there's an
  unread tip (message), never perpetually; the unread dot matches.
- Honors prefers-reduced-motion (motion-reduce:hidden).
- Map-aware: Layout passes isFullBleed, so on the map the bubble runs compact
  (smaller, no glow, no pulse) instead of competing with map controls.
- Hideable: a ✕ on the FAB hides it (useSasquatchHidden, localStorage
  sasquatch-hidden, same-tab synced via an event), with a "Show Sasquatch
  assistant" toggle in Settings → Appearance to bring it back.
2026-10-04 14:20:46 +07:00
SkateSpots
bbe4ba2e14 Lab forms: multi-step wizard (shared form + coach) and bespoke scroll-to
- FormWizard/WizardStep: a step shell for the long offer forms. Steps stay
  mounted (hidden, not unmounted) so nested components with their own state
  (LabExtrasFields) keep their values; a controlled step lets the form jump to
  the offending step on validation.
- Applied to CraftProfileForm (the 8 data-driven crafts, 5 steps: Identity /
  What you offer / Rates / Contact / Media) and the coach page (5 steps).
  Submit + Preview live in the wizard footer; submit errors jump to the right
  step before scrolling.
- The 4 bespoke pages now scroll to their inline contact error on a failed
  submit.
2026-10-04 12:29:42 +07:00
SkateSpots
3d52f07144 Lab forms: profile-completeness progress + scroll-to/inline validation
Progress (interpreted as a completeness indicator, not a wizard — the form
stays one scroll):
- LabProfileProgress shows photo/bio/skills/rate/location/contact as done/todo
  with a % bar; wired into CraftProfileForm, coach, and the 4 bespoke pages.

Validation (was toast-only):
- CraftProfileForm splits email/website/portfolio into per-field inline errors
  with a red field border, gives the first skill section + contact fields ids,
  and scrolls to the first problem on submit; required sections show an inline
  message after a submit attempt.
- Coach page likewise splits email/website, shows inline errors + red borders,
  and scrolls to the contact field or the skills grid.
2026-10-04 11:39:31 +07:00
SkateSpots
3e84310384 Autosave Lab profile drafts so long forms survive nav/reload
- useLabDraft persists the form snapshot to localStorage (content-debounced)
  and only offers a saved draft as resumable when it differs from the current
  state, so opening an existing profile never shows a spurious resume banner and
  a draft can't silently clobber a freshly loaded profile.
- LabDraftBanner offers Resume / Discard; wired into CraftProfileForm (8 crafts)
  and the coach + filmer/editor/photographer/builder pages.
- The draft clears on a successful publish.
2026-10-04 11:35:41 +07:00
SkateSpots
81b5fd0fab Add a live profile preview to every Lab offer form
Filling the long form was blind until publish. LabProfilePreview renders a
faithful profile card from the current draft — title, bio, image, location,
skills, structured rate, languages/availability/payments and gallery — with a
not-published-yet note.

- Wired into CraftProfileForm (8 crafts) and the coach + filmer/editor/
  photographer/builder pages via a Preview button next to Submit.
- parseLabExtras now accepts any { tags } so a draft tag list can be previewed
  without a published event.
- i18n for the preview copy (EN/ES).

Still open: draft/autosave and a multi-step layout.
2026-10-04 11:25:58 +07:00
SkateSpots
dd437693ab Lab offer UX: post-publish moment + "Your Lab profiles" quick access
- After publishing, land with ?published=1 and show a one-time "You're live —
  share it" banner on the profile (with a Share button), dismissible.
- /lab gains a "Your Lab profiles" strip listing the viewer's own profiles,
  each deep-linking to view/edit — so multi-craft providers aren't hunting
  through the list.
2026-10-04 11:21:40 +07:00
SkateSpots
9d1a6999ea Add owner Edit + Share to Lab provider profiles
The offer → publish loop had two dead ends: a provider's own page only offered
Delete (no Edit), and there was no way to share a listing (the whole point of
announcing a service).

- Owner "Manage" card gains an "Edit profile" button deep-linking to
  /become/<craft> (prefills the existing profile). Admins still get only
  take-down, not a misleading self-edit link.
- A Share button on every provider page opens ShareProfileDialog (QR + copy +
  native share) with the service URL /coach/<pubkey>/<identifier>.
- ShareProfileDialog accepts an optional `path` to override the default
  /<npub> share URL.
- main.tsx clean-link shim now also rewrites /coach/<pubkey>/<d> to the hash
  route, so shared /coach/... links resolve on the live site.

Follow-ups (not done): live preview before publishing, draft/autosave for the
long forms, a multi-step/progress layout, and a "your Lab profiles" manage
strip on /lab.
2026-10-04 11:16:54 +07:00
SkateSpots
681eab9592 Add craft-specific detail fields with matching display sections
Per-craft freeform fields, each wired end-to-end (form + detail display) so
none are stored invisibly:

- Artist: deliverables, turnaround
- Graffiti: surfaces, max_size
- Musician: set_length, tech_rider
- Judge: travel, certifications
- Organizer: insurance, permits
- Tattoo: deposit, minimum
- Trainer: modalities
- Barber: duration
- Filmer/Editor/Photographer: turnaround, deliverables (filmer adds drone)
- Builder: lead_time, warranty

Data-driven crafts declare them via CraftProfileForm's extraFields (now with
labelKey); the bespoke pages add matching state/inputs/prefill/save.
src/lib/craftFields.ts (CRAFT_DETAIL_FIELDS) maps each tag to a display label,
rendered on the shared detail page; toggle tags render as a localized Yes.
Labels are translated (EN/ES); placeholders stay as examples.
2026-10-04 10:53:57 +07:00
SkateSpots
88a3e4d1b4 Generalize the coach profile enrichment to every Lab craft
The coach portfolio/experience/payments treatment is now a shared layer instead
of 12 copies:

- src/lib/labExtras.ts: parseLabExtras + shared option sets (languages,
  availability, structured rate, payment methods, booking fee, gallery/reel).
- components/LabExtrasFields.tsx: the self-contained form block — prefills from
  the existing event, owns its state, and reports the managed tags via onChange.
- components/LabExtrasSection.tsx: the detail block (all crafts).

Wired into CraftProfileForm (the 8 data-driven crafts) and the four bespoke
pages (filmer/editor/photographer/builder); CoachDetailPage renders the shared
section for every craft. Per-craft buying units (rateUnits) are set per craft in
becomeRoles.tsx (e.g. barber 'per cut', musician 'per gig', photographer 'per
shoot'). /lab facet filters now read parseLabExtras, so availability/languages
filter across crafts.

Coach-specific formats/levels/ages/credentials remain coach-only. Money stays
custody-free: declared methods + a booking fee paid directly to the provider.
2026-10-04 10:49:48 +07:00
SkateSpots
357d042a13 Expand coach credentials with skate roles + a free-text note
- Rename 'Federation coach' -> 'National federation coach'.
- Add 'Coaching qualification' and 'Competition judge'.
- Add a free-form cert_note so a coach can name an exact cert/issuer.

The core safety chips (first aid, CPR, safeguarding, background check,
insured) stay - they're the load-bearing trust signals. cert_note is
displayed under the chips and included in the same self-attested disclaimer.
2026-10-04 10:35:23 +07:00
SkateSpots
2db13612d1 Drop PayPal from coach payment methods
Keep Cash on site / Lightning / Cashu / Bank transfer / Other. Existing
profiles that already carry a 'paypal' payment_method still render it (raw
fallback), so no data is lost. NIP.md + AGENTS.md updated.
2026-10-04 10:32:27 +07:00
SkateSpots
080a6edf8b Add declared payment methods, PWYC and a direct booking fee for coaches
Coaching payments, kept honest and custody-free:

- payment_method (cash/lightning/cashu/bank/paypal/other) + payment_note:
  a declaration of what the coach accepts, not an integration. 'Cash on site'
  lets a walletless coach list without being nagged.
- pay_what_you_can flag (sliding scale).
- booking_fee + booking_fee_currency: a small fee to confirm a booking, paid
  DIRECTLY to the coach's kind-0 lud16 via the existing PayWithSats rail
  (mounted only while the sheet is open). Only offered for sats amounts when
  the coach has a Lightning address; copy states plainly that SkateSpots does
  not hold or refund it.

Deliberately NOT added: fiat/card rails (no KYC/custody) and a refundable
deposit (needs the native-only, unverified Cashu escrow). NIP.md documents the
model; AGENTS.md records the decisions.
2026-10-04 10:27:53 +07:00
SkateSpots
fa0b9cfe1c Fix raw i18n keys on coach chips; add structured rate + form polish
The coach option chips rendered raw 'labUi.coachForm.…' keys because the 29
per-option translation keys were referenced dynamically (t(option.key)) and had
never been added — the literal-only i18n guard missed them. Add every option
key (EN/ES) and extend scripts/check-i18n.mjs to also scan data tables for
key/titleKey/labelKey/badgeKey entries so this can't recur.

Also fix the chip layout: the global mobile 'button { min-height:44px }' rule
turned every chip into a 44px slab; add min-h-0 to the coach option chips,
language chips, Lab facet chips and Lab active-filter chips.

Remaining nice-to-haves:
- Structured rate: rate_amount + rate_currency (sats/USD/EUR/GBP) + rate_unit
  (hour/session/half-day/day/camp), rendered on the detail page; legacy
  freeform 'rate' still shows and acts as an optional note.
- Section the long coach form with a FormSection divider.
- Restyle the coach identity block from a red error-looking box to a neutral
  avatar card.
2026-10-04 10:05:11 +07:00
SkateSpots
3e534c5ffc Add coach portfolio fields and gallery to Lab coach profile
Enrich the coach (kind 35001) beyond the shared base schema with the
experience/portfolio fields that actually win a booking:

- Session formats, levels, age groups (repeated tags, client-filtered)
- Self-attested credentials/safety (first-aid, safeguarding, …) with an
  honest 'not verified' label
- Languages spoken (free-form) and availability
- Optional booking link
- Portfolio photo gallery (up to 12) reusing PhotoDropzone + ImageLightbox
- Highlight-reel video link (YouTube/Instagram)

Option sets live in src/lib/coachOptions.ts with EN/ES keys. parseSkateCoach
exposes the new fields; NIP.md documents the tags.

Also fix the pre-existing ErrorBoundary test (ErrorFallback uses
useTranslation but the test rendered no LanguageProvider).
2026-10-04 09:44:15 +07:00
SkateSpots
b285db907d feat(map): show community confidence on spot cards
- New `useBatchSpotCommunityScores(spots)` — ONE query for many spots (same
  signals/weights as the single-spot hook; counts distinct accounts, drops the
  mapper, caps per account; skip WoT/activity weighting at this scale).
- SpotCard takes an optional `community` prop and shows a small "Community"
  (green) or "Seen" chip in the footer.
- MapPage scores the visible spot cards (shops excluded — different kind) and
  passes each card its score.
2026-10-03 19:28:28 +07:00
SkateSpots
77a3d592ea feat(spots): crews + Lab links count as strong signals; add a verify nudge
- useSpotCommunityScore now also reads crews (35006, `a` ref, weight 3) and Lab
  profile links (35001-35014, `a` ref, weight 2) as effortful signals — a whole
  crew listing the spot, or a Lab pro linking it, is strong evidence. A crew
  ref alone reaches "community". (Tours/events use geohash, not a spot coord,
  so they're not counted.)
- Spot page: on an unconfirmed spot, a logged-in non-owner now sees a "Be the
  first to confirm this spot" nudge with Add-photo + "I've skated here".
- Breakdown line/tooltip include crew + Lab counts.
2026-10-03 18:48:19 +07:00
SkateSpots
0e4e8c548a feat(spots): aggregate community confidence from all engagement signals
New `useSpotCommunityScore(spot)` — ONE batched relay query covering photos,
status reports, comments, reactions, zaps, collections and tag suggestions for
a spot, then a distinct-account, tier-weighted score:

- counts DISTINCT accounts and never the mapper's own signals;
- weights costly signals higher (zap 3, photo/status 2, comment/reaction/
  collection/tag 1) and caps each account's contribution at 3;
- multiplies by the actor's web-of-trust + real-activity (reusing
  useBatchTrustScores/useBatchActivity) so a ring of empty accounts can't move it;
- returns level (unverified | seen | community) + a breakdown.

SpotDetailPage now derives the Community-verified badge from this (not just
photos+status) and shows a small "why" line (e.g. "2 photos · 1 zap · 3
comments") under the badges. No new event kinds.
2026-10-03 18:39:54 +07:00
SkateSpots
4d30240df9 feat(crew): validate & normalise website, band and social links
Website/band/social inputs stored whatever was typed, so a bare "crew.com"
wasn't clickable. Now they run through normalizeUrlInput + sanitizeUrl (bare
domains become https://), with an inline "enter a valid link" hint on website
and band URL. A non-URL social (e.g. a @handle) is kept as typed so nothing is
dropped. Matches the profile/craft forms.
2026-10-03 18:05:07 +07:00
SkateSpots
0548a7a7c6 feat(crew): tidy the create-crew "Extras" step into collapsible groups
The Extras step stacked 9 ungrouped blocks. Now:
- Identity (NIP-05, links) — open by default.
- Sasquatch AI — open when the agent is on.
- Showcase (video, sponsor, band).
- Payments & treasury (treasury + the CLINK tip jar, moved out of "Links").
- Integrations (Lab link).
- Advanced (Coordinator) — collapsed + last, so a new founder isn't asked
  about experimental plumbing.

Other fixes:
- The crew AI agent is now labelled "Sasquatch · Pro" (it runs on the same
  Pro-gated Sasquatch proxy, falling back to a free model), with an honest note.
- crew founded year is clamped to 1954..current on save and bounded in the input.
- "Skip to end" is no longer hidden on mobile.
- Removed the coordinator copy keys left unused by the previous pass.
2026-10-03 18:02:12 +07:00
SkateSpots
3cc16d18ca feat(crew): clearer, consistent crew-creation form
- Coordinator block: shorter copy, a Remove (✕) button, and an honest
  "pointer only — real Cordn MLS isn't wired yet" note (the old paragraph was a
  wall of jargon).
- Save now blocks on a non-empty but unparseable coordinator (was silently
  dropped) instead of publishing a crew with no coordinator.
- One consistent block order for create (wizard "Extras") and edit, grouped:
  core -> squad/places -> identity (NIP-05, links, band) -> agent -> showcase
  (video, sponsor) -> treasury -> integrations (Lab link, coordinator).
2026-10-03 17:33:35 +07:00
SkateSpots
127819e3da feat(nip05): multiple handles for profiles, crews and shops
A NIP-05 name maps to one key, so "who owns it" decides where it lives:
- Crew: repeatable nip05 tags (up to 5), verified against the founder key.
- Shop: same on kind 30017, verified against the owner key (new field).
- Profile: kind-0 has ONE standard nip05; extra personal handles live in a
  separate NIP-78 record (kind 30078, d=skatespots:profile-handles) so another
  client re-saving the profile can't wipe them.

Shared pieces:
- components/Nip05Badges.tsx — generic verify-and-render chip row (any owner).
- components/Nip05HandlesEditor.tsx — shared add/remove editor with live
  verified/mismatch status; used by profile, crew and shop forms.
- CrewNip05Badge now delegates to Nip05Badges; CrewForm uses the shared editor.
- Profile page shows primary + extra handles, and each crew/shop chips its own
  verified handle.
- lib/profileHandles.ts + hooks/useProfileHandles.ts (read/publish the record).
- types: SkateShop gains nip05/nip05s; parseSkateShop parses repeatable nip05s.
- NIP.md + AGENTS.md + i18n (EN/ES) updated.
2026-10-03 17:26:59 +07:00
SkateSpots
2daa0a9832 feat(crews): support multiple NIP-05 handles per crew
A crew is our own tag convention (unlike a kind-0 profile, whose nip05 is a
single string), so it can now advertise up to 5 handles, each verified
independently against the founder key.

- parseSkateCrew: collect all nip05 tags -> SkateCrew.nip05s (normalised,
  deduped, regex-validated, cap 5); nip05 stays = first (back-compat).
- CrewForm: add/remove handle rows with a live verified/mismatch/loading status
  (useCrewNip05 per row); format validation + max-5 hint; Enter adds.
- CrewNip05Badge renders every handle; CrewHero passes nip05s.
- NIP.md: nip05 documented as repeatable on kind 35006. i18n EN/ES.
2026-10-03 17:13:26 +07:00
SkateSpots
7aeda11041 feat(stash): password strength meter + filename hint on encrypted export
- Export dialog shows a 4-segment strength meter (length + character classes)
  with a Weak/Fair/Good/Strong label as you type the password.
- Shows the filename that will be saved; encrypted exports get an
  '-encrypted' suffix so the file is self-describing at a glance.
2026-10-03 11:29:04 +07:00
SkateSpots
0b50ce684f feat(stash): password/key-encrypted backups for Export/Import
The stash export was plaintext JSON, so a leaked/shared file exposed the whole
gear list. Export now wraps the payload in a version-2 JSON envelope:

- password mode: scrypt (NIP-49 params, 2^16/r8/p1) -> AES-256-GCM, with the
  header (mode+cipher+KDF) bound as AAD so it can't be downgraded.
- nip44 mode: NIP-44-encrypted to your own key (no password to remember; needs
  the matching key to open).
- plaintext v1 exports still import unchanged.

New src/lib/stashBackup.ts (verified round-trip, wrong-password, tamper and
KDF-downgrade rejection) + StashBackupExportDialog/StashBackupUnlockDialog.
The import picker only prompts for a password on password-encrypted files.
Password mode needs no signer (Amber-friendly); 'My key' is hidden unless the
signer supports NIP-44.
2026-10-03 11:23:49 +07:00
SkateSpots
52eff36bce i18n/a11y polish: translate StashPage, honest Pro-activation copy, contrast
- StashPage: Friends only / Add to Wishlist / Add Item / Edit Item / Public
  stash / Uploading… / Add Photos / placeholders / item count / list-grid aria
  labels now go through t() (t added to the sub-components).
- Manual Pro copy no longer claims the pass is already active: it says the pass
  activates once the provider confirms the payment.
- Bumped low-contrast gray-600 guidance text to gray-400 (DM empty state, E2EE
  note, stash item count).
2026-10-03 10:03:48 +07:00
SkateSpots
d566af851e fix: H14 double-pay guard + H1 claim provenance
H14 (NWC double-pay): after the 15s pay() timeout, useNWC now polls
lookupInvoice to confirm settlement before reporting failure; if the status
stays unknown it throws a coded error (PAYMENT_STATUS_UNKNOWN) and the callers
that used to fall through to another rail (useZaps, usePurchasePro,
PlatformFeePayment) now STOP and tell the user to check their wallet — no
automatic second payment.

H1 (spot claim): claim-from now stores the ORIGINAL COORDINATE
(30818:pubkey:d) rather than an ambiguous bare pubkey; SkateSpot parses
claimedFrom/claimReason; a claimed listing shows a 'Claimed listing' notice so
the duplicate is understandable. NIP.md updated. Full reader-side supersede is
documented as the remaining design item (a wrong rule could hide real spots).
2026-10-03 09:34:00 +07:00
SkateSpots
caa5f8c56e fix: tier 4 — offer role trust, proof replay, crew alerts, error/nav i18n
- H13 offer threads: the seller is ALWAYS the listing author (derived from the
  event pubkey), never a self-declared role tag — a non-owner can no longer
  appear as the seller with a payment pointer.
- H12 trade reputation: a preimage/deposit proof counts at most once, so a real
  payment can't be reused across attestations to inflate payment-verified.
- Crew alerts: listed crews are filtered against the user's own membership
  confirmation labels (one batched query), so a confirmed member no longer gets
  a phantom 'confirm to join' badge/notification when the crew is edited.
- Error boundaries (app + route) and DesktopNavManager are translated; the
  class boundary renders a translated functional ErrorFallback. New batch4
  namespaces errorsUi.* / desktopNav.*.
2026-10-02 23:22:13 +07:00
SkateSpots
9189fc6584 fix(trust): tier 2 — moderation, labels & data integrity (H2–H17)
- H2 useAdminModeration now fetches the 'cleared' label too, so an admin Undo
  actually propagates (newest-verdict-wins) instead of only working on-device.
- H3 admin takedowns publish the NIP-32 'hidden' label (useAdminHide) instead of
  an author-only kind-5 relays ignore; the cross-app hide is real now.
- H5 the /stats outage banner works: queryCanonical reports relay failures via
  onError, so 'partial' can be true and zeros are never presented as truth.
- H6 useShops got its own cache namespace (was colliding with useSkateShops).
- H7 listing NIP-05 is verified against the user's key, not merely self-asserted.
- H8 NIP-22 spot comments/transfer now use uppercase root tags (A/K/P); readers
  accept legacy #a; NIP.md corrected.
- H9 Lab profile edits preserve foreign  spot refs (crew link = 35006 only).
- H10 collection edits preserve foreign NIP-51 tags.
- H11 LAB_KINDS = all 13 provider kinds (was 35001-35005).
- H16 skip-to-content is a button that focuses <main> (was href=#main, which
  HashRouter treated as a route → 404).
- H17 CustomizeNavSheet is translated; useLocalStorage re-reads on key change and
  keeps stateRef in sync cross-tab.
- Backup 'confirmed' flag is now per-account (backup-confirmed:<pubkey>).
2026-10-02 21:30:01 +07:00
SkateSpots
7347f874ad i18n(es): batch 4 — Lab option labels, Sasquatch static FAQ, fee tiers
- labOptions.* (430 keys): all ~50 role LABELS/DESCRIPTIONS maps in types.ts
  (coach/filmer/editor/photo/builder/artist/graffiti/tattoo/trainer/musician/
  judge/organizer/barber) + TOUR_SKILL_LEVELS; 51 shared key maps in
  labelKeys.ts; consumers in becomeRoles/CraftProfileForm, LabPage,
  CoachDetailPage, the /become/* pages, TourCard/TourDetailPage/CreateTourPage/
  EventsPage. Trick names + tool/brand nouns stay English.
- sasquatchChat.* (83 keys): the free-tier STATIC_FAQ and local-result/fallback
  chat bodies (live AI already replies in-language; prompts stay English).
- feesUi.* (57 keys): usePurchasePro receipt prose + errors, PlatformFeePayment
  fee/duration tiers (LabProUpgrade/FeaturedEventUpgrade were already done).
- New src/i18n/batch4/ fragments spread into en/es.
2026-10-02 15:57:44 +07:00
SkateSpots
e357833557 i18n(es): batch 3 part 3 — shared label maps + leftover routing/Sasquatch strings
- labelsUi.* (221+ keys): spot categories/features/types, access, shelter, event
  types, gear categories/conditions, price/listings, tours, crews,
  member-visibility, tag categories, spot status. New src/lib/labelKeys.ts holds
  the shared value->key maps (one source; the five duplicated EVENT_TYPE_KEYS
  copies now import it). spotStatus.statusTimeAgo() uses the locale date shim.
- formatPrice() now takes an optional t so FREE/Trade/Make Offer translate.
- Route helpers: skateTime/distanceReadout/maneuver descriptors/formatAge
  translated (routeUi.*), consumers updated.
- Sasquatch: PRO_TIERS labels/badges + summarizeDraft titles/lines/ctas
  translated (sasquatchUi.*).
- Lab *_LABELS/_DESCRIPTIONS maps remain English (documented out of scope).
2026-10-02 15:31:27 +07:00
SkateSpots
3e6354995a i18n(es): batch 3 part 1 — routing UI, Sasquatch panel, collections, splash
- routeUi.* (123 keys): RouteDialog (incl. follow-mode banner, transit time
  picker + mode filters, itinerary legs), StopDepartures, TripItineraryCard.
- sasquatchUi.* (81 keys): SasquatchGuide, SasquatchProDialog,
  SasquatchActionCard, SasquatchAiStatusCard. LLM prompts + chat bodies
  deliberately left English.
- collectionUi.* / bootUi.*: AddToCollectionDialog example placeholders,
  SplashScreen rotating status lines + aria labels.
- New src/i18n/batch3/* fragment files, spread into en/es so parallel passes
  never edit the big literal.
2026-10-02 15:15:06 +07:00
SkateSpots
a6dd32e653 feat(card): public pass hint + shop/listing pass badges (audit items 2 & 4)
- VenueAccessCard now has a visitor branch: a public 'this venue issues access
  passes' hint (count only, never a holder list) shown only when the venue has
  active passes. Rendered for everyone on shops and gated spots.
- New shared, cached useVenuesWithPasses() hook resolves active pass coords +
  owners in one query.
- SpotCard badges shops that issue passes; MarketplaceItemCard flags sellers
  whose venue issues passes (title explains it is the venue, not the listing).
- i18n: venueAccess.publicTitle/publicDesc/badge/sellerHint (EN + ES).
2026-10-02 15:05:49 +07:00
SkateSpots
346c5e4a3e feat(card): manage access passes from the venue page
- New owner-only VenueAccessCard on shop and gated spot pages: shows active/
  revoked pass counts for that venue and links into the card studio with the
  venue preselected (/card?venue=<kind>:<pubkey>:<d>).
- RiderCardPage reads the venue search param and opens the park editor with the
  matching venue + type selected.
- i18n: new venueAccess.* keys (EN + ES).
2026-10-02 15:00:24 +07:00
SkateSpots
733dc5200e chore(card): audit cosmetics — zoom preview, translated title, drop legacy fields, clean URLs
- U2: CardPreview is zoomable (click for a full-screen overlay); used in the
  studio, 'My passes' and the public card.
- U4: the page title is translated (Rider Card / Carné de Rider) — was hardcoded
  English.
- L4: removed the legacy style/skills from RiderCardConfig + CardData (the card
  derives style/level and shows only witnessed badges).
- I2: cardVerifyUrl/cardPublicUrl are clean (/verify/<naddr>, /card/<npub>) and a
  startup shim rewrites those paths to the hash (old /#/ links still work).
2026-10-02 14:53:12 +07:00
SkateSpots
ec02e4fe71 fix(card): audit fixes — share link, dup passes, dead stats, public style, holo persist
- B1: Share's clipboard fallback copied https://…/<npub> (a path HashRouter
  ignores → homepage). Now /#/card/<npub> (native share text too).
- B4: useIssuedPasses didn't dedupe by d, so a revoked pass showed twice;
  now newest-per-d like useReceivedPasses.
- B2/L5: the redesign dropped the stats row, so Spots/Zaps/Events toggles +
  useRiderStats + spotsAdded/zapsReceived/eventsCreated/memberYear were dead.
  Removed across lib/riderCard, CardData, CanvasCardData, both pages.
- B3: PublicCardPage ignored derived style/level (config.style is '' now) — it
  now derives them from the Skater profile like the studio; holographic honors
  the saved config.
- B6: holographic wasn't persisted — added to the card config/tags/parse.
- B5: 'Save PNG' is front-only while Print is both sides → label it
  'Save front (PNG)' when a back exists.
2026-10-02 14:43:43 +07:00
SkateSpots
2a165c7aad feat(nfc): write-to-NFC-tag for cards & passes (Web NFC)
- lib/nfc.ts: isNfcSupported() + writeNfcUrl() via Web NFC (Chrome/Android),
  writing ONLY the verify URL (the same pointer the QR carries).
- NfcWriteButton component (renders nothing where Web NFC is unavailable — iOS,
  desktop, Capacitor WebView — so there's no dead button).
- Wired into: the venue's issued-pass list (issuer copies a tag per holder), the
  organiser's issued list, and the holder's 'My passes' view (+ Print).
- i18n nfc.* EN/ES. Tags are passive and clonable like a QR; trust still comes
  from the relay signature, and /verify already handles taps.
2026-10-02 14:10:09 +07:00
SkateSpots
7f9b5d9ccc feat(verify): verifier-first /verify screen for door staff
Redesigned around the ONE question a verifier needs in half a second:
- Big verdict hero (✅ Valid / ❌ Expired / Revoked / Invalid / ⚠ Unverified
  issuer / Not bound / No RSVP / Admitted / Authentic) with a plain-language line.
- Facts grid (holder, venue/organiser, valid-thru, access level) large and legible.
- Technical detail moved into a collapsed 'Details' section below.
- Combined verdict for access passes (signature + not revoked + not expired +
  issuer owns the venue) and event tickets (organiser's event + holder RSVP).
No account/app needed; NFC tap and QR both land here.
2026-10-02 14:04:24 +07:00
SkateSpots
38ae1273ff feat(card): organiser's issued check-in passes list
The event organiser can see who they've issued check-in passes to (holder +
revoked state) and revoke them — same affordance the park panel has. (Skipped
listing a holder's passes on their public profile: a pass is between a venue
and a holder, so publishing it would leak where someone has access.)
2026-10-02 13:52:16 +07:00
SkateSpots
a4ef8218e3 feat(card): 'My passes' tab — view & print passes issued to you
A holder previously had no way to see a pass a venue/organiser issued them.
New useReceivedPasses (kind 35679 #p=me) + a fourth /card tab listing them;
selecting one renders it as a card (type park) with a Print button, shows the
access level / valid-thru, and flags revoked passes. Completes the
issue -> receive -> display -> verify loop.
2026-10-02 13:36:22 +07:00
SkateSpots
e9dce47d46 feat(roles): owner/admin/organiser badges across pages + card studio
- New ControllerBadge (owner / admin / organiser / verified owner).
- Crew page hero: founder -> Owner, delegated admin -> Admin.
- Spot page: Owner; Shop page: 'Verified owner' (NIP-05 domain); Event page: Organiser.
- Card studio card-type chips show your role on each entity (Park=owner,
  Crew=owner/admin, Event=organiser) so it's clear what you control.
- i18n role.* EN/ES.
2026-10-02 13:30:04 +07:00
SkateSpots
892631e9f5 feat(card): duotone photo, park revocation, organiser check-in passes
- Duotone: desaturate + theme-accent tint the background photo so any image
  reads as a branded SkateSpots card (text stays legible). Verified visually.
- Revocation: the venue owner can revoke an issued pass from the studio list;
  revoke re-publishes under the same d with status=revoked (addressable
  supersede — works on every relay). /verify shows a red 'Pass revoked'.
- Organiser check-in: an event's organiser can issue a signed access pass (kind
  35679) bound to the event (a=31923:...); /verify confirms the issuer OWNS the
  event (generalised the venue check to spots OR events) and labels it Organiser.
- useIssuedPasses/useRevokePass; i18n EN/ES.
2026-10-02 13:10:48 +07:00
SkateSpots
a1952a31e5 feat(card): event ticket vs host + holder RSVP check on /verify
- Attendee card = 'MY TICKET', organiser card = 'EVENT · HOST' — never present a
  self-made card as organiser-issued.
- A ticket's QR now carries ?h=<holder-npub>; /verify fetches that holder's RSVP
  (kind 31925, #a = the event) and shows Ticket holder + RSVP status, so a door
  has a real check with no server.
- NIP.md + AGENTS updated; i18n EN/ES.
2026-10-02 13:04:11 +07:00
SkateSpots
9f3278f135 feat(card): Park-pass issuance logic + gate card types by availability
- Park Access is only offered if you own a venue (a spot you authored), and
  publishing REQUIRES a venue binding — only the venue owner's key can issue it.
  The pass names the holder; /verify shows holder + venue match + expiry.
- Card types are now gated by what you can actually issue/derive (Park/ Crew /
  Event), with a note explaining how to unlock more.
- Park panel shows a 'map a spot you own' prompt when you have no venue; the
  Publish button is disabled until a venue + holder are set.
- NIP.md: venue binding is required for new passes. i18n EN/ES.
2026-10-02 12:42:53 +07:00
SkateSpots
80e9552d11 feat(card): 'Finish your card' checklist (photo, name, styles/level, crew)
A card is only as good as its content, so the studio now shows what's missing
with one-tap links to fix it, instead of a subtle amber hint.
2026-10-02 12:38:44 +07:00
SkateSpots
00753b2769 polish(card): bigger/higher QR, theme-tinted scrim, profile-photo nudge
- QR 150->178px and moved up (bottom-right, above the identity block).
- Bottom scrim now fades into the THEME's dark background + an accent glow
  (was plain black) so the card reads as branded.
- Studio shows an 'add a profile photo' nudge when the account has no kind-0
  picture (the card's placeholder name was the tell).
2026-10-02 12:30:14 +07:00
SkateSpots
ce0e004a59 feat(card): customization depth — custom accent, logo, back side, pass expiry
- Custom accent colour (native picker) overrides the theme accent via
  --ss-accent in CSS + the canvas palette.
- Optional personal logo/crest (Blossom upload) shown next to the name.
- Printable back side (RiderCardBack React + drawCardBack canvas); Print renders
  both sides, and the preview + public card can flip.
- Park passes enforce valid-thru: passExpiry() drives an EXPIRED badge on the
  card and verify page; the input is now a date picker.
- Card schema -> v:3 (accent/logo/back). i18n; NIP.md + AGENTS.
2026-10-02 11:47:19 +07:00
SkateSpots
9891101452 feat(card): Card Studio redesign + drop self-claimed skills
- Two-pane studio: sticky live preview (desktop) + inspector column; mobile
  stacks preview-first.
- Actions decluttered to a single primary Share + a '...' menu (Save PNG /
  Print / Copy ID); removed the competing big buttons.
- Unsaved-changes state + manual Save changes button (no autosave, so an
  interactive signer like Amber isn't prompted per keystroke).
- Card type is now a compact 4-up chip row; the two toggle grids merged into
  ONE 'Show on card' list with live values.
- Skills: removed the self-claimed skill chips (marginal/unverifiable). The card
  shows witnessed badges (kind 1293) only; style + level are now DERIVED from the
  Skater profile (kind 35000) instead of a manual 'Skate Style' select.
- React card + canvas export updated; i18n cardUi.studio.*; NIP.md + AGENTS.
2026-10-02 11:24:51 +07:00
SkateSpots
7cafcc0dfa feat(card,review): Event pass/wristband template + Claims tab in /review
- /card gains a fourth type: Event. Renders an event (kind 31923) you organise
  or RSVP'd going/interested to (useMyRsvpCoords, kind 31925) as a passport/
  wristband: title, date/time, venue, cover, who it admits, going count.
  Derived from the event's own signed event — its QR verifies that event;
  /verify/:naddr renders kind 31923 too. React + canvas renderers; i18n.
- /review gains a dedicated Claims tab (pending private-spot ownership claims),
  replacing the banner that sat above the tabs.
2026-10-02 11:13:40 +07:00
SkateSpots
411f6e219a feat(spots): claim ownership of a private spot (NIP-05 + moderator confirm)
Private spots can't be domain-verified like shops, so ownership is a gated
two-step flow — never an instant takeover:
- A NIP-05-verified user files a claim (kind 1985 label in the separate
  org.skatespots.spotclaim namespace) on a spot marked access=private.
- A moderator approves by publishing claim-approved (moderation namespace,
  a=coord + p=claimant); surfaced in a pending-claims list atop /review.
- The claimant then finishes by re-publishing the spot with
  claim-reason=private-approved (existing claim mechanism).

Adds useSpotClaims/useApprovedSpotClaims/useAllSpotClaims/useSubmitSpotClaim,
useAdminApproveClaim, the private branch in SpotOwnership, i18n, and docs.
Non-private spots keep the abandoned-claim flow. Nothing is auto-transferred.
2026-10-02 11:06:58 +07:00
SkateSpots
ad75120a43 feat(card): Crew Card template (derived from the crew's signed event)
- /card gains a third type: Crew. Pick one of your crews and render a printable
  crew card (name, logo, home turf, founded, members, spots, vibes, Open call).
- No new event: the card is derived from the crew's own signed kind 35006, so its
  QR verifies that event (the founder's key IS the signature).
- Rendered in the React card + canvas export; /verify/:naddr now renders kind
  35006 with a link to the crew page.
- i18n cardUi.crew.* / verify.kindCrew; NIP.md + AGENTS.md updated.
2026-10-02 10:42:26 +07:00
SkateSpots
e348796e05 feat(card): identity facets — crew crests, local badge, Lab role chips
- Card shows crew crests (kind 35006 memberships), a local badge (skater
  profile location/home_spot), and Lab role chips (kinds 35001-35014) — all
  DERIVED LIVE, so a card can't claim a role/crew the skater doesn't have.
- Rendered in the React card + the canvas export (print/PNG); local/crews as
  text lines, roles as blue chips.
- Per-facet show toggles in the editor; card schema bumped to v2 so the new
  flags default ON for cards saved before them.
- Also fixed: verifyUrl wasn't passed to the canvas renderer (QR fell back to
  the profile path).
- i18n cardUi.facets.* / cardUi.roles.*; NIP.md + AGENTS.md updated.
2026-10-02 10:35:06 +07:00
SkateSpots
dbb9ea98c0 feat(card): make Rider Cards real & verifiable
- Publish the Rider Card as an addressable event (kind 32271, d=rider-card) and
  hydrate the editor from it, so customization persists across devices.
- New routes: /card/:npub (public read-only card) and /verify/:naddr
  (signature check + park venue binding).
- Park Access Cards can bind to the issuer's own spot (a tag 30818:...) and the
  verify page confirms the issuer owns the venue.
- QR on the card (+ canvas export) now encodes the verify URL; the real QR
  renders in the React card too (was a placeholder / broken profile path).
- Extracted shared card visuals to src/components/RiderCard.tsx; helpers in
  src/lib/riderCard.ts; hooks useRiderCard/useRiderBadges.
- Documented in NIP.md; i18n cardUi namespace (EN/ES); smoke test covers the
  new routes.
2026-10-02 10:26:54 +07:00
SkateSpots
0b796440f1 feat(i18n): Spanish batch 2 — nav/chrome, events/shops/tours, crews/DMs, settings/auth, wallet/stash/newsletter/stats
~3,470 new keys (5,770 total, EN=ES), ~115 more files wired.
- nav + chromeUi: header, More menu, burger menu, footer, map toolbar widgets
  (sort/trust/weather), spot-type label helper.
- placesUi: events, shops, tours, food. socialUi: crews, DMs, notifications,
  collectives, crew chat/coordinator. settingsUi: settings, auth/signup,
  onboarding, profile edit, legal. walletUi: wallet/Cashu, stash, mints,
  app shop, newsletter, community stats, leaderboards, profile.
- Fixed 738 usages where an agent dropped the socialUi. prefix (caught by check:i18n).
2026-10-02 10:11:06 +07:00
SkateSpots
09735375c2 feat(i18n): Spanish (Castellano) — infra + Map/Spots, Marketplace, Lab (batch 1)
- First-visit language auto-detect (Spanish devices land in es; stored choice wins).
- Sasquatch AI replies in the app language (skate trick names kept English).
- i18n safety net: scripts/check-i18n.mjs wired into npm test — fails on any t()
  key missing from en/es or any en/es key drift.
- ~1,680 new keys (2,297 total) across mapUi / spotUi / marketUi / labUi and
  ~65 files wired to t(): Map, spots (detail/create/edit/chat), marketplace,
  trades, offers, escrow, shipping, The Lab, /become/* forms, coaches, skaters.
- Policy: only skate TRICK names stay English; user-generated content untouched.
2026-10-01 23:57:20 +07:00
SkateSpots
b70b9c677c fix(mobile-nav): keep tab labels on one line so the row stays aligned
'Shops & Spots' wrapped to two lines and pushed its icon out of line with the
rest of the bar. Shorten nav.map to 'Spots' (en/es) and render every tab label
single-line (w-full truncate text-center), so long customised labels can't wrap
and misalign the bar either.
2026-09-27 10:15:27 +07:00
shakespeare.diy
4dc2b0129e Make backup gently unavoidable, not skippable-forever (1.3.13)
Closes the real safety hole: "Remind me later" used to set a PERMANENT
localStorage flag, silently becoming "never remind again" — so users who
never backed up were never reminded again.

- BackupReminder: "Later" now snoozes for the current session only
  (sessionStorage), so it returns each visit until backup is actually
  done. Only `backup-confirmed` stops it for good. Never blocks the user.
- Downloading OR copying the recovery code (in SignupDialog's done step
  and in SecuritySettings) now marks backup-confirmed — genuinely safe
  users stop being nagged automatically.
- Plain-language copy pass (EN + ES): "Save your login" / "recovery code"
  instead of "nsec / recovery phrase" jargon.
- Non-blocking and identical UX on both phone and desktop (bottom sheet).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-09 11:23:44 +07:00
shakespeare.diy
2fa85c2bce Remove non-functional NoGood Radio everywhere (unused hook, i18n keys, onboarding refs)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 10:04:50 +07:00
shakespeare.diy
f10aa7971c Step 3: Crews — map-anchored collective feature (kind 35006)
- New SkateCrew kind 35006 + type/parser + useCrews/useCrew hooks
- /crews directory, /create-crew, /edit-crew, /crew/:pubkey/:identifier pages
- Reuses the collective engine: NIP-32 membership attestation (confirmed/pending),
  CollectiveMembers + ConfirmMembershipCard, ClinkPayButton, SpotPicker
- Activity v1: home spots + crew member feed (no events, per plan)
- Map: teal/cyan Crews layer at home turf with crew markers + popups;
  wired through SpotMap props, layer toggles (mobile popover + filters sheet),
  and mobileLayerCount badge
- Nav: Crews added to navItemKeys (desktop header, mobile bottom bar, sidebar)
  with en/es i18n keys
- NIP.md: documents kind 35006, tag taxonomy, and shared membership mechanism

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 13:59:50 +07:00
shakespeare.diy
6230948383 Fix build: externalize capacitor plugin + remove duplicate lab i18n keys
- vite.config.ts: mark nostr-signer-capacitor-plugin as external so
  Rollup doesn't try to bundle it (it's resolved at runtime by Capacitor)
- en.ts / es.ts: remove duplicate lab key that was causing esbuild warnings

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-09 11:40:53 +07:00
shakespeare.diy
e29d35f1e4 fix: restore full skate-spots source tree from misplaced src/src/ copy
The entire skate-spots codebase was accidentally copied into src/src/ and
public/public/ instead of src/ and public/ directly. This left the real
src/ directory as blank MKStack template stubs, explaining why the landing
page showed the default template instead of SkateSpots.

Changes made:
- Copied all files from src/src/ up to src/ (App.tsx, AppRouter.tsx, main.tsx,
  all pages, components, hooks, contexts, lib, i18n, test)
- Copied all files from public/public/ up to public/ (images, manifest, etc.)
- Deleted src/src/ and public/public/ nested directories
- Removed HelloWorld.tsx template stub from pages/
- Retained the previously fixed index.css (Tailwind v3 directives)

Full skate-spots feature set is now active:
- All 30+ routes (map, events, spots, coaches, marketplace, wallet, etc.)
- NWCProvider, LanguageProvider, PWAInstallPrompt, AmberCallbackHandler
- Skate community relays (goskatespots.nostr1.com, wotr.relatr.xyz, etc.)
- Outfit Variable font, Amber pre-check in main.tsx
- All hooks (useSkateSpots, useCoaches, useZaps, etc.)
- i18n support (en/es)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 22:18:28 +07:00