Commit graph

46 commits

Author SHA1 Message Date
SkateSpots
6def78fffe chore(lint): clear all no-unused-vars warnings (position-precise)
Removed/renamed the remaining 39 unused locals/dead code, file-by-file:
- unused destructured bindings/params (remove the binding, keep the call)
- unused single-line pure consts and dead functions/interfaces removed
- deleted the dead _FeedItem component and its now-unused imports
- amber _readChunks self-call fixed; SpotMap popup call site updated

Warnings: 182 -> 7 (only react-refresh/only-export-components remain).
no-explicit-any=0, exhaustive-deps=0, no-unused-vars=0.
Gate: 101 tests, 0 errors, boot smoke green. AGENTS.md baseline + guidance updated.
2026-09-26 22:20:21 +07:00
SkateSpots
d1f8870379 chore(lint): remove all explicit any + sweep dead imports
- Fix all 12 no-explicit-any (NPool, NostrEvent[], LabProfile, structural
  Cluster type, explicit SkateSpot cast) -> 0 remaining
- Remove unused imports/vars across 48 files (manual + a verified codemod for
  unused import specifiers only); tsc + tests + boot smoke all green
- Warning backlog: 182 -> 76 (remaining: ~55 no-unused-vars locals,
  14 react-hooks/exhaustive-deps, 7 react-refresh)
- AGENTS.md baseline updated
2026-09-26 21:40:42 +07:00
shakespeare.diy
3cc3b623cc Redesign signup "done" step to lead with recovery (1.3.15)
Reworked the final signup step so saving your login key is the hero,
not an afterthought:
- Celebration shrunk to a compact bar ("You're in! One quick thing…")
  so it no longer steals focus from the safety step.
- "Save your login key" card is now the centerpiece with a loud amber
  primary CTA (Reveal & save), a skater-native framing ("keys to your
  board", "only way back in — no email, no password reset").
- Download button flips to a green "Saved ✓" state for clear feedback.
- "Skate now" is intentionally quieter and honest: reads "Skip for now,
  skate" with a reassuring note until the key is saved, then becomes a
  confident lime "Let's skate" once done. Never blocks the user.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-09 11:30:06 +07:00
shakespeare.diy
4dc2b0129e Make backup gently unavoidable, not skippable-forever (1.3.13)
Closes the real safety hole: "Remind me later" used to set a PERMANENT
localStorage flag, silently becoming "never remind again" — so users who
never backed up were never reminded again.

- BackupReminder: "Later" now snoozes for the current session only
  (sessionStorage), so it returns each visit until backup is actually
  done. Only `backup-confirmed` stops it for good. Never blocks the user.
- Downloading OR copying the recovery code (in SignupDialog's done step
  and in SecuritySettings) now marks backup-confirmed — genuinely safe
  users stop being nagged automatically.
- Plain-language copy pass (EN + ES): "Save your login" / "recovery code"
  instead of "nsec / recovery phrase" jargon.
- Non-blocking and identical UX on both phone and desktop (bottom sheet).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-09 11:23:44 +07:00
shakespeare.diy
0bc2a7c60b Fix broken skater icon + add signup reassurance line (1.3.11)
- Regenerated the Lab skater icon (previous file was corrupted, showing a
  broken-image placeholder). Now a valid monochrome neon-red PNG.
- Signup welcome step: added a subtle, jargon-free reassurance line under
  "Create quick account" — "Takes 5 seconds · stays on this device ·
  nothing to remember" — to make quick signup feel effortless and safe.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-09 11:17:46 +07:00
shakespeare.diy
f730f0b200 Harden all Amber signing paths against empty-id / bare-signature responses
Extends the admin-delete hang fix across the whole app. Audited every
signing route:

- Native (CapacitorAmberSigner): already fixed — computes id locally.
- Web/PWA (AmberSigner redirect flow): the callback handlers did a raw
  JSON.parse(result), but Amber returns a BARE signature (returnType=
  'signature'), so this threw or produced an event with an empty id that
  stalls the relay — the same "stuck" defect as native. Added a shared
  reconstructSignedEvent() helper that rebuilds the event from the stored
  template + signature and computes the id via getEventHash. Applied in
  both AmberCallbackPage and AmberCallbackHandler.
- NIP-17 DMs, zap requests, vouches, crew invites, membership, etc. all
  sign via user.signer.signEvent and therefore inherit the fix.
- nsec / bunker / extension signers use Nostrify base classes (compute
  ids internally) — unaffected.
- Native nip04/nip44 encrypt/decrypt are timeout-protected — no hang.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-03 22:05:25 +07:00
shakespeare.diy
a0673a3244 Fix ESLint gate: scope ignores to app source (exclude android/.agents/cvm-server/public html/scripts), add index.html title, downgrade unused-vars & no-explicit-any to warnings, fix real lint errors (SkateGlobe unused-expr, EventsPage prefer-const, LicensePage Infinity shadow, useless-escape in SasquatchGuide/RiderCardPage, stale eslint-disable directives)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-03 21:30:56 +07:00
shakespeare.diy
3833625bde Signup: require a display name (changeable later), remove skip
Per request, a display name is now mandatory when signing up:
- Removed the "Skip for now" button from the profile step — the only forward
  path is Save, which is disabled until a non-empty name is entered.
- Marked the field required (*), added helper copy ("Required — how you'll show
  up across SkateSpots. Change it anytime."), and a hint under the button.
- finishSignup guards against an empty/whitespace name (defense-in-depth for
  keyboard/Enter), trims the name before publishing kind 0.
- Reworded the step copy: name required, bio + avatar still optional.

Only affects the primary nsec signup dialog (also the endpoint the mnemonic
flow feeds into); the mnemonic dialog has no name step.

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

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

Bump version to 1.1.7.

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

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

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

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

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 11:03:26 +07:00
shakespeare.diy
86a5a4b859 #2 Silence AmberCallback mount-time console spam (keep flow-error logs)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-08-27 10:18:32 +07:00
shakespeare.diy
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
de5de7abec Fix getPublicKey: JSON parse JSObject result + restore AMBER_PACKAGE arg
- JSObject from Capacitor needs JSON.parse(JSON.stringify()) to be
  accessible as plain JS object with property access
- Restore AMBER_PACKAGE as argument (null was causing broken URI)
- Remove debug toast from LoginDialog

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-09 14:28:58 +07:00
shakespeare.diy
fc1c8aca26 Add visible debug toast showing pubkey returned by Amber after login
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-09 13:50:28 +07:00
shakespeare.diy
ef22eb2de7 Fix Amber "not installed" false positive - skip isExternalSignerInstalled() check
Android 11+ package visibility rules cause isExternalSignerInstalled() to
return false even when Amber is installed, unless <queries> is declared in
AndroidManifest.xml. Skip the pre-check and attempt login directly instead,
with better error messages for cancel vs not-found vs other errors.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-09 11:49:16 +07:00
shakespeare.diy
8256f05ca8 Wire up nostr-signer-capacitor-plugin for Amber login and signing
- Add CapacitorAmberSigner: a full NostrSigner implementation backed by
  the native nostr-signer-capacitor-plugin (login, signEvent, nip04,
  nip44 all go through Amber via native Android intents)
- LoginDialog: on Capacitor, call getAmberPublicKeyViaPlugin() directly
  instead of the intent:// URL redirect — returns a Promise, no
  callback URL or polling needed. Shows clear error if Amber not installed.
- useCurrentUser: on Capacitor amber logins use CapacitorAmberSigner
  so every publish/sign goes through the plugin, not window.location.href
- Web/PWA path unchanged — still uses URL redirect + AmberSigner

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-09 11:38:23 +07:00
shakespeare.diy
7d071787d0 Fix Amber login for ZapStore/Capacitor app
- LoginDialog now uses openAmberForLogin() which sends the proper
  intent:// URL with a callbackUrl instead of the old nostrsigner:
  scheme that only copies to clipboard (broken on most setups)
- Add polling loop (500ms, 3min timeout) for amber_pending_pubkey
  written by AmberCallbackPage — works even when Amber opens the
  callback in a new browser tab (GrapheneOS/Android behaviour)
- visibilitychange listener now waits for app to regain focus before
  attempting clipboard read, fixing race condition
- DeepLinkHandler pre-captures ?event= param into sessionStorage
  when Amber fires appUrlOpen deep link in Capacitor, so
  AmberCallbackPage can always find the result
- Add 'paste' to amberStep union type (was missing, caused TS error)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-08 22:29:26 +07:00
shakespeare.diy
132b6cae7c fix: Amber login using confirmed correct URL from Amber source code
Read Amber's actual IntentUtils.kt to confirm exact URL parsing:
- Strips "nostrsigner:" prefix
- URLDecodes the remainder
- Parses ?key=value&key=value query params
- "type" param controls operation
- No callbackUrl → copies result to clipboard

Correct URL: nostrsigner:?type=get_public_key&compressionType=none&returnType=signature

Flow:
1. Tap "Open Amber" → window.location.href = nostrsigner: URL
2. Amber opens, user approves, copies hex pubkey to clipboard
3. App regains focus → tries navigator.clipboard.readText() auto
4. If clipboard denied → shows paste input (accepts hex or npub1)
5. Login complete

Removed broken Capacitor plugin dependency (couldn't install git dep).

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-08 12:22:58 +07:00
shakespeare.diy
9e6d6f1c39 fix: switch to Coracle's nostr-signer-capacitor-plugin fork + correct API
The chebizarro npm version has a different API to the Coracle fork that
Welshman/Coracle actually use in production. Now using:

  github:coracle-social/nostr-signer-capacitor-plugin#main

Correct API (from Welshman nip55.js source):
  NostrSignerPlugin.setPackageName(packageName: string)  // string not object
  NostrSignerPlugin.getPublicKey(packageName?: string)   // packageName as arg
  NostrSignerPlugin.isExternalSignerInstalled(packageName?)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-08 12:11:18 +07:00
shakespeare.diy
f010a61608 fix: use nostr-signer-capacitor-plugin for Amber NIP-55 login
All previous URL-scheme approaches (nostrsigner:, intent:) were fundamentally
wrong for a Capacitor app. JavaScript in a WebView cannot reliably dispatch
Android intents via URL navigation.

The correct solution — used by Coracle and other Capacitor-based Nostr apps —
is the nostr-signer-capacitor-plugin which communicates with Amber via native
Android intents through the Capacitor bridge:

  NostrSignerPlugin.setPackageName({ packageName: 'com.greenart7c3.nostrsigner' })
  NostrSignerPlugin.isExternalSignerInstalled()
  NostrSignerPlugin.getPublicKey() → { npub }

This is synchronous from the JS side, handles the entire Android intent
roundtrip natively, and returns the pubkey directly. No URL encoding,
no callback URLs, no redirects, no malformed request errors.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-08 11:59:04 +07:00
shakespeare.diy
92f317f5e5 fix: Amber login for GrapheneOS — clipboard + paste flow
GrapheneOS Vanadium browser blocks intent: URLs from web pages as a
security hardening measure. This is why every URL format we tried
caused "malformed request" — Vanadium was blocking the intent before
Amber ever saw it.

New flow that works on ALL Android browsers including GrapheneOS:
1. Tap "Open Amber" → navigates to nostrsigner: URL with no callbackUrl
   (Amber copies pubkey to clipboard when no callbackUrl is given)
2. User approves in Amber, comes back to app
3. App tries navigator.clipboard.readText() automatically on visibilitychange
4. If clipboard permission denied → shows paste input for hex pubkey
5. User pastes pubkey → logged in

No intent: scheme, no callback URL, no redirect loop. Simple and bulletproof.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-08 11:43:59 +07:00
shakespeare.diy
59eda93641 fix: Amber login callback URL for HashRouter
BUG #1 (critical): buildCallbackUrl() was returning
  https://skate-spots.org/amber-callback?event=
but the app uses HashRouter, so React Router never matched /amber-callback.
Amber would open the callback URL in a new tab, land on the homepage, and
AmberCallbackPage would never render — login was silently broken.

Fixed to:
  https://skate-spots.org/#/amber-callback?event=

BUG #2: AmberCallbackHandler (global, in App.tsx) was competing with
AmberCallbackPage on the /amber-callback route, causing a race condition
where the global handler could consume the pubkey before the page component.
Fixed by detecting the hash path and early-returning when on /amber-callback.

BONUS: openAmberForLogin() now persists amber_return_path from the current
hash path so AmberCallbackPage can navigate back to where the user was.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-03 13:58:41 +07:00
shakespeare.diy
4f7852fec7 Fix Amber login: replace nostrconnect/relay flow with proper NIP-55 intent flow
The entire nostrconnect relay approach was wrong for a native Android/Capacitor app.
Amber on Android uses NIP-55 intents (nostrsigner: URI scheme), not NIP-46 relays.

New flow (same as Amethyst):
1. openAmberForLogin() fires nostrsigner:?type=get_public_key&callbackUrl=...
2. Amber opens, user taps approve
3. Amber calls back to org.skatespots.app://amber-callback?event=<pubkey>
4. AmberCallbackPage writes pubkey to localStorage('amber_pending_pubkey')
5. LoginDialog polls localStorage every 500ms and calls login.amber(pubkey)
6. Done — no relays, no waiting, no spinning

Also added login.amber(pubkey) method to useLoginActions.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-03 13:24:46 +07:00
shakespeare.diy
e3dd66d078 Fix Amber login: store as 'amber' type (NIP-55) not 'bunker' (NIP-46)
The nostrconnect flow was incorrectly creating a bunker login and trying
to do NIP-46 key exchange. Amber signs as the user's own key directly,
so the pubkey from the connect response IS the user pubkey. Now stores
a proper amber login which uses AmberSigner via useCurrentUser.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-03 12:38:56 +07:00
shakespeare.diy
03ea569a2a Amber login: poll relay every 3s after returning from Amber, add elapsed timer + Check Now button
- Replace one-shot visibility handler with pollRelay() called immediately on foreground + every 3s
- Add elapsed time counter (M:SS) so user can see the app is still trying
- Add 'Check Now' button for manual relay poll without generating a new URI
- Poll interval auto-clears on success, timeout, or cancel

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-03 12:34:20 +07:00
shakespeare.diy
dfb60674c5 Fix Amber timeout: increase to 3 minutes so user has time to approve in Amber and return
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-03 12:31:48 +07:00
shakespeare.diy
bd149fdb2c Fix Amber nostrconnect flow: 20s timeout, error state, stale closure fix, better relays, debug logs
- Replace wss://relay.primal.net with wss://nos.lol (more reliable for NIP-46)
- Shorten timeout from 10 minutes to 20 seconds
- Fix stale closure bug: track amberStep in amberStepRef so timeout can correctly reset UI
- Add 'error' step with red UI and 'Try Again' button instead of endless spinner
- Extend since filter from 5s to 10s in the past
- Add console logs for URI, relays, incoming events, decryption result, secret matching

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-03 11:48:03 +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
84d80ac3a7 fix: handle Capacitor WebSocket suspension in NIP-46 Amber login
- Add visibilitychange listener: when app returns to foreground after Amber,
  does a one-shot relay query to catch Amber's connect response
- Store pending nostrconnect session in localStorage so it survives app suspend
- Keep live relay subscription as fallback for web/PWA
- Add nsec login tab as reliable fallback option
- Add proper step states: idle -> connecting -> waiting -> success

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-19 00:38:38 +07:00
shakespeare.diy
789a3d7a60 fix: use nostr-tools SimplePool+nip04 for NIP-46 relay subscription
Replace NRelay1 async iterator (unreliable) with nostr-tools SimplePool.subscribeMany()
using onevent callback — same pattern as Ditto/Coracle/Nostrudel.

- Switch to relay.nsec.app, relay.damus.io, relay.primal.net (support ephemeral events)
- Use nip04.decrypt() from nostr-tools for Amber's connect response decryption
- Proper cleanup of pool and subscription on abort/close
- Fall back to signerPubkey as userPubkey if NConnectSigner.getPublicKey() fails

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-19 00:28:46 +07:00
shakespeare.diy
61795db997 fix: implement NIP-46 nostrconnect:// Amber login properly
- Fix useLoginActions.ts: remove generateNostrConnectParams, generateNostrConnectURI,
  NostrConnectParams, NostrConnectStatus which don't exist in @nostrify/react@0.2.17/login
- Rewrite LoginDialog.tsx Amber tab with proper NIP-46 client-initiated flow:
  - Generates ephemeral client keypair + random secret
  - Builds nostrconnect:// URI (no nostrsigner: scheme, no intents)
  - Opens Amber via window.open(_blank) - standard URI scheme Amber handles natively
  - Listens on relay pool for Amber's kind 24133 connect response
  - Decrypts with NIP-04, verifies secret to prevent spoofing
  - Uses NConnectSigner to call get_public_key
  - Creates NLogin bunker object and stores it persistently
- Bunker tab: direct NLogin.fromBunker() paste flow
- Extension tab: NIP-07 browser extension
- No clipboard hacks, no nostrsigner: scheme, no Android intent issues

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-19 00:02:21 +07:00
shakespeare.diy
916f067238 fix: rewrite login to use NIP-46 nostrconnect flow (Ditto pattern)
Completely replaces the broken NIP-55 intent approach with NIP-46:

- Amber tab: generates nostrconnect:// URI, opens Amber, waits for
  Amber to connect back via relay using NLogin.fromNostrConnect()
  (same pattern as Ditto/soapbox-pub)
- Bunker tab: paste bunker:// URI from Amber or nsec.app
- Extension tab: NIP-07 browser extension (desktop)
- useLoginActions: updated to match Ditto's implementation with
  nostrconnect(), generateNostrConnectParams, generateNostrConnectURI
- Clean mobile-first UI — no clipboard hacks, no intent tricks

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 23:49:55 +07:00
shakespeare.diy
e8bc072787 fix: replace NIP-55 Amber login with NIP-46 bunker flow
NIP-55 (nostrsigner: intents) is fundamentally broken from Capacitor
WebView — no combination of window.open, window.location.href, or
anchor clicks works reliably across all Android WebView versions.

Switch to NIP-46 (bunker://) which works everywhere via WebSockets:
- User opens Amber → Connect apps → Add app → copies bunker:// URI
- Pastes it into the login dialog
- NLogin.fromBunker() completes the handshake over a relay
- Login works in browser, PWA, and Capacitor APK identically

This is exactly how Ditto (soapbox-pub) implements Amber login.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 23:33:42 +07:00
shakespeare.diy
55856e2935 fix: use clipboard mode for Amber login in Capacitor APK
Capacitor native apps fail with "malformed nostrsigner request" when
using callbackUrl because Amber treats custom URI schemes as invalid.

Fix: detect window.Capacitor.isNativePlatform() and use the no-callback
clipboard flow (openAmberForLoginNoCallback) instead of the callbackUrl
flow. Amber copies the pubkey to clipboard, app reads it on
visibilitychange when user returns.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 22:56:28 +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
0e5abcb2db fix: guard against undefined metadata in AccountSwitcher
user.metadata can be undefined when the profile hasn't loaded yet.
Changed all .metadata.x accesses to .metadata?.x to prevent crash.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 11:39:32 +07:00
shakespeare.diy
65914303f1 Fix Amber login for PWA installed to home screen
PWA standalone mode is single-tab. Opening nostrsigner: with a callbackUrl
sends the callback to the browser (a different app), not back to the PWA.
The PWA tab is suspended and never sees the result.

Fix:
- Detect PWA standalone mode via matchMedia('(display-mode: standalone)')
- In PWA mode: open Amber WITHOUT a callbackUrl — Amber copies pubkey to
  clipboard automatically when no callback is given
- Use visibilitychange to detect when user returns to PWA after approving
- Auto-read clipboard on return and complete login silently
- Manual "I approved" button now tries localStorage first (browser), then
  falls back to clipboard read (PWA) — covers both contexts with one button
- Browser mode unchanged: localStorage cross-tab polling still works

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-17 23:04:48 +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
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
122bdfb102 Sync with skate-spots: THUG2 neon theme, remove old AuthDialog and unused lib files
- Replace generic MKStack default CSS with THUG2-inspired neon theme:
  - Neon red primary (#ff0040) and neon lime accent (#ccff00)
  - Pure black dark mode background
  - Custom CSS variables for gradients, sidebar, and animations
  - Leaflet map dark mode overrides
  - Custom scrollbar styling
  - Mobile touch target improvements and iOS input zoom fix
  - Safe area insets for notched devices
  - scrollbar-hide utility class
  - Animation classes: neon-glow, stagger, fadeIn, slideUp, pulse-glow
  - Glass effect and gradient-text utilities
- Remove AuthDialog.tsx (old Sign Up flow replaced by newer LoginArea/SignupDialog)
- Remove appBlossom.ts and appRelays.ts (not used in this project)
- Remove useLoginActions.test.tsx (not present in reference version)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 23:00:01 +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
06fae294b8 New project created with Shakespeare
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 19:49:07 +07:00