166 lines
4.8 KiB
TypeScript
166 lines
4.8 KiB
TypeScript
import { Event, kinds } from 'nostr-tools'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
import { useDispatch, useSelector } from 'react-redux'
|
|
import { Outlet } from 'react-router-dom'
|
|
import { AppBar } from '../components/AppBar/AppBar'
|
|
import { LoadingSpinner } from '../components/LoadingSpinner'
|
|
import { MetadataController, NostrController } from '../controllers'
|
|
import {
|
|
restoreState,
|
|
setAuthState,
|
|
setMetadataEvent,
|
|
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'
|
|
|
|
export const MainLayout = () => {
|
|
const dispatch: Dispatch = useDispatch()
|
|
const [isLoading, setIsLoading] = useState(true)
|
|
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState(`Loading App`)
|
|
const authState = useSelector((state: State) => state.auth)
|
|
const usersAppData = useAppSelector((state) => state.userAppData)
|
|
|
|
// Ref to track if `subscribeForSigits` has been called
|
|
const hasSubscribed = useRef(false)
|
|
|
|
useEffect(() => {
|
|
const metadataController = MetadataController.getInstance()
|
|
|
|
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)
|
|
}
|
|
|
|
const restoredState = loadState()
|
|
if (restoredState) {
|
|
dispatch(restoreState(restoredState))
|
|
|
|
const { loggedIn, loginMethod, usersPubkey, nsecBunkerRelays } =
|
|
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))
|
|
}
|
|
|
|
metadataController.on(usersPubkey, (kind: number, event: Event) => {
|
|
if (kind === kinds.Metadata) {
|
|
handleMetadataEvent(event)
|
|
}
|
|
})
|
|
|
|
metadataController.findMetadata(usersPubkey).then((metadataEvent) => {
|
|
if (metadataEvent) handleMetadataEvent(metadataEvent)
|
|
})
|
|
} else {
|
|
setIsLoading(false)
|
|
}
|
|
} else {
|
|
setIsLoading(false)
|
|
}
|
|
}, [dispatch])
|
|
|
|
useEffect(() => {
|
|
if (authState.loggedIn && usersAppData) {
|
|
const pubkey = authState.usersPubkey || authState.keyPair?.public
|
|
|
|
if (pubkey && !hasSubscribed.current) {
|
|
// Call `subscribeForSigits` only if it hasn't been called before
|
|
// #193 disabled websocket subscribtion, until #194 is done
|
|
subscribeForSigits(pubkey)
|
|
|
|
// Mark `subscribeForSigits` as called
|
|
hasSubscribed.current = true
|
|
}
|
|
}
|
|
}, [authState, usersAppData])
|
|
|
|
/**
|
|
* When authState change user logged in / or app reloaded
|
|
* we set robohash avatar in the global state based on user npub
|
|
* so that avatar will be consistent across the app when kind 0 is empty
|
|
*/
|
|
useEffect(() => {
|
|
if (authState && authState.loggedIn) {
|
|
const pubkey = authState.usersPubkey || authState.keyPair?.public
|
|
|
|
if (pubkey) {
|
|
dispatch(setUserRobotImage(getRoboHashPicture(pubkey)))
|
|
}
|
|
|
|
setIsLoading(true)
|
|
setLoadingSpinnerDesc(`Loading SIGit history...`)
|
|
getUsersAppData()
|
|
.then((appData) => {
|
|
if (appData) {
|
|
dispatch(updateUserAppData(appData))
|
|
}
|
|
})
|
|
.finally(() => setIsLoading(false))
|
|
}
|
|
}, [authState, dispatch])
|
|
|
|
if (isLoading) return <LoadingSpinner desc={loadingSpinnerDesc} />
|
|
|
|
const isDev = import.meta.env.MODE === 'development'
|
|
|
|
return (
|
|
<>
|
|
<AppBar />
|
|
<main
|
|
className={styles.main}
|
|
// Add dev debug flag to show special dev css
|
|
// (visible marks border on verify)
|
|
{...(isDev && {
|
|
'data-dev': true
|
|
})}
|
|
>
|
|
<Outlet />
|
|
</main>
|
|
</>
|
|
)
|
|
}
|