diff --git a/src/App.tsx b/src/App.tsx index 6b08577f..7255443d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,7 @@ import { EventStore } from 'applesauce-core' import { AccountManager } from 'applesauce-accounts' import { registerCommonAccountTypes } from 'applesauce-accounts/accounts' import { RelayPool } from 'applesauce-relay' +import { NostrConnectSigner } from 'applesauce-signers' import { createAddressLoader } from 'applesauce-loaders/loaders' import Bookmarks from './components/Bookmarks' import RouteDebug from './components/RouteDebug' @@ -219,6 +220,9 @@ function App() { const pool = new RelayPool() + // Setup NostrConnectSigner to use the relay pool + NostrConnectSigner.pool = pool + // Create a relay group for better event deduplication and management pool.group(RELAYS) console.log('Created relay group with', RELAYS.length, 'relays (including local)') diff --git a/src/components/BookmarkList.tsx b/src/components/BookmarkList.tsx index 74e6adc5..eb920eab 100644 --- a/src/components/BookmarkList.tsx +++ b/src/components/BookmarkList.tsx @@ -21,6 +21,7 @@ import { RELAYS } from '../config/relays' import { Hooks } from 'applesauce-react' import BookmarkFilters, { BookmarkFilterType } from './BookmarkFilters' import { filterBookmarksByType } from '../utils/bookmarkTypeClassifier' +import LoginOptions from './LoginOptions' interface BookmarkListProps { bookmarks: Bookmark[] @@ -153,7 +154,9 @@ export const BookmarkList: React.FC = ({ /> )} - {filteredBookmarks.length === 0 && allIndividualBookmarks.length > 0 ? ( + {!activeAccount ? ( + + ) : filteredBookmarks.length === 0 && allIndividualBookmarks.length > 0 ? (

No bookmarks match this filter.

@@ -170,7 +173,6 @@ export const BookmarkList: React.FC = ({

No bookmarks found.

Add bookmarks using your nostr client to see them here.

-

If you aren't on nostr yet, start here: nstart.me

) ) : ( diff --git a/src/components/LoginOptions.tsx b/src/components/LoginOptions.tsx new file mode 100644 index 00000000..d5a72990 --- /dev/null +++ b/src/components/LoginOptions.tsx @@ -0,0 +1,170 @@ +import React, { useState } from 'react' +import { Hooks } from 'applesauce-react' +import { Accounts } from 'applesauce-accounts' +import { NostrConnectSigner } from 'applesauce-signers' + +const LoginOptions: React.FC = () => { + const accountManager = Hooks.useAccountManager() + const [showBunkerInput, setShowBunkerInput] = useState(false) + const [bunkerUri, setBunkerUri] = useState('') + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState(null) + + const handleExtensionLogin = async () => { + try { + setIsLoading(true) + setError(null) + const account = await Accounts.ExtensionAccount.fromExtension() + accountManager.addAccount(account) + accountManager.setActive(account) + } catch (err) { + console.error('Extension login failed:', err) + setError('Login failed. Please install a nostr browser extension and try again.') + } finally { + setIsLoading(false) + } + } + + const handleBunkerLogin = async () => { + if (!bunkerUri.trim()) { + setError('Please enter a bunker URI') + return + } + + if (!bunkerUri.startsWith('bunker://')) { + setError('Invalid bunker URI. Must start with bunker://') + return + } + + try { + setIsLoading(true) + setError(null) + + // Create signer from bunker URI + const signer = await NostrConnectSigner.fromBunkerURI(bunkerUri) + + // Get pubkey from signer + const pubkey = await signer.getPublicKey() + + // Create account from signer + const account = new Accounts.NostrConnectAccount(pubkey, signer) + + // Add to account manager and set active + accountManager.addAccount(account) + accountManager.setActive(account) + + // Clear input on success + setBunkerUri('') + setShowBunkerInput(false) + } catch (err) { + console.error('Bunker login failed:', err) + setError(err instanceof Error ? err.message : 'Failed to connect to bunker') + } finally { + setIsLoading(false) + } + } + + return ( +
+

Login with:

+ +
+ + + {!showBunkerInput ? ( + + ) : ( +
+ setBunkerUri(e.target.value)} + disabled={isLoading} + style={{ + padding: '0.75rem', + fontSize: '0.9rem', + width: '100%', + boxSizing: 'border-box' + }} + onKeyDown={(e) => { + if (e.key === 'Enter') { + handleBunkerLogin() + } + }} + /> +
+ + +
+
+ )} +
+ + {error && ( +

+ {error} +

+ )} + +

+ If you aren't on nostr yet, start here:{' '} + + nstart.me + +

+
+ ) +} + +export default LoginOptions + diff --git a/vite.config.ts b/vite.config.ts index f4534d2c..e8ed19a3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -141,7 +141,7 @@ export default defineConfig({ mainFields: ['module', 'jsnext:main', 'jsnext', 'main'] }, optimizeDeps: { - include: ['applesauce-core', 'applesauce-factory', 'applesauce-relay', 'applesauce-react'], + include: ['applesauce-core', 'applesauce-factory', 'applesauce-relay', 'applesauce-react', 'applesauce-accounts', 'applesauce-signers'], esbuildOptions: { resolveExtensions: ['.js', '.ts', '.tsx', '.json'] } @@ -158,7 +158,7 @@ export default defineConfig({ } }, ssr: { - noExternal: ['applesauce-core', 'applesauce-factory', 'applesauce-relay'] + noExternal: ['applesauce-core', 'applesauce-factory', 'applesauce-relay', 'applesauce-accounts', 'applesauce-signers'] } })