Commit graph

625 commits

Author SHA1 Message Date
SkateSpots
b1540f0608 docs: group messaging — what ships (NIP-17) and the upgrade paths (Marmot/Concord/Cordn) 2026-09-26 19:56:58 +07:00
SkateSpots
adc3a20094 feat(dm): create group DMs from the New Message picker
The people picker is now multi-select: tap to toggle (checkmark), then
'Start group (n)' opens the encrypted group thread, or 'Message' for one person.
Selecting one person still behaves exactly as before.
2026-09-26 19:55:03 +07:00
SkateSpots
2a47e5c9c5 feat(dm): group DMs (NIP-17) — inbox list + group thread
Uses the existing gift-wrap primitive (kind 14 rumor, one p-tag per recipient):
- useGroupConversations: lists multi-recipient threads, bucketed by the sorted
  participant key; the inbox shows a Groups section
- GroupConversationPage (/messages/group/:participants): encrypted group thread
  with sender names, send + error feedback
- smoke test now boots the new route

Group creation UI (multi-select in NewMessageDialog) is the next step. For
stronger guarantees (forward secrecy, rosters) Marmot/MLS or Cordn would be the
protocol to adopt later; noted in the hook.
2026-09-26 19:52:33 +07:00
SkateSpots
ca199fac6e chore(deps): Capacitor 8.4.2 -> 8.5.2 (safe minors)
npm update within existing ranges. @noble/* and @nostrify/types were held back
by peer ranges and stay put. Gate + boot test green; npx cap now 8.5.2 (a future
android:build will sync the native project to match).
2026-09-26 19:46:59 +07:00
SkateSpots
bcd99994a9 test: cover zap helpers and newsletter data
- useZapTotals: zapTargetKey (regular id vs addressable coord) and
  zapReceiptSats (amount tag -> bolt11 -> description JSON -> 0, malformed safe)
- newsletterIssues: schema, newest-first ordering, publish hashtags
2026-09-26 19:45:48 +07:00
SkateSpots
81eeba7797 perf(bundle): lazy on-demand modals in Layout; skip the Cashu chunk without a wallet
- BackupReminder, CustomizeNavSheet, SetStatusDialog and LoginDialog are only
  needed after the user acts -> React.lazy (Layout chunk 511 KB -> 486 KB)
- The Cashu balance widget now only mounts when a wallet likely exists (web:
  localStorage hint; native: always, since the seed is in the KeyStore), so
  wallet-less visitors never download the 245 KB Cashu chunk
2026-09-26 19:41:35 +07:00
SkateSpots
8cf35cf7db perf(bundle): entry chunk 1835 KB -> 724 KB
Data-driven pass (adds rollup-plugin-visualizer behind ANALYZE=1):
- MapPage was statically imported by the router, dragging the map route AND the
  IndexedDB offline-cache layer into the entry -> now lazy()
- NewsletterOverlay imported NEWSLETTER_ISSUES from the whole NewsletterPage
  module -> types + data moved to lib/newsletterIssues.ts
- SasquatchGuide (assistant overlay) lazy-loaded in Layout
- Cashu stack is now a separate chunk (useCashu), off the critical path

Net: entry down ~61%; first paint no longer parses the map, the newsletter
page, the assistant or the wallet stack.
2026-09-26 19:37:11 +07:00
SkateSpots
e6f1fe2673 perf(bundle): lazy-load the Cashu balance; load @getalby/sdk on demand
- Layout (app shell) imported useCashu, pulling the Cashu stack into the entry
  chunk every visitor downloads. The three balance widgets now live in
  components/wallet/CashuBalance.tsx and load via React.lazy.
- useNWC statically imported @getalby/sdk; it is now a type-only import plus a
  dynamic import at call sites, so the SDK only loads when NWC is used.
Entry chunk: 1879 KB -> 1835 KB.
2026-09-26 19:31:01 +07:00
SkateSpots
fb5ebb5cd4 chore(dm): remove the unused useGroupConversations hook
It was exported but referenced nowhere (only in a comment); the 1:1 inbox never
surfaced group chats. Comment updated accordingly.
2026-09-26 19:25:40 +07:00
SkateSpots
2edf7a797e feat(ui): adopt the shared PageHeader on Wallet, CreateListing, GearSwap, Messages
PageHeader now carries the app's neon glow (accent-tinted text-shadow), so the
unified header keeps the brand look rather than flattening it. Four more plain
headers migrate (title + accent + optional subtitle/actions).
2026-09-26 19:23:01 +07:00
SkateSpots
4d2c29edb0 feat(ui): PageHeader/PageContainer foundation (piloted on Contacts); profile info-first on mobile
- New shared PageHeader + PageContainer (width tokens: narrow/list/detail/wide)
  to end the max-w drift between sections; piloted on ContactsPage
- Profile: the identity/contact sidebar now comes first on mobile (order-first /
  lg:order-none) instead of being buried under every content section
2026-09-26 19:17:55 +07:00
SkateSpots
10ab45383d chore(ridercard): delete the unused AwardBadgeDialog
showAwardDialog was never set true, so the dialog could never open; the inline
AwardBadgeInline form is the live flow. Removes ~142 lines plus its now-unused
Dialog imports, state and helper.
2026-09-26 19:14:21 +07:00
SkateSpots
bf7fdbd621 fix(ux): calendar 'Past' opens on the newest month with events
The calendar always initialised to the current month, so the Past tab (all
events in the past) opened on an empty month. It now anchors to the newest
past event, and re-anchors when the tab changes.
2026-09-26 19:09:21 +07:00
SkateSpots
0aee937880 fix(a11y): remove duplicate profile status dot; raise low-contrast text
- Profile showed the live status indicator twice (avatar + name row) -> once
- Stash/Wallet informational text bumped from gray-600/700 to gray-400/500 for
  readable contrast on the near-black background
2026-09-26 19:05:35 +07:00
SkateSpots
c0ee70b8bb fix(ux): map empty state; marketplace-aware CreateListing return
- Map view now shows a 'No spots match / Clear filters' overlay when the active
  filters hide everything (it previously looked blank/unloaded)
- 'List Gear' passes returnTo=/marketplace, so the back link and the post-publish
  redirect return to Marketplace instead of dumping users on the map
2026-09-26 19:03:29 +07:00
SkateSpots
18d2af6366 fix(ux): remove dead marketplace payment dialog; show live sats estimate
The '$40k/BTC' era confirm-payment dialog was unreachable after the Pay button
was removed — deleted it, its handler and state (and the now-unused useCashu
import). In its place, listings show '≈ N sats' under the price using the live
rate, so buyers still see the sat amount.
2026-09-26 18:59:47 +07:00
SkateSpots
760bc9b82c fix(ux): Batch F part 3 — bounty ranks, Lab tab grid, contacts search
- Bounty leaderboards no longer slice to top 50 before computing the current
  user's rank (anyone outside the top 50 never saw their position), and each row
  no longer prints the count twice
- Lab craft tabs: 4-up grid (11 tabs were laid out 5-across, leaving a lone
  'Judges' orphan row); stale comment corrected
- Contacts search placeholder now says it filters by npub (it never matched the
  displayed names)
2026-09-26 18:54:55 +07:00
SkateSpots
a464913d6f fix(ux): Batch F part 2 — shop gallery lightbox; real FAQ content
- ShopDetailPage gallery reuses ImageLightbox: photos are now zoomable and the
  whole set is reachable (it showed at most 5 and clicks did nothing)
- FAQPage gains static quick answers so the page is useful even if The
  Sasquatch guide fails to open (it previously had zero intrinsic content)
2026-09-26 18:52:05 +07:00
SkateSpots
617c6cee82 fix(ux): Batch F part 1 — false 'no spots' while loading; calendar filter leak
- SpotPicker showed 'No spots found' during the initial load (also affected
  CreateListing); now shows a loading state first
- Events: switching to the calendar view clears the date-window filter, which
  only has chips in list view — it was silently hiding calendar events
2026-09-26 18:49:14 +07:00
SkateSpots
09e1612db7 fix(a11y): Batch E part 1 — labels, valid nesting, touch targets
- EmptyState: render <Link> as the button (a <button> inside <Link> was invalid
  and double-focusable); AboutPage <a href> -> <Link> (no full reload)
- aria-label on icon-only controls: settings Back, conversation Refresh/Send,
  crew chat Send
- Crew invite Accept/Decline raised to 44px touch targets
2026-09-26 18:46:51 +07:00
SkateSpots
0468ed1c1b fix(ux): Batch D part 2 — Marketplace in the nav; Clink fallback copy
- Add Marketplace to the More menu (it had no primary-nav entry)
- CLINK: explain the copy/QR fallback when no CLINK-compatible wallet handles
  the deep link, so 'Open in CLINK wallet' is not a silent no-op
2026-09-26 18:05:15 +07:00
SkateSpots
102d6017bc fix(marketplace): live BTC/USD sats estimate; drop dead Pay button
- Sats prices used a hardcoded $1 = 2500 sats (i.e. $40k/BTC), so buyers were
  quoted wrong amounts; now uses the live useUsdToSats rate, handles sats/BTC
  directly, and shows no estimate for unsupported fiat
- Removed the 'Pay with Cashu' button, which only ever showed a 'coming soon'
  toast — a dead affordance. Buyers use Contact Seller.
2026-09-26 18:02:24 +07:00
SkateSpots
dca0c0ede1 fix(ux): Batch C part 1 — Near Me timing & reachability
- geolocation: cap the overall deadline at 15s (was 25s) and shorten the native
  plugin attempt to 8s, so a failed fix no longer hangs ~25-40s
- MapPage: only warm the location cache when permission is ALREADY granted, so
  opening /map no longer fires the browser's 'Allow location?' prompt
- MapPage: add a desktop Near Me control (it was mobile-only)
- MapPage: on native, a denial is now retryable instead of permanently opening
  the browser-only help sheet
2026-09-26 17:58:38 +07:00
SkateSpots
8e35859188 fix(ux): Batch B — errors no longer masquerade as empty/404
Adds a shared ErrorState/PageError with a retry action. Detail pages (spot,
shop, collection, coach, crew, event) now show an honest failure + Try again
instead of '404 Not Found' — a relay hiccup previously implied the content was
deleted. The zap/bounty leaderboards surface errors instead of 'no content yet',
and the DM inbox gains a retry button.
2026-09-26 17:51:30 +07:00
SkateSpots
cbd12f3e52 fix(ux): Batch A part 2 — stash, crew invites, map marker
- Stash: remove the handler-less 'Share via DM' button; action overlay stays
  visible on touch (was hover-only, so edit/delete were unreachable on mobile)
- Crew invite accept no longer claims membership is confirmed (only the founder
  ratifies the roster) — both the toast and the accepted state now say so
- Crew invite share message no longer promises a one-click join
- Map marker: don't bind the Leaflet popup when a host supplies onSpotClick
  (MapPage's bottom sheet) — both used to appear at once
2026-09-26 17:47:38 +07:00
SkateSpots
f4192e2f3d fix(ux): Batch A part 1 — dead flows
- SpotDetailPage: 'Claim This Shop' was a no-op button; wire to NIP-05 settings
  (and stop the full-page <a href> reload)
- TourDetailPage: 'Contact Guide to Book' used /messages?to= which nothing read,
  so booking silently opened an empty inbox -> /messages/:npub
- ContactsPage: row 'Message' went to the generic inbox -> the contact's thread
- SettingsPage: ?section=follows|language|stash rendered a blank dead end;
  unknown sections now fall back to the menu
- DirectMessages/Conversation: logged-out states were dead ends -> add LoginArea
- Conversation: failed sends were silent -> error toast
2026-09-26 17:43:19 +07:00
SkateSpots
ff505ef885 docs: UI/UX audit + fix backlog (60 findings across all areas) 2026-09-26 17:40:25 +07:00
SkateSpots
d24e963fd9 fix(boot): auto-recover from stale chunks after a deploy
Deploys replace hashed chunk filenames; a tab that was already open can load a
chunk that no longer exists (404) and hit the error boundary ('THIS PAGE
SLAMMED'). Listen for Vite's vite:preloadError (and dynamic-import failures) and
reload once, guarded against loops.
2026-09-26 17:33:20 +07:00
SkateSpots
b6c33c8a9b feat(events): crowdfunding funding goals via zaps
CreateEventPage gains an optional 'Funding Goal (sats)' field that publishes a
'goal' tag. EventDetailPage shows a FundingGoalCard with live raised/target
progress, backer count and a Fund button — funded peer-to-peer via NIP-57 zaps
(no escrow).
2026-09-26 17:24:42 +07:00
SkateSpots
38c99c1777 feat(profile): zap badges (achievements) card
Derives First Zap / Regular / Generous / Patron / Top 10 from the batched
30-day supporter leaderboard and shows earned vs locked badges in the profile
sidebar. Client-side recognition only — publishes no events.
2026-09-26 17:22:06 +07:00
SkateSpots
046627b1dd feat(home): 'Top Zapped' section linking to the leaderboard
Shows the four most-zapped spots (last 30 days) on the home page, with their
sats totals, linking through to /leaderboards. Renders nothing when there is
no zap activity or no linkable spot, so the section never appears empty.
2026-09-26 17:19:55 +07:00
SkateSpots
6ae4e674dc feat(leaderboards): zap leaderboards page (top spots + top supporters)
New /leaderboards route (linked from the More menu): aggregates kind-9735
receipts into top-zapped spots and top supporters, with week/month/all-time
tabs. Reuses the batched zap helpers. Also hardens the deploy smoke test to
boot lazy routes (/, /leaderboards, /bounties), since route chunks can fail
independently.
2026-09-26 17:17:21 +07:00
SkateSpots
83a3e8ae03 feat(map): make 'Most Zapped' sort actually work + batched zap totals
SortDropdown offered 'Most Zapped' but MapPage's sort switch had no zaps case,
so picking it silently fell back to newest-first. Adds a batched useZapTotals
hook (ONE receipt query for every location) and implements the sort.
2026-09-26 17:12:14 +07:00
SkateSpots
6dc619ee91 feat(zaps): show zap totals to everyone, not just logged-in users
ZapButton already fetched zap receipts for every visitor but returned null
unless you were logged in, so visitors saw no social proof. Now it renders a
read-only '⚡ <total>' whenever there are zaps (with a zap-count tooltip) and
only requires login to OPEN the zap dialog. No extra network cost — the query
already ran unconditionally.
2026-09-26 17:06:25 +07:00
SkateSpots
8d8a46a73f feat(spot): full-screen photo lightbox for spot galleries
Gallery images were inert (click did nothing) and only the first five were
reachable. Adds ImageLightbox: click any photo to view full size, arrow-key /
button navigation across the whole gallery, position counter, caption and
community-photo attribution. Extra images show a +N overlay.
2026-09-26 17:03:30 +07:00
SkateSpots
acdde497b4 feat(deploy): boot-test the built bundle before deploying; document the vite chunking gotcha 2026-09-26 16:57:17 +07:00
SkateSpots
39b4156760 fix(build): drop custom manualChunks — cross-chunk TDZ crashed production boot
The vendor-* split created circular imports; the production bundle died with
'ReferenceError: Cannot access X before initialization' (vendor-charts, then
vendor-bitcoin). Dev mode and vite build do not execute the bundle, so it only
surfaced once deployed. Rollup's default splitting is cycle-safe.
2026-09-26 16:52:22 +07:00
SkateSpots
43d4773ce0 feat(deploy): add Netlify auto-deploy watcher (systemd timer) + ignore .netlify 2026-09-26 16:41:42 +07:00
SkateSpots
f56980ffcf docs(AGENTS): add project-specific dev, build, signing and release workflows 2026-09-26 16:21:49 +07:00
SkateSpots
299407a081 chore: sync package-lock version to 1.4.5 2026-09-26 16:07:50 +07:00
SkateSpots
645993cd64 chore: bump to 1.4.5 2026-09-26 16:07:01 +07:00
SkateSpots
413f9f600c fix(android): support absolute storeFile path in signing-config injector 2026-09-26 16:02:17 +07:00
SkateSpots
eb23ebc898 fix(deps): repair package-lock metadata so npm ci installs a complete tree
The lock from f25fc2b had package entries stripped of their dependencies/bin
metadata, leaving @nostrify/nostrify and @capacitor/cli (13/17 deps) with
missing transitive packages. Rebuilt the metadata from the installed manifests
and let npm fill only the gaps: 654 -> 902 packages, 3 existing versions changed
(debug, minipass, ws).
2026-09-26 16:02:17 +07:00
SkateSpots
fc0231bea5 chore(android): move release signing secrets to gitignored keystore.properties 2026-09-26 15:56:02 +07:00
SkateSpots
0ac57d4250 chore: ignore generated android/ project; add v1.4.4 release notes 2026-09-26 15:52:14 +07:00
SkateSpots
b8bf8c8534 fix(mobile): base grid column on remaining card grids + skeleton min-w-0 consistency 2026-09-26 15:24:09 +07:00
SkateSpots
2e4930af2c fix(cvm-server): add WebSocket polyfill from ws for non-Bun runtimes 2026-09-26 15:24:09 +07:00
Igor Bubelov
571257e5c3 fix(mobile): stop detail-page grids from overflowing horizontally
The spot page (and the other detail pages) used `grid lg:grid-cols-3` with
no explicit mobile column. Below the `lg` breakpoint the grid falls back to
a single implicit `auto` track, whose minimum is the min-content of its
items. A long mapper name ("Tip the Mapper" card) gave the sidebar a
~559px min-content, so the whole grid widened past the viewport and was
clipped by the global `html, body { overflow-x: hidden }` — content cut off
on the right with no way to scroll to it.

Fixes:
- Add `grid-cols-1` to detail-page grids so the mobile track is
  `minmax(0, 1fr)` and can never exceed the container. Applied to Spot,
  Shop, Event, NIP-19, Crew and Coach detail pages.
- Add `min-w-0` to their sidebar/main columns so long content can shrink
  and truncate instead of forcing the track wider.
- Make the Spot Chat header wrap (`flex-wrap` + `gap-y`) so the trust
  filter and channel badge no longer get clipped on narrow phones.
2026-09-26 15:13:59 +07:00
SkateSpots
07e844e0fb fix: declare @nostrify/types so nostrify type exports resolve (tsc gate) 2026-09-26 15:10:11 +07:00
SkateSpots
c7336de2d6 chore: ignore Android signing material and local build artifacts 2026-09-26 15:08:52 +07:00