Commit graph

456 commits

Author SHA1 Message Date
shakespeare.diy
a2c34800ce Add sponsor picker + notifiable crew join-requests
Part A — Sponsor picker:
- CrewSponsorPicker searches the shop directory and emits a 30017 coordinate
  so founders select a sponsoring shop instead of pasting a raw coordinate.
- Wired into CrewForm, replacing the raw coordinate input.

Part B — Notifiable join-requests (kind 31969):
- New addressable join-request event p-tagged to founder + admins, making the
  request→approve direction symmetric with invites (surfaced in the bell, not
  buried in a DM inbox).
- useCrewInvites: useSendJoinRequest, useIncomingJoinRequests,
  useApproveJoinRequest (founder adds member), useDismissJoinRequest (local).
- CrewRequestToJoin now publishes the structured event.
- JoinRequestCard (Add to crew / Dismiss) wired into NotificationBell and
  counted in the unread badge.
- Documented kind 31969 in NIP.md alongside invites.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-03 00:23:55 +07:00
shakespeare.diy
50d8d9e614 Wire live Cordn encrypted crew chat over the coordinator (ContextVM)
The crew coordinator is no longer just a pointer — when a crew configures one,
the Chat tab runs through it for real.

- src/lib/cordn: clean transport/crypto split so an MLS (ts-mls) upgrade is a
  drop-in swap of the crypto layer only.
  - types.ts: CordnMessage/CordnEndpoint + envelope kind 4497 + app tag.
  - crypto.ts: sealGroupMessage/openGroupMessage — real NIP-44 group encryption
    (per-recipient sealing), the exact seam MLS encrypt-to-epoch slots into.
  - client.ts: buildEnvelope/publishEnvelope/fetchGroupMessages with per-group
    cursor ordering and ciphertext→author sender binding (anti-spoof).
  - endpoint.ts: resolve a crew's `coordinator` to a usable relay endpoint
    (wss/ws direct; https falls back to the ContextVM relay).
- useCordnChat: fetch-then-poll hook mirroring the NIP-17 chat API.
- CrewChat: uses Cordn when the crew has a coordinator, NIP-17 fallback
  otherwise; header badge shows the active transport (Cordn vs Private).
- CrewCoordinatorCard: reflects that private chat is live.
- NIP.md: document envelope kind 4497, delivery (fetch-then-poll + cursors),
  the nip44-group sealed content, and the honest crypto→MLS seam.

Real encryption, real coordinator routing, non-breaking (fallback intact),
with the MLS ratchet as a clearly-marked next step.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-03 00:15:22 +07:00
shakespeare.diy
cbd30fee00 Add skate roles to the crew roster (filmer, photographer, etc.)
A skate team is more than "members" — the filmer, photographer and editor are
core functions. Crews can now assign optional, non-pretentious skate roles:

- Types: CrewRole (filmer/photographer/editor/artist/manager/og) + CREW_ROLE_META
  labels/emoji, `crew-role` tag parser, and `crewRoles` on SkateCrew. Roles are
  additive to membership and grant NO powers (only `admin` does); a member can
  hold roles AND be an admin. Only honoured for founder/listed members.
- CollectiveMembers: optional rolesByPubkey → renders 🎥 Filmer / 📷 Photographer
  flex chips next to each member.
- CrewLeadership: leader + captains show their skate-role chips.
- CrewForm: per-member role toggles + a founder ("Your roles") picker; publishes
  `crew-role` tags scoped to the roster.
- CrewDetailPage: builds rolesByPubkey and threads it to members + leadership.
- NIP.md: document the `crew-role` tag + example.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-03 00:07:30 +07:00
shakespeare.diy
91045e17ae Add "The Part" — crew video/montage (YouTube + Instagram)
A skate crew is defined by its footage. Crews can now feature a headline video:
- videoEmbed.ts: safe parser accepting ONLY YouTube and Instagram URLs (full
  parts on YT, clips on IG). Extracts video id / shortcode; rejects everything
  else so we never render an untrusted iframe. No native/self-hosted video.
- CrewVideoSection ("The Part"): cinematic section — YouTube click-to-load
  facade (poster + play button, no heavy iframe/cookies until play) and an
  Instagram permalink embed, framed with skate language.
- SkateCrew gains `videoUrl` (tag `video`), validated through the embed parser.
- CrewForm: "The Part" input with live source detection (YouTube/Instagram) and
  validation; publishes the `video` tag.
- Crews grid: crews with footage flex a "▶ Part" badge as a discovery signal.
- NIP.md: document the `video` tag (YouTube/Instagram only) + example.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 23:59:50 +07:00
shakespeare.diy
4f3339650d Add crew roles & relationships (Phase 3: sponsor + Cordn coordinator)
- Extend SkateCrew with `sponsorRef` (sponsoring shop, NIP-15 stall coord) and
  `coordinator` (Cordn/MLS encrypted-chat Delivery Service pointer), plus parser.
- CrewSponsorBanner: "Sponsored by [Shop]" banner that resolves and links the
  backing shop; renders nothing if the shop can't be resolved.
- CrewCoordinatorCard: surfaces the crew's private-chat coordinator (E2E · MLS),
  explains what the coordinator can/can't see, shows the operator, and prompts
  the leader to set one up when absent — the UI foundation for Cordn chat.
- Wire the sponsor banner (under hero) and coordinator card (sidebar) into
  CrewDetailPage.
- CrewForm: add "Sponsored by" and "Private Chat Coordinator" inputs with
  validation; publish `sponsor` and `coordinator` tags.
- NIP.md: document the `sponsor` and `coordinator` tags, the structured invite
  kind 36229 (with accept/decline flow), and the Cordn/MLS private-chat model
  and why it's the chosen fit for crews.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 23:52:33 +07:00
shakespeare.diy
8a384c79d2 Redesign crew detail + crews page (Phase 2: visual)
- New CrewHero: immersive full-bleed banner with per-crew accent glow, club-crest
  logo overlapping the banner, member-count crest, big team name with neon shadow,
  reputation crown + live activity pulse, focus/verification chips, "Led by"
  team-leader inline, vibes and bio. Actions are passed in from the page.
- New CrewLeadership card: frames the founder as "Team Leader" and admins as
  "Captains" with role crests, verified ticks and quick-DM — establishing the
  crew hierarchy and laying groundwork for coordinator roles.
- Rewire CrewDetailPage to use CrewHero + CrewLeadership; wire live activity via
  useCrewsActivity and confirmed-member/admin sets. Removed the old flat hero
  card and now-unused imports.
- Upgrade CrewsPage header into a proper hero band with gradient, glow and
  stat pills.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 23:07:27 +07:00
shakespeare.diy
e8c1fbc71a Add crew invitation & notification system (Phase 1)
- Upgrade CrewInviteDialog with a "Send invite" tab: invite a specific npub
  with live invitee preview, optional note, and founder-only "invite as admin"
  toggle. Preserves the shareable-link flow on a second tab.
- Add NotificationBell to desktop + mobile headers: unread badge, popover with
  incoming crew invites (accept/decline) and pending-confirmation crews
  (confirm with public/crew-only visibility).
- Wire useSendCrewInvite / useMyCrewInvites / accept / decline hooks and the
  useCrewNotifications aggregator into the UI.
- Membership stays unforgeable (founder p-tag + invitee NIP-32 attestation).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 22:59:34 +07:00
shakespeare.diy
6ea96171ad Surface Sponsors & Support up top on the profile
- Move sponsors from a buried sidebar card to a prominent full-width "Backed by"
  bar right under the stats strip, above the About/content grid
- Redesigned as a horizontal pill row with an Award badge + gradient/glow accent,
  so reppin' brands reads as a badge of pride
- Removed the duplicate sidebar Sponsors card

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 22:38:36 +07:00
shakespeare.diy
2bb0f98f45 Redesign profile stats strip into a balanced, scannable grid
- Even 2x2 (mobile) / 1x4 (desktop) grid instead of a ragged flex-wrap where
  "Gear" dropped to its own row and "Member since" floated awkwardly
- Each stat now has an icon badge + prominent count + label, so the number reads
  as a number (previously the tiny colored count looked like a status dot)
- Zero-value stats render in a muted, inert state — a brand-new profile now reads
  as "nothing yet" rather than a wall of dead grey zeros
- Moved "Zaps received" + "Member since" into a clean secondary meta line below,
  keeping the primary grid focused on the four core contributions

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 22:29:10 +07:00
shakespeare.diy
9958020776 Make profile stat pills clickable — jump to their section
- StatPill now accepts an onClick and renders as a hover-lift button when it has
  a non-zero value (inert div when empty), with clear affordance
- Spots / Shops / Crews / Gear pills smooth-scroll to their matching section
  (added ids + scroll-mt-24 offset so headings aren't hidden under sticky nav)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 22:23:22 +07:00
shakespeare.diy
deb8fe1995 Profile polish: NIP-05 field UX, share+QR, at-a-glance stats, cleaner empty states
NIP-05 (the field you couldn't find — it existed but was a bare input):
- Promote to its own prominent, self-explaining row (out of the cramped grid)
- Live verification: checks the handle resolves back to your own key, with checking/verified/mismatch/unreachable states, an inline status icon, plain-language copy, and a "How?" help link

Share + QR:
- New ShareProfileDialog: QR of the profile URL (great for IRL/stickers), copy-link, and native share-sheet fallback
- "Share" button added to every profile header

At-a-glance stats:
- Compact stat strip near the top (Spots / Shops / Crews / Gear / Zaps + "Member since")
- Removed the now-redundant sidebar "Contributions" card

Cleaner empty states:
- Spots/Shops sections no longer show dead "nothing here" cards on other people's profiles — hidden entirely when empty
- Own empty Spots state is now an actionable nudge linking to /create-spot

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 22:08:49 +07:00
shakespeare.diy
a6c90941d2 Ensure Become-Skater ↔ Profile field consistency
Fields collected on the Become-Skater form were being dropped on the profile:
- Instagram: now rendered in the profile Info card (normalizes @handle / full URL, links to instagram.com), matching where website/lightning live
- Location (city/area): now shown as "Based in" in the skate-layer stats row
- hasSkateLayer now also triggers on location/instagram so the skate card renders even when those are the only fields filled

All other fields (photo from kind-0, bio, styles, stance, level/custom level, years, home spot + mapped link, website) already round-trip consistently between the two pages.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 21:56:28 +07:00
shakespeare.diy
dbae3a1501 Add soft NIP-05 verification for skaters (open directory + "Verified" filter)
Keeps the skater directory fully open — NIP-05 is a trust signal, not a gate:
- Extract reusable NIP-05 verification into lib/nip05.ts (parse, fetch well-known, verify against expected pubkey); refactor useCrewNip05 to use it
- New useSkaterVerifications hook: batch-fetches kind-0 metadata for all skaters and verifies each nip05 resolves to its own pubkey, returning a Set of verified pubkeys
- SkatersPage: add a "Verified (N)" toggle (disabled when none verified) that filters to genuinely verified skaters; show ✓ badge only for truly-verified skaters; verified-aware empty state
- NIP19Page profile: nip05 line now reflects real verification (lime ShieldCheck + "Verified" pill when the handle resolves to the key, muted grey otherwise)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 21:51:11 +07:00
shakespeare.diy
bb6245e53c Skater profile: browse real map spots for home spot + always use kind-0 profile pic
Home spot:
- Add "Browse spots" button opening a searchable dialog of real mapped spots (useSkateSpots)
- Picking a spot stores its title + an `a` (30818:pubkey:d) coordinate; shows a linked-spot chip with unlink
- Profile page renders a linked home spot as a link to /spot/:pubkey/:identifier
- Parse homeSpotRef from the `a` tag; document in NIP.md

Profile pic consistency:
- Remove the skate-specific image override/upload — the skater card now always
  uses the kind-0 profile picture so both sections show the identical photo
- "Profile Pic" section is read-only with a "Change profile photo" link to settings
- Drop the `image` tag from published skater events; note the change in NIP.md

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 21:44:12 +07:00
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
3ef11eda61 Refine Lab banner copy, stance to Regular/Goofy, full profile carry-over chips on Become-Skater, remove Back to Leaderboard from profile
- Lab "Just a skater?" banner: drop redundant "→ join the directory" trailer (arrow icon already present)
- Skater stances distinct emojis (🛹 Regular / 🤙 Goofy); already limited to Regular & Goofy
- Become-Skater: fix missing ShieldCheck/Zap imports; show ALL carried-over kind-0 fields (photo, name, bio, website, nip05, zaps) as read-only chips
- Profile (NIP19Page): remove hardcoded "Back to Leaderboard" link and unused ArrowLeft import

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 21:28:55 +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
b3a49499bb Rename "The Quiver" to "Stash" for brand consistency
Use the established "Stash" term everywhere instead of introducing a new
word. The profile section is now titled "Stash", visibility copy reads
"Shown in your public Stash on your profile", and internal identifiers
were renamed (useUserQuiver → useUserPublicStash, QuiverItem →
PublicStashItem, parseQuiverItem → parsePublicStashItem). NIP.md updated
to match.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 19:41:45 +07:00
shakespeare.diy
32a3cbca88 Public Stash items: "The Quiver" showcase + marketplace hybrid
Previously the "Public" visibility on Stash items did nothing — the
whole Stash is a single NIP-44 encrypted blob, so nobody could ever see
a "public" item. This makes Public actually mean public.

Model (hybrid A + marketplace):
- Public items are now ALSO published as separate unencrypted NIP-99
  kind 30402 events tagged `t: stash-showcase`, keyed by `stash-<id>`.
- Public + not-for-sale → showcase-only (`showcase: true`, price 0):
  shows on the profile "Quiver", filtered OUT of the sellable market.
- Public + for sale/trade → full listing: shows on the profile Quiver
  (badged For Sale/Trade) AND flows into the marketplace.
- Going private or deleting an item publishes a NIP-09 deletion for its
  public event, keeping everything consistent.

Changes:
- New hook useStashShowcase (sync/unpublish + useUserQuiver query).
- StashPage wires add/edit/remove through sync handlers; honest
  visibility copy in add + edit dialogs.
- Profile page (NIP19Page) renders a new "The Quiver" grid with
  For Sale/Trade badges + prices, and a Gear count in Contributions.
- Marketplace hooks skip showcase-only events so they don't clutter
  buy/sell listings.
- NIP.md documents the showcase event schema.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 19:33:34 +07:00
shakespeare.diy
9086123240 Fix messy Stash header/toolbar layout on mobile
- Let the title badges (Encrypted · Syncs everywhere · sats) wrap
  gracefully instead of colliding with the toolbar controls
- Hide the list/grid view toggle on the empty state (nothing to toggle)
  so it no longer clutters the space next to the Add button
- Give the title row room to grow on mobile (min-h-14 + padding)
  instead of forcing everything into a fixed 56px row
- Make the search input flex-fill and shrink the category select on
  small screens so the filter bar no longer overflows
- Add aria-labels to the view-toggle buttons

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 19:17:40 +07:00
shakespeare.diy
955d5902d5 Add RK69 DIY Skatepark to Supporters / Friends
Links to https://www.instagram.com/rk69diyskatepark, placed next to SNT4EVER.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-31 19:42:16 +07:00
shakespeare.diy
8ae852f1b8 Add Judges tab to The Lab
- Flesh out SkateJudge as a full craft-based Lab profile (parseCraftBase),
  with JUDGE_FORMAT / SPECIALTY / LEVEL label sets covering Game of S.K.A.T.E.,
  best trick, jams, contests, am series, world tour stops, etc.
- Wire judge into useLabProfiles (union type, self-tag, parse case)
- Add "judging" ServiceType to LabPage: tab, accent, filter sections, empty
  state, counts, profile card icon, and ?craft=judging deep link
- New BecomeJudgePage + /become-judge route + FAB entry
- CoachDetailPage renders judge profiles (accent, icon, labels, noun, title)
- Community Stats "Judges" card now links to /lab?craft=judging
- Document kind 35012 (skatejudge) in NIP.md

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 15:30:43 +07:00
shakespeare.diy
9cefa29640 Make community stat cards clickable to view full lists
- Wire `to` links on stat cards: spots/shops/tours → map, events → /events,
  mappers → /bounties leaderboard, Lab crafts → /lab?craft=<type>, crews → /crews
- Stats already read from the canonical goskatespots relay and the map limit
  matches (2000) so numbers stay consistent with what's on the map

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 15:17:45 +07:00
shakespeare.diy
94a88b2ba0 v1.2.1: Community stats now show ALL Lab crafts + count every craft type
- useGrowthStats now queries and exposes every Lab craft individually: artists,
  graffiti, tattoo, trainers, musicians and judges (previously folded into an
  opaque "other" bucket with no visible breakdown).
- CommunityStatsPage renders a card for all 11 Lab crafts, so the section matches
  what /lab actually lists instead of only showing 5 categories.
- labTotal + the content-mix donut's "Lab" slice now sum every craft, keeping the
  "total" badge and chart consistent.

Note on relay mismatch: the app dedupes addressable events (keeps newest per
pubkey+kind+d) and merges across the community relay + the user's relays, so
counts can legitimately differ from a raw single-relay browser that shows stale
duplicate versions.

Bump version to 1.2.1 (versionCode 10201).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 15:07:13 +07:00
shakespeare.diy
677ccb8a82 v1.2.0: Spedn label, multi-relay community stats, GrapheneOS Near-Me fix
1. Map filter: renamed the "Bitcoin" toggle to "Spedn" (matches the toolbar
   button); tooltip now says "Spedn your sats".

2. Community stats (/stats): useGrowthStats now queries the same relay set as the
   rest of the app (community relay + user relays) via useSkateRelays instead of
   only the single hardcoded SKATE_RELAY. Lab profiles / spots / etc. that live on
   a user's own relay are now counted, so the numbers match what /lab shows.

3. Near Me on GrapheneOS: even after granting the OS location permission, the
   WebView's navigator.geolocation stays blocked → "permission denied". Fixed by
   fetching the position from the native Capacitor Geolocation plugin first
   (LocationManager, honours the granted permission), falling back to
   navigator.geolocation only if that fails. Also auto-inject
   ACCESS_FINE/COARSE_LOCATION into AndroidManifest via the manifest script.

Bump version to 1.2.0 (versionCode 10200).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 14:54:00 +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
a83611072a Fix Amber NIP-44 gate on Stash, mobile Crew detail layout, remove Quick Snap
- signerCapabilities: recognize x-amber login type as NIP-44/NIP-04
  capable (Amber implements nip44_encrypt/decrypt via native intents and
  URL redirects). Fixes "Encryption Required" wall on Stash for users
  logged in with Amber on Android.
- CrewDetailPage: restructure hero header so actions wrap below the
  title on mobile instead of squeezing it in a cramped 3-column flex;
  smaller avatar/title on small screens, truncating location.
- MapPage: remove Quick Snap from the mobile Add speed-dial and all its
  handlers, refs, web-fallback input, and now-unused imports.
- zapstore.yaml: explicit orange icon + metadata_sources: [] so the
  correct logo is always used on the Zapstore listing.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 12:31:34 +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
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
b5b9faeec8 #1 Onboarding polish: add 'Why bother?' skater value-prop section, fix dead /radio link (-> Crews), soften Nostr FAQ overclaim
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 10:02:03 +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
1c4987c8bf Add desktop-only "Get on Zapstore" CTA to map Welcome Banner
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-20 19:41:53 +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
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
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
ede0440244 Make meeting point coordinates optional on tour creation
- Latitude/longitude fields are now optional for local/national tours
- If provided, both must be filled and valid; otherwise geohash and
  coords tags are omitted from the published event
- Updated input placeholders and added helper text clarifying coordinates
  are optional

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-16 17:17:06 +07:00
shakespeare.diy
8abdb25593 Fix CreateTourPage crash: add missing usePlatformFee import
CreateTourPage called usePlatformFee('featuredTour') but never imported the
hook, causing a "usePlatformFee is not defined" ReferenceError that crashed
the /create-tour page. Added the import to match the other pages that use it.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-16 16:15:09 +07:00
shakespeare.diy
8b080fb7f1 Spot detail: make mapper clickable + add Message Mapper button
The "Tip the Mapper" card now links the avatar/name to the mapper's profile
(/{npub} via the NIP-19 router) and adds a "Message Mapper" button that opens
an encrypted DM (/messages/{npub}). Makes it easy to contact whoever added a
spot — e.g. to ask them to add photos to a photoless submission. The message
button is hidden when you're viewing your own spot.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-15 10:03:58 +07:00
shakespeare.diy
aba79900e4 Newsletter Drop #005: OpenCrack roast, bigger Lab, Crews web-of-trust
Add issue #5 (2026-08-08) to NEWSLETTER_ISSUES with a Groucho-Marx satiric
tone. Highlights: the 3x-bigger Lab (10 crafts), the Lab mobile grid fix,
Crews covering every scene with opt-in visibility + web-of-trust vouching,
the redesigned CrewDetail page, and Music<->Events interconnect. Mocks
"OpenCrack" for volunteering to fix Bitcoin's crypto libraries, and owns
that the app has never been audited (one day it will be — and we'll shoot
ourselves in both feet). Includes matching nostrText teaser for kind 1.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 23:15:50 +07:00
shakespeare.diy
d476896efe Lab: replace horizontal-scroll craft tabs with a wrapping grid
On mobile the 10 craft filters (Coaches, Filmers, Photo, Editors, Builders,
Artists, Graffiti, Tattoo, Trainers, Music) were in a sideways-scrolling
strip, hiding options off-screen. Switch to a 5-column grid on phones (two
tidy rows of five) while keeping the flex-wrap layout on desktop, so every
craft is visible at a glance with no horizontal scrolling.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-10 23:07:13 +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
f4318a8b50 Add Newsletter Drop #004 (Sasquatch Pro, Crews, Angor, Coldcard PSA)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-06 09:55:52 +07:00
shakespeare.diy
38343eb438 Fix profile page horizontal overflow on narrow/mobile screens
The public profile page overflowed sideways on phones, pushing the Info /
Contributions / Crews sidebar off-screen. Cause: the About text contained a
64-char unbreakable URL (ContextVM server link) with no word-breaking, forcing
the layout wider than the viewport.

- About text now uses break-words + overflow-wrap:anywhere so long URLs wrap
- Added min-w-0 to both grid columns so they can shrink instead of overflowing

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-30 22:00:43 +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
5d07524751 Glossary: drop difficulty levels, expand & correct tricks
- Removed difficulty categorization entirely (type, badges, styles) —
  cleaner and no arbitrary skill labels.
- Added many requested/missing tricks with proper research and correct
  alt-names, including:
  • Rotations category: FS/BS 180, FS/BS 360, half-cab, full cab
    (caballerial), fakie 180s.
  • Flip tricks: backside flip, frontside flip, FS/BS heelflip, double
    heelflip, 360 hardflip, 360 pressure flip.
  • Grinds: feeble grind now notes "Grosso grind" aka; added Willy/
    Barley, pole jam; inventor credits (Suski, Losi, etc.).
  • Fundamentals: nose ollie/north, one-foot, no-foot/nothing, nose
    manual, caveman, tic-tac.
  • Shoves: nollie shove, ghetto bird.
  • Grabs: tailbone, airwalk. Stance/slang: pop, line, kook, sticker
    slap, etc. Hardware: durometer.
- Updated the local glossary answerer index to cover all new aka names
  (e.g. "grosso grind", "half cab", "north ollie" now answer locally).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 23:08:37 +07:00
shakespeare.diy
309d012db1 Massively expand Trick Glossary (tricks + terminology + hardware)
Grew the glossary from ~80 to ~150 entries across 15 categories.

- Added many more tricks per the community list: impossible, gazelle
  flip, pressure/hospital/dolphin flips, bigger spin/flip, suski/willy/
  losi/hurricane grinds, japan/madonna/slob/seatbelt/roastbeef/benihana/
  lien/cannonball/christ grabs, 720/1080, casper/anti-casper, spacewalk,
  walk the dog, g-turn, and more — with inventor credits and aka names.
- New reference categories: "Transition & Vert Terms", "Stance &
  Movement", and "Hardware & Equipment" (deck, trucks, bushings,
  bearings, risers, wheel bite, etc.).
- Reference categories hide difficulty badges (added `reference` flag +
  `hideLevel` on TrickRow); search still spans everything.
- Updated hero copy + SEO description to reflect the broader scope.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-29 22:54:40 +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
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
f938a6c900 Newsletter #003: harder tone, extensive coverage of the full session
Expanded Drop #003 from 7 to 16 update cards covering everything actually
shipped this session (per git log), grouped: the geolocation war (crash
fix, parallel first-fix GPS, progress UI, you-are-here pin, crash-proofing,
no-provider messaging), Spot Status Reports (reporting, trust/recency
consensus, badges, gone-spot hiding), Quick Snap camera-first + snap→Add
Spot, Amber signing timeout + plugin fix, crew/Lab DELETION via NIP-09
(framed as the new deletion capability, not the existing features), and
native build plumbing.

Cranked the tone harder throughout and leaned fully into the "weekly
newsletter, shipped whenever the f*** we want" bit in both the vibe line
and masthead. Updated nostrText to match.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 01:10:54 +07:00
shakespeare.diy
35c7283970 Newsletter: add Drop #003 (geolocation saga + Spot Status Reports)
New issue covering everything shipped this session, written funny:
- Near Me crash fix (Kotlin 2.1 alignment)
- Parallel first-fix GPS strategy that works on GrapheneOS / no Play Services
- "Locating you…" / "Found you" progress feedback
- "You are here" user-location pin
- Community Spot Status Reports (active/bust/skate-stopped/closed/gone)
- Gone spots hidden from map markers
- Defensive native geolocation hardening

Also updated the masthead tagline to lean into the "weekly newsletter,
shipped whenever the f*** we want" bit. Includes ready-to-post nostrText
for Drop #003.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 01:06:24 +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
59a801d525 Geolocation: GrapheneOS-correct fix — parallel hi/lo watches, 60s, progress
The persistent failure on GrapheneOS (no Play Services, GPS-only) was
caused by the acquisition strategy itself, not the OS:

- The old low-accuracy-only watch never fired without a fused/network
  provider, and the 25s deadline killed it before a cold GPS lock (which
  takes 30–60s on GrapheneOS) could ever deliver.

Rewrite raceForFirstFix to actually work on GPS-only devices:
- Run BOTH a high-accuracy (GPS) and low-accuracy (network) watch in
  parallel — high accuracy is what engages the GPS chip and is usually
  the one that delivers on GrapheneOS.
- Also fire a one-shot high-accuracy getCurrentPosition.
- Extend the deadline to 60s (real cold-GPS lock time) and classify a
  timeout-flavored failure correctly.
- Ignore invalid/null-island (0,0) readings.
- Drop the redundant post-race extra attempt.

UX: show a persistent "Locating you…" toast (dismissed on success/fail)
so the up-to-60s GPS lock doesn't look frozen, plus a "Found you"
confirmation. Failure still surfaces the raw error text for diagnosis.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 00:36:16 +07:00
shakespeare.diy
a3dad2cbec Geolocation: handle 'no provider' case (GrapheneOS / no Play Services)
Root cause of the persistent Near Me failure: the test device runs
GrapheneOS with no Google Play Services, so there's no network/fused
location provider — only raw GPS, which can't fix indoors. This was
never a code bug.

- Add 'no_provider' GeoErrorKind; detect it from native error text
  (disabled / provider / settings / unavailable).
- Show accurate, actionable messaging: tell the user to enable Network
  Location (Settings → Location) — the GrapheneOS-friendly fix — or use
  GPS outdoors, instead of a vague timeout.
- Refine the timeout copy along the same lines.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 00:32:03 +07:00
shakespeare.diy
8870a3e07f Near Me: surface raw geolocation error text on-screen for diagnosis
Append the underlying native/browser error message to the failure toast
(and log it) with a 12s duration, so the exact failure reason is visible
on-device without needing adb. This lets us distinguish OS Location-off,
provider-unavailable, timeout, and plugin errors at a glance.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-25 00:22:46 +07:00
shakespeare.diy
700ebee17e Geolocation: fast first-fix via watchPosition race (Google-Maps-like)
The real reason Near Me failed indoors: getCurrentPosition with high
accuracy waits for a single satisfactory GPS reading, which needs
satellite visibility and times out indoors. Google Maps feels instant
because it takes the FIRST update from any provider (wifi/cell/fused).

Rewrite the acquisition strategy to do the same:
- raceForFirstFix() starts a low-accuracy watchPosition and resolves on
  the very first update (the fast network/fused fix, ~1s indoors),
  racing it against a cheap cached getCurrentPosition. First to return
  wins; watch is cleared immediately after.
- 25s deadline falls back to one long high-accuracy attempt for the
  cold-outdoor-GPS case.
- Apply the same watchPosition-race to the web path.
- watchPosition/clearWatch added to the plugin proxy interface, guarded
  so builds without them fall back to getCurrentPosition.
- Update timeout toast (drop the "walk to a window" advice; point at the
  GPS/Location quick-setting toggle instead).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 23:30:17 +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
9c7cd5ec4e Improve geolocation reliability: 3-stage fix with forgiving timeouts
Now that the native crash is fixed (Kotlin 2.1 alignment on the Android
side), the remaining issue was GPS "Location timed out" on a cold first
fix / indoors. Rework the native position acquisition into three
increasingly-forgiving stages that return the first success:

  1. INSTANT — accept a cached/coarse fix up to 5 min old (resolves
     immediately on a warm chip).
  2. PRECISE — high-accuracy GPS with a generous 30s window so a cold
     first-fix has room to complete.
  3. COARSE FALLBACK — network/wifi fix with the longest window to
     rescue the "GPS can't lock indoors" case.

Also bump the web-path timeout to 20s, and make the "Location timed out"
toast actionable on native (suggests stepping near a window / retrying).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 22:47:48 +07:00
shakespeare.diy
7aad972099 Quick Snap: navigate to Add Spot immediately, upload photo on the form (v1.0.76)
Bug: after taking a Quick Snap photo and choosing "keep", the user never
reached the Add Spot form. handleQuickCameraCapture uploaded the photo to
Blossom FIRST (which needs an Amber signature and can be slow/fail) and only
navigated on success — so a stalled upload stranded the user right after the
camera.

Fix: Quick Snap now prioritizes speed —
- MapPage stashes the raw photo as a data URL (quickCapturePendingPhoto),
  grabs location (non-blocking), and navigates to /create-spot immediately.
- CreateSpotPage reads the pending photo on mount, converts it back to a File,
  and uploads it in the background with an "Uploading photo…" tile, adding the
  resulting URL to the images list. A failed upload now shows a toast on the
  form instead of blocking navigation. Old quickCaptureImage (already-uploaded
  URL) path kept for backward-compat.
- Removed the now-unused useUploadFile from MapPage.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 21:55:15 +07:00
shakespeare.diy
59493ac784 Geolocation: coarse fallback + clearer native error copy (v1.0.75)
The native Geolocation fix only takes effect once the APK is rebuilt — the
current phone build still uses navigator.geolocation, hence the error. To make
the rebuilt version more robust:

- If a high-accuracy fix fails/times out (common indoors or weak GPS), retry
  with a coarse (network/wifi) fix and a longer window before giving up. This
  rescues most "Location unavailable" cases.
- On native, the "unavailable" toast now tells the user to turn on Location/GPS
  AND allow the app permission, instead of the generic web message.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 21:17:20 +07:00
shakespeare.diy
b3a025ca77 Native geolocation for Near Me + desktop map fits viewport with footer (v1.0.73)
1. Near Me "location blocked" on the APK:
   - navigator.geolocation returns PERMISSION_DENIED inside the Android WebView
     because the WebView never gets the OS location permission, so the app
     showed a bogus "your browser blocked location" help sheet.
   - New src/lib/geolocation.ts: platform-aware getCurrentPosition(). On native
     it uses Capacitor's Geolocation plugin via registerPlugin('Geolocation')
     from bundled @capacitor/core (same runtime pattern as the camera/Amber
     fixes) and calls requestPermissions() to trigger the real Android prompt;
     on web it falls back to navigator.geolocation.
   - MapPage handleNearMe, proximity sort, and quick-snap location now use it.
     On native, a denied permission shows an OS-permission toast instead of the
     browser help sheet.
   - Added @capacitor/geolocation dependency (native side picked up via cap sync;
     no bare import in the JS bundle).

2. Desktop map didn't fit the viewport (had to scroll to see footer):
   - Map wrapper was md:h-[calc(100vh-4rem)] — full height minus header only,
     leaving no room for the footer.
   - Added --footer-desktop token (5rem) and changed the desktop height to
     calc(100vh-4rem-var(--footer-desktop)) so map + footer fit without scroll.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 20:28:06 +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
6d0480961c Quick Snap: camera-first on native (runtime Camera plugin), file input on web
The previous file-input-only approach fixed the crash but opened the gallery
on native because capture="environment" is only a hint the OS can ignore.

New platform-aware flow (lib/quickCapture.ts):
- Native (APK): open the OS camera directly via the runtime-injected
  window.Capacitor.Plugins.Camera with source:'CAMERA' — forces the real
  camera and gives built-in preview/retake. Accessed off the runtime object,
  so no '@capacitor/camera' specifier in the bundle (keeps the white-screen
  fix intact). User cancel is treated as a no-op.
- Web: falls back to the file input with capture="environment".
- If the native camera errors, gracefully falls back to the file input.

MapPage Quick Snap now routes through handleQuickSnap() → the captured photo
feeds the existing upload → create-spot flow unchanged. Bump 1.0.69.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 19:38:46 +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
87c710bf4a Quick Snap: use native camera/gallery input instead of custom camera
The custom in-app CameraCapture failed on the native APK ("Could not open
the camera") due to Capacitor camera permission plumbing, and a single
photo is all a spot snap needs. Replace the Quick Snap flow on the map with
a hidden native file input (accept="image/*" capture="environment"): one
tap opens the OS camera/gallery, the chosen photo feeds the existing upload
→ create-spot flow unchanged. Works identically on web and native, no
plugin/permission fragility. Removes the CameraCapture usage + import from
MapPage (component still used by CreateSpotPage).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 19:13:49 +07:00
shakespeare.diy
cc2e35f569 Lab tabs: fixed 5-across grid instead of horizontal scroll
The Lab category tabs (Coaches/Filmers/Photo/Editors/Builders) overflowed
the viewport and scrolled sideways on phones, hiding Builders. Replace the
overflow-x row with a 5-column grid of compact icon-over-label tabs so all
five are visible at once without lateral scrolling, with the count badge
floated to the corner so it never widens a tab.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 18:56:06 +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
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
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
cf514a7c3f Add Photographers & Builders to community stats + restructure Lab section
- useGrowthStats now queries kind 35004 (photographers) and 35005 (builders),
  which were previously omitted; added to totals, monthly buckets, allContent,
  and the content-mix donut (folded into the "Lab" slice)
- Community Stats page reorganized into clear sub-sections:
  * "The Lab · Creators & Services" — Coaches, Filmers, Editors,
    Photographers, Builders, with a total badge
  * "Community" — Tours, Crews
  This scales the growing role list cleanly instead of cramming one row.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 15:06:18 +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
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
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
657c36d264 Add Builder category to The Lab (kind 35005), collective-aware + spot-linked
Step 2 of the collective plan:
- types.ts: SKATE_BUILDER_KIND 35005, BUILDER_SERVICE/SKILL/SCALE label maps,
  SkateBuilder interface + parseSkateBuilder (entity/members/noffer/spotRefs +
  service_area & insured fields)
- useLabProfiles/useLabProfile: query + parse builders (profileType 'builder')
- BecomeBuilderPage (/become-builder): amber accent, build-type/materials/scale
  chip selectors, "how we work" gear, service area, licensed/insured switch,
  entity toggle (defaults to collective), noffer, and "spots you've built" picker
- LabPage: 'building' ServiceType — amber accent, Builders tab (5 tabs now,
  horizontally scrollable on mobile), OfferFAB item, filter sections, card
  type-icon/labels, empty state, counts & filtering
- CoachDetailPage: builder accent/icon/kind, "What We Build" + "How We Work"
  sections, service-area & insured cards, portfolio/social handling
- NIP.md: document Builder kind 35005 with tag taxonomy + collective example

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 13:33:52 +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
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
88d610c947 Add "Add Event" and "Sell Gear" to the map Add speed-dial
The mobile "+" speed-dial was missing two core actions:
- Add Event (lime #ccff00, Calendar icon) -> /create-event
- Sell Gear (orange #f97316, ShoppingBag icon) -> /create-listing

Now fans out: Add Spot / Add Shop / Add Event / Sell Gear / Offer Tour /
Quick Snap. Colors follow the canonical entity palette. Bumped Quick Snap's
stagger delay so it animates in last.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 10:36:48 +07:00
shakespeare.diy
aa4c5ca697 Desktop map Layers popover (#6) + canonical entity colors (#7)
#6 Desktop parity:
- Replaced the four separate Spots/Shops/Gear/Tours toggle buttons in the
  desktop map toolbar with a single grouped "Layers" popover (with count
  badge), matching the mobile control. Declutters the dense toolbar row.

#7 Entity color discipline:
- Locked the four map-entity colors to a single canonical value each,
  matching the map markers (source of truth): Spots #ff0040, Shops #9333ea,
  Gear #f97316, Tours #06b6d4.
- Fixed the mismatch I'd introduced (Layers popover + Add FAB used #a855f7
  purple instead of the marker's #9333ea). Everything now agrees.

Note: broader purple usage (Stash, Cashu, NWC, Editors) is intentionally
left as-is — those are separate semantic uses, not the map entity language.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 10:32:57 +07:00
shakespeare.diy
bb6b3e6b7e Fix map search input visibility; align header sizes; de-dupe map Quick Actions
- Map search input: replaced non-standard `bg-white/6` with `bg-white/5`
  (matching The Lab's inputs) and removed the focus ring offset so typed
  text is now visible on the dark toolbar instead of a white field. Also
  normalized the mobile filter button's `bg-white/6` -> `bg-white/5`.
- Bumped Wallet and Settings page titles to text-4xl md:text-5xl to match
  The Lab and Events headers for consistent title rhythm.
- Trimmed the map filter drawer's Quick Actions to Marketplace + Stash,
  since Add Spot / Shop / Tour / Quick Snap now live in the Add speed-dial
  FAB (removes duplication).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 10:24:52 +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
d8e49e7f65 redesign: /onboarding full rewrite
Hero:
- "SKATE YOUR WORLD" headline with neon red/lime treatment
- Beta badge pill with live pulse dot
- Feature pills row (spots, lab, events, gear, stash, radio, zaps)
- Primary CTA right in the hero — no scrolling to get started
- "No email. No password. Own your identity forever." copy

Layout:
- Removed mechanical 4-step progress tracker (step 2 auto-completing was confusing)
- Replaced with natural scrolling sections with clear headings
- Logged-in state shows green confirmation bar with FounderFollow

What you can do:
- 6-card grid: Map, Lab, Events, Buy/Sell, Stash, Radio — all linked

What is Nostr:
- Skater-friendly explanation with 4 benefit pills
- Deep dive button for relay education modal

Zaps & Wallet:
- "Optional" badge so new users don't feel overwhelmed
- Side-by-side: Cashu (built-in) + External wallet (NWC)

FAQ (accordion — 8 questions, no duplicates):
- "Is it free?" — combined the two identical free questions
- "What is Nostr?" — proper explanation
- "What's an nsec?" — key backup warning
- "What signers work?" — desktop/Android/iOS with real links
- "Can I use it on my phone?" — PWA + Zapstore mention
- "What is a zap?" — simple skater framing
- "Will my spots be deleted?" — relay permanence
- "Private property spots?" — community responsibility
- "How to report a bug?" — links to cordn.net beta coordinator

Beta section:
- cordn.net/p/npub1e2ayjl... as primary beta contact (clickable card)
- Founder npub with Copy + DM buttons as secondary
- Removed v0.1.0 hardcoded version string

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 02:00:12 +07:00
shakespeare.diy
254484a354 fix: add Add Spot (red) + Add Shop (green) back into right control strip
- Both + buttons are 36px circles matching the strip visual system
- Add Spot = red circle (bottom of layer toggles section)
- Add Shop = green circle (right below Add Spot)
- Removed redundant left-side FAB entirely — everything is now in one column on the right

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 01:51:18 +07:00
shakespeare.diy
056846fc38 redesign: map page mobile UI overhaul v1.0.61
Toolbar:
- Slim rounded-pill search input (was tall boxy default)
- Compact icon-only view toggle pills
- Small circle filter button (red when active, badge count)

Right-side control strip:
- Layer toggles (Spots/Shops/Gear/Tours) as 36px circles
  active=opaque, inactive=35% opacity — no more giant emoji FABs
- Near Me button integrated into strip (removed from top-right)
- Quick Snap in strip, same style — no more big red circle
- All transitions: translate-x-2 slide-out when overlays open

Left-side Add FAB:
- Single gradient pill (red→orange) replaces two competing circles
- Smaller footprint, less visual noise

Loading indicator:
- Replaced center-screen "Loading..." pill with slim animated
  gradient bar at top edge of map (top-0 left-0 right-0 h-0.5)

Attribution:
- Moved from floating top-right to bottom-left corner
- 9px text, 50% opacity — required but invisible

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 01:49:07 +07:00
shakespeare.diy
76b834ddd2 redesign /stash desktop layout — premium sidebar, tighter grid, better hierarchy
- Sidebar expanded to 300px with real visual weight
- Inventory card: big stat number (e.g. "5 items in stash"), est. sats value, 3-cell secondary stats row (Wishlist / For Sale / To Trade), clickable category breakdown bars
- Marketplace CTA: gradient header with branded icon, per-type badges showing sell/trade counts, full-width gradient CTA button
- Wishlist teaser card: number callout + "View Wishlist" action button
- Quick-add buttons (Add to Stash / Add to Wishlist) moved to top of sidebar on desktop — header no longer needs them
- Grid defaults to 2/3/4 cols (sm/md/xl) — no more awkward single-item stretch
- Empty states bigger and more purposeful with better copy
- Mobile layout unchanged; FABs and mobile CTA banner retained

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 01:20:38 +07:00
shakespeare.diy
f68864bb8f redesign: Stash page v2 — eliminate noise, get to items faster
- REMOVED: Sync banner (already in header subtitle)
- REMOVED: ProfileStatsBar self-intro card (it's YOUR page, you know who you are)
- REMOVED: Chunky tab switcher — replaced with underline tabs inside the sticky header,
  saving ~50px of vertical space before you see any items
- MERGED: All header info (title + item count + encryption + sats value) into one
  compact 56px sticky bar — nothing duplicated
- IMPROVED: Search + category filter in a single tight row (h-9 inputs)
- IMPROVED: Category chips only render when >1 category present (no orphan chips)
- IMPROVED: Sidebar is desktop-only (lg:hidden) — on mobile it was buried below items,
  now replaced by a contextual bottom banner that only appears when items are for sale
- IMPROVED: FABs slightly smaller (not competing with Sasquatch bubble)
- IMPROVED: Empty state is more focused — no walls of text

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 01:04:41 +07:00
shakespeare.diy
df64a44c4e fix: remove duplicate Browse Marketplace button in Stash sidebar
Merged the two separate marketplace cards (Ready to Sell + Stash→Marketplace)
into a single smart card:
- When items are marked for sale/trade: shows orange "Ready to Sell" CTA
  with item count and a prominent Browse Marketplace button
- When no items for sale: shows a quieter grey card explaining how to
  mark items for sale, with a subdued Browse Marketplace link
One card, one button, no duplication.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 00:54:50 +07:00
shakespeare.diy
2cfb1ad566 redesign: Stash page full UX overhaul
Key improvements:
- NEW: List view (default) — compact swipeable rows for mobile, perfect
  for scanning inventory quickly. Grid view still available via toggle.
- NEW: Floating FABs on mobile — red + to add to stash, cyan heart to
  add to wishlist, positioned above the nav bar with safe-area support
- NEW: Cross-device sync info banner explaining Nostr = synced everywhere
  (answers the user's question: yes, items added on desktop show on mobile)
- FIX: "Browse Marketplace" now navigates to /marketplace (GearSwapPage)
  instead of /map?view=gear which incorrectly loaded the map
- NEW: Sticky page header with item count badge, encrypt indicator, and
  view-mode toggle — always visible when scrolling
- NEW: Compact ProfileStatsBar replaces the heavy CharacterPanel sidebar card
- NEW: Category quick-chips — tap an emoji to filter by category instantly
- IMPROVED: Location filter only shows locations that have items (no empty chips)
- IMPROVED: Sidebar consolidated — removed redundant cards, kept what matters:
  "Ready to Sell" card (only shown if items are marked for sale), marketplace
  link, wishlist status, and compact tips
- IMPROVED: Empty states mention cross-device sync as a feature
- IMPROVED: List row actions are always visible (no hover-only on mobile)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 00:51:18 +07:00
shakespeare.diy
eba7542097 fix: FAB buttons above nav + Sasquatch no auto-trigger on map
- FABs (add spot/shop): switch from fixed bottom-[88px] to
  calc(3.5rem + env(safe-area-inset-bottom, 0px) + 0.75rem) so they
  respect device safe-area and always clear the bottom nav on all
  Android/iOS Capacitor builds
- Right-side filter pills: same fix, switched from absolute to fixed
  with the same safe-area-aware bottom value
- useSasquatchGuide: /map page now returns undefined message, meaning
  Sasquatch never auto-opens when navigating to Shops & Spots — user
  must tap the floating bubble to open it manually

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 00:12:53 +07:00
shakespeare.diy
f2c9c3d118 fix(map): FABs bottom-4 inside map container, icon-only circles
- Root cause: map container is already clipped to exclude bottom nav
  (h-[calc(100dvh-var(--header-mobile)-var(--bottom-nav))]) so
  bottom-[calc(5rem+...)] was pushing buttons ~80px up inside the
  clipped box — wrong and covered filter pills
- Both FABs and right-side pills now use bottom-4 (16px from container edge)
- FABs redesigned as clean 48×48 icon-only circles, no text labels:
    red circle  (+) → Add Spot
    green circle (+) → Add Shop
  thick Plus strokeWidth=3, high-contrast (black on green, white on red)
- Only shown when user is logged in (no logged-out ghost button)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 23:28:55 +07:00
shakespeare.diy
85a77b36f2 fix(map): replace speed-dial FAB with two always-visible pill buttons
- Remove speed-dial/tap-to-expand pattern — buttons are now always visible
- "Add Spot" — red (#ff0040), white text, same style as before
- "Add Shop" — green (#00cc66), black text, sits above Add Spot
- Both cleared of bottom nav via bottom-[calc(5rem+env(safe-area-inset-bottom))]
- Removed unused fabOpen state

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 23:08:11 +07:00
shakespeare.diy
2500b549f5 fix(map): speed-dial FAB + location help dialog
FAB (Add Spot / Add Shop):
- Removed old bottom-left "Add Spot" pill that sat on top of the bottom nav
- New speed-dial FAB in bottom-left, positioned at
  bottom-[calc(5rem+env(safe-area-inset-bottom,0px))] — always clears
  the iOS/Android safe area + bottom nav height
- Tap the red + button to expand upward: "Add Spot" (red/MapPin) and
  "Add Shop" (purple/Store) pill buttons slide in with animation
- + button rotates 45° when open (becomes ×), closes on map tap
- Logged-out state shows "Log in to add" prompt instead
- Tapping either option closes the speed-dial before navigating

Right-side filter pills:
- Bumped from bottom-20 to bottom-[calc(5rem+env(safe-area-inset-bottom))]
  so they're consistently safe-area aware on devices with home indicator

Near Me — location denied fix:
- PERMISSION_DENIED no longer shows a toast (which was useless — user
  can't act on a disappearing toast)
- Tapping the red LocateOff button now opens a bottom-sheet help dialog
  with per-platform instructions (Android Chrome, iOS Safari, desktop
  Chrome, Firefox) on how to re-enable location permission
- "Got it" dismisses and resets the error state so the button returns
  to normal for a fresh attempt
- handleNearMe still fires normally on first tap (not denied state)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 23:05:30 +07:00
shakespeare.diy
05c7b76d6d fix(crew-network): comprehensive UX/accessibility overhaul
QR rendering:
- Fixed layout shift (CLS) — canvas and spinner now occupy the same fixed
  236×236 container; spinner fades out, canvas fades in, no height jump
- Added error state with Retry button when QRCode.toCanvas() throws
- Canvas now has role="img" + descriptive aria-label for screen readers
- "Save QR" button exports canvas → PNG via toDataURL (mobile share flow)

Deep link / "Open in app":
- Replaced document.createElement('a').click() with window.location.href
  (the only reliable way to fire custom schemes on iOS Safari PWA)
- Added 2 s fallback timeout: if the page doesn't lose focus (app not
  installed), automatically redirects to App Store / GitHub releases
- Button shows "Opening…" state while the timeout is pending
- Added hint text explaining the fallback behaviour

Platform detection:
- detectPlatform() reads navigator.userAgent to identify iOS/Android/desktop
- Download list is sorted so the most-relevant entry appears first
- Each download row now has a sublabel (e.g. "iPhone & iPad", "arm64 devices")
- Added Android APK entry (was missing)

Accessibility:
- All interactive buttons have focus-visible:ring-2 keyboard focus rings
- Decorative icons marked aria-hidden="true" throughout
- "How it works" list changed from <div> to <ol>/<li> with aria-label
- Download list uses role="list" / role="listitem"
- Use-cases grid uses <ul>/<li> with aria-label
- "Learn more" link has full descriptive aria-label inc. "(opens in new tab)"
- Action buttons have aria-label for screen readers

Contrast & copy:
- URI preview: text-muted-foreground/60 → text-white/70 (passes WCAG AA)
- URI box is now select-all / cursor-text for easy manual copy
- Hero icon changed from Network → Shield (semantically: encrypted/private)
- Step 2 text adapts when logged out ("Log in to generate your QR")
- Use-case emoji changed 🎬→🔒 for "Private crew comms" (more on-message)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 22:23:20 +07:00
shakespeare.diy
fb6c1bca36 feat: add Crew Network section to Settings with nostr-vpn deep link integration
- New "Crew Network" menu item in Settings > App, positioned below Relays
- Full CrewNetworkScreen sub-screen with:
  - Hero explainer card (nostr-vpn private mesh VPN concept)
  - Live QR code generated via `qrcode` canvas for the join-request URI
    (nvpn://join-request?pubkey=<hex>&name=<displayName>&relay=...)
  - Copy link button + "Open in app" deep link button
  - Step-by-step "How It Works" list
  - Download links for iOS App Store, TestFlight beta, and GitHub releases
  - 2x2 use-case grid (private crew comms, secret spots, censorship bypass, offline mesh)
  - GitHub "Learn more" row
- Logged-out state shows a LoginArea prompt
- TypeScript Section type extended with 'crewnetwork'
- Cleaned up unused Palette/Settings lucide imports

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 22:16:53 +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
3e6fc305ac map: shrink filter pills to h-7 w-7, hide right column + Near Me when filter sheet open
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 21:07:57 +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
5edf4d2a9d map: replace hardcoded Google Maps with universal directions picker (geo:, OSM, Google, Apple)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:46:53 +07:00
shakespeare.diy
bb89a6d3a5 newsletter: expand #002 nostrText and add Lab/Events UX + Android build fix entries
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:43:20 +07:00
shakespeare.diy
4d2b27107a newsletter: fix Edit first button — opens inline textarea, Preview toggle, publish with edited text
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:35:59 +07:00
shakespeare.diy
05463eabb4 newsletter: add issue #002 — map redesign, settings iOS overhaul, wallet, rider card, v1.0.54
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:29:06 +07:00
shakespeare.diy
ecf8e0967b fix: add missing SPOT_TYPE_LABELS import to MapPage (SpotBottomSheet crash)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:24:59 +07:00
shakespeare.diy
54e135d976 fix: add missing MapPin import to MapPage (SpotBottomSheet crash)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:23:20 +07:00
shakespeare.diy
f4cdafa145 card: full UX rebuild — hero preview, 3-tab nav, single-column layout, inline award form
- Logged-out state: proper hero with value prop copy instead of bare login form
- Hero card preview: full-width dark stage with theme-matched glow behind card
- Card slightly larger (360x227) with depth shadow
- Top tab bar: My Card / Badges (with count) / Award — replaces buried sections
- Single-column max-w-2xl layout — no more awkward 2-col grid on mobile
- Primary CTA: one big Share button, Print/Save/Copy as secondary 3-col row
- Card type as prominent 2-up tile choice, not a buried dropdown-style control
- Theme picker: 6-up square grid with emoji + label, ring on active
- Rides For + Sponsors side by side (saves vertical space)
- Stats as interactive tiles showing the actual numbers
- Skills as pill grid with live counter (X/6 selected)
- Badges tab: shows ReceivedBadgesPanel (was previously never rendered)
- Award tab: inline form, no dialog — cleaner flow, redirects to Badges on success
- AwardBadgeDialog kept for backward compat but Award tab uses AwardBadgeInline

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:20:26 +07:00
shakespeare.diy
2e32605948 map: consolidate right-side controls into single column (pills → divider → Quick Snap)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:10:24 +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
cc2e3d3165 map: replace Vibed with Shakespeare with Sasquatch trigger button below Quick Snap
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 20:03:19 +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
682e4d802b map: dark tiles, near me button, bottom sheet, floating filter pills
- Swap OSM tiles for CartoDB Dark Matter (near-black bg, neon markers pop)
- Add floating Near Me button (top-right, crosshair icon, flies to user location)
- Add SpotBottomSheet: tap a marker → drawer slides up from bottom with
  spot thumbnail, title, location, type badges, author, View Details + Directions CTAs
  (map stays fully visible behind the sheet)
- Add flyTo prop to SpotMap for programmatic map navigation
- Add floating layer filter pills (spots/shops/gear/tours) on map surface
  bottom-left, always visible, no toolbar required

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 19:57:06 +07:00
shakespeare.diy
1ce2837fef settings: replace 7-tab horizontal scroll with iOS-style vertical menu + sub-screens
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 19:32:38 +07:00
shakespeare.diy
f0eadf307b wallet: remove duplicate balance hero, single CashuWalletCard as source of truth
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 19:29:52 +07:00
shakespeare.diy
1876d14b0f wallet: mobile-first redesign - balance hero, compact status pills, collapsible educational content
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 19:27:14 +07:00
shakespeare.diy
456f42a262 events: apply same UX pattern as /lab — sticky toolbar, filter drawer, location filter, active chips
- Replace scattered filter controls (search + Select dropdowns + ScrollArea chips + TrustFilter dropdown + SortDropdown) with the same unified toolbar + filter drawer pattern used on /lab
- Sticky toolbar: Events/Tours tab toggle with live counts, Upcoming/Past sub-tabs (events only) with counts, inline search + city location inputs, filter button with active count badge
- Responsive filter drawer: bottom sheet on mobile, right side panel on desktop (md:)
- Filter drawer: Sort pills, Trust Level inline pills (no nested dropdowns), Event Type chips (events) or Tour Category + Skill Level chips (tours) — all in one place
- Active filter chips row: selected event types as dismissible pills, sort/trust badges, "Clear all" — only visible when filters are active
- CreateFAB: replaces plain Link button with a dropdown for both "Create Event" and "Create Tour" from any view
- Unified EmptyState component with context-aware "Clear Filters" vs. "Create" CTA
- Dedicated location filter input split from general search (City field)
- Results count shown below toolbar, no empty-height gaps while loading
- Removed: Input, ScrollArea, ScrollBar, Select, Tabs, TabsList, TabsContent, TabsTrigger, TrustFilter, SortDropdown imports (all replaced inline)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 17:02:46 +07:00
shakespeare.diy
41ede8a122 lab: UX improvements — location filter, responsive drawer, inline trust pills, active filter chips
- Add dedicated City location input field in toolbar alongside search (splits "Name/bio" and "City" search into two focused inputs)
- Fix Filter drawer responsiveness: bottom sheet on mobile, right side panel on desktop (md:) 
- Replace TrustFilter compact dropdown in drawer with inline pill buttons (no nested dropdowns)
- Add active filter chips row in toolbar showing selected skills as dismissible pills, sort/trust badges, and "Clear all" button — only visible when filters are active
- Tab count badges now always show (including zero) to give clear feedback
- Fix results count row: remove empty min-h gap, now only renders when not loading
- Search now also matches against genUserName fallback for pubkey-derived names

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 16:57:49 +07:00
shakespeare.diy
0dc0d272e1 fix: filter panel overlap, Wallet & Stash rename, Sasquatch on all pages
Filter panel (MapPage):
- Sheet now h-[82vh] max-h-[680px] with flex-col layout
- Scrollable content area uses flex-1 min-h-0 (proper flex children)
- "Show Results" button is now flex-shrink-0 at bottom of column
- No more overlap between Show Results and Stash/Tour quick action buttons

Wallet & Stash rename:
- Settings tab renamed from "Wallet" to "Wallet & Stash"
- "Me" dropdown in mobile header shows "Wallet & Stash" for the wallet link

Sasquatch persistent button (all pages):
- Minimized bubble is now ALWAYS visible on every page (not just pages with a guide message)
- Tapping the bubble on any page opens Sasquatch with a generic helpful greeting
- Pages with specific context (/, /map, /lab, /onboarding) still show their custom message
- Pages without context (/wallet, /settings, /stash, /events, etc.) now show a generic "Ask me anything" greeting with common questions
- Removed the separate X close button (minimize was already the same) — bubble stays visible as entry point at all times
- onPointerUp now also sets isVisible=true so panel opens on pages without a message

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 16:45:11 +07:00
shakespeare.diy
340f6fd89f fix(mobile): redesign wallet page UI for mobile + global mobile polish
Wallet page (/wallet):
- Add BalanceHero banner at top showing Cashu balance + wallet status pills
- Cashu/Lightning tabs with colored active states (orange/yellow)
- Remove nested balance display in CashuWalletCard (now shown in hero)
- Compact balance row in CashuWalletCard
- Stacked full-width inputs on mobile (no side-by-side on small screens)
- h-11 touch targets on all inputs and buttons (44px min)
- inputMode="numeric" on number fields (prevents iOS zoom)
- Receive/Send tabs with green/red active colors for clear context
- History items now 52px min height with colored amount text
- WalletComparisonGuide stays collapsed by default
- Fix: add Nut icon import to CashuWalletCard (was crashing)
- All modals use w-[calc(100%-1rem)] for proper mobile sizing

Settings page (/settings):
- 7-tab bar now scrollable horizontally on mobile (no overflow/wrapping)
- -mx-3 bleed trick so scrollable area goes edge-to-edge
- Wallet tab buttons full-width on mobile
- Container max-w-3xl for better desktop centering

FAQ page (/faq):
- Compact hero on mobile (smaller icon, tighter padding)
- py-6 on mobile instead of py-12

CSS utilities:
- Add xs: breakpoint (375px) variants: flex, flex-row, px-4, -mx-4, gap-2, text-xs/sm, hidden

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 16:35:49 +07:00
shakespeare.diy
e8151bee17 revert: remove 3D globe, restore flat map as default
- Removed SkateGlobe component from view toggle
- ViewMode back to 'map' | 'list', defaulting to 'map'
- Removed Globe import, selectedGlobeMarker state, and all globe render blocks
- Flat Leaflet map loads as default again

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-23 16:18:24 +07:00
shakespeare.diy
6331a937f2 feat: map as homepage, 3D globe, camera fix, Sasquatch upgrades, UI/UX polish
## 1. Map as Homepage
- `/` now renders MapPage (the globe/map) — removed old Index homepage
- `/home` redirects to Index (legacy), `/map` alias still works
- Navbar: removed HOME link, SPOTS (MapPin) is now first nav item
- isActive() treats both `/` and `/map` as active for SPOTS tab

## 2. 3D Globe View
- New SkateGlobe component using pure Three.js (no h3-js/CDN issues)
- Earth night texture with atmospheric neon red glow
- Spot pins (red) + shop pins (lime) on globe surface
- Click pin → zoom camera in + show info card
- Drag to rotate, scroll/pinch to zoom, auto-rotation when idle
- Globe toggle button added to map/list view switcher
- Selected marker ring + info card with View Spot/Shop links
- Lazy-loaded with Suspense skeleton

## 3. Settings → Stash Link
- Added My Stash card in Settings > Wallet tab
- Links to /stash with Package icon and description
- Smooth hover animation with ChevronRight

## 4. Camera Fix (Capacitor Native)
- CameraCapture now detects native Capacitor platform at runtime
- On native Android/iOS: uses @capacitor/camera (OS-level camera, no WebView permission popup)
- On browser: falls back to getUserMedia stream (existing behaviour)
- Added Upload from Gallery fallback when camera permission denied
- Better error handling for user cancellation

## 5. Sasquatch Upgrades
- FAQ page: replaced static accordion wall with Sasquatch-powered UI
  - Quick question chips that pre-load questions into Sasquatch
  - "Open The Sasquatch" CTA button
  - Quick facts grid, clean modern design
- About page: added Sasquatch CTA card at bottom
- SasquatchGuide: listens for sasquatch:open and sasquatch:ask custom events
  - External pages can now trigger and pre-fill the Sasquatch
  - Sasquatch now shows even without a page-level message prop (for external triggers)

## 6. UI/UX Audit & Polish
- Focus rings: on-brand red (WCAG accessible)
- Card hover micro-interactions: subtle translateY + shadow
- Button/link transition consistency (150ms ease)
- Reduced motion support (@media prefers-reduced-motion)
- Modal backdrop blur improvement
- Skeleton shimmer animation utility
- Globe CSS utilities (touch-action: none for canvas)
- Typography helpers (text-balance, text-pretty)
- Improved map-full-height responsive calculation

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-22 22:47:39 +07:00
shakespeare.diy
dd650ee57c Show stored pubkey in Settings when profile not loaded (debug)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-09 14:42:47 +07:00
shakespeare.diy
f73cb0ec8c Add SNT4EVER as partner on Supporters page
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-08 16:44:51 +07:00
shakespeare.diy
6ed5ac23cb Invert SNT4EVER logo for contrast on dark background
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-08 16:37:38 +07:00
shakespeare.diy
2904df4eab Update SNT4EVER logo to new version
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-08 16:35:51 +07:00
shakespeare.diy
81dd69679c Add SNT4EVER as friend on Supporters page
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-08 16:12:28 +07:00
shakespeare.diy
2c3697ba93 SupportersPage: use red transparent No Teeth logo
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 22:08:25 +07:00
shakespeare.diy
e03150c19f SupportersPage: remove Geyser from Friends, update No Teeth entry with white logo, Instagram link, and correct tagline
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 22:06:27 +07:00
shakespeare.diy
10b9d646fc SupportersPage: add No Teeth Skateboarding to Friends section with red logo
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 22:04:11 +07:00
shakespeare.diy
4aaa7007bf AboutPage: remove 'Learn About Nostr' card and nostr.com link
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 21:30:26 +07:00
shakespeare.diy
4ffa39ee95 Various UI cleanups: remove Nostr footer link, hide Map&Earn, update NostrHub links
- Index.tsx: remove Nostr.com button from homepage footer (next to FAQ/About)
- SupportersPage: change 'View on GitHub' → 'View on NostrHub' with correct naddr URL
- Layout: remove 'Map & Earn' (/bounties) from desktop More dropdown menu
- Layout: remove 'Map & Earn' (/bounties) from mobile slide-out nav
- Layout: clean up unused Gift icon import and stale isActive check

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 21:27:10 +07:00
shakespeare.diy
8633b38125 fix: friends CTA - only if you have the vibes
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 19:15:00 +07:00
shakespeare.diy
5c49946e5b fix: Geyser powered by emoji ⚡ -> 🧦
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 19:11:02 +07:00
shakespeare.diy
affe663291 fix: friends subtitle -> People we vibe with
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 18:26:52 +07:00
shakespeare.diy
80be48275e fix: update No Teeth tagline to Handmade Long Lasting Skateboards
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 18:02:26 +07:00
shakespeare.diy
089c6c234c feat: add No Teeth Skateboarding as first Friend, clear Partners
- Partners: removed all entries, now shows 4 placeholder slots
- Friends: added No Teeth Skateboarding with red transparent logo
  linking to instagram.com/noteethskateboarding
- Friends grid: always shows real entries + fills remaining slots to 5
  with "Coming soon" placeholders

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 17:57:41 +07:00
shakespeare.diy
baf4c5e10d feat: redesign /supporters with 4 sections + footer label update
- Supporters: existing Geyser leaderboard (unchanged)
- Collaborators: new section, empty placeholder with GitHub CTA
- Partners: Geyser, Angor, Zapstore, ContextVM, MKStack
- Friends: placeholder grid (5 slots) + logo spec instructions
- Footer: "Supporters" → "Collaborators & Friends"
- Static COLLABORATORS/PARTNERS/FRIENDS arrays at top of file for easy editing

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-27 17:51:11 +07:00
shakespeare.diy
5dffa5cac3 feat: UX improvements - credits, draggable sasquatch, mobile fixes
1. AboutPage: add Credits section (Zapstore, ContextVM, Angor, Geyser, MKStack,
   Shakespeare, Nostr), add Zapstore download button, fix powered-by text overflow
   on mobile (wrap-friendly flex layout)

2. SasquatchGuide: make minimized bubble draggable on all devices
   - pointer events for mouse, touch, and stylus
   - saves position to localStorage, persists across sessions
   - tap still opens, drag moves (5px threshold to distinguish)

3. Layout (burger menu): add overflow-y-auto + pb-8 to SheetContent so
   mobile menu is scrollable when content exceeds screen height

4. CashuWalletCard (wallet): fix input+button rows overflowing on mobile
   - flex-col on small screens, flex-row on xs+ (stacked on phones)
   - whitespace-nowrap on buttons to prevent text wrapping

5. LabPage: compact action buttons on mobile (h-8, text-[10px], px-2)
   so all 4 buttons fit in 2x2 grid without overflow/truncation

6. LoginDialog (Amber): improve visibility-change handler - use fresh
   SimplePool subscription on each foreground return instead of querySync
   (which doesn't exist in nostr-tools v2.13)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-23 09:33:46 +07:00
shakespeare.diy
a677e8687d fix: Amber Capacitor APK + mobile UI improvements
Amber login (critical):
- Replace window.location.href with window.open(_system) in Capacitor WebViews
- Capacitor's WebView throws "malformed nostrsigner request" on href navigation
- New openAmberUrl() helper detects Capacitor via window.Capacitor.isNativePlatform()
- Add openAmberForLoginNoCallback() for PWA/Capacitor clipboard flow
- Fix all 6 Amber URL opens in amber.ts + LoginDialog.tsx

Mobile nav:
- Newsletter and Sasquatch AI (/pyramid) added to hamburger menu
- Sasquatch floating guide now shows on mobile (removed hidden md:block)

Lab page mobile:
- Action buttons (Offer Coaching/Filming/Editing/Get Card) now 2-col grid on mobile
- Service type filter (Coaches/Filmers/Editors) now 3-col grid, full width
- Reduced top padding on mobile (py-6 → py-12 on md+)
- Text truncates cleanly, icons don't overflow

Wallet page mobile:
- Reduced padding (py-6 md:py-8)
- Tab triggers use h-auto + py-2.5 to avoid overflow
- Smaller header text on mobile

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 20:48:11 +07:00
shakespeare.diy
576df73a6b fix: restore original SkateSpots logo (icon-1024.png) everywhere
Replace all icon.svg references with icon-1024.png across:
- index.html (favicon, apple-touch-icon, og:image, twitter:image)
- src/components/Layout.tsx (desktop header + mobile header)
- src/pages/Index.tsx (footer)
- public/manifest.webmanifest (PWA icon entry)

Also fix og:url and og:image/twitter:image to use correct domain
skate-spots.org instead of skate-spots.com

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 20:02:27 +07:00
shakespeare.diy
9ed2c12de1 fix: remove sponsor mention + add Nostr preview modal before publishing
- "Sponsors see the organic number" -> "Users can now see both numbers"
- PublishToNostrButton now opens a preview modal first (NostrPreviewModal):
  - Mock Nostr post card: avatar, display name, timestamp, post body
  - nostrText rendered with hashtags in blue and URL underlined
  - Fake engagement bar (replies/reposts/zaps at 0) for visual context
  - Character count shown below the card
  - Two actions: "Edit first" (closes modal) / "Publish" (confirms)
  - Full-width on mobile, correct safe-area handling via Dialog component
  - isPending state flows through so button shows "Publishing..." while signing

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 15:12:37 +07:00
shakespeare.diy
e4a9b454a4 fix: rename issue -> drop, fix hashtags, add 6 tags
- "Issue" replaced with "Drop" everywhere in UI and copy:
  Drop #001, Weekly Drops, Write Drop #002, next drop, etc.
- nostrText: hashtags consolidated to one line (some clients break
  each hashtag onto its own line when separated by newlines)
- nostrText: removed "Full issue:" prefix — bare URL unfurls cleaner
- 6 hashtags added: #skateboarding #nostr #skatespots #spotr
  #newsletter #skatespotsnews
- Publish button tags updated to include all 6 as t-tags
- Template comment updated to match new format

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 15:09:32 +07:00
shakespeare.diy
b5edb0ab52 fix: nostrText format — short teaser, URL as the star
Kind 1 notes render differently on every client (Damus, Primal, Amethyst,
Snort, etc). The fix: keep the kind 1 as a 5-line teaser that works as
plain text on any client, with the full formatted issue at the URL.

Issue #001 nostrText trimmed from 14 lines to 4.
Added template comment with rules for all future issues:
- Max 5 content lines before the URL
- No bullet lists
- URL always on its own line
- 3 hashtags max

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 15:00:26 +07:00
shakespeare.diy
4f7b741d77 fix: newsletter mobile layout — full phone support
- Page container: py-8 on mobile + pb-24 so content clears fixed bottom nav
- Masthead headline: single line (no br), text-4xl mobile / text-6xl sm+
- Issue header: vertical stack on all screens — date/badge row, large number,
  vibe text, then publish button full-width on mobile / auto on sm+
- Stats grid: always 3 cols (clean 2-row layout on phone), larger text-2xl
  numbers, text-[10px] labels readable on all Androids, shorter relay label
- Floating pill: max-w-[calc(100vw-1.5rem)] prevents overflow on narrow screens,
  right-3 tighter margin, whitespace-nowrap on text, touch-manipulation + 
  active:scale-95 for native tap feel, correct bottom offset for iOS safe area
- Monday admin banner: unchanged, already stacks correctly

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 14:56:30 +07:00
shakespeare.diy
248f5b5ff2 feat: newsletter v2 — structured issues, live stats, Nostr publish button
Structured issue schema (NewsletterIssue):
- updates[]: typed update items with icon/title/tag/body
- spotOfTheWeek?: optional typed spot block
- communityNote: free-text community section
- nostrText: plain-text ready to post as kind 1

Live community stats block (LiveStatsBlock):
- Pulls from useGrowthStats on every page load
- Shows spots, shops, events, mappers, coaches, tours
- Trusted/active account counts shown below spots + mappers
- "Live · pulled from goskatespots.nostr1.com" attribution

Publish to Nostr button (PublishToNostrButton):
- Only visible to 2 admin pubkeys — nobody else sees it
- Publishes kind 1 with nostrText + tags (skateboarding, skatespots, nostr)
- Includes relay URL tag pointing to /newsletter
- One-shot: button becomes "Published to Nostr" checkmark after success
- Full error handling with toast

Issue template now enforces consistent sections every week:
1. Community by the Numbers (live stats)
2. What's New (typed update cards)
3. Spot of the Week (optional — shows placeholder when omitted)
4. Community note
5. Support / Geyser

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 14:45:59 +07:00
shakespeare.diy
c146d94b9f feat: weekly newsletter system with Issue #001
- NewsletterPage (/newsletter) — zine aesthetic, dark bg, neon accents
  Issue #001 covers all 8 security measures shipped this week:
  geohash validation, URL sanitization, content limits, WoT defaults,
  trusted stats, OfficialBadge, marker clustering, DM phishing warning

- NewsletterOverlay — two overlays rendered globally via Layout:
  1. Floating "Latest Drop" pill — shows for 7 days after each issue,
     dismissible per-issue via localStorage, hides on /newsletter page
  2. Monday admin reminder — only visible to 2 authorized pubkeys
     (founder + second admin), shows every Monday, one-click opens
     Shakespeare AI with prompt pre-filled to write the next issue,
     dismisses for the rest of that Monday via localStorage

- Footer — Newsletter link added to desktop footer alongside License/Supporters

- AppRouter — /newsletter route added (lazy loaded)

To add Issue #002: prepend a new object to NEWSLETTER_ISSUES array
in NewsletterPage.tsx. The pill and Monday reminder auto-update.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 14:39:54 +07:00
shakespeare.diy
f19851da92 security: implement all 8 protection measures
## 1. sanitizeUrl() (already existed, verified applied)
- Applied to ShopDetailPage, CoachDetailPage with urlDomain() tooltip hints
- Blocks javascript:, data:, vbscript: and all non-http(s) schemes
- Returns null for invalid URLs — link simply doesn't render

## 2. Geohash validation in parsers
- Added isValidGeohash() in types.ts using ngeohash.decode()
- Min 4 chars (country-level precision rejected), max 12 chars
- Rejects lat/lng outside Earth bounds
- Rejects 0,0 "null island" — classic bot artifact
- VPN/Tor unaffected: geohash comes from map pin, not user IP

## 3. Content length limits in all parsers
- MAX_TITLE_LEN=100, MAX_DESCRIPTION_LEN=3000, MAX_LOCATION_LEN=200
- MIN_TITLE_LEN=2, MIN_GEOHASH_LEN=4
- Applied to parseSkateSpot, parseSkateShop, parseSkateCoach,
  parseSkateFilmer, parseSkateEditor, parseSkateEvent

## 4. Smart WoT default (already in codebase, verified correct)
- Logged out / <5 follows → 'all' (don't punish new/anon users)
- Logged in with ≥5 follows → 'trusted' (sort trusted first)
- Soft mode: trusted content floats to top, nothing hidden
- Active in MapPage and EventsPage

## 5. Trusted count stats in useGrowthStats
- Added trustedSpots and trustedMappers to GrowthStats type
- Heuristic: pubkeys with ≥2 published items = organic community member
- CommunityStatsPage now shows "🛡 X from active accounts" below spot count
- Purely additive — both numbers always visible

## 6. OfficialBadge component (already existed, wired up)
- Added to SpotCard, EventCard, ShopDetailPage, CoachDetailPage
- Only renders for FOUNDER_PUBKEY (20e7c953...)
- Zero-friction: invisible for all other pubkeys
- Impersonation attempts immediately visible

## 7. Leaflet marker clustering
- Installed leaflet.markercluster v1.5.3 + @types
- MarkerClusterGroup replaces direct marker.addTo(map)
- Custom cluster icon: red/orange gradient circle with count
- maxClusterRadius: 60px, spiderfyOnMaxZoom: true
- MarkerCluster CSS loaded from CDN in index.html

## 8. DM phishing warning banner
- PhishingWarningBanner component in DirectMessagesPage
- One-time dismissible (localStorage key: dm-phishing-warning-dismissed)
- Message: SkateSpots will never DM you asking for keys/payments
- Uses useLocalStorage hook, Shield icon, X dismiss button

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 14:03:15 +07:00
shakespeare.diy
0451887a17 feat: extended community stats page with Nostr growth data
New useGrowthStats hook:
- Queries wss://goskatespots.nostr1.com directly (authoritative source)
- Fetches all 7 content types in parallel: spots, shops, events,
  coaches, filmers, editors, tours
- Deduplicates addressable events (keep newest per pubkey+kind+d)
- Buckets events by month with gap-filling for continuous X axis
- Computes cumulative spots line chart data
- Extracts top 10 mappers by spot count
- 15 min stale time — stats don't need to be live

New useGeyserStats hook:
- Queries Geyser GraphQL for supporter count + total sats raised
- Reuses same API already used in SupportersPage

Rewritten CommunityStatsPage:
- Section 1: 8 hero stat cards (spots, shops, events, coaches,
  filmers, editors, tours, mappers) with per-type accent colours
- Section 2: 4 charts (cumulative spots area, monthly stacked bar,
  active mappers bar, content mix donut)
- Section 3: Top 10 mappers leaderboard with avatars + 🥇🥈🥉
- Section 4: Supporters banner with live Geyser count + sats raised
- Footer: data source + verifiability note
- Full skeleton loading for each section independently
- Error state for relay connectivity issues

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 12:54:32 +07:00
shakespeare.diy
bd3e4bee4e perf: lazy loading, faster timeouts, better caching, chunk splitting
- AppRouter: lazy load all 40+ pages (only Index is eager)
  → initial JS bundle no longer includes code for pages never visited

- MapPage: lazy load SpotMap/Leaflet (~150KB) behind Suspense
  → Leaflet only downloads when user navigates to /map

- useSkateSpots/useShops/useMarketplace/useTours:
  → timeout: 30s → 8s (fail fast, show error sooner)
  → staleTime: 30s → 5min (trust cache, fewer relay round-trips)
  → gcTime: 5min → 30min (keep data in memory longer)
  → refetchOnMount: 'always' → true (use cache when data is fresh)

- Remove console.log/warn/debug spam from production hooks

- vite.config: manual chunk splitting for leaflet, nostr-tools, bitcoin
  libs, radix-ui, react-router — each chunk cached independently
  by browser, only re-downloaded when that library updates

- vite.config: esbuild drops console.log/warn/debug in prod build

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 11:32:24 +07:00
shakespeare.diy
3cba958437 New logo: map pin on top of skateboard deck, same gradient colors
- Redesigned icon.svg: skateboard deck shape (rounded nose/tail, truck axles,
  wheels) beneath the existing map pin, all using the same red→orange→lime
  gradient as the original
- Replace MapPin component icons in desktop header, mobile header, and footer
  with the actual icon.svg image so the new logo shows everywhere

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 00:46:05 +07:00
shakespeare.diy
fad3a3323d Replace Impact with Bebas Neue + Barlow Condensed for authentic skate typography
- Install @fontsource/bebas-neue and @fontsource/barlow-condensed
- Add .font-display (Bebas Neue) and .font-condensed (Barlow Condensed) CSS utilities
- Hero headline: split into two lines SKATE / SPOTS with clamp() fluid sizing,
  Bebas Neue replaces Impact throughout
- Subtitle: Barlow Condensed 700 with wide letter-spacing
- Description: Barlow Condensed italic
- All section headings (Featured Spots, Events, The Lab, CTA, New Here?): Bebas Neue
- Section subheadings: Barlow Condensed with tracked uppercase
- Desktop + mobile header logo: Bebas Neue
- Footer logo: Bebas Neue
- Tagline badge: Barlow Condensed 600

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 00:37:32 +07:00
shakespeare.diy
0c87fa5827 Fix Amber login not completing on GrapheneOS new-tab callback
Root cause: on GrapheneOS, Amber opens /amber-callback in a BRAND NEW TAB.
login.amber() was being called in that new tab's React instance, which has
no effect on the original tab where the user is waiting.

Fix:
- AmberCallbackPage now writes pubkey to localStorage('amber_pending_pubkey')
  instead of calling login.amber() — a cross-tab signal
- LoginDialog polls for 'amber_pending_pubkey' every 400ms and calls
  login.amber() itself in the correct React context
- Manual "I approved" button also reads the same key
- Removed reliance on getAmberLogin() which checked the wrong localStorage key

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-17 22:46:39 +07:00
shakespeare.diy
4398c2e73b Fix Amber login stuck on GrapheneOS/Android browsers
Root cause: window.location.href navigates the tab away entirely on Android,
destroying sessionStorage. When Amber calls back to /amber-callback the login
completes there, but returning to the dialog the state is lost.

Fixes:
- Switch from sessionStorage to localStorage for amber_return_path and
  amber_login_attempt_ts — survives tab navigation on Android
- Add 500ms polling loop in handleAmberLogin that checks getAmberLogin()
  every 500ms — detects when AmberCallbackPage stored the pubkey and
  auto-completes login without needing visibilitychange to fire
- Add "I approved — complete login" manual button in waiting state as
  explicit fallback for stuck users (reads localStorage right away)
- AmberCallbackPage now uses localStorage for all persistence
- Clean up poll interval on dialog close and unmount

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-17 22:37:26 +07:00
shakespeare.diy
03d0fe93b0 Improve Amber login flow - zero friction, auto-complete on return
- Add dedicated /amber-callback route so Amber always has a stable return URL
  - Handles get_public_key (login), sign_event, nip44/nip04 encrypt/decrypt
  - Shows animated processing screen while completing, then redirects back
- Add subscribeToAmberReturn() helper in amber.ts using visibilitychange
  - Auto-detects when user returns to the tab after cancelling in Amber
  - Resets login UI to initial state gracefully
- Update buildCallbackUrl() to use /amber-callback instead of current page URL
- Rewrite Amber tab in LoginDialog:
  - Remove 2-step clipboard paste flow entirely
  - Single tap opens Amber, login completes automatically on return
  - Waiting state shows animated dots + cancel button
  - No manual pubkey entry needed
- Save return path in sessionStorage so AmberCallbackPage redirects user back

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-17 22:29:06 +07:00
shakespeare.diy
aac2b7b9a2 Improve mobile/Android UI responsiveness
- Add xs (375px) breakpoint to Tailwind config for small phone targeting
- Fix main content bottom padding to account for safe-area-inset-bottom
- Redesign mobile bottom nav: active dot indicator, proper safe-area padding,
  flex-1 tabs that scale to any width, tighter text on xs screens
- Fix hero section: smaller padding/text on tiny phones (320-375px),
  horizontal badge wrapping, full-width buttons stack below xs
- Add CSS improvements: overscroll-behavior, -webkit-text-size-adjust,
  input font-size fix for iOS zoom prevention, scroll-x helper class,
  touch-friendly min tap target sizes

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-17 22:04:29 +07:00
shakespeare.diy
84aa009b33 fix: CVM server tag mapping + add city/country geocoding to spots
CVM server fixes (cvm-server/server.ts):
- Use 'title' tag instead of 'name' to read spot names (app publishes 'title')
- Search now checks 'title' tag (not just 'name') for keyword matches
- City filter now also checks 'title' tag and event content
- Country filter also checks event content
- Features filter now checks 't' tags (app uses 't' not 'feature')
- Validate spots with 't: skatespot' tag (same rule as the app)
- Return 'location' field and proper features list in results
- Image parsing now supports both 'image' (legacy) and 'imeta' (NIP-92)

CreateSpotPage fix (src/pages/CreateSpotPage.tsx):
- Add reverse geocoding via Nominatim on spot submit
- Emit structured 'city' and 'country' tags so CVM can filter by them
- Auto-fill location field from geocoding if user left it blank

SasquatchGuide improvement (src/components/SasquatchGuide.tsx):
- Add data model notes to system prompt explaining tag structure
- Instruct AI to handle 0-result responses gracefully (encourage adding spots)
- Use limit:20+ for city/country searches

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-17 11:14:50 +07:00
shakespeare.diy
c2b7a29d07 feat: update default relay set to all 5 relays
Added auth.nostr1.com and wotr.relatr.xyz back as default relays.
All 5 relays are now the default for all new users:
- wss://auth.nostr1.com (auth)
- wss://wotr.relatr.xyz (web of trust)
- wss://relay.ditto.pub (general)
- wss://goskatespots.nostr1.com (community - toggleable)
- wss://purplepag.es (profiles - read only)

Updated across: App.tsx, NostrProvider.tsx, NostrSync.tsx, TestApp.tsx
Updated Settings page Default Relays info cards to show all 5 with descriptions.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 22:35:09 +07:00
shakespeare.diy
b815cbfda6 fix: remove dead relay.nostr.band from all relay lists across the app
Removed from: useCVMClient, NostrSync, useLoggedInAccounts, useAuthor, SettingsPage
Replaced with relay.damus.io where a fallback was still needed.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 21:49:02 +07:00
shakespeare.diy
9d1e875c36 fix: remove wss://wotr.relatr.xyz relay that was consistently failing
The wotr.relatr.xyz relay was generating a flood of AbortError warnings
on every query because it was consistently unreachable/timing out.

Removed from:
- src/App.tsx (default config)
- src/components/NostrProvider.tsx (DEFAULT_RELAYS)
- src/components/NostrSync.tsx (NEW_USER_RELAYS for new users)
- src/test/TestApp.tsx (test config)

Replaced the Settings page entry with purplepag.es (profile aggregator)
which is already in the relay list and actually working.

Updated Sasquatch FAQ answer to reflect the correct relay list.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 19:52:02 +07:00
shakespeare.diy
320032ea37 fix: add missing User and Building2 lucide icon imports to RiderCardPage
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 17:07:42 +07:00
shakespeare.diy
ebbf2d6326 fix: remove orphaned canvas code from RiderCardPage causing ctx/Printer is not defined crash
The drawCardToCanvas function had an old version of the card rendering logic
that leaked outside the function body (lines 505-731 were orphaned code).
This code referenced local variables (ctx, p, data, qrX, qrSize, PAD, etc.)
that only existed inside the function, causing ReferenceError at module load time
and crashing the entire app with a blank screen.

Also added missing lucide-react imports (Printer, Loader2, Share2, Download,
Check, Copy, Award, Zap, Info, Palette, Shield, ChevronUp, ChevronDown) that
were used in the JSX but not imported, causing the secondary Printer is not
defined ReferenceError.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 17:02:59 +07:00
shakespeare.diy
125bce3637 Replace QRCodeCanvas DOM preview with static placeholder
The qrcode library internally uses var ctx and canvas.getContext('2d') at
initialization time. In the Shakespeare preview iframe environment, canvas
API is restricted causing "ctx is not defined" console errors on every render.

Fix: replace QRCodeCanvas in the DOM screen preview with a static decorative
QR placeholder (5x5 pixel grid). The real, scannable QR code is still
generated correctly in the exported PNG via QRCode.toDataURL() in the
drawCardToCanvas() async function - this is unaffected by the env restriction
since it only runs when the user clicks Print/Save/Share.

Also remove unused QRCodeCanvas import from RiderCardPage.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 16:21:58 +07:00
shakespeare.diy
8d273428f3 Card redesign: 3-zone layout, shop/sponsor styling, Share button, remove Badges tab
Canvas renderer overhaul:
- New 3-zone layout: left sidebar (avatar + stats + since) | right zone
  (name/identity/shop/sponsors/skills) | top-right QR block
- Left sidebar has a subtle tinted background with accent-colored separator
- Stats shown as balanced column grid under the avatar
- Shop: "RIDES FOR" label + bold italic Georgia serif in theme shopColor (distinct from accent)
- Sponsors: "SPONSORS" label + regular weight in theme sponsorColor (warm gold/orange)
- Each theme now has shopColor + sponsorColor + sidebarBg + chipBorder fields
- Skills row appears only when skills/badges exist, with accent border on chips
- Full npub (44+remainder chars) shown at bottom of right zone in monospace
- Bottom bar: subtle fill + "SKATE-SPOTS.ORG" left, year right
- Park Access card: same sidebar layout, access level chip in solid accent fill

Share button:
- Uses Web Share API (navigator.share) if device supports it — opens native
  share sheet (WhatsApp, iMessage, Telegram, AirDrop, etc.) with PNG file
- Falls back to copying profile URL to clipboard
- Lime green color to visually stand out from Print button

UI cleanup:
- Remove Badges tab entirely — was cluttering the nav
- Award Badge moved inline to secondary button row (yellow, subtle)
- Primary actions: Print | Share | Save PNG (all same size, flex-wrap)
- Secondary actions: Copy npub | Award Badge | Publish to Nostr
- Fixed stray closing divs from tab removal

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 16:07:47 +07:00
shakespeare.diy
79dc1e1dcf Fix QR scanning, full npub display, explicit shop/sponsor labels on card
QR fix (critical):
- loadImage() was routing ALL urls through proxy.shakespeare.diy including
  data: URIs from QRCode.toDataURL() — proxy rejected them, QR never loaded
- Fix: only proxy http/https URLs; data: and blob: URIs load directly
- QR now renders reliably; scanning opens skate-spots.org/<npub> profile

npub display:
- Split full npub across two lines (32 chars each) instead of truncating
- Both lines shown in monospace below the brand header
- npub is now fully readable on the card

Explicit labeled sections:
- "SKATE SHOP" label in muted above shop name in accent color
- "SPONSORS" label in muted above sponsors text
- Sections only appear if data is entered — no empty space when blank
- Stats (Spots/Zaps/Events) shown as 3-column grid with label+value

Layout improvements:
- QR moved to top-right, rendered before other content (ensures it draws)
- Avatar sized 180px with "SINCE YYYY" underneath
- Identity column flows: name → nip05 → style chip → shop → sponsors → stats → skills
- Bottom divider line + year issued in footer
- fillTextFit() helper for clean text truncation with ellipsis

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 15:51:56 +07:00
shakespeare.diy
a7158d4901 Replace html2canvas with Canvas 2D renderer for card export
html2canvas was capturing the full browser viewport instead of just the
340×214px card element — causing the giant oversized/wrong layout in the PNG.

New approach: drawCardToCanvas() draws the card entirely via Canvas 2D API:
- Creates an 856×540px canvas (10× credit-card mm dimensions, print-ready)
- Draws gradient background, top stripe, avatar (with CORS proxy), text,
  skill chips, stats, and QR code using direct canvas drawing commands
- QR code generated via qrcode lib toDataURL(), then drawn as image on canvas
- Avatar loaded via proxy.shakespeare.diy for CORS, with 4s timeout fallback
- Theme colors from THEME_PALETTE map — same 6 themes as the screen card
- No DOM capture, no scaling issues, no CSS stripping — output is pixel-perfect

QR code explanation:
- Encodes https://skate-spots.org/<npub>
- Scanning opens the rider's SkateSpots profile (NIP19Page handles /npub1...)
- White background patch behind QR for reliable scanning on dark card themes

Other changes:
- "Rider License" → "Skater License" everywhere
- Remove html2canvas dependency usage (kept package but unused for now)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 15:40:28 +07:00
shakespeare.diy
68922280cb Fix card layout, add real QR code, npub as identity
Problems fixed:
- Card was rendering too tall: replaced flex-1/justify-between layout with
  strict pixel-height rows (row1: 26px header, row2: 118px body, fixed bottom bar)
  — content no longer floats or overflows
- No QR code: added QRCodeCanvas (existing component) in bottom-right of card
  body, encoding https://skate-spots.org/<npub> — scanning takes you to the
  rider's profile for verification
- License ID removed: replaced SS-XXXXXXX with shortNpub (npub1xxx...yyyy)
  in top-right. The npub IS the identity — no made-up number needed
- Added crossOrigin="anonymous" on avatar img for html2canvas CORS handling
- Updated html2canvas onclone to lock card to 340×214px during capture
- Download filename uses npub prefix instead of licenseId
- verifyUrl() helper generates skate-spots.org/<npub> for QR content

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 15:33:02 +07:00
shakespeare.diy
046a109eca Fix card printing: html2canvas renders card to PNG before printing
The root cause of white/blank print: browsers strip CSS background gradients
and colors by default even with print-color-adjust: exact, especially inside
nested components wrapped by Layout.

Solution: use html2canvas to render the live card DOM to a canvas at 4x scale,
convert to PNG, then open a dedicated print window containing ONLY that image.
Images always print in full color — no CSS override needed.

Changes:
- Add html2canvas dependency
- renderCardToImage(): captures card DOM element at 4x scale with useCORS for
  cross-origin avatars, temporarily strips holo animation for clean capture
- handlePrint(): calls renderCardToImage, opens popup window with CSS @page
  set to 85.6×54mm, injects the PNG, auto-triggers print dialog
- handleDownload(): same render pipeline, saves as PNG file (for sharing
  digitally, WhatsApp, iMessage, or taking to a print shop)
- Added Download / Save PNG button alongside Print Card button
- Added Loader2 spinner on buttons while rendering
- Removed print-only-card div and broken @media print CSS (no longer needed)
- Clean up CSS print block to minimal fallback only
- Fix "on-chain proof" → "Signed proof" in badge dialog

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 15:22:13 +07:00
shakespeare.diy
3b99392f3b Fix card print colors + clarify Nostr verification language
Print fixes:
- Add -webkit-print-color-adjust: exact + print-color-adjust: exact to ALL
  card theme classes so dark backgrounds/gradients actually print (browsers
  strip them by default unless forced)
- Increase chip/muted opacity values slightly for print legibility
- Fix multiple-card issue: hide body > * on print, show only .print-only-card
- Remove complex repeating-linear-gradient from wood theme (simplify for print)

Language fixes:
- Remove "on-chain" terminology (Nostr is not a blockchain)
- Replace with plain explanation: "signed message on the Nostr network"
- Park access note now explains exactly what gets verified and how
- Park card info box now lists exactly what shows on a scan:
  who issued it, who it was issued to, access level, expiry, timestamp
- Clarifies why a rider can't forge the card (would need your private key)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 15:07:38 +07:00
shakespeare.diy
e2582aa013 Rider Card: badge system, remove shop card, fix print, better license ID
- Remove "Shop Rider Card" type — shop affiliation is now a "Rides For" field
  on the Rider License, separate from sponsors (brands/companies)
- Fix print: use visibility:hidden approach with .print-only-card overlay,
  properly isolated from Layout wrapper (fixes blank print output)
- Replace "No." top-right with "LICENSE ID" showing SS-XXXXXXX derived from
  pubkey + truncated npub underneath — meaningful, not random-looking
- Add Skill Badge system (kind 1293): witnessed badges are signed Nostr events
  - "Award Badge" dialog: paste rider npub, pick skills, optional spot name
  - Witnessed badges shown in gold ⭐ on card, distinct from self-claimed
  - Badges tab shows full history with witness avatar, name, and location
- Park Access Card: only 2 types now (Rider License / Park Access),
  added blue info note explaining owner-only issuing constraint
- Rename "Member npub" to "Rider's Nostr Address (npub)" with explanation
- Update NIP.md: document kind 1293 skill badges, remove kind 37097

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 14:55:10 +07:00
shakespeare.diy
838ef3f355 feat: SkateSpots Rider Cards — 3 card types, 6 themes, holographic shimmer, print-ready
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 14:29:54 +07:00
shakespeare.diy
f9327c952d fix: update license page tagline
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 13:27:00 +07:00
shakespeare.diy
9fc39651ca fix: remove MIT note from /license, fix Buy/Sell Gear button on /map, expand stash storage locations
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 11:21:45 +07:00
shakespeare.diy
805edd86ca fix: remove Lightning address card from /supporters, keep only Geyser
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 11:15:41 +07:00
shakespeare.diy
223c311920 fix: add editor icon + colored badges to Lab profile cards
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-16 11:11:16 +07:00
shakespeare.diy
4e227f0367 feat: License page, Video Editors in Lab, Buy/Sell Gear, Stash locations, Supporters page
1. /license page
   - New LicensePage with "YOU ARE FREE TO SKATEBOARD" license text
   - Copyright 2025-∞ with neon THUG2 styling
   - Footer: "License" and "Supporters" links added next to OpenSats

2. /lab — Video Editor category
   - New SKATE_EDITOR_KIND (35003) in types.ts
   - EDITOR_SERVICE_LABELS/DESCRIPTIONS (8 edit types)
   - EDITOR_SKILL_LABELS/DESCRIPTIONS (8 software/hardware skills)
   - SkateEditor interface + parseSkateEditor() parser
   - useLabProfiles updated to query kind 35003 + 'skateeditor' tag
   - LabPage: new "Video Editors" tab (purple, Scissors icon)
   - LabPage: "Offer Editing" button in header
   - BecomeEditorPage: full form with edit types, skills, hardware,
     software, portfolio, contact, location, wallet requirement
   - Route /become-editor registered in AppRouter

3. /map Quick Actions
   - "Sell Gear" → "Buy/Sell Gear" with ArrowRightLeft icon
   - Button now links to /marketplace (browse + sell)

4. /stash — Storage locations
   - StashItem.storageLocation field: home/warehouse/friends/parents/wardrobe/other
   - AddItemDialog: 6-button grid to tag where gear is stored
   - StashItemCard: shows location badge on each card
   - Filter bar: pill filter chips to filter stash by location
   - storageLocation saved/restored via onAdd handler

5. /supporters page
   - btcmap.org-style supporters page
   - Live Geyser GraphQL leaderboard (skatespots project)
   - Avatar grid with sat amounts, anon fallbacks
   - Mission section + "Send Sats" section (Lightning address + Geyser)
   - Added to footer and desktop "More" dropdown and mobile menu

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

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

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

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 22:18:28 +07:00
shakespeare.diy
20422fbb84 Add Hello World page at /hello route
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 19:49:38 +07:00
shakespeare.diy
06fae294b8 New project created with Shakespeare
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 19:49:07 +07:00