This commit is contained in:
Bekbolsun 2024-02-06 22:47:40 +06:00
parent 14940a4345
commit 9d565ddbde
14 changed files with 231 additions and 231 deletions

View File

@ -12,7 +12,6 @@ import { useState } from 'react'
import { swicCall } from '@/modules/swic' import { swicCall } from '@/modules/swic'
import { ACTION_TYPE } from '@/utils/consts' import { ACTION_TYPE } from '@/utils/consts'
export const ModalConfirmConnect = () => { export const ModalConfirmConnect = () => {
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams() const { getModalOpened, createHandleCloseReplace } = useModalSearchParams()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_CONNECT) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_CONNECT)
@ -45,7 +44,7 @@ export const ModalConfirmConnect = () => {
sp.delete('appNpub') sp.delete('appNpub')
sp.delete('reqId') sp.delete('reqId')
await swicCall('confirm', pendingReqId, false, false) await swicCall('confirm', pendingReqId, false, false)
} },
}, },
) )
const closeModalAfterRequest = createHandleCloseReplace( const closeModalAfterRequest = createHandleCloseReplace(
@ -55,27 +54,27 @@ export const ModalConfirmConnect = () => {
sp.delete('appNpub') sp.delete('appNpub')
sp.delete('reqId') sp.delete('reqId')
}, },
} },
) )
async function confirmPending( async function confirmPending(
id: string, id: string,
allow: boolean, allow: boolean,
remember: boolean, remember: boolean,
options?: any options?: any,
) { ) {
call(async () => { call(async () => {
await swicCall('confirm', id, allow, remember, options) await swicCall('confirm', id, allow, remember, options)
console.log('confirmed', id, allow, remember, options) console.log('confirmed', id, allow, remember, options)
closeModalAfterRequest() closeModalAfterRequest()
}) })
if (isPopup) window.close(); if (isPopup) window.close()
} }
const allow = () => { const allow = () => {
const options: any = {}; const options: any = {}
if (selectedActionType === ACTION_TYPE.BASIC) if (selectedActionType === ACTION_TYPE.BASIC)
options.perms = [ACTION_TYPE.BASIC]; options.perms = [ACTION_TYPE.BASIC]
// else // else
// options.perms = ['connect','get_public_key']; // options.perms = ['connect','get_public_key'];
confirmPending(pendingReqId, true, true, options) confirmPending(pendingReqId, true, true, options)
@ -86,16 +85,16 @@ export const ModalConfirmConnect = () => {
} }
if (isPopup) { if (isPopup) {
document.addEventListener('visibilitychange', function() { document.addEventListener('visibilitychange', function () {
if (document.visibilityState == 'hidden') { if (document.visibilityState === 'hidden') {
disallow(); disallow()
} }
}) })
} }
return ( return (
<Modal <Modal
open={isModalOpened} open={isModalOpened}
withCloseButton={!isPopup} withCloseButton={!isPopup}
onClose={!isPopup ? handleCloseModal : undefined} onClose={!isPopup ? handleCloseModal : undefined}
> >
@ -147,16 +146,10 @@ export const ModalConfirmConnect = () => {
/> />
</StyledToggleButtonsGroup> </StyledToggleButtonsGroup>
<Stack direction={'row'} gap={'1rem'}> <Stack direction={'row'} gap={'1rem'}>
<StyledButton <StyledButton onClick={disallow} varianttype='secondary'>
onClick={disallow}
varianttype='secondary'
>
Disallow Disallow
</StyledButton> </StyledButton>
<StyledButton <StyledButton fullWidth onClick={allow}>
fullWidth
onClick={allow}
>
{/* Allow {selectedActionType} actions */} {/* Allow {selectedActionType} actions */}
Connect Connect
</StyledButton> </StyledButton>

View File

@ -94,10 +94,11 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
sp.delete('appNpub') sp.delete('appNpub')
sp.delete('reqId') sp.delete('reqId')
selectedPendingRequests.forEach( selectedPendingRequests.forEach(
async (req) => await swicCall('confirm', req.id, false, false), async (req) =>
await swicCall('confirm', req.id, false, false),
) )
} },
} },
) )
const closeModalAfterRequest = createHandleCloseReplace( const closeModalAfterRequest = createHandleCloseReplace(
@ -106,8 +107,8 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
onClose: (sp) => { onClose: (sp) => {
sp.delete('appNpub') sp.delete('appNpub')
sp.delete('reqId') sp.delete('reqId')
} },
} },
) )
async function confirmPending(allow: boolean) { async function confirmPending(allow: boolean) {
@ -119,7 +120,7 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
}) })
}) })
closeModalAfterRequest() closeModalAfterRequest()
if (isPopup) window.close(); if (isPopup) window.close()
} }
const handleChangeCheckbox = (reqId: string) => () => { const handleChangeCheckbox = (reqId: string) => () => {
@ -141,8 +142,8 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
if (isPopup) { if (isPopup) {
document.addEventListener('visibilitychange', () => { document.addEventListener('visibilitychange', () => {
if (document.visibilityState == 'hidden') { if (document.visibilityState === 'hidden') {
confirmPending(false); confirmPending(false)
} }
}) })
} }

View File

@ -1,17 +1,7 @@
import * as yup from 'yup' import * as yup from 'yup'
export const schema = yup.object().shape({ export const schema = yup.object().shape({
username: yup username: yup.string().required(),
.string()
.test('Domain validation', 'The domain is required!', function (value) {
if (!value || !value.trim().length) return false
const USERNAME_WITH_DOMAIN_REGEXP = new RegExp(
/^[\w-.]+@([\w-]+\.)+[\w-]{2,8}$/g,
)
return USERNAME_WITH_DOMAIN_REGEXP.test(value)
})
.required(),
password: yup.string().required().min(4), password: yup.string().required().min(4),
}) })

View File

@ -10,7 +10,7 @@ import { Button } from '@/shared/Button/Button'
import { CheckmarkIcon } from '@/assets' import { CheckmarkIcon } from '@/assets'
import { swicCall } from '@/modules/swic' import { swicCall } from '@/modules/swic'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { DOMAIN, NOAUTHD_URL } from '@/utils/consts' import { DOMAIN } from '@/utils/consts'
import { fetchNip05 } from '@/utils/helpers/helpers' import { fetchNip05 } from '@/utils/helpers/helpers'
export const ModalSignUp = () => { export const ModalSignUp = () => {
@ -36,20 +36,17 @@ export const ModalSignUp = () => {
} }
} }
const inputHelperText = enteredValue const inputHelperText = enteredValue ? (
? (
isAvailable ? ( isAvailable ? (
<> <>
<CheckmarkIcon /> Available <CheckmarkIcon /> Available
</> </>
) : ( ) : (
<> <>Already taken</>
Already taken
</>
) )
) : ( ) : (
"Don't worry, username can be changed later." "Don't worry, username can be changed later."
); )
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
const name = enteredValue.trim() const name = enteredValue.trim()
@ -96,13 +93,13 @@ export const ModalSignUp = () => {
helperTextProps={{ helperTextProps={{
sx: { sx: {
'&.helper_text': { '&.helper_text': {
color: enteredValue && isAvailable color:
? theme.palette.success.main enteredValue && isAvailable
: (enteredValue && !isAvailable ? theme.palette.success.main
? theme.palette.error.main : enteredValue && !isAvailable
: theme.palette.textSecondaryDecorate.main ? theme.palette.error.main
) : theme.palette
, .textSecondaryDecorate.main,
}, },
}, },
}} }}

40
src/hooks/useProfile.ts Normal file
View File

@ -0,0 +1,40 @@
import { useCallback, useEffect, useState } from 'react'
import { fetchProfile } from '@/modules/nostr'
import { MetaEvent } from '@/types/meta-event'
import { getProfileUsername, getShortenNpub } from '@/utils/helpers/helpers'
import { useAppSelector } from '@/store/hooks/redux'
import { selectKeyByNpub } from '@/store'
const getFirstLetter = (text: string | undefined): string | null => {
if (!text || text.trim().length === 0) return null
return text.substring(0, 1).toUpperCase()
}
export const useProfile = (npub: string) => {
const [profile, setProfile] = useState<MetaEvent | null>(null)
const currentKey = useAppSelector((state) => selectKeyByNpub(state, npub))
const userName = getProfileUsername(profile) || currentKey?.name
const userAvatar = profile?.info?.picture || ''
const avatarTitle = getFirstLetter(userName)
const loadProfile = useCallback(async () => {
try {
const response = await fetchProfile(npub)
setProfile(response)
} catch (error) {
console.error('Failed to fetch profile:', error)
}
}, [npub])
useEffect(() => {
loadProfile()
}, [loadProfile])
return {
profile,
userName: userName || getShortenNpub(npub),
userAvatar,
avatarTitle,
}
}

View File

@ -2,35 +2,20 @@ import { Avatar, Stack, Toolbar, Typography } from '@mui/material'
import { AppLogo } from '../../assets' import { AppLogo } from '../../assets'
import { StyledAppBar, StyledAppName } from './styled' import { StyledAppBar, StyledAppName } from './styled'
import { Menu } from './components/Menu' import { Menu } from './components/Menu'
import { useParams } from 'react-router-dom' import { useNavigate, useParams } from 'react-router-dom'
import { useCallback, useEffect, useState } from 'react'
import { MetaEvent } from '@/types/meta-event'
import { fetchProfile } from '@/modules/nostr'
import { ProfileMenu } from './components/ProfileMenu' import { ProfileMenu } from './components/ProfileMenu'
import { getShortenNpub } from '@/utils/helpers/helpers' import { useProfile } from '@/hooks/useProfile'
export const Header = () => { export const Header = () => {
const { npub = '' } = useParams<{ npub: string }>() const { npub = '' } = useParams<{ npub: string }>()
const [profile, setProfile] = useState<MetaEvent | null>(null) const { userName, userAvatar, avatarTitle } = useProfile(npub)
const showProfile = Boolean(npub)
const load = useCallback(async () => { const navigate = useNavigate()
if (!npub) return setProfile(null)
try { const handleNavigate = () => {
const response = await fetchProfile(npub) navigate(`/key/${npub}`)
setProfile(response as any) }
} catch (e) {
return setProfile(null)
}
}, [npub])
useEffect(() => {
load()
}, [load])
const showProfile = Boolean(npub || profile)
const userName = profile?.info?.name || getShortenNpub(npub)
const userAvatar = profile?.info?.picture || ''
return ( return (
<StyledAppBar position='fixed'> <StyledAppBar position='fixed'>
@ -41,17 +26,30 @@ export const Header = () => {
alignItems={'center'} alignItems={'center'}
width={'100%'} width={'100%'}
> >
{showProfile ? ( {showProfile && (
<Stack <Stack
gap={'1rem'} gap={'1rem'}
direction={'row'} direction={'row'}
alignItems={'center'} alignItems={'center'}
flex={1} flex={1}
> >
<Avatar src={userAvatar} alt={userName} /> <Avatar
<Typography fontWeight={600}>{userName}</Typography> src={userAvatar}
alt={userName}
onClick={handleNavigate}
>
{avatarTitle}
</Avatar>
<Typography
fontWeight={600}
onClick={handleNavigate}
>
{userName}
</Typography>
</Stack> </Stack>
) : ( )}
{!showProfile && (
<StyledAppName> <StyledAppName>
<AppLogo /> <AppLogo />
<span>Nsec.app</span> <span>Nsec.app</span>

View File

@ -0,0 +1,31 @@
import { useProfile } from '@/hooks/useProfile'
import { DbKey } from '@/modules/db'
import { Avatar, ListItemIcon, MenuItem, Typography } from '@mui/material'
import React, { FC } from 'react'
type ListItemProfileProps = {
onClickItem: () => void
} & DbKey
export const ListItemProfile: FC<ListItemProfileProps> = ({
onClickItem,
npub,
}) => {
const { userName, userAvatar, avatarTitle } = useProfile(npub)
return (
<MenuItem sx={{ gap: '0.5rem' }} onClick={onClickItem}>
<ListItemIcon>
<Avatar
src={userAvatar}
alt={userName}
sx={{ width: 36, height: 36 }}
>
{avatarTitle}
</Avatar>
</ListItemIcon>
<Typography variant='body2' noWrap>
{userName}
</Typography>
</MenuItem>
)
}

View File

@ -1,13 +1,7 @@
import { DbKey } from '@/modules/db' import { DbKey } from '@/modules/db'
import { getShortenNpub } from '@/utils/helpers/helpers' import { Stack } from '@mui/material'
import { import { FC } from 'react'
Avatar, import { ListItemProfile } from './ListItemProfile'
ListItemIcon,
MenuItem,
Stack,
Typography,
} from '@mui/material'
import React, { FC } from 'react'
type ListProfilesProps = { type ListProfilesProps = {
keys: DbKey[] keys: DbKey[]
@ -21,26 +15,12 @@ export const ListProfiles: FC<ListProfilesProps> = ({
return ( return (
<Stack maxHeight={'10rem'} overflow={'auto'}> <Stack maxHeight={'10rem'} overflow={'auto'}>
{keys.map((key) => { {keys.map((key) => {
const userName =
key?.profile?.info?.name || getShortenNpub(key.npub)
const userAvatar = key?.profile?.info?.picture || ''
return ( return (
<MenuItem <ListItemProfile
sx={{ gap: '0.5rem' }} {...key}
onClick={() => onClickItem(key)}
key={key.npub} key={key.npub}
> onClickItem={() => onClickItem(key)}
<ListItemIcon> />
<Avatar
src={userAvatar}
alt={userName}
sx={{ width: 36, height: 36 }}
/>
</ListItemIcon>
<Typography variant='body2' noWrap>
{userName}
</Typography>
</MenuItem>
) )
})} })}
</Stack> </Stack>

View File

@ -8,26 +8,26 @@ import {
TypographyProps, TypographyProps,
styled, styled,
} from '@mui/material' } from '@mui/material'
import { getShortenNpub } from '../../../utils/helpers/helpers'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { useProfile } from '@/hooks/useProfile'
type ItemKeyProps = DbKey type ItemKeyProps = DbKey
export const ItemKey: FC<ItemKeyProps> = (props) => { export const ItemKey: FC<ItemKeyProps> = (props) => {
const { npub, profile } = props const { npub } = props
const navigate = useNavigate() const navigate = useNavigate()
const { userName, userAvatar, avatarTitle } = useProfile(npub)
const handleNavigate = () => { const handleNavigate = () => {
navigate('/key/' + npub) navigate('/key/' + npub)
} }
const { name = '', picture = '' } = profile?.info || {}
const userName = name || getShortenNpub(npub)
const userAvatar = picture || ''
return ( return (
<StyledKeyContainer onClick={handleNavigate}> <StyledKeyContainer onClick={handleNavigate}>
<Stack direction={'row'} alignItems={'center'} gap='1rem'> <Stack direction={'row'} alignItems={'center'} gap='1rem'>
<Avatar src={userAvatar} alt={userName} /> <Avatar src={userAvatar} alt={userName}>
{avatarTitle}
</Avatar>
<StyledText variant='body1'>{userName}</StyledText> <StyledText variant='body1'>{userName}</StyledText>
</Stack> </Stack>
</StyledKeyContainer> </StyledKeyContainer>

View File

@ -11,7 +11,6 @@ import { ModalSettings } from '@/components/Modal/ModalSettings/ModalSettings'
import { ModalExplanation } from '@/components/Modal/ModalExplanation/ModalExplanation' import { ModalExplanation } from '@/components/Modal/ModalExplanation/ModalExplanation'
import { ModalConfirmConnect } from '@/components/Modal/ModalConfirmConnect/ModalConfirmConnect' import { ModalConfirmConnect } from '@/components/Modal/ModalConfirmConnect/ModalConfirmConnect'
import { ModalConfirmEvent } from '@/components/Modal/ModalConfirmEvent/ModalConfirmEvent' import { ModalConfirmEvent } from '@/components/Modal/ModalConfirmEvent/ModalConfirmEvent'
import { useProfile } from './hooks/useProfile'
import { useBackgroundSigning } from './hooks/useBackgroundSigning' import { useBackgroundSigning } from './hooks/useBackgroundSigning'
import { BackgroundSigningWarning } from './components/BackgroundSigningWarning' import { BackgroundSigningWarning } from './components/BackgroundSigningWarning'
import UserValueSection from './components/UserValueSection' import UserValueSection from './components/UserValueSection'
@ -22,23 +21,23 @@ import { DOMAIN } from '@/utils/consts'
const KeyPage = () => { const KeyPage = () => {
const { npub = '' } = useParams<{ npub: string }>() const { npub = '' } = useParams<{ npub: string }>()
const { keys, apps, pending, perms } = useAppSelector((state) => state.content) const { keys, apps, pending, perms } = useAppSelector(
(state) => state.content,
)
const isSynced = useLiveQuery(checkNpubSyncQuerier(npub), [npub], false) const isSynced = useLiveQuery(checkNpubSyncQuerier(npub), [npub], false)
const { handleOpen } = useModalSearchParams() const { handleOpen } = useModalSearchParams()
// const { userNameWithPrefix } = useProfile(npub)
const { handleEnableBackground, showWarning, isEnabling } = const { handleEnableBackground, showWarning, isEnabling } =
useBackgroundSigning() useBackgroundSigning()
const key = keys.find(k => k.npub === npub) const key = keys.find((k) => k.npub === npub)
let username = '' let username = ''
if (key?.name) { if (key?.name) {
if (key.name.includes('@')) if (key.name.includes('@')) username = key.name
username = key.name else username = `${key?.name}@${DOMAIN}`
else }
username = `${key?.name}@${DOMAIN}`
}
const filteredApps = apps.filter((a) => a.npub === npub) const filteredApps = apps.filter((a) => a.npub === npub)
const { prepareEventPendings } = useTriggerConfirmModal( const { prepareEventPendings } = useTriggerConfirmModal(

View File

@ -1,31 +0,0 @@
import { useCallback, useEffect, useState } from 'react'
import { fetchProfile } from '@/modules/nostr'
import { MetaEvent } from '@/types/meta-event'
import { getProfileUsername } from '@/utils/helpers/helpers'
import { DOMAIN } from '@/utils/consts'
export const useProfile = (npub: string) => {
const [profile, setProfile] = useState<MetaEvent | null>(null)
const userName = getProfileUsername(profile, npub)
// FIXME use nip05?
const userNameWithPrefix = userName + '@' + DOMAIN
const loadProfile = useCallback(async () => {
try {
const response = await fetchProfile(npub)
setProfile(response)
} catch (error) {
console.error('Failed to fetch profile:', error)
}
}, [npub])
useEffect(() => {
loadProfile()
}, [loadProfile])
return {
profile,
userNameWithPrefix,
}
}

View File

@ -1,7 +1,6 @@
import { Suspense, lazy } from 'react' import { Suspense, lazy } from 'react'
import { Route, Routes, Navigate } from 'react-router-dom' import { Route, Routes, Navigate } from 'react-router-dom'
import HomePage from '../pages/HomePage/Home.Page' import HomePage from '../pages/HomePage/Home.Page'
import WelcomePage from '../pages/Welcome.Page'
import { Layout } from '../layout/Layout' import { Layout } from '../layout/Layout'
import { CircularProgress, Stack } from '@mui/material' import { CircularProgress, Stack } from '@mui/material'

View File

@ -44,6 +44,10 @@ export type AppDispatch = typeof store.dispatch
export const selectKeys = (state: RootState) => state.content.keys export const selectKeys = (state: RootState) => state.content.keys
export const selectKeyByNpub = (state: RootState, npub: string) => {
return state.content.keys.find((key) => key.npub === npub)
}
export const selectAppsByNpub = memoizeOne((state: RootState, npub: string) => { export const selectAppsByNpub = memoizeOne((state: RootState, npub: string) => {
return state.content.apps.filter((app) => app.npub === npub) return state.content.apps.filter((app) => app.npub === npub)
}, isDeepEqual) }, isDeepEqual)

View File

@ -1,101 +1,100 @@
import { nip19 } from "nostr-tools"; import { nip19 } from 'nostr-tools'
import { ACTION_TYPE, NIP46_RELAYS } from "../consts"; import { ACTION_TYPE, NIP46_RELAYS } from '../consts'
import { DbPending } from "@/modules/db"; import { DbPending } from '@/modules/db'
import { MetaEvent } from "@/types/meta-event"; import { MetaEvent } from '@/types/meta-event'
export async function call(cb: () => any) { export async function call(cb: () => any) {
try { try {
return await cb(); return await cb()
} catch (e) { } catch (e) {
console.log(`Error: ${e}`); console.log(`Error: ${e}`)
} }
} }
export const getShortenNpub = (npub = "") => { export const getShortenNpub = (npub = '') => {
return npub.substring(0, 10) + "..." + npub.slice(-4); return npub.substring(0, 10) + '...' + npub.slice(-4)
}; }
export const getProfileUsername = (profile: MetaEvent | null, npub: string) => { export const getProfileUsername = (profile: MetaEvent | null) => {
return ( if (!profile) return null
profile?.info?.name || profile?.info?.display_name || getShortenNpub(npub) return profile?.info?.name || profile?.info?.display_name
); }
};
export const getBunkerLink = (npub = "") => { export const getBunkerLink = (npub = '') => {
if (!npub) return ""; if (!npub) return ''
const { data: pubkey } = nip19.decode(npub); const { data: pubkey } = nip19.decode(npub)
return `bunker://${pubkey}?relay=${NIP46_RELAYS[0]}`; return `bunker://${pubkey}?relay=${NIP46_RELAYS[0]}`
}; }
export async function askNotificationPermission() { export async function askNotificationPermission() {
return new Promise<void>((ok, rej) => { return new Promise<void>((ok, rej) => {
// Let's check if the browser supports notifications // Let's check if the browser supports notifications
if (!("Notification" in window)) { if (!('Notification' in window)) {
rej("This browser does not support notifications."); rej('This browser does not support notifications.')
} else { } else {
Notification.requestPermission().then(() => { Notification.requestPermission().then(() => {
if (Notification.permission === "granted") ok(); if (Notification.permission === 'granted') ok()
else rej(); else rej()
}); })
} }
}); })
} }
export function getSignReqKind(req: DbPending): number | undefined { export function getSignReqKind(req: DbPending): number | undefined {
try { try {
const data = JSON.parse(JSON.parse(req.params)[0]); const data = JSON.parse(JSON.parse(req.params)[0])
return data.kind; return data.kind
} catch {} } catch {}
return undefined; return undefined
} }
export function getReqPerm(req: DbPending): string { export function getReqPerm(req: DbPending): string {
if (req.method === "sign_event") { if (req.method === 'sign_event') {
const kind = getSignReqKind(req); const kind = getSignReqKind(req)
if (kind !== undefined) return `${req.method}:${kind}`; if (kind !== undefined) return `${req.method}:${kind}`
} }
return req.method; return req.method
} }
export function isPackagePerm(perm: string, reqPerm: string) { export function isPackagePerm(perm: string, reqPerm: string) {
if (perm === ACTION_TYPE.BASIC) { if (perm === ACTION_TYPE.BASIC) {
switch (reqPerm) { switch (reqPerm) {
case "connect": case 'connect':
case "get_public_key": case 'get_public_key':
case "nip04_decrypt": case 'nip04_decrypt':
case "nip04_encrypt": case 'nip04_encrypt':
case "sign_event:0": case 'sign_event:0':
case "sign_event:1": case 'sign_event:1':
case "sign_event:3": case 'sign_event:3':
case "sign_event:6": case 'sign_event:6':
case "sign_event:7": case 'sign_event:7':
case "sign_event:9734": case 'sign_event:9734':
case "sign_event:10002": case 'sign_event:10002':
case "sign_event:30023": case 'sign_event:30023':
case "sign_event:10000": case 'sign_event:10000':
return true; return true
} }
} }
return false; return false
} }
export async function fetchNip05(value: string, origin?: string) { export async function fetchNip05(value: string, origin?: string) {
try { try {
const [username, domain] = value.split("@"); const [username, domain] = value.split('@')
if (!origin) origin = `https://${domain}` if (!origin) origin = `https://${domain}`
const response = await fetch( const response = await fetch(
`${origin}/.well-known/nostr.json?name=${username}` `${origin}/.well-known/nostr.json?name=${username}`,
); )
const getNpub: { const getNpub: {
names: { names: {
[name: string]: string; [name: string]: string
}; }
} = await response.json(); } = await response.json()
const pubkey = getNpub.names[username]; const pubkey = getNpub.names[username]
return nip19.npubEncode(pubkey); return nip19.npubEncode(pubkey)
} catch (e) { } catch (e) {
console.log("Failed to fetch nip05", value, "error: " + e); console.log('Failed to fetch nip05', value, 'error: ' + e)
return '' return ''
} }
} }