From 637e26bf35c77e866fa4088174898be1682f349f Mon Sep 17 00:00:00 2001 From: enes Date: Sat, 5 Oct 2024 14:56:34 +0200 Subject: [PATCH] refactor: init nostr-login, login method strategies, remove bunker --- src/App.tsx | 16 +- src/components/AppBar/AppBar.tsx | 47 +- src/controllers/AuthController.ts | 2 +- src/controllers/NostrController.ts | 448 +----------------- src/hooks/useLogout.tsx | 27 ++ src/layouts/Main.tsx | 107 +++-- src/pages/create/index.tsx | 17 - src/pages/nostr/index.tsx | 374 ++------------- src/pages/settings/Settings.tsx | 47 +- src/pages/settings/cache/index.tsx | 3 +- src/pages/settings/nostrLogin/index.tsx | 71 +++ src/pages/settings/profile/index.tsx | 40 +- src/pages/sign/index.tsx | 20 - src/pages/sign/style.module.scss | 1 + src/routes/index.tsx | 8 +- .../LoginMethodStrategy/NostrLoginStrategy.ts | 75 +++ .../LoginMethodStrategy/PrivateKeyStrategy.ts | 118 +++++ .../LoginMethodStrategy/loginMethodContext.ts | 43 ++ .../loginMethodStrategy.ts | 31 ++ src/store/actionTypes.ts | 2 - src/store/auth/action.ts | 20 +- src/store/auth/reducer.ts | 23 +- src/store/auth/types.ts | 23 +- src/utils/localStorage.ts | 13 +- 24 files changed, 577 insertions(+), 999 deletions(-) create mode 100644 src/hooks/useLogout.tsx create mode 100644 src/pages/settings/nostrLogin/index.tsx create mode 100644 src/services/LoginMethodStrategy/NostrLoginStrategy.ts create mode 100644 src/services/LoginMethodStrategy/PrivateKeyStrategy.ts create mode 100644 src/services/LoginMethodStrategy/loginMethodContext.ts create mode 100644 src/services/LoginMethodStrategy/loginMethodStrategy.ts diff --git a/src/App.tsx b/src/App.tsx index 1523e6f..cbb919e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'react' import { useSelector } from 'react-redux' import { Navigate, Route, Routes } from 'react-router-dom' -import { AuthController, NostrController } from './controllers' +import { AuthController } from './controllers' import { MainLayout } from './layouts/Main' import { appPrivateRoutes, @@ -11,7 +11,6 @@ import { recursiveRouteRenderer } from './routes' import { State } from './store/rootReducer' -import { getNsecBunkerDelegatedKey, saveNsecBunkerDelegatedKey } from './utils' import './App.scss' const App = () => { @@ -25,23 +24,10 @@ const App = () => { window.location.hostname = 'localhost' } - generateBunkerDelegatedKey() - const authController = new AuthController() authController.checkSession() }, []) - const generateBunkerDelegatedKey = () => { - const existingKey = getNsecBunkerDelegatedKey() - - if (!existingKey) { - const nostrController = NostrController.getInstance() - const newDelegatedKey = nostrController.generateDelegatedKey() - - saveNsecBunkerDelegatedKey(newDelegatedKey) - } - } - const handleRootRedirect = () => { if (authState.loggedIn) return appPrivateRoutes.homePage const callbackPathEncoded = btoa( diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index ff68469..084a8f0 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -9,44 +9,28 @@ import { } from '@mui/material' import { useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { - setAuthState, - setMetadataEvent, - userLogOutAction -} from '../../store/actions' +import { useSelector } from 'react-redux' import { State } from '../../store/rootReducer' -import { Dispatch } from '../../store/store' import Username from '../username' import { Link, useNavigate } from 'react-router-dom' -import { MetadataController, NostrController } from '../../controllers' import { appPublicRoutes, appPrivateRoutes, getProfileRoute } from '../../routes' -import { - clearAuthToken, - getProfileUsername, - hexToNpub, - saveNsecBunkerDelegatedKey -} from '../../utils' +import { getProfileUsername, hexToNpub } from '../../utils' import styles from './style.module.scss' -import { setUserRobotImage } from '../../store/userRobotImage/action' import { Container } from '../Container' import { ButtonIcon } from '../ButtonIcon' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faClose } from '@fortawesome/free-solid-svg-icons' import useMediaQuery from '@mui/material/useMediaQuery' - -const metadataController = MetadataController.getInstance() +import { useLogout } from '../../hooks/useLogout' export const AppBar = () => { const navigate = useNavigate() - - const dispatch: Dispatch = useDispatch() - + const logout = useLogout() const [username, setUsername] = useState('') const [userAvatar, setUserAvatar] = useState('') const [anchorElUser, setAnchorElUser] = useState(null) @@ -94,28 +78,7 @@ export const AppBar = () => { const handleLogout = () => { handleCloseUserMenu() - dispatch( - setAuthState({ - keyPair: undefined, - loggedIn: false, - usersPubkey: undefined, - loginMethod: undefined, - nsecBunkerPubkey: undefined - }) - ) - dispatch(setMetadataEvent(metadataController.getEmptyMetadataEvent())) - dispatch(setUserRobotImage(null)) - - // clear authToken saved in local storage - clearAuthToken() - - dispatch(userLogOutAction()) - - // update nsecBunker delegated key after logout - const nostrController = NostrController.getInstance() - const newDelegatedKey = nostrController.generateDelegatedKey() - saveNsecBunkerDelegatedKey(newDelegatedKey) - + logout() navigate('/') } const isAuthenticated = authState?.loggedIn === true diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index cc8def5..b9d9779 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -31,7 +31,7 @@ export class AuthController { /** * Function will authenticate user by signing an auth event * which is done by calling the sign() function, where appropriate - * method will be chosen (extension, nsecbunker or keys) + * method will be chosen (extension or keys) * * @param pubkey of the user trying to login * @returns url to redirect if authentication successfull diff --git a/src/controllers/NostrController.ts b/src/controllers/NostrController.ts index 0547ffb..9e47ff3 100644 --- a/src/controllers/NostrController.ts +++ b/src/controllers/NostrController.ts @@ -1,194 +1,25 @@ -import NDK, { - NDKEvent, - NDKNip46Signer, - NDKPrivateKeySigner, - NDKUser, - NostrEvent -} from '@nostr-dev-kit/ndk' -import { - Event, - EventTemplate, - UnsignedEvent, - finalizeEvent, - nip04, - nip19, - nip44 -} from 'nostr-tools' +import { EventTemplate, UnsignedEvent } from 'nostr-tools' +import { WindowNostr } from 'nostr-tools/nip07' import { EventEmitter } from 'tseep' -import { updateNsecbunkerPubkey } from '../store/actions' -import { AuthState, LoginMethods } from '../store/auth/types' +import { AuthState } from '../store/auth/types' import store from '../store/store' import { SignedEvent } from '../types' -import { getNsecBunkerDelegatedKey, verifySignedEvent } from '../utils' +import { LoginMethodContext } from '../services/LoginMethodStrategy/loginMethodContext' export class NostrController extends EventEmitter { private static instance: NostrController - private bunkerNDK: NDK | undefined - private remoteSigner: NDKNip46Signer | undefined - private constructor() { super() } - private getNostrObject = () => { - // fix: this is not picking up type declaration from src/system/index.d.ts - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if (window.nostr) return window.nostr as any + if (window.nostr) return window.nostr as WindowNostr throw new Error( `window.nostr object not present. Make sure you have an nostr extension installed/working properly.` ) } - public nsecBunkerInit = async (relays: string[]) => { - // Don't reinstantiate bunker NDK if exists with same relays - if ( - this.bunkerNDK && - this.bunkerNDK.explicitRelayUrls?.length === relays.length && - this.bunkerNDK.explicitRelayUrls?.every((relay) => relays.includes(relay)) - ) - return - - this.bunkerNDK = new NDK({ - explicitRelayUrls: relays - }) - - try { - await this.bunkerNDK - .connect(2000) - .then(() => { - console.log( - `Successfully connected to the nsecBunker relays: ${relays.join( - ',' - )}` - ) - }) - .catch((err) => { - console.error( - `Error connecting to the nsecBunker relays: ${relays.join( - ',' - )} ${err}` - ) - }) - } catch (err) { - console.error(err) - } - } - - /** - * Creates nSecBunker signer instance for the given npub - * Or if npub omitted it will return existing signer - * If neither, error will be thrown - * @param npub nPub / public key in hex format - * @returns nsecBunker Signer instance - */ - public createNsecBunkerSigner = async ( - npub: string | undefined - ): Promise => { - const nsecBunkerDelegatedKey = getNsecBunkerDelegatedKey() - - return new Promise((resolve, reject) => { - if (!nsecBunkerDelegatedKey) { - reject('nsecBunker delegated key is not found in the browser.') - return - } - const localSigner = new NDKPrivateKeySigner(nsecBunkerDelegatedKey) - - if (!npub) { - if (this.remoteSigner) resolve(this.remoteSigner) - - const npubFromStorage = (store.getState().auth as AuthState) - .nsecBunkerPubkey - - if (npubFromStorage) { - npub = npubFromStorage - } else { - reject( - 'No signer instance present, no npub provided by user or found in the browser.' - ) - return - } - } else { - store.dispatch(updateNsecbunkerPubkey(npub)) - } - - // Pubkey of a key pair stored in nsecbunker that will be used to sign event with - const appPubkeyOrToken = npub.includes('npub') - ? npub - : nip19.npubEncode(npub) - - /** - * When creating and NDK instance we create new connection to the relay - * To prevent too much connections and hitting rate limits, if npub against which we sign - * we will reuse existing instance. Otherwise we will create new NDK and signer instance. - */ - if (!this.remoteSigner || this.remoteSigner?.remotePubkey !== npub) { - this.remoteSigner = new NDKNip46Signer( - this.bunkerNDK!, - appPubkeyOrToken, - localSigner - ) - } - - /** - * when nsecbunker-delegated-key is regenerated we have to reinitialize the remote signer - */ - if (this.remoteSigner.localSigner !== localSigner) { - this.remoteSigner = new NDKNip46Signer( - this.bunkerNDK!, - appPubkeyOrToken, - localSigner - ) - } - - resolve(this.remoteSigner) - }) - } - - /** - * Signs the nostr event and returns the sig and id or full raw nostr event - * @param npub stored in nsecBunker to sign with - * @param event to be signed - * @param returnFullEvent whether to return full raw nostr event or just SIG and ID values - */ - public signWithNsecBunker = async ( - npub: string | undefined, - event: NostrEvent, - returnFullEvent = true - ): Promise<{ id: string; sig: string } | NostrEvent> => { - return new Promise((resolve, reject) => { - this.createNsecBunkerSigner(npub) - .then(async (signer) => { - const ndkEvent = new NDKEvent(undefined, event) - - const timeout = setTimeout(() => { - reject('Timeout occurred while waiting for event signing') - }, 60000) // 60000 ms (1 min) = 1000 * 60 - - await ndkEvent.sign(signer).catch((err) => { - clearTimeout(timeout) - reject(err) - return - }) - - clearTimeout(timeout) - - if (returnFullEvent) { - resolve(ndkEvent.rawEvent()) - } else { - resolve({ - id: ndkEvent.id, - sig: ndkEvent.sig! - }) - } - }) - .catch((err) => { - reject(err) - }) - }) - } - public static getInstance(): NostrController { if (!NostrController.instance) { NostrController.instance = new NostrController() @@ -207,59 +38,10 @@ export class NostrController extends EventEmitter { nip44Encrypt = async (receiver: string, content: string) => { // Retrieve the current login method from the application's redux state. const loginMethod = (store.getState().auth as AuthState).loginMethod + const context = new LoginMethodContext(loginMethod) // Handle encryption when the login method is via an extension. - if (loginMethod === LoginMethods.extension) { - const nostr = this.getNostrObject() - - // Check if the nostr object supports NIP-44 encryption. - if (!nostr.nip44) { - throw new Error( - `Your nostr extension does not support nip44 encryption & decryption` - ) - } - - // Encrypt the content using NIP-44 provided by the nostr extension. - const encrypted = await nostr.nip44.encrypt(receiver, content) - return encrypted as string - } - - // Handle encryption when the login method is via a private key. - if (loginMethod === LoginMethods.privateKey) { - const keys = (store.getState().auth as AuthState).keyPair - - // Check if the private and public key pair is available. - if (!keys) { - throw new Error( - `Login method is ${LoginMethods.privateKey} but private & public key pair is not found.` - ) - } - - // Decode the private key. - const { private: nsec } = keys - const privateKey = nip19.decode(nsec).data as Uint8Array - - // Generate the conversation key using NIP-44 utilities. - const nip44ConversationKey = nip44.v2.utils.getConversationKey( - privateKey, - receiver - ) - - // Encrypt the content using the generated conversation key. - const encrypted = nip44.v2.encrypt(content, nip44ConversationKey) - - return encrypted - } - - // Throw an error if the login method is nsecBunker (not supported). - if (loginMethod === LoginMethods.nsecBunker) { - throw new Error( - `nip44 encryption is not yet supported for login method '${LoginMethods.nsecBunker}'` - ) - } - - // Throw an error if the login method is undefined or unsupported. - throw new Error('Login method is undefined') + return await context.nip44Encrypt(receiver, content) } /** @@ -273,65 +55,15 @@ export class NostrController extends EventEmitter { nip44Decrypt = async (sender: string, content: string) => { // Retrieve the current login method from the application's redux state. const loginMethod = (store.getState().auth as AuthState).loginMethod + const context = new LoginMethodContext(loginMethod) - // Handle decryption when the login method is via an extension. - if (loginMethod === LoginMethods.extension) { - const nostr = this.getNostrObject() - - // Check if the nostr object supports NIP-44 decryption. - if (!nostr.nip44) { - throw new Error( - `Your nostr extension does not support nip44 encryption & decryption` - ) - } - - // Decrypt the content using NIP-44 provided by the nostr extension. - const decrypted = await nostr.nip44.decrypt(sender, content) - return decrypted as string - } - - // Handle decryption when the login method is via a private key. - if (loginMethod === LoginMethods.privateKey) { - const keys = (store.getState().auth as AuthState).keyPair - - // Check if the private and public key pair is available. - if (!keys) { - throw new Error( - `Login method is ${LoginMethods.privateKey} but private & public key pair is not found.` - ) - } - - // Decode the private key. - const { private: nsec } = keys - const privateKey = nip19.decode(nsec).data as Uint8Array - - // Generate the conversation key using NIP-44 utilities. - const nip44ConversationKey = nip44.v2.utils.getConversationKey( - privateKey, - sender - ) - - // Decrypt the content using the generated conversation key. - const decrypted = nip44.v2.decrypt(content, nip44ConversationKey) - - return decrypted - } - - // Throw an error if the login method is nsecBunker (not supported). - if (loginMethod === LoginMethods.nsecBunker) { - throw new Error( - `nip44 decryption is not yet supported for login method '${LoginMethods.nsecBunker}'` - ) - } - - // Throw an error if the login method is undefined or unsupported. - throw new Error('Login method is undefined') + // Handle decryption + return await context.nip44EDecrypt(sender, content) } /** * Signs an event with private key (if it is present in local storage) or - * with browser extension (if it is present) or - * with nSecBunker instance. + * with browser extension (if it is present) * @param event - unsigned nostr event. * @returns - a promised that is resolved with signed nostr event. */ @@ -339,113 +71,16 @@ export class NostrController extends EventEmitter { event: UnsignedEvent | EventTemplate ): Promise => { const loginMethod = (store.getState().auth as AuthState).loginMethod + const context = new LoginMethodContext(loginMethod) - if (!loginMethod) { - return Promise.reject('No login method found in the browser storage') - } - - if (loginMethod === LoginMethods.nsecBunker) { - // Check if nsecBunker is available - if (!this.bunkerNDK) { - return Promise.reject( - `Login method is ${loginMethod} but bunkerNDK is not created` - ) - } - - if (!this.remoteSigner) { - return Promise.reject( - `Login method is ${loginMethod} but bunkerNDK is not created` - ) - } - - const signedEvent = await this.signWithNsecBunker( - '', - event as NostrEvent - ).catch((err) => { - throw err - }) - - return Promise.resolve(signedEvent as SignedEvent) - } else if (loginMethod === LoginMethods.privateKey) { - const keys = (store.getState().auth as AuthState).keyPair - - if (!keys) { - return Promise.reject( - `Login method is ${loginMethod}, but keys are not found` - ) - } - - const { private: nsec } = keys - const privateKey = nip19.decode(nsec).data as Uint8Array - - const signedEvent = finalizeEvent(event, privateKey) - - verifySignedEvent(signedEvent) - - return Promise.resolve(signedEvent) - } else if (loginMethod === LoginMethods.extension) { - const nostr = this.getNostrObject() - - return (await nostr - .signEvent(event as NostrEvent) - .catch((err: unknown) => { - console.log('Error while signing event: ', err) - - throw err - })) as Event - } else { - return Promise.reject( - `We could not sign the event, none of the signing methods are available` - ) - } + return await context.signEvent(event) } nip04Encrypt = async (receiver: string, content: string): Promise => { const loginMethod = (store.getState().auth as AuthState).loginMethod + const context = new LoginMethodContext(loginMethod) - if (loginMethod === LoginMethods.extension) { - const nostr = this.getNostrObject() - - if (!nostr.nip04) { - throw new Error( - `Your nostr extension does not support nip04 encryption & decryption` - ) - } - - const encrypted = await nostr.nip04.encrypt(receiver, content) - return encrypted - } - - if (loginMethod === LoginMethods.privateKey) { - const keys = (store.getState().auth as AuthState).keyPair - - if (!keys) { - throw new Error( - `Login method is ${LoginMethods.privateKey} but private & public key pair is not found.` - ) - } - - const { private: nsec } = keys - const privateKey = nip19.decode(nsec).data as Uint8Array - - const encrypted = await nip04.encrypt(privateKey, receiver, content) - return encrypted - } - - if (loginMethod === LoginMethods.nsecBunker) { - const user = new NDKUser({ pubkey: receiver }) - - this.remoteSigner?.on('authUrl', (authUrl) => { - this.emit('nsecbunker-auth', authUrl) - }) - - if (!this.remoteSigner) throw new Error('Remote signer is undefined.') - const encrypted = await this.remoteSigner.encrypt(user, content) - - return encrypted - } - - throw new Error('Login method is undefined') + return await context.nip04Encrypt(receiver, content) } /** @@ -457,50 +92,9 @@ export class NostrController extends EventEmitter { */ nip04Decrypt = async (sender: string, content: string): Promise => { const loginMethod = (store.getState().auth as AuthState).loginMethod + const context = new LoginMethodContext(loginMethod) - if (loginMethod === LoginMethods.extension) { - const nostr = this.getNostrObject() - - if (!nostr.nip04) { - throw new Error( - `Your nostr extension does not support nip04 encryption & decryption` - ) - } - - const decrypted = await nostr.nip04.decrypt(sender, content) - return decrypted - } - - if (loginMethod === LoginMethods.privateKey) { - const keys = (store.getState().auth as AuthState).keyPair - - if (!keys) { - throw new Error( - `Login method is ${LoginMethods.privateKey} but private & public key pair is not found.` - ) - } - - const { private: nsec } = keys - const privateKey = nip19.decode(nsec).data as Uint8Array - - const decrypted = await nip04.decrypt(privateKey, sender, content) - return decrypted - } - - if (loginMethod === LoginMethods.nsecBunker) { - const user = new NDKUser({ pubkey: sender }) - - this.remoteSigner?.on('authUrl', (authUrl) => { - this.emit('nsecbunker-auth', authUrl) - }) - - if (!this.remoteSigner) throw new Error('Remote signer is undefined.') - const decrypted = await this.remoteSigner.decrypt(user, content) - - return decrypted - } - - throw new Error('Login method is undefined') + return await context.nip04EDecrypt(sender, content) } /** @@ -523,12 +117,4 @@ export class NostrController extends EventEmitter { return Promise.resolve(pubKey) } - - /** - * Generates NDK Private Signer - * @returns nSecBunker delegated key - */ - generateDelegatedKey = (): string => { - return NDKPrivateKeySigner.generate().privateKey! - } } diff --git a/src/hooks/useLogout.tsx b/src/hooks/useLogout.tsx new file mode 100644 index 0000000..f7f81fd --- /dev/null +++ b/src/hooks/useLogout.tsx @@ -0,0 +1,27 @@ +import { logout as nostrLogout } from 'nostr-login' +import { clear } from '../utils/localStorage' +import { useDispatch } from 'react-redux' +import { Dispatch } from '../store/store' +import { userLogOutAction } from '../store/actions' +import { LoginMethod } from '../store/auth/types' +import { useAppSelector } from './store' + +export const useLogout = () => { + const loginMethod = useAppSelector((state) => state.auth?.loginMethod) + const dispatch: Dispatch = useDispatch() + + const logout = () => { + // Log out of the nostr-login + if (loginMethod === LoginMethod.nostrLogin) { + nostrLogout() + } + + // Reset redux state with the logout + dispatch(userLogOutAction()) + + // Clear the local storage states + clear() + } + + return logout +} diff --git a/src/layouts/Main.tsx b/src/layouts/Main.tsx index 3a1f10e..262e739 100644 --- a/src/layouts/Main.tsx +++ b/src/layouts/Main.tsx @@ -1,34 +1,42 @@ import { Event, kinds } from 'nostr-tools' -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' -import { Outlet } from 'react-router-dom' +import { Outlet, useNavigate, useSearchParams } from 'react-router-dom' import { AppBar } from '../components/AppBar/AppBar' import { LoadingSpinner } from '../components/LoadingSpinner' -import { MetadataController, NostrController } from '../controllers' +import { + AuthController, + MetadataController, + NostrController +} from '../controllers' import { restoreState, - setAuthState, setMetadataEvent, + updateLoginMethod, + updateNostrLoginAuthMethod, updateUserAppData } from '../store/actions' -import { LoginMethods } from '../store/auth/types' import { State } from '../store/rootReducer' import { Dispatch } from '../store/store' import { setUserRobotImage } from '../store/userRobotImage/action' import { - clearAuthToken, - clearState, getRoboHashPicture, getUsersAppData, loadState, - saveNsecBunkerDelegatedKey, subscribeForSigits } from '../utils' import { useAppSelector } from '../hooks' import styles from './style.module.scss' +import { useLogout } from '../hooks/useLogout' +import { LoginMethod } from '../store/auth/types' +import { NostrLoginAuthOptions } from 'nostr-login/dist/types' +import { init as initNostrLogin } from 'nostr-login' export const MainLayout = () => { + const [searchParams] = useSearchParams() + const navigate = useNavigate() const dispatch: Dispatch = useDispatch() + const logout = useLogout() const [isLoading, setIsLoading] = useState(true) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState(`Loading App`) const authState = useSelector((state: State) => state.auth) @@ -37,51 +45,63 @@ export const MainLayout = () => { // Ref to track if `subscribeForSigits` has been called const hasSubscribed = useRef(false) - useEffect(() => { - const metadataController = MetadataController.getInstance() + const navigateAfterLogin = (path: string) => { + const callbackPath = searchParams.get('callbackPath') - const logout = () => { - dispatch( - setAuthState({ - keyPair: undefined, - loggedIn: false, - usersPubkey: undefined, - loginMethod: undefined, - nsecBunkerPubkey: undefined - }) - ) - - dispatch(setMetadataEvent(metadataController.getEmptyMetadataEvent())) - - // clear authToken saved in local storage - clearAuthToken() - clearState() - - // update nsecBunker delegated key - const newDelegatedKey = - NostrController.getInstance().generateDelegatedKey() - saveNsecBunkerDelegatedKey(newDelegatedKey) + if (callbackPath) { + // base64 decoded path + const path = atob(callbackPath) + navigate(path) + return } + navigate(path) + } + + const login = useCallback(async () => { + const nostrController = NostrController.getInstance() + const authController = new AuthController() + const pubkey = await nostrController.capturePublicKey() + + dispatch(updateLoginMethod(LoginMethod.nostrLogin)) + + const redirectPath = + await authController.authAndGetMetadataAndRelaysMap(pubkey) + + if (redirectPath) { + navigateAfterLogin(redirectPath) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [dispatch]) + + useEffect(() => { + const handleNostrAuth = (_: string, opts: NostrLoginAuthOptions) => { + console.log(opts.method) + if (opts.type === 'logout') { + logout() + } else { + dispatch(updateNostrLoginAuthMethod(opts.method)) + login() + } + } + + initNostrLogin({ + darkMode: false, + noBanner: true, + onAuth: handleNostrAuth + }) + + const metadataController = MetadataController.getInstance() + const restoredState = loadState() if (restoredState) { dispatch(restoreState(restoredState)) - const { loggedIn, loginMethod, usersPubkey, nsecBunkerRelays } = - restoredState.auth + const { loggedIn, loginMethod, usersPubkey } = restoredState.auth if (loggedIn) { if (!loginMethod || !usersPubkey) return logout() - if (loginMethod === LoginMethods.nsecBunker) { - if (!nsecBunkerRelays) return logout() - - const nostrController = NostrController.getInstance() - nostrController.nsecBunkerInit(nsecBunkerRelays).then(() => { - nostrController.createNsecBunkerSigner(usersPubkey) - }) - } - const handleMetadataEvent = (event: Event) => { dispatch(setMetadataEvent(event)) } @@ -101,7 +121,8 @@ export const MainLayout = () => { } else { setIsLoading(false) } - }, [dispatch]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) useEffect(() => { if (authState.loggedIn && usersAppData) { diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index 59fdb4f..055b33f 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -74,8 +74,6 @@ export const CreatePage = () => { const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') - const [authUrl, setAuthUrl] = useState() - const [title, setTitle] = useState(`sigit_${formatTimestamp(Date.now())}`) const [selectedFiles, setSelectedFiles] = useState([]) @@ -183,10 +181,6 @@ export const CreatePage = () => { } }) }, [metadata, users]) - // Set up event listener for authentication event - nostrController.on('nsecbunker-auth', (url) => { - setAuthUrl(url) - }) useEffect(() => { if (uploadedFiles) { @@ -761,17 +755,6 @@ export const CreatePage = () => { } } - if (authUrl) { - return ( -