diff --git a/src/layouts/Main.tsx b/src/layouts/Main.tsx index 262e739..06902a1 100644 --- a/src/layouts/Main.tsx +++ b/src/layouts/Main.tsx @@ -1,6 +1,5 @@ -import { Event, kinds } from 'nostr-tools' +import { Event, getPublicKey, kinds, nip19 } from 'nostr-tools' import { useCallback, useEffect, useRef, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' import { Outlet, useNavigate, useSearchParams } from 'react-router-dom' import { AppBar } from '../components/AppBar/AppBar' import { LoadingSpinner } from '../components/LoadingSpinner' @@ -12,12 +11,11 @@ import { import { restoreState, setMetadataEvent, + updateKeyPair, updateLoginMethod, updateNostrLoginAuthMethod, updateUserAppData } from '../store/actions' -import { State } from '../store/rootReducer' -import { Dispatch } from '../store/store' import { setUserRobotImage } from '../store/userRobotImage/action' import { getRoboHashPicture, @@ -25,7 +23,7 @@ import { loadState, subscribeForSigits } from '../utils' -import { useAppSelector } from '../hooks' +import { useAppDispatch, useAppSelector } from '../hooks' import styles from './style.module.scss' import { useLogout } from '../hooks/useLogout' import { LoginMethod } from '../store/auth/types' @@ -33,13 +31,13 @@ import { NostrLoginAuthOptions } from 'nostr-login/dist/types' import { init as initNostrLogin } from 'nostr-login' export const MainLayout = () => { - const [searchParams] = useSearchParams() + const [searchParams, setSearchParams] = useSearchParams() const navigate = useNavigate() - const dispatch: Dispatch = useDispatch() + const dispatch = useAppDispatch() const logout = useLogout() const [isLoading, setIsLoading] = useState(true) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState(`Loading App`) - const authState = useSelector((state: State) => state.auth) + const authState = useAppSelector((state) => state.auth) const usersAppData = useAppSelector((state) => state.userAppData) // Ref to track if `subscribeForSigits` has been called @@ -125,7 +123,7 @@ export const MainLayout = () => { }, []) useEffect(() => { - if (authState.loggedIn && usersAppData) { + if (authState && authState.loggedIn && usersAppData) { const pubkey = authState.usersPubkey || authState.keyPair?.public if (pubkey && !hasSubscribed.current) { @@ -164,6 +162,53 @@ export const MainLayout = () => { } }, [authState, dispatch]) + useEffect(() => { + // Developer login with ?nsec= (not recommended) + const nsec = searchParams.get('nsec') + if (!nsec) return + + // Clear nsec from the url immediately + searchParams.delete('nsec') + setSearchParams(searchParams) + + if (!authState?.loggedIn) { + if (!nsec.startsWith('nsec')) { + console.error('Invalid format, use private key (nsec)') + return + } + + try { + const privateKey = nip19.decode(nsec).data as Uint8Array + if (!privateKey) { + console.error('Failed to convert the private key.') + return + } + + const publickey = getPublicKey(privateKey) + + dispatch( + updateKeyPair({ + private: nsec, + public: publickey + }) + ) + dispatch(updateLoginMethod(LoginMethod.privateKey)) + ;(async () => { + const authController = new AuthController() + await authController + .authAndGetMetadataAndRelaysMap(publickey) + .catch((err) => { + console.error('Error occurred in authentication: ' + err) + return null + }) + })() + } catch (err) { + console.error(`Error decoding the nsec. ${err}`) + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [dispatch, searchParams]) + if (isLoading) return const isDev = import.meta.env.MODE === 'development'