Compare commits
6 Commits
refactor/e
...
refactor/d
Author | SHA1 | Date | |
---|---|---|---|
6186f3dd3d | |||
04c425c32c | |||
aac537c7a2 | |||
2058b900ac | |||
43e375efe9 | |||
0be2159efb |
@ -68,6 +68,7 @@ export const ModalAppDetails = () => {
|
||||
if (isEmptyString(url)) return
|
||||
|
||||
try {
|
||||
|
||||
const u = new URL(url)
|
||||
|
||||
if (isEmptyString(name)) setDetails((prev) => ({ ...prev, name: u.hostname }))
|
||||
@ -119,7 +120,7 @@ export const ModalAppDetails = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const isFormValid = !isEmptyString(url) && !isEmptyString(name)
|
||||
const isFormValid = !isEmptyString(name)
|
||||
|
||||
return (
|
||||
<Modal open={isModalOpened} onClose={handleCloseModal}>
|
||||
@ -130,6 +131,13 @@ export const ModalAppDetails = () => {
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<Input
|
||||
label="Name"
|
||||
fullWidth
|
||||
placeholder="Enter app name"
|
||||
onChange={handleInputChange('name')}
|
||||
value={details.name}
|
||||
/>
|
||||
<Autocomplete
|
||||
options={[]}
|
||||
freeSolo
|
||||
@ -150,13 +158,6 @@ export const ModalAppDetails = () => {
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
label="Name"
|
||||
fullWidth
|
||||
placeholder="Enter app name"
|
||||
onChange={handleInputChange('name')}
|
||||
value={details.name}
|
||||
/>
|
||||
<Input
|
||||
label="Icon"
|
||||
fullWidth
|
||||
|
@ -1,43 +1,20 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||
import { Button } from '@/shared/Button/Button'
|
||||
import { Modal } from '@/shared/Modal/Modal'
|
||||
import { MODAL_PARAMS_KEYS } from '@/types/modal'
|
||||
import { Fade, Stack } from '@mui/material'
|
||||
import { AppLink } from '@/shared/AppLink/AppLink'
|
||||
import { Stack } from '@mui/material'
|
||||
|
||||
export const ModalInitial = () => {
|
||||
const { getModalOpened, createHandleCloseReplace, handleOpen } = useModalSearchParams()
|
||||
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.INITIAL)
|
||||
|
||||
const handleCloseModal = createHandleCloseReplace(MODAL_PARAMS_KEYS.INITIAL)
|
||||
|
||||
const [showAdvancedContent, setShowAdvancedContent] = useState(false)
|
||||
|
||||
const handleShowAdvanced = () => {
|
||||
setShowAdvancedContent(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (isModalOpened) {
|
||||
setShowAdvancedContent(false)
|
||||
}
|
||||
}
|
||||
}, [isModalOpened])
|
||||
|
||||
return (
|
||||
<Modal open={isModalOpened} onClose={handleCloseModal}>
|
||||
<Stack paddingTop={'0.5rem'} gap={'1rem'}>
|
||||
<Button onClick={() => handleOpen(MODAL_PARAMS_KEYS.SIGN_UP)}>Sign up</Button>
|
||||
<Button onClick={() => handleOpen(MODAL_PARAMS_KEYS.LOGIN)}>Login</Button>
|
||||
<AppLink title="Advanced" alignSelf={'center'} onClick={handleShowAdvanced} />
|
||||
|
||||
{showAdvancedContent && (
|
||||
<Fade in>
|
||||
<Button onClick={() => handleOpen(MODAL_PARAMS_KEYS.IMPORT_KEYS)}>Import key</Button>
|
||||
</Fade>
|
||||
)}
|
||||
<Button onClick={() => handleOpen(MODAL_PARAMS_KEYS.IMPORT_KEYS)}>Import key</Button>
|
||||
</Stack>
|
||||
</Modal>
|
||||
)
|
||||
|
@ -8,12 +8,12 @@ import { 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'
|
||||
import { LoadingSpinner } from '@/shared/LoadingSpinner/LoadingSpinner'
|
||||
@ -32,11 +32,14 @@ export const ModalLogin = () => {
|
||||
const navigate = useNavigate()
|
||||
const { hidePassword, inputProps } = usePassword()
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [searchParams] = useSearchParams()
|
||||
const isPopup = searchParams.get('popup') === 'true'
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
reset,
|
||||
register,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<FormInputType>({
|
||||
defaultValues: FORM_DEFAULT_VALUES,
|
||||
@ -79,7 +82,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}${isPopup ? '?popup=true' : ''}`)
|
||||
}, 300)
|
||||
} catch (error: any) {
|
||||
console.log('error', error)
|
||||
notify(error?.message || 'Something went wrong!', 'error')
|
||||
@ -87,6 +93,22 @@ export const ModalLogin = () => {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpened) {
|
||||
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) {
|
||||
|
@ -62,8 +62,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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -43,8 +43,10 @@ const AppPage = () => {
|
||||
|
||||
const { icon = '', name = '', url = '' } = currentApp || {}
|
||||
const appDomain = getDomain(url)
|
||||
const appName = name || appDomain || getShortenNpub(appNpub)
|
||||
const shortAppNpub = getShortenNpub(appNpub)
|
||||
const appName = name || appDomain || shortAppNpub
|
||||
const appAvatarTitle = getAppIconTitle(name || appDomain, appNpub)
|
||||
const isAppNameExists = !!name
|
||||
|
||||
const { timestamp } = connectPerm || {}
|
||||
const connectedOn = connectPerm && timestamp ? `Connected at ${formatTimestampDate(timestamp)}` : 'Not connected'
|
||||
@ -65,13 +67,20 @@ const AppPage = () => {
|
||||
<>
|
||||
<Stack maxHeight={'100%'} overflow={'auto'} alignItems={'flex-start'} height={'100%'}>
|
||||
<IOSBackButton onNavigate={() => navigate(`key/${npub}`)} />
|
||||
<Stack marginBottom={'1rem'} direction={'row'} gap={'1rem'} width={'100%'}>
|
||||
<Stack marginBottom={'1rem'} direction={'row'} gap={'1rem'} width={'100%'} alignItems={'center'}>
|
||||
<StyledAppIcon src={icon}>{appAvatarTitle}</StyledAppIcon>
|
||||
<Box flex={'1'} overflow={'hidden'}>
|
||||
<Stack direction={'row'} alignItems={'center'} gap={'0.5rem'}>
|
||||
<Typography variant="h4" noWrap flex={1}>
|
||||
{appName}
|
||||
</Typography>
|
||||
<Stack direction={'row'} alignItems={'flex-start'} gap={'0.5rem'} marginBottom={'0.5rem'}>
|
||||
<Box display={'flex'} flexDirection={'column'} flex={1}>
|
||||
<Typography variant="h4" noWrap>
|
||||
{appName}
|
||||
</Typography>
|
||||
{isAppNameExists && (
|
||||
<Typography noWrap display={'block'} variant="body1" color={'GrayText'}>
|
||||
{shortAppNpub}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
<IconButton onClick={handleShowAppDetailsModal}>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { FC } from 'react'
|
||||
import { FC } from 'react'
|
||||
import { DbHistory } from '@/modules/db'
|
||||
import { Box, IconButton, Typography } from '@mui/material'
|
||||
import { StyledActivityItem } from './styled'
|
||||
@ -7,15 +7,17 @@ import ClearRoundedIcon from '@mui/icons-material/ClearRounded'
|
||||
import DoneRoundedIcon from '@mui/icons-material/DoneRounded'
|
||||
import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded'
|
||||
import { ACTIONS } from '@/utils/consts'
|
||||
import { getReqActionName } from '@/utils/helpers/helpers'
|
||||
|
||||
type ItemActivityProps = DbHistory
|
||||
|
||||
export const ItemActivity: FC<ItemActivityProps> = ({ allowed, method, timestamp }) => {
|
||||
export const ItemActivity: FC<ItemActivityProps> = (req) => {
|
||||
const { allowed, timestamp } = req
|
||||
return (
|
||||
<StyledActivityItem>
|
||||
<Box display={'flex'} flexDirection={'column'} gap={'0.5rem'} flex={1}>
|
||||
<Typography flex={1} fontWeight={700}>
|
||||
{ACTIONS[method] || method}
|
||||
{getReqActionName(req)}
|
||||
</Typography>
|
||||
<Typography variant="body2">{formatTimestampDate(timestamp)}</Typography>
|
||||
</Box>
|
||||
|
@ -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'
|
||||
@ -18,13 +18,18 @@ import { useTriggerConfirmModal } from './hooks/useTriggerConfirmModal'
|
||||
import { useLiveQuery } from 'dexie-react-hooks'
|
||||
import { checkNpubSyncQuerier } from './utils'
|
||||
import { DOMAIN } from '@/utils/consts'
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
const KeyPage = () => {
|
||||
const { npub = '' } = useParams<{ npub: string }>()
|
||||
const { keys, apps, pending, perms } = useAppSelector((state) => state.content)
|
||||
const [searchParams] = useSearchParams()
|
||||
|
||||
const [isCheckingSync, setIsChecking] = useState(true)
|
||||
const handleStopChecking = () => setIsChecking(false)
|
||||
|
||||
const isSynced = useLiveQuery(checkNpubSyncQuerier(npub, handleStopChecking), [npub], false)
|
||||
|
||||
const isSynced = useLiveQuery(checkNpubSyncQuerier(npub), [npub], false)
|
||||
const { handleOpen } = useModalSearchParams()
|
||||
const { handleEnableBackground, showWarning, isEnabling } = useBackgroundSigning()
|
||||
|
||||
@ -41,6 +46,16 @@ 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)
|
||||
@ -71,7 +86,11 @@ const KeyPage = () => {
|
||||
Connect app
|
||||
</StyledIconButton>
|
||||
|
||||
<StyledIconButton bgcolor_variant="secondary" onClick={handleOpenSettingsModal} withBadge={!isSynced}>
|
||||
<StyledIconButton
|
||||
bgcolor_variant="secondary"
|
||||
onClick={handleOpenSettingsModal}
|
||||
withBadge={!isCheckingSync && !isSynced}
|
||||
>
|
||||
<SettingsIcon />
|
||||
Settings
|
||||
</StyledIconButton>
|
||||
|
@ -9,9 +9,12 @@ type ItemAppProps = DbApp
|
||||
|
||||
export const ItemApp: FC<ItemAppProps> = ({ npub, appNpub, icon, name, url }) => {
|
||||
const appDomain = getDomain(url)
|
||||
const appName = name || appDomain || getShortenNpub(appNpub)
|
||||
const shortAppNpub = getShortenNpub(appNpub)
|
||||
const appName = name || appDomain || shortAppNpub
|
||||
const appIcon = icon || `https://${appDomain}/favicon.ico`
|
||||
const appAvatarTitle = getAppIconTitle(name || appDomain, appNpub)
|
||||
const appAvatarTitle = getAppIconTitle(name || appDomain, appNpub)
|
||||
const isAppNameExists = !!name
|
||||
|
||||
return (
|
||||
<StyledItemAppContainer
|
||||
direction={'row'}
|
||||
@ -21,18 +24,18 @@ export const ItemApp: FC<ItemAppProps> = ({ npub, appNpub, icon, name, url }) =>
|
||||
component={Link}
|
||||
to={`/key/${npub}/app/${appNpub}`}
|
||||
>
|
||||
<Avatar
|
||||
variant="rounded"
|
||||
sx={{ width: 56, height: 56 }}
|
||||
src={appIcon}
|
||||
alt={appName}
|
||||
>
|
||||
<Avatar variant="rounded" sx={{ width: 56, height: 56 }} src={appIcon} alt={appName}>
|
||||
{appAvatarTitle}
|
||||
</Avatar>
|
||||
<Stack>
|
||||
<Typography noWrap display={'block'} variant="body2">
|
||||
<Typography noWrap display={'block'} variant="body1">
|
||||
{appName}
|
||||
</Typography>
|
||||
{isAppNameExists && (
|
||||
<Typography noWrap display={'block'} variant="body2" color={'GrayText'}>
|
||||
{shortAppNpub}
|
||||
</Typography>
|
||||
)}
|
||||
<Typography noWrap display={'block'} variant="caption" color={'GrayText'}>
|
||||
Basic actions
|
||||
</Typography>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { db } from '@/modules/db'
|
||||
|
||||
export const checkNpubSyncQuerier = (npub: string) => async () => {
|
||||
export const checkNpubSyncQuerier = (npub: string, onResolve: () => void) => async () => {
|
||||
const count = await db.syncHistory.where('npub').equals(npub).count()
|
||||
if (!count) onResolve()
|
||||
return count > 0
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { nip19 } from 'nostr-tools'
|
||||
import { ACTIONS, ACTION_TYPE, DOMAIN, NIP46_RELAYS } from '../consts'
|
||||
import { DbPending, DbPerm } from '@/modules/db'
|
||||
import { ACTIONS, ACTION_TYPE, DOMAIN, NIP46_RELAYS, NOAUTHD_URL } from '../consts'
|
||||
import { DbHistory, DbPending, DbPerm } from '@/modules/db'
|
||||
import { MetaEvent } from '@/types/meta-event'
|
||||
|
||||
export async function call(cb: () => any) {
|
||||
@ -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
|
||||
@ -119,7 +134,7 @@ export const getAppIconTitle = (name: string | undefined, appNpub: string) => {
|
||||
return name ? name[0].toLocaleUpperCase() : appNpub.substring(4, 7)
|
||||
}
|
||||
|
||||
export function getReqActionName(req: DbPending) {
|
||||
export function getReqActionName(req: DbPending | DbHistory) {
|
||||
const action = ACTIONS[req.method]
|
||||
if (req.method === 'sign_event') {
|
||||
const kind = getSignReqKind(req)
|
||||
|
@ -13,6 +13,7 @@
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"downlevelIteration": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
|
Reference in New Issue
Block a user