Compare commits
4 Commits
e2b3dc13fb
...
7c80643aba
Author | SHA1 | Date | |
---|---|---|---|
7c80643aba | |||
9c545a477c | |||
4d1e672268 | |||
4bc5882ab6 |
@ -1,12 +1,14 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import styles from './style.module.scss'
|
||||
import { PropsWithChildren } from 'react'
|
||||
|
||||
interface Props {
|
||||
desc?: string
|
||||
variant?: 'small' | 'default'
|
||||
}
|
||||
|
||||
export const LoadingSpinner = (props: Props) => {
|
||||
const { desc, variant = 'default' } = props
|
||||
export const LoadingSpinner = (props: PropsWithChildren<Props>) => {
|
||||
const { desc, children, variant = 'default' } = props
|
||||
|
||||
switch (variant) {
|
||||
case 'small':
|
||||
@ -20,16 +22,22 @@ export const LoadingSpinner = (props: Props) => {
|
||||
)
|
||||
|
||||
default:
|
||||
return (
|
||||
return createPortal(
|
||||
<div className={styles.loadingSpinnerOverlay}>
|
||||
<div
|
||||
className={styles.loadingSpinnerContainer}
|
||||
data-variant={variant}
|
||||
>
|
||||
<div className={styles.loadingSpinner}></div>
|
||||
{desc && <p className={styles.loadingSpinnerDesc}>{desc}</p>}
|
||||
{desc && (
|
||||
<div className={styles.loadingSpinnerDesc}>
|
||||
{desc}
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>,
|
||||
document.getElementById('root')!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -42,11 +42,15 @@
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border-top: solid 1px rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
|
@ -18,12 +18,16 @@ import {
|
||||
} from '../../store/actions'
|
||||
import { LoginMethods } from '../../store/auth/types'
|
||||
import { Dispatch } from '../../store/store'
|
||||
import { npubToHex, queryNip05 } from '../../utils'
|
||||
import { npubToHex, queryNip05, timeout } from '../../utils'
|
||||
import { hexToBytes } from '@noble/hashes/utils'
|
||||
import { NIP05_REGEX } from '../../constants'
|
||||
|
||||
import styles from './styles.module.scss'
|
||||
|
||||
import { TimeoutError } from '../../types/errors/TimeoutError'
|
||||
const EXTENSION_LOGIN_DELAY_SECONDS = 2
|
||||
const EXTENSION_LOGIN_TIMEOUT_SECONDS = EXTENSION_LOGIN_DELAY_SECONDS + 10
|
||||
|
||||
export const Nostr = () => {
|
||||
const [searchParams] = useSearchParams()
|
||||
|
||||
@ -36,6 +40,7 @@ export const Nostr = () => {
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
|
||||
const [isExtensionSlow, setIsExtensionSlow] = useState(false)
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [authUrl, setAuthUrl] = useState<string>()
|
||||
|
||||
@ -72,27 +77,39 @@ export const Nostr = () => {
|
||||
}
|
||||
|
||||
const loginWithExtension = async () => {
|
||||
try {
|
||||
// Wait EXTENSION_LOGIN_DELAY_SECONDS before showing extension delay message
|
||||
const waitTimeout = window.setTimeout(() => {
|
||||
setIsExtensionSlow(true)
|
||||
}, 2000)
|
||||
|
||||
setIsLoading(true)
|
||||
setLoadingSpinnerDesc('Capturing pubkey from nostr extension')
|
||||
|
||||
nostrController
|
||||
.capturePublicKey()
|
||||
.then(async (pubkey) => {
|
||||
const pubkey = await nostrController.capturePublicKey()
|
||||
dispatch(updateLoginMethod(LoginMethods.extension))
|
||||
|
||||
setLoadingSpinnerDesc('Authenticating and finding metadata')
|
||||
const redirectPath =
|
||||
await authController.authAndGetMetadataAndRelaysMap(pubkey)
|
||||
const redirectPath = await Promise.race([
|
||||
authController.authAndGetMetadataAndRelaysMap(pubkey),
|
||||
timeout(EXTENSION_LOGIN_TIMEOUT_SECONDS * 1000)
|
||||
])
|
||||
|
||||
if (redirectPath) navigateAfterLogin(redirectPath)
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('Error capturing public key from nostr extension: ' + err)
|
||||
})
|
||||
.finally(() => {
|
||||
if (redirectPath) {
|
||||
window.clearTimeout(waitTimeout)
|
||||
navigateAfterLogin(redirectPath)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof TimeoutError) {
|
||||
toast.error("Extension didn't respond in time")
|
||||
} else {
|
||||
toast.error('Error capturing public key from nostr extension: ' + error)
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
setLoadingSpinnerDesc('')
|
||||
})
|
||||
setIsExtensionSlow(false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -354,7 +371,25 @@ export const Nostr = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
|
||||
{isLoading && (
|
||||
<LoadingSpinner desc={loadingSpinnerDesc}>
|
||||
{isExtensionSlow && (
|
||||
<>
|
||||
<p>Extension is not responding</p>
|
||||
<Button
|
||||
fullWidth
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setLoadingSpinnerDesc('')
|
||||
setIsLoading(false)
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</LoadingSpinner>
|
||||
)}
|
||||
|
||||
{isNostrExtensionAvailable && (
|
||||
<>
|
||||
|
6
src/types/errors/TimeoutError.ts
Normal file
6
src/types/errors/TimeoutError.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export class TimeoutError extends Error {
|
||||
constructor() {
|
||||
super('Timeout')
|
||||
this.name = this.constructor.name
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import { TimeoutError } from '../types/errors/TimeoutError.ts'
|
||||
import { CurrentUserFile } from '../types/file.ts'
|
||||
import { SigitFile } from './file.ts'
|
||||
|
||||
@ -63,7 +64,7 @@ export const timeout = (ms: number = 60000) => {
|
||||
// Set a timeout using setTimeout
|
||||
setTimeout(() => {
|
||||
// Reject the promise with an Error indicating a timeout
|
||||
reject(new Error('Timeout'))
|
||||
reject(new TimeoutError())
|
||||
}, ms) // Timeout duration in milliseconds
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user