Commit graph

49 commits

Author SHA1 Message Date
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