diff --git a/src/components/auth/AmberCallbackHandler.tsx b/src/components/auth/AmberCallbackHandler.tsx index c1580bc..e7c36fd 100644 --- a/src/components/auth/AmberCallbackHandler.tsx +++ b/src/components/auth/AmberCallbackHandler.tsx @@ -40,6 +40,14 @@ export function AmberCallbackHandler() { console.log('[AmberCallback] Search:', window.location.search); console.log('[AmberCallback] Pre-captured event:', sessionStorage.getItem('amber_callback_event')); + // If we're already on the /amber-callback route, AmberCallbackPage handles + // the processing — this global handler must stay silent to avoid a race. + const hashPath = window.location.hash.replace(/^#/, '').split('?')[0]; + if (hashPath === '/amber-callback') { + console.log('[AmberCallback] On /amber-callback route — deferring to AmberCallbackPage'); + return; + } + // Check if this is an Amber callback const isCallback = isAmberCallback(); console.log('[AmberCallback] isAmberCallback():', isCallback); diff --git a/src/lib/amber.ts b/src/lib/amber.ts index ce0311a..68b7898 100644 --- a/src/lib/amber.ts +++ b/src/lib/amber.ts @@ -270,9 +270,15 @@ function generateOperationId(): string { /** * Build the dedicated Amber callback URL. * + * This app uses HashRouter, so ALL in-app routes live inside the hash fragment: + * https://skate-spots.org/#/amber-callback + * + * Amber will open the callbackUrl in the browser after the user approves. + * The URL must therefore use the hash-based path so React Router can match + * the /amber-callback route and render AmberCallbackPage. + * * In the Capacitor APK we must use the app's custom URI scheme so Android fires * an intent back into the app (not a browser navigation). - * Web/PWA builds use the normal https origin. * * The app ID is `org.skatespots.app` — Capacitor registers this as a URI scheme * automatically via the `appId` in capacitor.config.ts. @@ -285,7 +291,10 @@ function buildCallbackUrl(): string { // correct route inside the HashRouter. return 'org.skatespots.app://amber-callback?event='; } - return `${window.location.origin}/amber-callback?event=`; + // HashRouter: routes live in the # fragment, so the callback URL must be: + // https://skate-spots.org/#/amber-callback?event= + // This is what Amber will open in the browser tab after user approves. + return `${window.location.origin}/#/amber-callback?event=`; } /** @@ -336,6 +345,11 @@ export function openAmberForLogin(): void { setAmberPendingOperation(pendingOp); + // Store the current hash path so AmberCallbackPage can navigate back here + // after login (e.g. user was on /map and tapped Login → returns to /map). + const currentPath = window.location.hash.replace(/^#/, '') || '/'; + localStorage.setItem('amber_return_path', currentPath); + // NIP-55 nostrsigner: URI — Amber opens, user approves, Amber calls callbackUrl const amberUrl = `nostrsigner:?compressionType=none&returnType=signature&type=get_public_key&callbackUrl=${encodeURIComponent(callbackUrl)}`;