Commit graph

234 commits

Author SHA1 Message Date
shakespeare.diy
4c9839eb45 Rename "Action shot" to "Profile Pic" and add custom Level option for skaters
- Become-Skater form: photo section relabeled "Profile Pic"
- Level picker now includes a "Custom" toggle with a free-text input (≤40 chars) for labels like "OG", "Weekend Warrior", "Retired"
- Store custom level as a `level_custom` tag (mutually exclusive with preset `level`)
- Render custom level everywhere presets show: profile (NIP19Page), SkatersPage cards, SkaterDirectoryCard
- Document `level_custom` tag in NIP.md

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 21:34:59 +07:00
shakespeare.diy
67d8a24d73 Fuse skater profile (35000) with Nostr identity (kind 0)
Make the skater layer and the universal Nostr profile feel like one person,
with kind-0 as the single source of truth for shared fields.

1. Public profile (NIP19Page): render the skate layer inline — stance, level,
   styles, years, home spot as a branded card right under the About. Own-profile
   nudge ("Add your skate details") when no 35000 exists. Website in the sidebar
   now falls back to the skater profile when kind-0 has none.

2. Become-Skater: prefill bio/website from kind-0 and default the photo to the
   kind-0 avatar (with "using your profile photo" affordance). Store shared
   fields ONLY as overrides — matching values are dropped so kind-0 stays
   authoritative and nothing goes stale. "From your profile" hints + a
   linked-to-your-profile banner make the relationship explicit.

3. Fallback-on-render for website/image/bio wherever the skater field is empty.

4. Bidirectional cross-links: new SkaterDirectoryCard in Settings > Profile
   shows directory status (level + style chips) linking to edit, or nudges the
   user to join; Become-Skater links back to full-profile editing.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 21:11:36 +07:00
shakespeare.diy
b42c234709 Add Skater profiles (kind 35000) + /skaters directory
- New app-native Skater Profile kind 35000 with `skateskater` marker tag,
  the anti-spam boundary that keeps generic kind-0 Nostr profiles out of the
  directory (queried only from the community relay).
- Skater type, stance/style/level constants, and parser in types.ts.
- useSkaters + useUserSkaterProfile hooks.
- /skaters directory page: immersive hero, live count, search, style filter
  chips, ranked skater grid with NIP-05 verification badges and lazy author
  metadata.
- /become-skater opt-in flow (bio, photo, styles, stance, level, years, home
  spot, location w/ geohash, socials).
- Wire "Skaters" into the mobile nav and a cross-link banner in The Lab so
  plain skaters finally have a home.
- Document kind 35000 in NIP.md.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 20:58:51 +07:00
shakespeare.diy
9599adaf0d Redesign profile edit form UI/UX + per-line sponsors
- Restructure EditProfileForm into clean, titled sections (Identity,
  Bitcoin & Payments, Skate Profile, Account) with gradient icon
  headers, consistent spacing, and a reusable FieldRow with inline
  icon + validation status.
- Sponsors is now a dynamic per-line list (add/remove rows, Enter to
  add) instead of a textarea; stored newline-separated. The profile
  renders each sponsor as its own badge.
- Lightning address placeholder is now generic (you@yourdomain.com) —
  no specific wallet names anywhere.
- Nicer image upload: click-the-thumbnail-to-upload with hover overlay,
  round avatar preview / wide banner preview.
- Sticky Save bar at the bottom of the form.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 20:06:32 +07:00
shakespeare.diy
11c67fcf22 Profile: add Lightning, Silent Payment, Sponsors & Cordn fields
Editable in EditProfileForm and displayed on the profile:
- Bitcoin Lightning Address (lud16) — now editable in-form, shown with a
  labeled Lightning row in the Info card.
- Bitcoin Silent Payment Address (BIP-352, custom `bitcoin_silent_payment`)
  — validated (sp1…), shown as a copyable, truncated row.
- Sponsors & Support (custom `sponsors`) — freeform highlight, rendered in
  a dedicated pink "Sponsors & Support" sidebar card.
- Cordn contact link (custom `cordn`) — private MLS messaging link
  (cordn.net), shown as a "Message privately" row in the Info card.

Crews and the CLINK noffer tip button were already present, so they are
left as-is. Added isValidSilentPayment / isValidLightningAddress
validators and documented the custom kind-0 fields in NIP.md.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 20:00:21 +07:00
shakespeare.diy
889f6311ed Mobile bottom nav: Profile tab now opens your profile, not Settings
The avatar "Profile" tab in the mobile bottom bar linked to /settings,
so tapping your own face took you to Settings instead of your public
profile. It now routes to /:npub (View Profile) when logged in, and
still falls back to /settings when logged out (where login lives).
Long-press to customize the nav is preserved.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 19:23:56 +07:00
shakespeare.diy
a0c8493502 Sasquatch: open the panel on the LEFT again on desktop
- Panel now anchors md:bottom-16 md:left-4 (was bottom-right) so it opens on
  the left as before, and sits ABOVE the desktop footer so it never covers the
  "Made with..." row or footer links.
- Tightened max-height to clear the top nav too (never slides under the menu).
- Bubble stays bottom-right on desktop for discoverability (clear of footer).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 16:28:07 +07:00
shakespeare.diy
407ba40a33 Sasquatch: full public-data coverage (all Lab crafts + shops + tours)
- findCoaches now covers EVERY Lab craft (coach, filmer, editor, photographer,
  builder, artist, graffiti, tattoo, trainer, musician, judge) via role param;
  reads skills from `t` tags and strips self-tags.
- New findShops (kind 30017, t=skateshop) and findTours (kind 30823) tools.
- getCommunityStats counts all crafts individually + lab_total + shops + tours,
  and filters spots to t=skatespot (drops foreign kind-30818 events).
- Wired new tools + expanded role vocabulary into the Sasquatch brain: system
  prompt, intent parser, keyword detector, and both dispatch/format paths.
- Privacy preserved: the public CVM data layer never queries the encrypted
  Stash (kind 30001) — verified no 30001/decrypt references. Personal 'my
  stash' stays login+Pro gated and decrypts locally with the user's own key.
- Normalized useCVMClient.ts to clean UTF-8 (comment glyphs only; no code change)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 16:14:20 +07:00
shakespeare.diy
e996d96fa0 Sasquatch: fix desktop discoverability + repetitive greeting
- Move bubble & panel to bottom-RIGHT on desktop (was bottom-left, colliding
  with / hiding behind the desktop footer). Mobile stays bottom-left.
- Raise bubble/panel z-index to z-60 so it always sits above the footer.
- Re-clamp a stale saved drag position on mount: a bubble dragged off-screen
  on one device no longer disappears on another.
- Stop showing the long welcome greeting + action button on every open once a
  conversation has started (chatMessages > 0), so it no longer "says the same
  thing all over again".

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 15:51:42 +07:00
shakespeare.diy
c6372742a9 Fix Amber native login in APK: reliable Capacitor detection (v1.1.7)
Root cause: LoginDialog and useCurrentUser detected the native app via
window.Capacitor, which is unreliable inside the WebView (false at mount / not
attached in some builds). So the ZapStore APK fell into the web intent:// redirect
flow and never opened Amber ("Waiting for Amber…" hang).

Fix: add isNativeAmberAvailable() in capacitorAmberSigner.ts using the imported
@capacitor/core Capacitor + native plugin presence, and use it (evaluated at
click time) in LoginDialog.handleAmberLogin and in useCurrentUser's signer
selection. The APK now calls getAmberPublicKeyViaPlugin() → native intent → Amber
opens directly, no redirects.

Bump version to 1.1.7.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 14:00:39 +07:00
shakespeare.diy
90a6af810e Make Amber login/signup the polished, no-copy-paste primary flow (NIP-55)
Login (Amber tab):
- Emphasize the proper NIP-55 redirect flow: one tap → approve in Amber →
  redirected back to /amber-callback → poll auto-completes login. No copy/paste.
- Replace the indefinite spinner with a clear "Waiting for Amber…" state that
  auto-signs-in on return, with an "Open Amber again" retry.
- Demote manual pubkey entry to an explicit "Having trouble?" escape hatch that
  the user must tap — it never hijacks the flow anymore.
- Removed the aggressive 2.5s auto-switch to paste; keep background polling on
  both waiting and paste states so a late callback still completes.

Signup:
- Welcome step now recommends "Sign up with Amber" first on Android (key lives
  inside Amber, SkateSpots never touches it), with a clearly-secondary "quick
  account on this device" option. Wired onUseAmber through LoginArea to open the
  Amber login flow.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 13:35:13 +07:00
shakespeare.diy
4ed1d9a803 Fix crew card layout, Amber web login hang, Stash encryption gate, and add signup welcome step
- CrewCard: rebuilt to fit any screen width — clip overflow, truncate name/location, single wrapping badge row, consistent avatar sizing; responsive header
- Amber web login: after opening Amber, drop user on manual paste step after 2.5s so the spinner never hangs when the callback opens in a new tab or clipboard is blocked; keep polling on the paste step so a late callback still auto-completes
- Stash: hasNip44/hasNip04 now fall back to the active login when pubkey matching fails, so logged-in nsec/amber users are no longer wrongly told "Encryption Required"
- Signup: added an explicit welcome step that explains account creation and only generates the keypair when the user taps "Create my account" (no more silent auto-creation on dialog open)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 13:27:24 +07:00
shakespeare.diy
f1cb2a32ba Add recovery-code retrieval to Settings > Security (closes the deferred-backup loop)
Since signup now defers the key backup, users who skip it MUST be able to get
their recovery code later — otherwise we've just moved the trap. This adds a
"Your Recovery Code" card at the top of Settings > Security that:

- For nsec logins: reveals the recovery code on demand (hidden by default),
  with Copy, Download (.txt), and QR options, plus a Hide button and clear
  "never share this" warnings.
- For bunker / browser-extension / Amber logins: explains that SkateSpots
  never held the key and points the user to back it up in their signer app.

Also removed the old placeholder QR handler that only showed an error, and
updated the "Backup Not Confirmed" copy to direct users to the new reveal
card instead of falsely claiming lost keys are unrecoverable.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 11:10:51 +07:00
shakespeare.diy
54e6b4a809 UX Move 1: kill the signup wall — one-tap silent account, no forced key download
- "Sign Up" now goes straight into a one-tap flow: a key is generated silently
  and the user is logged in immediately, landing on optional profile setup.
- Removed the method-choice dialog (mnemonic vs nsec) and the generate/download
  steps from the default path — no crypto vocabulary, no gates.
- Removed the forced key-download gate (Continue was disabled until download).
  Backup is now optional and deferred (BackupReminder already nudges post-login;
  the "done" step offers a gentle, jargon-free recovery-code reveal).
- Softened copy throughout: "secret key" -> "recovery code / account backup",
  "your identity on Nostr" -> plain reassurance, CTA -> "Save & Start Skating".
- Advanced signer-app path preserved under a subtle link.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 11:03:26 +07:00
shakespeare.diy
33cfe692ed Add privacy transparency: dedicated /privacy page, onboarding privacy note, Sasquatch VPN/Tor Q&A, footer link
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 10:46:47 +07:00
shakespeare.diy
86a5a4b859 #2 Silence AmberCallback mount-time console spam (keep flow-error logs)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 10:18:32 +07:00
shakespeare.diy
3fd82f2425 #2 Reduce console noise: strip debug logs from relay queries, publish, trust score, weather, coaches, NostrSync
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 10:17:01 +07:00
shakespeare.diy
49325be38e #5 Reframe 'permanent/can't delete' copy around user data ownership & relay choice (onboarding, relay education, about)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 09:59:43 +07:00
shakespeare.diy
8a6d84ca3d Fix map tiles (drop keyed CARTO for free OSM + CSS dark filter), restore corrupted parseSkateCoach in types.ts
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 09:56:47 +07:00
shakespeare.diy
13934c0e74 Fix newsletter admin gating: use isAdminPubkey from lib/admins instead of undefined NEWSLETTER_ADMIN_PUBKEYS
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-20 16:16:09 +07:00
shakespeare.diy
c683b7e8bf Add clinkme.dev specs and apps page links across the app
- NofferField: link → https://clinkme.dev/specs.html (instead of just /)
- EditProfileForm: link → https://clinkme.dev/specs.html (instead of just /)
- SasquatchGuide: added [specs] and [apps] inline links next to the clink description, pointing to the new pages

The app already referenced clinkme.dev homepage; these add the two new deeper pages for discoverability.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-19 15:13:32 +07:00
shakespeare.diy
09418ac660 Replace cashumints.space links with mintradar.pedani.eu across wallet UI
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-18 20:49:10 +07:00
shakespeare.diy
82a74e01e0 Extend admin takedown to Lab profiles and gear/marketplace listings
Completes the admin moderation coverage from the previous commit.

Lab profiles (coaches, filmers, editors, photographers, builders, artists,
graffiti, tattoo, trainers, musicians):
- useLabProfiles / useLabProfile and useCoaches / useCoach now filter out
  admin-hidden entries (list) and 404 hidden ones (detail) via the shared
  moderation layer. Added filterAdminHiddenMixed() for the mixed-kind Lab
  union (kind read from each item's event).
- CoachDetailPage (the shared Lab detail page) now shows the delete/takedown
  card to admins too, not just the owner, with admin-specific copy.
  DeleteEntityDialog already handles the owner-or-admin logic.

Gear / marketplace listings (NIP-99, kind 30402):
- useMarketplaceItems / useMarketplaceAtSpot filter out admin-hidden listings.
- GearSwapItemCard: admins get a "Remove (admin)" action in the card menu with
  a confirmation dialog that routes through useDeleteEntity (NIP-09 delete +
  hidden label). Report stays for non-owners.

SECURITY.md: updated the admin-moderation coverage list and auditor file map.
Build + lint pass, no console errors.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-18 19:17:16 +07:00
shakespeare.diy
13eece3a1e Admins can delete/take down any listing (spot, crew, tour, event, shop)
The two SkateSpots admin pubkeys (already in src/lib/admins.ts) can now remove
any listing, not just their own. Because a NIP-09 deletion signed by a
non-author isn't honored by most relays, admin takedowns are made reliable with
a client-side moderation layer:

- useDeleteEntity: owners still publish a plain NIP-09 kind:5. Admins acting on
  someone else's content publish the kind:5 AND a NIP-32 kind:1985 `hidden`
  label referencing the target coordinate/id, authored by the admin.
- useAdminModeration (new): fetches admin-authored `hidden` labels app-wide and
  exposes a fast lookup + filterAdminHidden() helper. Only labels whose author
  is in the hardcoded ADMIN_PUBKEYS are honored (author = trust boundary,
  re-verified client-side; never derived from a d-tag).
- Wired the hide filter into the list + detail hooks for spots, crews (incl.
  by-focus), tours, events, and shops via React Query `select`, so removed
  content disappears from lists and 404s on its detail page.

UI:
- DeleteEntityDialog now renders for owner OR admin, with distinct
  "Remove (admin)" framing and copy (used on Crew, Coach, Tour, Event pages).
- SpotDetailPage & ShopDetailPage: routed their inline delete through
  useDeleteEntity, show the delete button for admins, and relabel the confirm
  dialog for admin takedowns.
- Added the delete control to Tour and Event detail pages (previously none).

SECURITY.md: documented the admin moderation trust boundary and extended the
auditor file map. Verified build + no console errors.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-18 17:51:34 +07:00
shakespeare.diy
538ef67b0b Security: remove hardcoded OpenRouter key (C1) + escape weather HTML (H1)
Address the two genuinely exploitable findings from the OpenCode audit:

C1 (critical) — sasquatchBrain.ts shipped a hardcoded OpenRouter API key in
the client bundle, extractable and abusable by anyone. Removed it; OpenRouter
is now opt-in via VITE_OPENROUTER_API_KEY and skipped when unset. Sasquatch
still works for everyone via the existing keyless Pollinations fallback.
(The leaked key must also be rotated/revoked on openrouter.ai.)

H1 (high) — SpotMap popup interpolated external weather-API data directly into
innerHTML. Added an escapeHtml() helper and applied it to every weather/AQI/
forecast value so a compromised weather API can't inject HTML/JS.

Triage of the rest: C3 (crew agent derived key) is a documented, deliberate
tradeoff — agent holds no funds and only speaks for itself. H2 (chart.tsx
dangerouslySetInnerHTML) is the stock shadcn chart with app-controlled config,
not user input. H3/H4 (GitHub Actions) don't apply — no .github/ in this repo.
C2 (NWC in localStorage) is standard for web Nostr clients. npm audit fix +
key rotation are operator steps done outside the app.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-18 17:04:53 +07:00
shakespeare.diy
acfbff6a07 Wallet: warn users to set an NWC spending budget before connecting
An NWC connection string is a spend credential stored client-side. If a
connection is granted an unlimited budget, a compromise could drain the
wallet up to that limit. Add a prominent security notice in the "Connect
NWC Wallet" dialog instructing users to set a small daily budget and scope
the connection to pay_invoice only, and reminding them it can be revoked in
their wallet. This caps the blast radius of the credential the app must hold.

(The CSP referenced in the report was already added in the prior commit via
public/_headers; storage of nsec/NWC strings in localStorage is inherent to
client-side Nostr signing/payments and is mitigated by the XSS + CSP work.)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-16 22:16:05 +07:00
shakespeare.diy
b4b2375921 Security: fix stored XSS in map popups + add web CSP
Critical: Leaflet marker popups built untrusted Nostr event fields (and
BTC Map API fields) into innerHTML, bypassing React's output encoding. Any
user could publish a spot/tour/crew/event with an <img onerror> payload in a
text field; it executed on /map render with no interaction, letting injected
script read the plaintext nsec from localStorage (nostr:login) and the Cashu
seed (cashu-mnemonic) — permanent, unrotatable account + wallet takeover.

Fixes in src/components/SpotMap.tsx:
- Rebuild all popup DOM with createElement + textContent instead of innerHTML
  for every untrusted field: spot.location; tour.title/location/meetingPoint;
  evt.title/location; crew.name/location; BTC Map name/address/opening_hours/
  phone/description.
- Validate every event/API URL with sanitizeUrl() before it reaches an
  href/src: crew.image (popup + marker icon) and BTC Map website. Drop
  anything that isn't http(s). tel: links reduced to safe characters.
- Replace inline onerror handler on the crew marker image (would be blocked
  by CSP anyway) with a scripted img.onerror fallback.
- Add popupRow() helper so untrusted detail rows can never be innerHTML'd.

Defense in depth (web only, native Capacitor build unaffected):
- Add public/_headers with a strict Content-Security-Policy: default-src
  'none'; script-src 'self' 'wasm-unsafe-eval' (no unsafe-inline/eval);
  object-src 'none'; base-uri 'none'; frame-ancestors 'none'; plus the
  https:/wss: allowances a Nostr client genuinely needs for relays, media,
  and mints. Also X-Content-Type-Options, X-Frame-Options, Referrer-Policy,
  Permissions-Policy.
- Move the inline boot-crash-guard script to public/boot-guard.js so
  script-src can stay 'self' with no 'unsafe-inline'; index.html now loads it
  externally. The guard also now paints errors via textContent, not innerHTML.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-16 22:12:26 +07:00
shakespeare.diy
eed8552e37 Fix Sasquatch panel overflow on mobile: cap height to dvh + safe-area, pin header, scroll body internally
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 22:23:48 +07:00
shakespeare.diy
6e11aaab7d Crew form UX: expand Crew Type to 7 scenes (Art, Music, Wellness) mapping to new Lab crafts; rethink Crew Band with Lab-music interconnect nudge; surface member visibility to founders; add Artist/Musician open-call options; update Crews page filters + NIP.md
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 22:14:21 +07:00
shakespeare.diy
96f7c3192d Interconnect Music ↔ Events: featured musician on events + gig list on musician profiles; count new Lab crafts in growth stats; document visibility/vouch/gig conventions in NIP.md
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 22:02:22 +07:00
shakespeare.diy
926470a86e Crews: opt-in member visibility + Lab vouch/web-of-trust anti-fraud + CrewDetail redesign
Step 3 — member visibility (best-of-both, honest):
- MemberVisibility ('public' | 'crew') stored on the member's OWN kind-1985
  attestation via a `visibility` tag, so a public crew can contain a private
  member. Confirmed crew-mates + founder see them; outsiders see anonymised
  "hidden member" slots. Framed honestly in-UI (app-level discovery privacy).
- VisibilityPicker in ConfirmMembershipCard (choose on join) +
  MemberVisibilityControl for confirmed members to change it anytime.
- useCollectiveMembers parses visibility; CollectiveMembers hides crew-only
  members from non-members.

Step 4 — anti-fraud vouch layer:
- useVouches/useVouchFor: NIP-32 vouch labels, trust-WEIGHTED via web-of-trust
  so fake-account vouch rings carry no weight. VouchCard with new/vouched/
  scene-verified tiers, wired into every Lab profile detail page.

Step 4 — CrewDetail UX redesign:
- Activity consolidated into clean Feed/Events/Chat tabs (no more giant scroll)
- Sidebar reordered: Members → Support → Band → Connections → Founder
- Member visibility control surfaced; tighter hero

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 21:01:57 +07:00
shakespeare.diy
0071bcba36 Lab: add Designer/Artist, Graffiti, Tattoo, Trainer/Physio & Musician crafts (kinds 35007–35011)
- New addressable kinds 35007–35011 with types, label maps and parsers
- Shared parseCraftBase helper + parseLabProfile dispatcher in useLabProfiles
- Reusable, config-driven CraftProfileForm (polished shared shell, per-craft
  skill sections + extra fields, entity/collective, CLINK, Pro upgrade)
- Five thin Become* pages wired into AppRouter and the Lab "Offer" menu
- LabPage: extended ServiceType, accents, scrollable tab strip, filter
  sections, counts, empty states and ProfileCard to all 10 crafts
- CoachDetailPage renders all craft types (map-driven accents/icons/labels)
  plus craft-specific fields (studio, books-open, credentials, remote,
  graffiti crew, music links)
- NIP.md documents all five new craft schemas

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 20:28:59 +07:00
shakespeare.diy
6e226d5561 Add first-launch beta disclaimer dialog + footer BETA badge
- BetaDisclaimerDialog: shown once on first load, versioned storage key
  (bump DISCLAIMER_VERSION to re-show after terms change), soft single
  "Got it" button, matches dark + #ff0040 style, cannot be dismissed by
  outside click/Esc
- Permanent BETA badge in desktop footer
- Wired into App.tsx

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-01 12:14:42 +07:00
shakespeare.diy
1528c7b343 Unify the two "Profiles": public page + Edit, cross-linked and clearly named
The app had two things both called "Profile": the /settings editor and the
public /npub page. They're not duplicates (edit vs. view) but the naming and
lack of links between them was confusing. Now:

- Public profile (/npub) gets an owner-only "Edit Profile" button that deep-
  links to /settings?section=profile
- Settings row renamed "Profile" -> "Edit Profile" (section title already said
  Edit Profile); it already has a "View my public profile" link back
- Result: one Profile (the public page), one obvious way to edit it, linked
  both directions — the standard IG/GitHub pattern

Bonus fix: mobile menu "My Data" used a broken ?tab=mydata param; corrected to
?section=mydata (the settings page reads ?section=).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 21:44:05 +07:00
shakespeare.diy
433f59415b Enrich profiles (crews, bot, CLINK), best-in-class New Message picker, map toolbar declutter
Profile page (NIP19Page):
- Add "member of crew X" summary under the header (with founder crown)
- Add a full Crews sidebar card listing every crew the user founds/belongs to
- Add crews count to the Contributions stats (now Spots/Shops/Crews)
- Show a Bot badge for kind 0 bot accounts
- CLINK "Tip in Bitcoin" already surfaces on profiles (from prior work)
- New useUserCrews(pubkey) hook powering the above

Messages:
- Replace paste-only "New Message" dialog with NewMessageDialog: a searchable
  people picker (crew-mates + following, grouped, name-searchable) that also
  accepts a pasted npub/hex as a fallback. Starting a DM now feels like a
  real contacts screen.

Map toolbar consistency:
- Consolidate secondary add actions (Shop / Gear / Tour) into a single
  "+ More" popover; keep primary "Add Spot" prominent. 4 buttons -> 2 controls.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 21:36:42 +07:00
shakespeare.diy
ee9e04d3e0 Profile access, real gig messaging, desktop Local Chat removal + audit fixes
#0 Crew band gig booking:
- Rename "Book them for a gig" -> "Set up a gig" (honest: it creates an event)
- Add "Message about a gig" -> pre-filled encrypted DM (NIP-17) to the crew
  founder via existing /messages/:npub?message= flow (real communication)

#1 Profile access (was missing/mislabeled everywhere):
- Desktop header: add "View my profile" icon linking to /npub
- Mobile user menu: split mislabeled "My Profile"(->Settings) into
  "View Profile" (-> /npub) + "Settings"
- Burger menu: add "View Profile" entry
- Settings > Edit Profile: add "View my public profile" quick link

#2 Remove standalone Local Chat button on desktop map toolbar (kept on mobile)

Audit fixes:
- Mobile menu "Wallet & Stash" -> "Wallet" (Stash is already its own item)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 21:16:15 +07:00
shakespeare.diy
8056b02ad1 Crew band gig booking, hide treasury, CLINK on profiles, friendlier CLINK, remove desktop messages icon
- Add "punk" + "rap/hip-hop" crew vibe presets
- Hide Crew Treasury behind SHOW_CREW_TREASURY flag (detail page + form), code kept
- Improve crew band section into a dedicated card with a "Book them for a gig"
  action that deep-links to a Concert event hosted by the crew
- CreateEventPage: prefill event type + title from ?type= / ?title= params
- CLINK (a): expose CLINK tip code on user profiles (EditProfileForm field +
  "Tip in Bitcoin" button on profile view, stored as custom kind 0 field)
- CLINK (c): relabel "Pay via CLINK" -> "Tip in Bitcoin" with a "what's this?"
  tooltip explaining it in plain language
- Remove standalone desktop Messages icon (kept in the More menu)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 20:29:44 +07:00
shakespeare.diy
d465eabd87 ux(crews): rename "Start a Crew" -> "Create Crew" + clarify Crew vs Lab
"Start a Crew" was ambiguous (team? chat? listing?). Renamed to the clearer
"Create Crew" across the Crews page button/empty state, Create Crew page
title/header/login gate, and Sasquatch FAQ. Added a subtitle distinguishing a
Crew (skate WITH) from The Lab (hire).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 20:02:17 +07:00
shakespeare.diy
0ac5c1019d fix: eliminate all render-path nip44 getter crashes (Browser extension not available)
Root cause: NUser's extension signer exposes .nip44 as a getter that calls
.nostr (window.nostr) and THROWS when the extension isn't present. Any
synchronous access during render crashes the whole tree — the actual culprit
was useStash's `isEncryptionAvailable: !!user?.signer?.nip44` in its return
object, evaluated every render even when logged out.

- Add src/lib/signerCapabilities.ts: hasNip44/hasNip04 helpers that determine
  capability via login type + direct window.nostr probe (no throwing getter).
- useStash: use the helper for hasNip44 + isEncryptionAvailable
- useDirectMessages: replace all render-path `enabled: !!user?.signer?.nip44`
  with a pre-computed nip44Available across all 4 conversation hooks
- CrewChat, CrewRequestToJoin, ConversationPage, DirectMessagesPage,
  ContactsPage: use the safe helper in render/guard paths

Async accesses (inside queryFn/callbacks) are left as-is — they reject
gracefully within react-query and never crash render.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 19:37:08 +07:00
shakespeare.diy
ef9bbea465 feat: replace Geyser with Angor across the entire app
Remove all Geyser.fund references and replace with the SkateSpots Angor
project (angor1qtunna00uqyx52rr0nvqa059z2gknhrmn7urd6z).

- SupportersPage: remove Geyser leaderboard + API calls entirely; replace
  Supporters section with an Angor CTA card including the on-chain wallet
  address (bc1q2hdfyqa7utrxrd95rlr27shsdr6gtk7dzdqres)
- useGrowthStats: delete useGeyserStats hook + GEYSER_API_URL constant
- CommunityStatsPage: remove Geyser stats widget, replace with Angor CTA
- SasquatchGuide: update system prompt + FAQ (is it free, supporters)
- NewsletterPage: update both Geyser links (body + footer) to Angor
- AboutPage: remove Geyser row, update Angor entry with real project URL
- README.md: update crowdfunding section + credits

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 13:23:55 +07:00
shakespeare.diy
d6b50de349 feat(sasquatch): Pro receipt DM + local Pro FAQ + tougher free-tier answers
1. Pro purchase confirmation: on a paid pass, send the buyer an encrypted
   NIP-17 receipt to their own inbox (tier, expiry, unlocked perks). Best-effort
   so it never blocks the purchase. Success toast now points to the DM.

2. Frictionless Pro answers: add local static-FAQ entries for "sasquatch pro",
   "pro perks/version", pricing, "go pro", "upgrade" — so those questions are
   answered instantly and never dead-end when the free AI model is rate-limited
   (the exact failure seen when asking "what perks has the pro version?").

3. FAQ matching now prefers the longest matching key so specific phrases win
   over broad ones ("sasquatch pro" > "pro").

4. Refresh the free-model fallback list (add deepseek-v3, gemini-2.0-flash-exp,
   qwen-2.5-72b) for better free-tier AI availability.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 00:21:34 +07:00
shakespeare.diy
1c18163a90 chore(sasquatch): remove joke book, clean up default suggested questions
- Delete sasquatchJokes.ts and all joke usages (idle chat line, fallback
  taglines, Lab/Crew guide messages).
- Tidy the default/generic suggested questions to a clean Nostr-focused set
  (Plan a trip, spots, events, crews, zaps, Nostr, Stash, free).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 00:08:52 +07:00
shakespeare.diy
1efeae0960 feat(sasquatch): add Trip Planner + tougher free-tier fallback + new joke book
- Trip Planner: `plan_trip` tool orchestrates spots+events+coaches+crews+weather
  into a weather-aware, day-by-day itinerary card (TripItineraryCard). Actions:
  save all spots to a NIP-51 Collection, book/zap a coach via their profile,
  and deep-link out to flights/stays (assist, never fake-book, no KYC).
- Free-tier resilience: local intent parser answers "spots/events/coaches/
  crews/gear/weather in <city>" and full trip plans directly via CVM even when
  both AI providers are rate-limited — no more dead-end fallback.
- New shared joke book (sasquatchJokes.ts): skate humor + gentle roasts of the
  KYC-your-audience Bitcoin-influencer crowd. Idle joke on the empty chat state,
  ethos jokes in fallbacks. Dedupes the old inline joke arrays.
- UX polish: taller chat scroll area, trip-planner suggested questions, FAQ
  entries for the planner.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 00:04:28 +07:00
shakespeare.diy
5655fbc976 Sasquatch Pro Phase 2: action drafting (draft → you confirm)
Sasquatch can now DRAFT actions from natural language and publish them
only after the user reviews & confirms — it never publishes on its own.

- useSasquatchActions: publishes confirmed drafts via the app's normal
  publish path. Supports post_status (NIP-38 kind 30315, auto-expiring)
  and list_gear (NIP-99 kind 30402, skateboarding-tagged), reusing the
  existing event schemas.
- SasquatchActionCard: inline "review & confirm" card with editable key
  fields (status text / listing title, price, condition), Publish +
  Cancel, and done/cancelled states. Nothing is signed until Publish.
- SasquatchGuide: new draft_action tool (Pro-gated). The AI emits a
  draft; a card renders in-chat; confirmDraft signs & publishes. Non-Pro
  users get an upgrade nudge. Draft lifecycle tracked per message.
- Prompt teaches the model to use draft_action for "post/sell/list/set
  my status" requests and to fill gear fields from the stash first.

Examples now working (Pro): "post that I'm skating Southbank for 3 hours"
· "list my Spitfire wheels for 8000 sats".

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:41:11 +07:00
shakespeare.diy
79d1d47823 Sasquatch Pro Phase 3: npub-bound Lightning paywall + gating
Adds the Sasquatch Pro supporter pass with strong, forge-resistant
verification bound to the paying user's npub — no backend required.

Verification (useSasquatchPro):
- STRONG: NIP-57 zap receipt (kind 9735). Purchase signs a zap request
  tagged pro-tier/pro-expiry; the receipt embeds it, cryptographically
  binding payment → identity (unforgeable by the user).
- FALLBACK: self-signed kind-32671 receipt so paying users are never
  locked out if the provider emits no zap receipt.
- Platform owners always Pro. Access = max unexpired expiry.

Purchase (usePurchasePro):
- Zap to skatespots@minibits.cash (falls back to plain invoice), pay via
  NWC → WebLN → manual QR. Publishes the fallback receipt on success.

Pricing (sasquatchPro.ts): 4 tiers — 7d/2k, 1mo/5k, 3mo/12k (popular),
1yr/36k sats.

UI (SasquatchProDialog): upgrade sheet with tiers, perk list, QR
fallback, "already Pro" state, extend flow.

Gating (SasquatchGuide):
- Personal tools (get_my_*) now require login + Pro; prompt only exposes
  them to Pro users; tool switch guards with a pro_required nudge.
- ⚡ Pro badge when active, ⚡ Go Pro button otherwise; local upgrade
  nudge for "my/mine" questions from non-Pro users.

Docs: kind 32671 + verification model documented in NIP.md.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:33:39 +07:00
shakespeare.diy
a37224d8a2 Sasquatch Pro Phase 1: read-only personal assistant tools
Sasquatch can now answer questions about the logged-in user's OWN data
("what's in my stash?", "my crews", "events near my crew", "my spots",
"what am I selling?").

- New usePersonalContext hook: client-side getters for the user's Stash
  (decrypted locally), crews (founder/member), marketplace listings, and
  created spots. Reads own data only — nothing personal leaves the device.
- Registered 4 personal tools (get_my_stash / get_my_crews /
  get_my_listings / get_my_spots) in the Sasquatch tool switch.
- Personal-tools prompt block is appended to the system prompt ONLY when
  a user is logged in, so logged-out UX is unchanged.
- needsLiveData now routes "my/mine" questions to the AI tool path.

Phase 1 is intentionally ungated so we can validate the experience
before wrapping the Pro paywall (Phase 3).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:21:37 +07:00
shakespeare.diy
cc84713fe4 Make Sasquatch resilient: local glossary answers + backup AI provider
Fixes the "brain's catching traffic" dead-end for trick/terminology
questions and reduces reliance on a single flaky free API.

- Local glossary answerer (answerFromGlossary): trick/how-to/terminology
  questions ("what is a feeble grind", "how do I do a kickflip", "grosso
  grind") are now answered instantly from the 150+ entry glossary with
  ZERO API calls — so they never hit a rate limit.
- Wired into SasquatchGuide as (a) a fast path before the AI and (b) a
  last-resort fallback when the AI is unreachable.
- sasquatchBrain now has multi-provider fallback: OpenRouter free models
  (with 429 backoff) → keyless Pollinations backup. Only throws if both
  fail, letting the caller fall back to local knowledge.
- Replaced the unhelpful fallback message with useful local links +
  a nudge to ask about a specific trick.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:04:41 +07:00
shakespeare.diy
b3cda77224 Add optional crew band / music project to Skate Crews
Skate scenes and local music go hand in hand — crews can now list a band
name + a music link (Spotify/Bandcamp/YouTube/Nostr).

- SkateCrew type: new optional bandName / bandUrl fields.
- Parser reads `band` + `band-url` tags (band-url validated as http(s)).
- CrewForm: dedicated optional "Crew Band" section.
- CrewDetailPage: purple band row (linked when a valid URL is present).
- CrewsPage cards: small "Band" badge when a crew has a band.
- Kept scoped to crews only (not The Lab) since bands aren't a
  skate-specific craft.
- NIP.md: documented the new `band` / `band-url` crew tags.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:44:17 +07:00
shakespeare.diy
a371ff3645 Add Trick Glossary + harden Sasquatch AI rate-limit handling
Glossary:
- New /glossary page: 80+ tricks across 11 categories (fundamentals,
  flips, shoves/spins, grinds, slides, grabs, airs, lip, stalls,
  handplants, boneless, freestyle) with alternate names, difficulty
  badges, live search, and category filters.
- Added to desktop "More" dropdown and mobile burger menu.

Sasquatch AI:
- Fixed/expanded free-model fallback list with valid OpenRouter slugs
  (dropped the non-existent gemma-4 slug).
- Added exponential backoff + one retry on 429 rate limits, respecting
  Retry-After headers.
- SasquatchGuide now reuses the shared sasquatchBrain caller instead of
  a duplicated implementation.
- Friendlier fallback message when the AI is saturated.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:39:51 +07:00
shakespeare.diy
e6517b5c70 Add owner-only ContextVM server health check (Settings > Advanced)
Probes the ContextVM relay for the CVM server's own events and reports
online/stale/offline with a last-seen timestamp. Gated to the two
platform owner pubkeys (renders null for everyone else). Notes that
Sasquatch's data layer queries relays directly and is unaffected by the
server being down.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:27:22 +07:00
shakespeare.diy
148ae251ef Add desktop top-nav customization (Settings > Appearance > Navigation)
Option B: a lightweight reorder / show-hide list for the desktop header.

- useDesktopNavPreferences: separate localStorage-backed preference for which
  catalog items appear as primary top-nav tabs and their order (Map fixed
  first, no hard slot cap). Reuses the shared NAV_CATALOG.
- DesktopNavManager: settings UI to reorder primary tabs (up/down) and toggle
  items between the top bar and the "More" menu; Map locked; reset to default.
- Layout desktop header now renders fixed Map + user's primary tabs, and the
  "More" dropdown dynamically lists the hidden catalog items (auth-gated),
  so each item appears in exactly one place.
- Manager shown only on md+ screens (mobile uses the bottom-bar customizer).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:09:20 +07:00
shakespeare.diy
834f33d53e Make Sasquatch bubble open reliably on first tap (mobile)
The "1st tap does nothing, 2nd/3rd works" bug came from relying solely on
pointer-capture tap detection, which is flaky on the first interaction after
mount in mobile WebViews — and the earlier e.preventDefault() on pointerup was
suppressing the click we could otherwise use.

Fix: add a plain onClick as a reliable open path (the most dependable "tapped"
signal in a WebView), extract an idempotent openPanel() used by both pointerup
and click, and drop the preventDefault so the click isn't swallowed. The
justOpenedRef lock still blocks the ghost-click that would re-close it. Double
firing is harmless since openPanel just sets visible=true.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 15:36:11 +07:00
shakespeare.diy
ebbd0fd125 Fix Sasquatch panel opening then instantly closing on mobile
On touch devices, the bubble's pointerup opened the panel and unmounted the
bubble; the synthesized "ghost click" from the same tap then landed on the
panel's minimize/close button (which renders in roughly the same bottom-left
spot), instantly re-closing it.

Fix: preventDefault/stopPropagation on pointerup to suppress the ghost click,
guard releasePointerCapture against unmount throws, and add a 400ms
"just opened" lock so stray close/minimize events from the same tap are
ignored.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 15:24:05 +07:00
shakespeare.diy
7d37a5effd Make goskatespots.nostr1.com the top-priority catching relay
- reqRouter now always queries the community relay (SKATE_RELAY) first,
  even when a user hasn't added it to their read list, so curated skate
  content takes priority in NPool's first-seen dedup ordering
- eventRouter publishes to the catching relay first, then user write relays
- Gated behind the existing useSkateRelay setting (default: true)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 13:14:50 +07:00
shakespeare.diy
d7af2626ff Customizable mobile bottom nav: fixed Map + 3 user-picked tabs + fixed Profile
- Add useNavPreferences hook (localStorage) with a curated catalog of pinnable
  destinations (Lab, Events, Crews, Wallet, Stash, Collections, Contacts,
  Messages, Stats), defaults, normalization/validation and login-gating
- Map (Shops & Spots) is fixed on the left; Profile/Login fixed on the right;
  the 3 middle slots are user-customizable — nothing is ever lost since the
  burger menu still lists everything
- CustomizeNavSheet: bottom-sheet editor with a live preview bar (locked slots
  shown with 🔒), a toggle grid with a 3/3 counter + "bar full" state, up/down
  reordering, and reset-to-default. Draft state so changes are cancelable
- Entry points: long-press (or right-click) any bottom tab, plus a "Customize
  nav bar" item in the burger menu
- Login-gated tabs are hidden from logged-out users and gracefully fall back if
  a saved layout references them
- Resolve tabs in Layout and pass to MobileNav as props (avoids any module-init
  ordering issues with the hoisted MobileNav declaration); defensive ?? [] guard

Docs unchanged (client-only preference, no new Nostr kinds).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 12:53:06 +07:00
shakespeare.diy
6848a72869 Crews: proximity discovery, activity/rep signals, treasury, hiring pipeline, crew-hosted events + reverse links; add Contacts page
Crews page
- "Near me" geolocation toggle with haversine distance sort + distance chips
- Crew count + cities social-proof stat header
- Deterministic per-crew vibe-colour accents (no more wall of teal)
- Activity pulse ("🔥 active this week") + OG/Verified/Established rep badges
- Rich empty state ("Claim your local scene") instead of dead "No crews yet"

Crew synergies
- Crew treasury: transparent, non-custodial NIP-75 zap goal (kind 9041) with
  live progress bar tallied from zap receipts; funds settle to founder's own
  Lightning wallet — no shared wallet or new wallet settings needed
- Crew-hosted events: host a jam/comp as the crew (event a-tag → crew coord),
  "Hosted by [crew]" on event pages, Crew Events list on the crew page
- Crew ↔ Lab hiring pipeline: "Open Call / looking-for" flags (filmer, editor,
  coach, builder, members) surfaced on the crew page
- Request to join: encrypted NIP-17 DM to founder + admins (no founder-push
  bottleneck, no public join event)
- Reverse links: "Crews that skate here" on spot pages

Contacts (0xchat-style + Armada-style privacy)
- New /contacts page with Crew-mates / Follows / Recent DMs / Saved tabs
- Per-contact quick actions: message, zap, view profile, star
- Private encrypted contacts list (NIP-51 kind 30000, NIP-44 encrypted to self)
- Added to burger menu + desktop More menu (gated to logged-in users)

Docs: NIP.md updated with crew treasury, crew-hosted events, looking-for tags,
and the private encrypted contacts list.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 12:38:45 +07:00
shakespeare.diy
58aaeb7b16 Map: add "you are here" user-location marker (SkateSpots logo + pulse)
After Near Me centers the map, there was no indicator of where the user
actually is. Add a Google-Maps-style location marker, brand-flavored:

- Circular badge using the SkateSpots logo (public/images/user-location.png)
  with a red (#ff0040) ring, sitting at the user's exact coordinates.
- Pulsing translucent accuracy halo (ssPulse keyframes) so it reads as a
  live "you are here" and doubles as subtle motion.
- Rendered above all other markers (zIndexOffset), with a "You are here"
  popup. Created/moved/removed reactively from a new `userLocation` prop.

Wired MapPage's userLocation state through to SpotMap (lat/lon → lat/lng).
Marker image path uses import.meta.env.BASE_URL so it resolves on both
web ('/') and the Capacitor APK ('./').

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 00:55:52 +07:00
shakespeare.diy
a9e0c1dcb7 Add community Spot Status Reports (kind 2259)
Solves the "spots that change appearance or disappear" problem with
crowd-sourced, trust-weighted status reports — since the spot owner
often isn't the one who notices a spot got knobbed/demolished/busted.

New custom kind 2259 (regular, append-only): any user can report a
spot's current physical state — active / bust / skate-stopped / closed /
gone — with an optional note, referencing the spot via its `a`
coordinate.

Client aggregation (useSpotStatus / useBatchSpotStatus):
- recency-weighted (30-day half-life) so fresh reports dominate and a
  rebuilt spot naturally recovers
- trust-weighted via existing useBatchTrustScores (0.2–1.0 multiplier)
- most-recent-report-per-reporter (no ballot stuffing)
- reports older than 180 days ignored

UI:
- SpotStatusBadge (colored consensus badge + tooltip w/ report count)
- ReportSpotStatusDialog (status picker, optional note, recent reports)
- Badge + Report Status button on SpotDetailPage
- Batched status badge on SpotCard; "gone" spots dimmed/grayscaled
- "gone" spots hidden from map markers (still listed dimmed) to keep the
  map uncluttered while preserving the record

Documented the kind, tags, status values, and aggregation rules in
NIP.md.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 23:06:10 +07:00
shakespeare.diy
a6a377f468 Add delete/dissolve for crews and Lab profiles via NIP-09 (v1.0.72)
- New useDeleteEntity hook: publishes a kind:5 deletion request referencing
  the target addressable event by `a` coord, `e` id, and `k` kind. Owner-only.
- New reusable DeleteEntityDialog: owner-gated trigger + confirmation with a
  clear "deletion is best-effort on decentralized relays" warning, redirect,
  and query invalidation so the item drops out of lists.
- Wired "Disband" into CrewDetailPage (founder only, redirects to /crews).
- Wired a "Manage → Delete Profile" card into CoachDetailPage for all Lab
  profile types (coach/filmer/editor/photographer/builder), redirects to /lab.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 20:02:33 +07:00
shakespeare.diy
939a101b38 Fix Quick Snap opening gallery instead of camera on native (v1.0.70)
Root cause: quickCapture.ts relied on window.Capacitor.Plugins.Camera,
which is only populated once something imports '@capacitor/camera'. Since
we deliberately never import that package (to avoid the old white-screen
crash), the plugin was never registered, the native check failed, and Quick
Snap fell through to the web file input — opening the gallery.

Fix: use Capacitor's official registerPlugin('Camera') from the bundled
@capacitor/core. This returns a proxy that dispatches to the native Camera
implementation over the bridge without needing the JS @capacitor/camera
package to self-register, and without putting a bare '@capacitor/camera'
specifier in the bundle. Applied the same pattern to CameraCapture.tsx.

Also: on a permission error, show a clear "enable camera access" toast
instead of silently opening the gallery.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 19:47:36 +07:00
shakespeare.diy
2a55d1b3df Crew Agent Phases 0–2: opt-in AI guide (🐝 Sasquatch) per crew
Phase 0 — identity (lib/crewAgent.ts):
- Deterministic per-crew agent keypair derived from the crew coordinate
  (sha256 of a public salt + coord). Documented security tradeoff: the key
  is reproducible by design and holds no funds — used only for the agent to
  speak for itself. Includes kind-0 profile builder (bot:true, NIP-24) and
  a signAsCrewAgent helper for Phase 3.

Phase 1 — opt-in toggle:
- SkateCrew.agentEnabled parsed from a queryable ["t","crew-agent"] marker.
- "Crew Agent" toggle (Beta) in CrewForm; publishes the marker when on.
- NIP.md documents the crew-agent tag and the agent identity derivation.

Phase 2 — crew-scoped Sasquatch with receipts:
- lib/sasquatchBrain.ts: shared AI caller (model fallback) + tool-call parser.
- useCrewAgentChat: reuses the CVM data tools, seeded with crew context
  (name/location) so "near us" resolves to the crew's turf. Ephemeral/local,
  no keys, no publishing yet.
- CrewAgentPanel: chat dialog rendering agent answers PLUS structured receipt
  cards (spots / events / Lab pros / gear / crews) linking into the app.
- CrewDetailPage: "Ask Sasquatch" card + panel, shown only when the crew
  enabled the agent.

Also: Lab category tabs → fixed 5-across grid (no horizontal scroll). Bump 1.0.68.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 19:26:19 +07:00
shakespeare.diy
3a96184001 Fix native build: access Camera via runtime plugin, not @capacitor/camera import
Local CAPACITOR_BUILD failed: Rollup couldn't resolve import
"@capacitor/camera" from CameraCapture.tsx — the package is listed in
package.json but isn't present in node_modules on the build machine.

Root pattern (same class as the @capacitor/core crash): a package that
must NOT be resolved at build time was being imported by specifier.

Fix:
- CameraCapture.tsx no longer imports '@capacitor/camera' at all. The
  Camera plugin is accessed via the runtime-injected
  window.Capacitor.Plugins.Camera (Capacitor injects it natively), using
  string enum values ('dataUrl' / 'CAMERA'). No bare specifier remains in
  the bundle, and it degrades gracefully if the plugin is absent.
- vite.config: @capacitor/camera moved back to `external` (belt-and-braces;
  it's no longer imported anyway). @capacitor/core stays BUNDLED (static
  import in platform.ts — the white-screen fix). nostr-signer-capacitor-plugin
  and the 4 uninstalled @capacitor/* plugins remain external.

Verified: no @capacitor/camera specifier and no unresolved @capacitor/core
import in the built bundle; no static imports of any externalized package
remain in src/. Bump to 1.0.67.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 18:40:19 +07:00
shakespeare.diy
9a8609354e Polish native-only wallet card + add Crew↔Lab scene focus
Wallet (Request A): redesign the browser CashuWalletBrowserNotice into a
premium, intentional card — gradient hero header, "App only" badge, and a
"works in your browser now" panel highlighting NWC / Alby / Lightning
address zaps. Keeps the native-only gate; no bug reintroduced.

Crew ↔ Lab interconnection: crews can now declare an optional Lab scene
focus that surfaces them inside the matching Lab category, while regular
crews stay "Mixed" and only appear on the Crews page.
- types: CrewFocus ('mixed'|'builder'|'photo'|'coaching'), labels,
  crew-focus-* t-tag scheme, serviceType→focus mapping; parseSkateCrew
  extracts focus and excludes focus tags from vibes; SkateCrew.focus field.
- CrewForm: "Crew Type" selector (Mixed / DIY & Builders / Photo & Film /
  Coaching); publishes a queryable crew-focus-* t-tag when not mixed.
- useCrews: new useCrewsByFocus hook (relay-level #t filtering).
- CrewsPage: focus badge on cards + focus filter chips.
- LabPage: "Crews in this scene" horizontal strip beneath results, mapping
  building→builder, filming/photography/editing→photo, coaching→coaching.
- NIP.md: document the crew-focus-<scene> tag.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 18:08:51 +07:00
shakespeare.diy
e76076c1d9 Fix missing DialogTitle/SheetTitle accessibility warnings
Add visually-hidden (sr-only) titles to the three rendered dialogs/sheets
that were missing them:
- OnboardingPage relay-setup dialog (RelayEducation)
- Layout mobile navigation sheet
- MapPage spot detail bottom sheet

These satisfy Radix's accessibility requirement (screen readers announce
the dialog purpose) without altering the visual design.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 17:41:25 +07:00
shakespeare.diy
18f32a14bf Gate built-in Cashu mint wallet to native-only (browser CORS reality)
Browsers block most Cashu mints (no CORS headers), so minting/melting fails unreliably in-browser — and the failed fetches spam the console. Rather than ship a broken browser feature:

- CashuWalletCard: in a browser, render a clear "built-in ecash runs in the app" notice pointing users to zaps (NWC/WebLN/Lightning address) which work fine in-browser; full mint wallet renders only on native (Capacitor)
- useCashu: skip coco/IndexedDB/mint init entirely in the browser (isNativePlatform guard) — stops the recurring "Failed to fetch mint" console errors at the source
- Zaps/tips (the app's real money flow) are unaffected — they never touch a Cashu mint

Native builds get the full wallet with minibits working directly (no CORS jail), per the earlier platform-aware work.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 17:32:40 +07:00
shakespeare.diy
fc7564a6f8 Platform-aware wallet mints (Option C: native + browser scoping)
- Add @capacitor/core + src/lib/platform.ts (isNativePlatform/getPlatform, web-safe)
- mintReachability: on native (no CORS jail) short-circuit probe to 'ok' so ANY mint works, including mint.minibits.cash
- useCashu default mint: native prefers minibits (matches skatespots@minibits.cash brand identity) then coinos/21mint; browser uses CORS-friendly candidates
- MintPrivacyManager recommended list is platform-aware: native leads with Minibits (recommended), web leads with Coinos and flags Minibits as native-only
- CashuWallet: 'This mint can't be reached from your browser' banner for the browser CORS case (never shows on native), pointing users to a web-friendly mint or the native app
- Lightning invoice error mentions the native-app option

Same codebase now uses minibits directly when running as the native app, and a CORS-safe mint when running in a web browser.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 17:22:41 +07:00
shakespeare.diy
b830bf965d Auto-heal wallets stuck on a CORS-blocked mint
- useCashu now probes existing mints on every init (not just when empty); if none are browser-reachable it adds a reachable candidate mint. This fixes existing users whose only saved mint is the legacy CORS-blocked mint.minibits.cash default.
- CashuWallet active-mint selection now prefers the first browser-reachable mint instead of blindly mints[0], so users aren't stuck with a mint that fails every invoice.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 17:11:31 +07:00
shakespeare.diy
a0c59375ed Add crew chat moderation (client-side hide for E2EE group DMs)
- CrewChat: founder+admins can hide chat messages via a per-message hide button; reuses useCrewModeration/useHideCrewPost with the rumor (kind-14) message id
- Hidden messages filtered from chat for everyone; moderators see them greyed + "Hidden" (transparent/reversible)
- Since NIP-17 messages are E2EE and can't be deleted from relays, moderation is enforced client-side via public NIP-32 labels (label reveals only the hidden id, never content)
- Wire admins/crewCoord/canModerate from CrewDetailPage; header hint for moderators
- NIP.md: expand moderation section to cover both feed (kind-1 note id) and chat (kind-14 rumor id) with the E2EE client-side caveat

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 17:08:18 +07:00
shakespeare.diy
f5483a0dbc Add crew admin moderation + invite actions
- useCrewModeration: fetch NIP-32 `hidden` labels authored by founder+admins for a crew; useHideCrewPost publishes a kind-1985 hidden label (L/l org.skatespots.moderation, e=post, a=crew)
- CrewDetailPage feed: moderators can hide posts via an EyeOff button + confirm dialog; hidden posts filtered from the feed for everyone, shown greyed/"Hidden from feed" to moderators (reversible/auditable)
- CrewInviteDialog: founder+admins share an invite link/message (native share + copy); founder gets a shortcut to add a member key in crew settings
- Gate all admin actions on useCrewRole().canManage; moderators = founder + delegated admins
- Add MODERATION_LABEL_NAMESPACE/HIDDEN constants
- NIP.md: document feed moderation label format (author must be founder/admin) + invite semantics

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 17:01:28 +07:00
shakespeare.diy
0078f75f94 Fix wallet invoice failure (CORS-blocked mint) + history crash
- Root cause: mint.minibits.cash sends no Access-Control-Allow-Origin header, so browsers block it (NetworkError → "Failed to create invoice / Failed to fetch mint"). Default to CORS-friendly mint.coinos.io instead.
- useLightningOperations: detect CORS/network failures on receive & send, show actionable "Can't reach this mint — switch mints" message instead of a dead end.
- MintPrivacyManager: reorder recommended mints by browser reachability; flag Minibits as mobile-app-only (not web-reachable).
- useHistoryManager: fix `mintQuoteRepository.getAll is not a function` crash by probing for whichever list method the installed coco-cashu version exposes (getAll/getAllQuotes/list/...), failing gracefully.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 16:53:40 +07:00
shakespeare.diy
40e4a27565 Add crew NIP-05 (any-domain, founder-verified) + founder/delegated-admin governance
- SkateCrew: add nip05 + admins fields; parse `admin` role marker on p tags (admins = subset of members)
- CrewForm: NIP-05 input + per-member "Make admin" toggle; write nip05 tag + admin-marked p tags (works on create & edit via shared form)
- useCrewNip05: verify crew handle on ANY domain against the founder pubkey (verified/mismatch/unreachable), CORS-proxy fallback + caching
- CrewNip05Badge: green ✓ / red ⚠ / muted handle states with tooltips
- CollectiveMembers: show Admin chips, sort admins first, admin count in summary
- useCrewRole: centralized founder/admin/member/none roles (canManage, canEditProfile) — founder owns identity, admins get delegated powers with their own keys; egalitarian shared-editing intentionally unsupported
- CrewDetailPage: NIP-05 badge by crew name, viewer role badges (admin/member)
- NIP.md: document crew nip05 verification, admin role marker, and governance/conflict-resolution model

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 16:46:20 +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
dcfa1fe1b2 Add Pro/Featured display + optional featured events (Option B)
Lab:
- useLabProfiles sorts featured (Pro) profiles first
- ProBadge shown on Lab cards (LabPage) + Lab detail page; featured cards
  get a subtle amber highlight

Events (same no-paywall model as Lab):
- New FeaturedEventUpgrade: every event posts for free; optional fee adds a
  featured badge + top placement (reuses existing featuredEvent fee)
- CreateEventPage: removed the paid-event fee gate; opt-in featured toggle,
  writes a `featured` tag when active
- useSkateEvents sorts featured events first; EventCard shows ProBadge
  (compact in list view, overlay on the image in full view)

Docs:
- NIP.md documents the `featured` tag (unix-seconds expiry, self-attested)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 15:39:41 +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
77dc4b562c Add Crew Chat: private E2E-encrypted group chat for crew members
Built on the existing NIP-17 gift-wrapped group DM system (useGroupConversation
+ useSendDirectMessage), so the relay only ever sees encrypted blobs.

- New CrewChat component: message list w/ author profiles, composer
  (Enter to send), auto-scroll, skeleton/empty states
- Participants = founder + all listed members (deduped)
- Only crew members can read/write (others see a locked "ask to join" state);
  warns if the signer lacks NIP-44
- "Private" lock badge makes the E2E-encrypted nature unmistakable
- Surfaced as a card on the crew detail page under the Crew Feed

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 15:18:39 +07:00
shakespeare.diy
e122ee826b Add "Public" badge to Spot Chat header + clearer privacy note
Makes it unmistakable that Spot Chat messages are public and unencrypted:
- Amber "🌐 Public" badge next to the title with an explanatory tooltip
- Footer note reworded to point users to Direct Messages for private chats

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 15:12:39 +07:00
shakespeare.diy
3b25e7bcf6 Add trust filter to Spot Chat + include crews in community stats
Spot Chat:
- Compact TrustFilter dropdown in the chat header (All / Verified / Trusted / Following)
- Filters messages by author trust via useBatchTrustScores; own messages
  always stay visible; shows a "N hidden by your trust filter" indicator

Community stats:
- useGrowthStats now queries kind 35006 crews (was missing entirely)
- totalCrews added to totals + monthly buckets + content-mix donut
- New "Crews" StatCard on the Community Stats page

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 14:58:10 +07:00
shakespeare.diy
ba3921b15c Add Spot Chat: ephemeral geohash-anchored local chat channels
Inspired by BitChat's location-channel concept, reframed for skaters and
kept fully SkateSpots-native (own kind + tag conventions, no interop).

- New ephemeral kind 24899 tagged with a geohash `g` (documented in NIP.md)
- useSpotChat / useSendSpotChat hooks (polled, community-relay aware)
- SpotChat component: live message list, composer, author profiles,
  Block/Neighborhood/City precision toggle, and light IRC-style flavor
  (/me action messages, /who participant count)
- Spot detail pages now show a per-spot block-level channel
- Map gains a "Local Chat" sheet anchored to the map center with the
  precision selector

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 14:52:48 +07:00
shakespeare.diy
dec2e730d2 CVM find_crews: Sasquatch can now live-search crews
- useCVMClient: add findCrews (queries kind 35006 across data relays, filters by
  city/country/vibe/search, returns CrewResult with member_count, vibes, url);
  add crews to getCommunityStats + CrewResult type
- SasquatchGuide: register find_crews tool in system prompt + switch handler,
  add crew keywords to needsLiveData, update app-facts (crews are searchable +
  mention cross-linking and Zap-the-Crew)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 14:35:16 +07:00
shakespeare.diy
f881c6513f Zap a whole crew: split a zap across confirmed members
- useCrewZap hook: splits a total evenly across recipients, resolves each
  member's Lightning address (batched kind-0 query), fetches an invoice per
  recipient and auto-pays via NWC → WebLN; per-recipient status
  (resolving/paying/success/skipped/failed) with manual-invoice fallback
- splitAmount util distributes remainder fairly (no lost sats)
- ZapCrewDialog: amount presets + custom, per-head preview, live progress rows,
  copy-invoice fallback when no wallet; excludes the current user
- CrewDetailPage: "Zap the Crew" button (recipients = founder + confirmed
  members via useCollectiveMembers); shown only when >1 recipient

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 14:31:45 +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
ed27f719fa Sasquatch: teach him all the new features + fix zap comment
- Expand AI system prompt with full feature map: Lab (coaches/filmers/
  photographers/editors/builders + individual vs collective), Crews (map-anchored,
  how they differ from the Lab), Stash, Tours, Shops, Gear Swap, Rider Card
- Document money layer interconnections: wallet rails (Cashu/NWC/WebLN), zap
  fallback order, nutzaps (NIP-61, receiving), CLINK/noffer, Geyser supporters
- Instruct Sasquatch that Crews/Stash/Wallet/Tours/Shops have no live search tool
  and must be answered from knowledge (prevents bogus tool calls)
- Add ~15 new STATIC_FAQ offline answers (crews, stash, builder, nutzap, clink,
  noffer, tours, shops, sell gear, supporters)
- Add Crews page guide context + crew jokes; refresh Lab suggestions
- Fix leftover "Zapped with Engorile!" default comment → SkateSpots branding

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 14:12:30 +07:00
shakespeare.diy
28f4ef8c34 Crew form: refine vibes + multiple socials
- Remove LGBTQ+ and Longboard preset vibes
- Rename "Girls / Womxn" → "Girl / Woman"
- Add custom vibe/focus field: users define their own #hashtag focus
  (normalized to a lowercase hyphenated slug, published as t tags)
- Support multiple "Other Socials" links (SkateCrew.socials[]) with add/remove UI;
  parser collects all social tags; detail page renders each (URL or plain handle)
- Update NIP.md crew vibe taxonomy

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 14:06:37 +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
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
88a36e52ae Add Events as an opt-in map layer
Events can now be discovered directly on the map:
- New "Events" layer (lime #ccff00, calendar pin marker) plotting upcoming,
  geo-located events. Off by default — users opt in via the Layers control.
- Event markers show a popup with title, date/time, location, and a
  "View Event" button linking to the event detail page.
- Only future/ongoing events with a valid geohash are plotted (past events
  are filtered out to keep the map clean).
- Added to the mobile Layers popover, desktop Layers popover, and the mobile
  filter drawer's "Show on Map" grid. Layer count badge now tracks 5 layers.
- SpotMap gains `events` + `showEvents` props and a createEventIcon marker.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 10:48:24 +07:00
shakespeare.diy
1d1e6d18b1 Redesign mobile map controls: Layers popover + Add speed-dial FAB
Replaces the crowded 12-item vertical right rail with a clean, minimal set:
- Right edge now has just two 44px controls: Near Me and a Layers button.
- Layers opens a labelled popover (Spots/Shops/Gear/Tours) with color dots
  and checkmarks, plus a count badge on the button so state is obvious.
- All "add" actions consolidated into a single red speed-dial FAB
  (bottom-right) that fans out to Add Spot / Add Shop / Offer Tour /
  Quick Snap, with a tap-catcher backdrop to dismiss.
- Sasquatch bubble moved to bottom-left on mobile so it no longer competes
  with the Add FAB for the bottom-right corner.
- Larger touch targets (44px) for better native/ZapStore ergonomics.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 10:18:35 +07:00
shakespeare.diy
d107c0a860 Fix Sasquatch auto-popup on / and /lab; move map attribution above mobile nav
- Sasquatch panel no longer auto-opens on the map (/) or The Lab (/lab).
  Added an `autoShow` flag to GuideMessage; only opt-in messages (onboarding)
  auto-open. The bubble stays minimized until tapped, and context messages
  still populate the panel when opened manually.
- Map CARTO/OSM attribution now sits above the mobile bottom nav (safe-area
  aware) instead of overlapping the Shops & Spots buttons; made it smaller
  and more subtle. Desktop keeps it in the bottom-left corner.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 10:09:15 +07:00
shakespeare.diy
a74225fe0d feat: platform monetization — listing fees for paid events & Lab profiles v1.0.62
Architecture:
- usePlatformFee hook: central fee config (featuredEvent/labProfile/featuredShop)
  5,000 sats each, 30-day validity, localStorage persistence, owner bypass
- PlatformFeePayment component: generic reusable payment gate
  auto-pay via NWC/WebLN, QR fallback, active/expired state display
  all fees route to skatespots@minibits.cash

Events (CreateEventPage):
- New optional "Entry Fee" field
- Triggers 5,000 sat platform fee when filled
- Stores entry-fee + paid tags on published event
- Free events: zero friction unchanged

The Lab (Become{Coach,Filmer,Editor}Page):
- Triggers 5,000 sat fee when "Rate" field is filled
- Submit disabled with clear CTA until fee paid
- Free/volunteer profiles: zero friction unchanged

Rule: "pay only when you earn"
- No rate set → publish instantly, no fee
- Rate set → 5,000 sats to SkateSpots, valid 30 days
- Platform owners always bypass

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 02:19:45 +07:00
shakespeare.diy
f39c251e90 fix: Sheet z-index — burger menu now covers all map floating buttons
Map FABs and controls sit at z-[1000]–z-[1001]. The Sheet overlay/content
were at z-50 (= 50), so the map buttons punched through on top.

Bumped SheetOverlay and SheetContent from z-50 → z-[2100], which is above
the highest map element (z-[2001] for the spot detail modal) so the slide-in
drawer always fully covers the map UI.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 01:25:46 +07:00
shakespeare.diy
ceff6962ec Bottom nav: rename Me→Profile, link to settings root instead of edit profile
- "Me" label renamed to "Profile" — clearer intent
- Link changed from /settings?section=profile to /settings — lands on the
  settings root menu so users see all options, not just the edit form

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 22:03:52 +07:00
shakespeare.diy
f48844b495 Fix Sasquatch / Add Spot FAB overlap on mobile map
- SasquatchGuide now dispatches 'sasquatch:panelchange' event when panel opens/closes
- MapPage listens to the event and hides the Add Spot FAB (with scale-down animation)
  whenever the Sasquatch panel is open — no more overlap
- FAB reappears with smooth opacity + scale transition when panel closes
- Added a visible close (X) button to the Sasquatch panel header alongside the existing
  minimize button — gives users a clear way to fully dismiss the panel
- Sasquatch panel bottom edge stays 10px above the nav bar on all screen sizes

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 22:00:50 +07:00
shakespeare.diy
a1cd936bc4 UX audit round 2: Near Me fix, map height, touch targets, iOS dirs, a11y, mobile layout
CRITICAL:
- Near Me button: completely rewritten — was silently discarding ALL errors. Now shows
  toast for denied, timeout, unavailable with actionable copy. Button shows red LocateOff
  icon when denied so user sees the state at a glance. Raised to 44×44px (Apple minimum).
- Map height on mobile: switched to CSS variable system (--header-mobile, --bottom-nav)
  for pixel-accurate height. Added Layout noPadding prop so the map page gets full bleed
  without the 80px bottom-padding fighting the map container calc.
- Layer toggle pills: raised from 28×28 to 40×40px — was below touch target minimum
- Right-side control column: now hides when spot bottom sheet is open (was always visible)

iOS/Directions:
- SpotDetailPage directions button: now detects Apple platform and opens Apple Maps on
  iOS/macOS, Google Maps everywhere else (was always Google Maps)
- Added aria-label to directions button

Accessibility:
- LoginDialog: was missing DialogTitle, causing console errors on every login open
- CameraCapture: fallback dialog was missing DialogTitle (sr-only)
- CommandDialog: added sr-only DialogTitle

Layout:
- SpotDetailPage: reduced top padding mobile pt-4 (was py-8 = 32px wasted space)
- Directions popover: centered in viewport (top-1/2 left-1/2) with zoom-in animation,
  never clips behind Sheet stacking context

CSS:
- Added --header-mobile and --bottom-nav CSS variables for consistent calculations
- Added .page-fullbleed helper

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 21:56:11 +07:00
shakespeare.diy
dd85644fca UX audit fixes: iOS directions, Me tab profile link, onboarding banner, empty states, stats deltas, newsletter pill
- Fix iOS directions: Apple Maps first on Apple platforms, removed broken geo: URI,
  added Waze option, fixed popover z-index clipping behind Sheet component
- Fix 'Me' bottom nav: now deep-links to /settings?section=profile for logged-in users
  so tapping 'Me' shows your profile, not a generic settings menu
- Add SettingsPage URL param support: ?section=X opens sub-screen directly
- Add onboarding welcome banner: dismissible strip at top of map for logged-out users
  with 'Get Started' CTA pointing to /onboarding, stored in localStorage
- Fix Lab empty state: FAQ link + login prompt for logged-out users
- Fix Events empty state: FAQ link added to all empty state instances
- Fix GearSwap empty state: actionable 'Clear Filters' button + FAQ link
- Community stats: add 'this month' delta badges (+N this month) on all stat cards
- Newsletter pill: hide on map page to eliminate bottom sheet overlap conflict

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 21:36:33 +07:00
shakespeare.diy
08e20b0f39 newsletter: fix hash-router URL (#/newsletter), add to desktop More dropdown
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:59:29 +07:00
shakespeare.diy
c9bb5abfff sasquatch: left-anchor panel on mobile, leave right side free for Quick Snap + filter pills
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:08:03 +07:00
shakespeare.diy
be4e0bc986 map: remove duplicate sasquatch, reposition original bubble to bottom-right below Quick Snap
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:05:46 +07:00
shakespeare.diy
889eb922eb map: move attribution to top-right below Near Me, add Vibed with Shakespeare below Quick Snap
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:01:28 +07:00
shakespeare.diy
f8b14f1056 map: switch tile layer to CartoDB Voyager
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 19:59:11 +07:00