Commit graph

14 commits

Author SHA1 Message Date
SkateSpots
087f490acc refactor(layout): hoist Layout into the router so the chrome mounts once
- AppRouter wraps every route (except /amber-callback and the 404) in ONE
  <Route element={<Layout/>}> with an <Outlet/>; the chrome now persists across
  navigation instead of being torn down and rebuilt on every page.
- Layout: children optional (falls back to <Outlet/>); the map is full-bleed via
  a route check (isFullBleed) instead of a per-page noPadding prop.
- All 59 pages + CraftProfileForm/ErrorState/NewsletterIssueView now render
  fragments instead of <Layout> (structure-preserving), imports dropped.

Removes the whole remount class: chrome no longer re-runs its hooks per
navigation, Sasquatch chat state survives navigation, and the header can't
re-open Amber on every page change.
2026-09-27 13:41:42 +07:00
SkateSpots
f5ce4002e9 feat(lab,spots): validate contact details + drag & drop photo upload
- Lab forms: a non-empty email must be valid and website/portfolio must
  resolve to an https URL; invalid input shows an inline hint and blocks
  publish. Adds isValidEmail() (reused by sanitizeEmail). social/instagram
  are exempt since they may be bare @handles.
- New PhotoDropzone component: drag & drop plus multi-select image input,
  emitting File[] to the caller. Wired into CreateSpotPage + EditSpotPage
  (drop several photos at once) and AddPhotoToSpot, replacing the old
  one-file-at-a-time inputs.
- Spot photo uploads run sequentially and dedupe URLs.
2026-09-27 09:46:37 +07:00
SkateSpots
f73a360983 fix(lab): complete offer-a-service audit — data preservation, rendering, stats, input handling
- Preserve the geo 'g' tag and paid 'featured' tag when editing any Lab
  profile (all 7 craft roles via CraftProfileForm + 5 bespoke pages).
- Render previously collected-but-hidden fields on the Lab profile page:
  judge 'events_judged', organizer 'events_hosted', editor hardware/software,
  and spot references.
- Include Event Organizer (35013) in Community Stats + counts.
- Cap Title inputs at 100 (was 120) so profiles no longer fail the parser
  and silently vanish; MIN_TITLE_LEN 2 -> 1 for 1-char display names.
- Normalize bare-domain URLs (example.com) to https:// before storing instead
  of dropping/hiding them (new normalizeUrlInput helper; fixes extra url fields).
- No crew strip for judging/organizing (was wrongly showing Photo & Film);
  scrollable Offer Services FAB; live bio counters.
2026-09-27 09:38:03 +07:00
SkateSpots
a4b561fa79 feat(lab): coach/filmer/editor/photographer/builder are editable + have a title field
Finishes the Lab audit: the 5 bespoke 'become' pages now prefill from your
existing profile (edit, not blind overwrite) and expose a 'Headline' field used
as the profile title. Also:
- submit button shows Update... when editing; success toast says updated
- removed the dead ExistingLabProfileNotice (the warning it gave is now moot)

Every one of the 12 Lab provider flows (5 bespoke + 7 craft) now edits + titles
consistently. Gate: 101 tests, 0 errors/warnings, boot smoke green.
2026-09-27 00:25:10 +07:00
shakespeare.diy
5d96f8dfa8 Add tour countdown badges + fix/standardize NIP-05 provider prompts
Countdown badges (upcoming tours):
- New getCountdown() in TourCard: "In 3 days" / "Tomorrow" / "In 2 weeks" /
  "Happening now" (pulsing) based on start/end dates, category-tinted
- Shown on both compact and full TourCard, and in the TourDetailPage schedule card

NIP-05 provider fix (removes obsolete nip05.social):
- New single source of truth src/lib/nip05Providers.ts with working, curated
  providers: nostrcheck.me + nostr.directory (free), Nostr Plebs + nostr.band (paid)
- New reusable Nip05Required component (full-page "gate" + inline "banner"
  variants) listing those providers and deep-linking to /settings?section=nip05
- Wired into CreateTourPage (gate), CreateListingPage (banner) and all four
  Become* pages (Coach/Filmer/Photographer/Builder) for a consistent prompt
- SettingsPage NIP-05 screen now reads from the shared provider list
- Updated internal planning docs to match

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-09 22:31:44 +07:00
shakespeare.diy
1cb7c109e7 The Lab: warn before a "Become a …" form silently overwrites an existing profile (1.2.28)
Lab provider profiles (coach, filmer, editor, photographer, builder) are
addressable events with a fixed per-type `d` identifier, so re-publishing
REPLACES the existing profile. Unlike BecomeSkaterPage — which pre-fills from
the existing profile so re-submitting edits rather than clobbers — these five
forms start blank and reconstruct nothing. A returning provider who opened the
form again and submitted would silently wipe every field they'd previously set
(bio, rate, spots, contact, links) down to whatever they happened to retype.

Rather than fragile per-page reconstruction of the mixed services/skills tag
sets (high regression risk), added a small honest guardrail:

- New ExistingLabProfileNotice component: appears only when the current user
  already has a profile of that type (looked up via useLabProfile by their
  pubkey + the known identifier). It states plainly that publishing will
  REPLACE the current profile and links to view the existing one first.
- Wired into all five Become* pages with the correct identifier/label.

Also confirmed (not a bug): all five navigate to /coach/:pubkey/:identifier
after publish — CoachDetailPage is a generic Lab-profile renderer that resolves
any type by the distinct `d` identifier, so this is correct despite the name.

Investigation of the shared submit handlers found them otherwise consistent
and well-formed; left untouched.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-07 22:06:49 +07:00
shakespeare.diy
aea66d11e2 Fix 78 pre-existing TypeScript errors surfaced by npm test
- useDirectMessages: correct nip44 getConversationKey (Uint8Array) + finalizeEvent EventTemplate (remove pubkey/unused ephemeralPubkey/bytesToHex)
- Access current-user metadata via useCurrentUser() spread instead of user.metadata across ~10 files (rename to userMetadata where author metadata already bound)
- useCrewGoal: explicit UseQueryResult<CrewGoal|null> return type (fixes CrewForm/CrewTreasuryCard {} inference)
- New useCommentCounts hook; fix EventsPage/MapPage misuse of useComments(string[])
- CoachDetailPage: typed pget/pbool accessors for craft-specific union fields
- MapPage: shop entries include tags/category to satisfy SkateSpot
- SkateGlobe: read event.tags; LabPage: remove duplicate serviceType; LabCrewsStrip: add 'judging'
- Icon prop widened to accept style; ReportDialog subtype guard; SecuritySettings/TourCard/EventsPage comparisons; CashuWallet onClick; ConversationPage recipients; useLoggedInAccounts NLoginType import; LicensePage icon; TourDetailPage ZapButton target + real npub link; TestApp useSkateRelay

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-03 21:18:02 +07:00
shakespeare.diy
a89fca8e28 Lab: payment-readiness prompt on every provider form + drop downhill/longboard from judge
- New PaymentReadinessNote component: checks the logged-in user's Nostr profile
  (kind 0) for a Lightning address / Silent Payment and either confirms "you can
  get paid" or links them to Edit Profile to add one. Wired into CraftProfileForm
  (coach, filmer, editor, photographer, builder, artist, graffiti, tattoo,
  trainer, musician, judge) and the standalone coach/filmer/editor/photographer/
  builder forms, alongside the existing CLINK (noffer) field. Lightning address
  stays a single source of truth on kind 0 — never duplicated/stale per event.
- become-judge: removed the "Downhill / Longboard" event format (not part of the
  skateboarding activities this app covers).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-03 15:21:34 +07:00
shakespeare.diy
e48a5e6bc5 Add duration tiers + layered NIP-57 zap verification, extend featured/Pro to tours
- Add 4 duration tiers (7d/30d/90d/365d) with per-day price discrimination to usePlatformFee
- PlatformFeePayment: tier selector UI + attempt NIP-57 zap request (a/p tags), graceful fallback to plain LNURL invoice
- Add getZapProviderPubkey + getZapInvoiceFromLightningAddress helpers
- Add useFeaturedVerification hook (verified/claimed/none) querying kind 9735 zap receipts
- ProBadge: optional entityCoord to show green Verified state on detail pages
- Wire entityCoord through all 5 Become* Lab pages + CoachDetailPage
- Add featuredTour fee type; FeaturedUpgrade generalized for events + tours
- CreateTourPage: featured tag on publish + FeaturedUpgrade UI
- TourCard/TourDetailPage: render ProBadge; sort featured tours first
- Document tiers + zap-receipt verification scheme in NIP.md

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 16:18:15 +07:00
shakespeare.diy
5c0d4715d5 Rework Lab fee into optional "Pro" upgrade (Option B, no paywall)
Basic Lab profiles are now always free to publish. The 5,000 sat fee is
opt-in and unlocks a Pro badge + featured placement instead of gating
publication behind a rate.

- types.ts: `featuredUntil` field on all 5 Lab types + SkateEvent,
  parseFeaturedUntil() + isFeaturedActive() helpers, wired into parsers
- usePlatformFee: exposes featuredUntilSeconds for the `featured` tag
  (platform owners get a 1yr window)
- New ProBadge + LabProUpgrade (opt-in toggle → reveals PlatformFeePayment)
- All 5 Become pages: removed rate-based fee gate and wallet-required gate;
  publish is free; when Pro is active a `["featured", "<expiry>"]` tag is added

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 15:31:49 +07:00
shakespeare.diy
d8d1ba32c9 Nutzap safety gate + crew↔Lab cross-linking
Nutzap:
- Gate sending behind NUTZAP_SENDING_ENABLED=false with clear docs; sending
  would require P2PK-locking proofs (fund-loss risk if wrong), so it's disabled
  until implementable/auditable. Receiving + reading unchanged. No dead-end
  button exists (hook was never wired to UI).

Cross-linking crews ↔ Lab collectives:
- types: parseAddrCoord/parseCrewRef/parseLabRefs helpers + LAB_KINDS; add
  crewRef to all 5 Lab interfaces/parsers and labRefs[] to SkateCrew
- CrewLinkField (pick one of your own crews) added to all 5 Become-* forms,
  shown only for collectives; publishes an a-tag on the Lab profile
- LabLinkField (pick your own Lab profiles) added to CrewForm; publishes a-tags
- CrossLinkCards: LinkedCrewCard on Lab detail ("Part of a Crew"), LinkedLabCard
  on crew detail ("Hire This Crew"), resolving coordinates via useCrew/useLabProfile

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 14:27:51 +07:00
shakespeare.diy
9cb8d1181d Add individual/collective entity type + NIP-32 membership attestation to The Lab
Step 1 of the collective plan (shared primitive on existing Lab categories):
- types.ts: EntityType + entity/members fields on coach/filmer/editor/
  photographer interfaces & parsers; membership label namespace constants +
  parseMemberPubkeys / parseEntityType helpers
- useCollectiveMembers: cross-references claimed `p` tags against members'
  own NIP-32 (kind 1985) attestations -> confirmed / pending status
- useConfirmMembership: publishes a member's self-signed membership label
- EntityTypeField: individual/collective toggle + member npub picker (per-
  category accent) wired into all 4 Become-* forms
- CollectiveMembers: sidebar card with confirmed/pending trust chips
- ConfirmMembershipCard: one-tap confirm shown only to listed pending members
- CoachDetailPage: collective badge, members card, confirm-membership prompt
- Lab cards: compact collective badge with member count
- NIP.md: document entity tag + collective membership attestation flow

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 13:18:18 +07:00
shakespeare.diy
feddbaf5ef Add optional CLINK (noffer) self-custodial payments to Lab profiles + shops
Phase 1 CLINK integration (recognize & display only):
- types.ts: optional noffer field + isValidNoffer guard on SkateCoach,
  SkateFilmer, SkateEditor, SkatePhotographer, SkateShop; parsers read it
- NofferField: reusable opt-in input with live validation, added to all 4
  Become-* Lab forms and both Create/Edit shop forms
- ClinkPayButton: secondary "Pay via CLINK" action (QR + copy + nostr: deep
  link), rendered on CoachDetailPage and ShopDetailPage only when a valid
  noffer is present; never replaces the existing zap / Lightning Address flow
- useQRCodeScanner: classify scanned noffer1/ndebit1 CLINK codes
- NIP.md: document the noffer tag on Lab kinds + shops and a CLINK section

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 12:34:50 +07:00
shakespeare.diy
adeab62aa4 Add Photographer card to The Lab (kind 35004) + improve Filmer section
New Photographers category (blue-indigo) — separate from Filmers:
- New addressable kind 35004 + SkatePhotographer type + parseSkatePhotographer.
- Rich photographer taxonomy as queryable `t` tags: services (action,
  sequence, editorial, product, events…), camera bodies, lenses, lighting
  (Profoto/Elinchrom/Godox/triggers…), support/rig, and shooting style,
  plus a freeform "gear" tag and a portfolio link.
- BecomePhotographerPage form with chip-grid selectors for all gear
  categories + "what's in the bag" textarea, wired to /become-photographer.
- The Lab: 4th "Photo" tab (indigo accent) with its own filters, cards,
  empty state; OfferFAB gains "Offer Photography"; useLabProfiles fetches
  35004; CoachDetailPage renders photographers (and editors) properly with
  per-type accent, gear + portfolio display.
- NIP.md: documented the whole Lab (kinds 35001-35004) incl. photographer.

Filmer improvements (now video-focused, photography split out):
- Removed the token "Action Photography" service; added follow/line filming.
- Richer equipment list: cinema camera, HVX, action cam, gimbal, 4K/HFR.
- Filmers gain a freeform "what's in the bag" gear field + a reel/portfolio
  link, both surfaced on the detail page.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 11:27:22 +07:00