import { launch as launchNostrLoginDialog } from 'nostr-login' import { Button, Divider, TextField } from '@mui/material' import { getPublicKey, nip19 } from 'nostr-tools' import { useEffect, useState } from 'react' import { useDispatch } from 'react-redux' import { useNavigate, useSearchParams } from 'react-router-dom' import { toast } from 'react-toastify' import { LoadingSpinner } from '../../components/LoadingSpinner' import { AuthController } from '../../controllers' import { updateKeyPair, updateLoginMethod } from '../../store/actions' import { LoginMethod } from '../../store/auth/types' import { Dispatch } from '../../store/store' import { hexToBytes } from '@noble/hashes/utils' import styles from './styles.module.scss' export const Nostr = () => { const [searchParams] = useSearchParams() const dispatch: Dispatch = useDispatch() const navigate = useNavigate() const authController = new AuthController() const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') const [inputValue, setInputValue] = useState('') const navigateAfterLogin = (path: string) => { const callbackPath = searchParams.get('callbackPath') if (callbackPath) { // base64 decoded path const path = atob(callbackPath) navigate(path) return } navigate(path) } const [isNostrExtensionAvailable, setIsNostrExtensionAvailable] = useState(false) useEffect(() => { setTimeout(() => { setIsNostrExtensionAvailable(!!window.nostr) }, 500) }, []) /** * Call login function when enter is pressed */ const handleInputKeyDown = (event: React.KeyboardEvent) => { if (event.code === 'Enter' || event.code === 'NumpadEnter') { event.preventDefault() login() } } /** * Login with NSEC or HEX private key * @param privateKey in HEX format */ const loginWithNsec = async (privateKey?: Uint8Array) => { let nsec = '' if (privateKey) { nsec = nip19.nsecEncode(privateKey) } else { nsec = inputValue try { privateKey = nip19.decode(nsec).data as Uint8Array } catch (err) { toast.error(`Error decoding the nsec. ${err}`) } } if (!privateKey) { toast.error( 'Snap, we failed to convert the private key you provided. Please make sure key is valid.' ) setIsLoading(false) return } const publickey = getPublicKey(privateKey) dispatch( updateKeyPair({ private: nsec, public: publickey }) ) dispatch(updateLoginMethod(LoginMethod.privateKey)) setIsLoading(true) setLoadingSpinnerDesc('Authenticating and finding metadata') const redirectPath = await authController .authAndGetMetadataAndRelaysMap(publickey) .catch((err) => { toast.error('Error occurred in authentication: ' + err) return null }) if (redirectPath) navigateAfterLogin(redirectPath) setIsLoading(false) setLoadingSpinnerDesc('') } const login = () => { if (inputValue.startsWith('nsec')) { return loginWithNsec() } // Check if maybe hex nsec try { const privateKey = hexToBytes(inputValue) const publickey = getPublicKey(privateKey) if (publickey) return loginWithNsec(privateKey) } catch (err) { console.warn('err', err) } toast.error('Invalid format, please use: private key (hex or nsec)') return } return ( <> {isLoading && } {isNostrExtensionAvailable && ( <> or )}
setInputValue(e.target.value)} fullWidth margin="dense" /> ) }