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>
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>
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>
#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>
- 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>
"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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
- 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>
- 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>
- 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>
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>
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>
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>
- 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>
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>
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>
- 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>
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>
- 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>