Merge pull request #68 from nostrband/develop

Add logic to confirm after login
This commit is contained in:
Nostr.Band 2024-02-15 08:42:14 +03:00 committed by GitHub
commit 4b1f7564e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 130 additions and 36 deletions

View File

@ -8,12 +8,12 @@ import { CircularProgress, Stack, Typography } from '@mui/material'
import { StyledAppLogo } from './styled'
import { Input } from '@/shared/Input/Input'
import { Button } from '@/shared/Button/Button'
import { useNavigate } from 'react-router-dom'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { useForm } from 'react-hook-form'
import { FormInputType, schema } from './const'
import { yupResolver } from '@hookform/resolvers/yup'
import { DOMAIN } from '@/utils/consts'
import { fetchNip05 } from '@/utils/helpers/helpers'
import { fetchNip05, fetchNpubNames } from '@/utils/helpers/helpers'
import { usePassword } from '@/hooks/usePassword'
import { dbi } from '@/modules/db'
@ -36,6 +36,7 @@ export const ModalLogin = () => {
handleSubmit,
reset,
register,
setValue,
formState: { errors },
} = useForm<FormInputType>({
defaultValues: FORM_DEFAULT_VALUES,
@ -78,7 +79,10 @@ export const ModalLogin = () => {
notify(`Fetched ${k.npub}`, 'success')
dbi.addSynced(k.npub)
cleanUpStates()
navigate(`/key/${k.npub}`)
setTimeout(() => {
// give frontend time to read the new key first
navigate(`/key/${k.npub}`)
}, 300)
} catch (error: any) {
console.log('error', error)
notify(error?.message || 'Something went wrong!', 'error')
@ -86,6 +90,24 @@ export const ModalLogin = () => {
}
}
const [searchParams] = useSearchParams()
useEffect(() => {
if (isModalOpened) {
const isPopup = searchParams.get('popup') === 'true'
const npub = searchParams.get('npub') || ''
const appNpub = searchParams.get('appNpub') || ''
if (isPopup && isModalOpened) {
swicCall('fetchPendingRequests', npub, appNpub)
fetchNpubNames(npub).then(names => {
if (names.length) {
setValue('username', `${names[0]}@${DOMAIN}`)
}
})
}
}
}, [searchParams, isModalOpened, setValue])
useEffect(() => {
return () => {
if (isModalOpened) {

View File

@ -61,8 +61,11 @@ export const ModalSignUp = () => {
setIsLoading(true)
const k: any = await swicCall('generateKey', name)
notify(`Account created for "${name}"`, 'success')
navigate(`/key/${k.npub}`)
setIsLoading(false)
setTimeout(() => {
// give frontend time to read the new key first
navigate(`/key/${k.npub}`)
}, 300)
} catch (error: any) {
notify(error?.message || 'Something went wrong!', 'error')
setIsLoading(false)

View File

@ -46,6 +46,12 @@ interface IAllowCallbackParams {
params?: any
}
class Nip46Backend extends NDKNip46Backend {
public async processEvent(event: NDKEvent) {
this.handleIncomingEvent(event)
}
}
class Nip04KeyHandlingStrategy implements IEventHandlingStrategy {
private privkey: string
private nip04 = new Nip04()
@ -137,10 +143,16 @@ export class NoauthBackend {
private confirmBuffer: Pending[] = []
private accessBuffer: DbPending[] = []
private notifCallback: (() => void) | null = null
private pendingNpubEvents = new Map<string, NDKEvent[]>()
private ndk = new NDK({
explicitRelayUrls: NIP46_RELAYS,
enableOutboxModel: false
})
public constructor(swg: ServiceWorkerGlobalScope) {
this.swg = swg
this.keysModule = new Keys(swg.crypto.subtle)
this.ndk.connect()
const self = this
swg.addEventListener('activate', (event) => {
@ -568,22 +580,21 @@ export class NoauthBackend {
}
private async connectApp({
npub,
appNpub,
appUrl,
perms,
appName = '',
appIcon = ''
}: {
npub: string,
appNpub: string,
appUrl: string,
appName?: string,
appIcon?: string,
perms: string[]
}) {
await dbi.addApp({
npub,
appNpub,
appUrl,
perms,
appName = '',
appIcon = '',
}: {
npub: string
appNpub: string
appUrl: string
appName?: string
appIcon?: string
perms: string[]
}) {
await dbi.addApp({
appNpub: appNpub,
npub: npub,
timestamp: Date.now(),
@ -772,7 +783,7 @@ export class NoauthBackend {
ndk.connect()
const signer = new NDKPrivateKeySigner(sk) // PrivateKeySigner
const backend = new NDKNip46Backend(ndk, signer, () => Promise.resolve(true))
const backend = new Nip46Backend(ndk, signer, () => Promise.resolve(true))
this.keys.push({ npub, backend, signer, ndk, backoff })
// new method
@ -829,6 +840,27 @@ export class NoauthBackend {
r.on('connect', onConnect)
r.on('disconnect', onDisconnect)
}
const pendingEvents = this.pendingNpubEvents.get(npub)
if (pendingEvents) {
this.pendingNpubEvents.delete(npub)
for (const e of pendingEvents) {
backend.processEvent(e)
}
}
}
private async fetchPendingRequests(npub: string, appNpub: string) {
const { data: pubkey } = nip19.decode(npub)
const { data: appPubkey } = nip19.decode(appNpub)
const events = await this.ndk.fetchEvents({
kinds: [KIND_RPC],
"#p": [pubkey as string],
authors: [appPubkey as string]
});
console.log("fetched pending for", npub, events.size)
this.pendingNpubEvents.set(npub, [...events.values()]);
}
public async unlock(npub: string) {
@ -1011,6 +1043,8 @@ export class NoauthBackend {
result = await this.deletePerm(args[0])
} else if (method === 'enablePush') {
result = await this.enablePush()
} else if (method === 'fetchPendingRequests') {
result = await this.fetchPendingRequests(args[0], args[1])
} else {
console.log('unknown method from UI ', method)
}

View File

@ -5,6 +5,7 @@ export let swr: ServiceWorkerRegistration | null = null
const reqs = new Map<number, { ok: (r: any) => void; rej: (r: any) => void }>()
let nextReqId = 1
let onRender: (() => void) | null = null
const queue: (() => Promise<void>)[] = []
export async function swicRegister() {
serviceWorkerRegistration.register({
@ -17,14 +18,17 @@ export async function swicRegister() {
},
})
navigator.serviceWorker.ready.then((r) => {
console.log('sw ready')
navigator.serviceWorker.ready.then(async (r) => {
console.log('sw ready, queue', queue.length)
swr = r
if (navigator.serviceWorker.controller) {
console.log(`This page is currently controlled by: ${navigator.serviceWorker.controller}`)
} else {
console.log('This page is not currently controlled by a service worker.')
}
while (queue.length)
await (queue.shift()!)()
})
navigator.serviceWorker.addEventListener('message', (event) => {
@ -57,19 +61,25 @@ export async function swicCall(method: string, ...args: any[]) {
nextReqId++
return new Promise((ok, rej) => {
if (!swr || !swr.active) {
rej(new Error('No active service worker'))
return
const call = async () => {
if (!swr || !swr.active) {
rej(new Error('No active service worker'))
return
}
reqs.set(id, { ok, rej })
const msg = {
id,
method,
args: [...args],
}
console.log('sending to SW', msg)
swr.active.postMessage(msg)
}
reqs.set(id, { ok, rej })
const msg = {
id,
method,
args: [...args],
}
console.log('sending to SW', msg)
swr.active.postMessage(msg)
if (swr && swr.active) call()
else queue.push(call)
})
}

View File

@ -1,5 +1,5 @@
import { useAppSelector } from '../../store/hooks/redux'
import { Navigate, useParams } from 'react-router-dom'
import { Navigate, useParams, useSearchParams } from 'react-router-dom'
import { Stack } from '@mui/material'
import { StyledIconButton } from './styled'
import { SettingsIcon, ShareIcon } from '@/assets'
@ -23,6 +23,7 @@ import { useCallback } from 'react'
const KeyPage = () => {
const { npub = '' } = useParams<{ npub: string }>()
const { keys, apps, pending, perms } = useAppSelector((state) => state.content)
const [searchParams] = useSearchParams()
const isSynced = useLiveQuery(checkNpubSyncQuerier(npub), [npub], false)
const { handleOpen } = useModalSearchParams()
@ -41,6 +42,14 @@ const KeyPage = () => {
const { prepareEventPendings } = useTriggerConfirmModal(npub, pending, perms)
const isKeyExists = npub.trim().length && key
const isPopup = searchParams.get('popup') === 'true'
console.log({ isKeyExists, isPopup })
if (isPopup && !isKeyExists) {
searchParams.set('login', 'true')
searchParams.set('npub', npub)
const url = `/home?${searchParams.toString()}`
return <Navigate to={url} />
}
if (!isKeyExists) return <Navigate to={`/home`} />
const handleOpenConnectAppModal = () => handleOpen(MODAL_PARAMS_KEYS.CONNECT_APP)

View File

@ -1,5 +1,5 @@
import { nip19 } from 'nostr-tools'
import { ACTIONS, ACTION_TYPE, DOMAIN, NIP46_RELAYS } from '../consts'
import { ACTIONS, ACTION_TYPE, DOMAIN, NIP46_RELAYS, NOAUTHD_URL } from '../consts'
import { DbHistory, DbPending, DbPerm } from '@/modules/db'
import { MetaEvent } from '@/types/meta-event'
@ -97,6 +97,21 @@ export async function fetchNip05(value: string, origin?: string) {
}
}
export async function fetchNpubNames(npub: string) {
try {
const url = `${NOAUTHD_URL}/name?npub=${npub}`
const response = await fetch(url)
const names: {
names: string[]
} = await response.json()
return names.names
} catch (e) {
console.log('Failed to fetch names for', npub, 'error: ' + e)
return []
}
}
export const getDomain = (url: string) => {
try {
return new URL(url).hostname

View File

@ -13,6 +13,7 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"downlevelIteration": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": ".",