Merge branch 'develop' of https://github.com/nostrband/noauth into develop
This commit is contained in:
commit
cddf0b7805
32
package-lock.json
generated
32
package-lock.json
generated
@ -23,7 +23,9 @@
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"crypto": "^1.0.1",
|
||||
"date-fns": "^3.3.1",
|
||||
"dexie": "^3.2.4",
|
||||
"dexie-react-hooks": "^1.1.7",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"nostr-tools": "^1.17.0",
|
||||
@ -7245,6 +7247,15 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz",
|
||||
"integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
@ -7465,6 +7476,16 @@
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dexie-react-hooks": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/dexie-react-hooks/-/dexie-react-hooks-1.1.7.tgz",
|
||||
"integrity": "sha512-Lwv5W0Hk+uOW3kGnsU9GZoR1er1B7WQ5DSdonoNG+focTNeJbHW6vi6nBoX534VKI3/uwHebYzSw1fwY6a7mTw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=16",
|
||||
"dexie": "^3.2 || ^4.0.1-alpha",
|
||||
"react": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/didyoumean": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
@ -23335,6 +23356,11 @@
|
||||
"whatwg-url": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"date-fns": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz",
|
||||
"integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
@ -23496,6 +23522,12 @@
|
||||
"resolved": "https://registry.npmjs.org/dexie/-/dexie-3.2.4.tgz",
|
||||
"integrity": "sha512-VKoTQRSv7+RnffpOJ3Dh6ozknBqzWw/F3iqMdsZg958R0AS8AnY9x9d1lbwENr0gzeGJHXKcGhAMRaqys6SxqA=="
|
||||
},
|
||||
"dexie-react-hooks": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/dexie-react-hooks/-/dexie-react-hooks-1.1.7.tgz",
|
||||
"integrity": "sha512-Lwv5W0Hk+uOW3kGnsU9GZoR1er1B7WQ5DSdonoNG+focTNeJbHW6vi6nBoX534VKI3/uwHebYzSw1fwY6a7mTw==",
|
||||
"requires": {}
|
||||
},
|
||||
"didyoumean": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
|
@ -18,7 +18,9 @@
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"crypto": "^1.0.1",
|
||||
"date-fns": "^3.3.1",
|
||||
"dexie": "^3.2.4",
|
||||
"dexie-react-hooks": "^1.1.7",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"nostr-tools": "^1.17.0",
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||
import { Modal } from '@/shared/Modal/Modal'
|
||||
import { MODAL_PARAMS_KEYS } from '@/types/modal'
|
||||
import { call, getShortenNpub } from '@/utils/helpers'
|
||||
import { call, getShortenNpub } from '@/utils/helpers/helpers'
|
||||
import { Avatar, Box, Stack, Typography } from '@mui/material'
|
||||
import { useParams, useSearchParams } from 'react-router-dom'
|
||||
import { useAppSelector } from '@/store/hooks/redux'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||
import { Modal } from '@/shared/Modal/Modal'
|
||||
import { MODAL_PARAMS_KEYS } from '@/types/modal'
|
||||
import { call, getShortenNpub, getSignReqKind } from '@/utils/helpers'
|
||||
import { call, getShortenNpub, getSignReqKind } from '@/utils/helpers/helpers'
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
@ -47,6 +47,7 @@ type ModalConfirmEventProps = {
|
||||
export const ACTIONS: { [type: string]: string } = {
|
||||
get_public_key: 'Get public key',
|
||||
sign_event: 'Sign event',
|
||||
connect: 'Connect',
|
||||
nip04_encrypt: 'Encrypt message',
|
||||
nip04_decrypt: 'Decrypt message',
|
||||
}
|
||||
@ -134,8 +135,7 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
|
||||
const action = ACTIONS[req.method]
|
||||
if (req.method === 'sign_event') {
|
||||
const kind = getSignReqKind(req)
|
||||
if (kind !== undefined)
|
||||
return `${action} of kind ${kind}`
|
||||
if (kind !== undefined) return `${action} of kind ${kind}`
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { Input } from '@/shared/Input/Input'
|
||||
import { InputCopyButton } from '@/shared/InputCopyButton/InputCopyButton'
|
||||
import { Modal } from '@/shared/Modal/Modal'
|
||||
import { MODAL_PARAMS_KEYS } from '@/types/modal'
|
||||
import { getBunkerLink } from '@/utils/helpers'
|
||||
import { getBunkerLink } from '@/utils/helpers/helpers'
|
||||
import { Stack, Typography } from '@mui/material'
|
||||
import { useRef } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||
import { Button } from '@/shared/Button/Button'
|
||||
import { Modal } from '@/shared/Modal/Modal'
|
||||
@ -18,9 +18,17 @@ export const ModalInitial = () => {
|
||||
setShowAdvancedContent(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (isModalOpened) {
|
||||
setShowAdvancedContent(false)
|
||||
}
|
||||
}
|
||||
}, [isModalOpened])
|
||||
|
||||
return (
|
||||
<Modal open={isModalOpened} onClose={handleCloseModal}>
|
||||
<Stack paddingTop={'1rem'} gap={'1rem'}>
|
||||
<Stack paddingTop={'2.5rem'} gap={'1rem'}>
|
||||
<Button onClick={() => handleOpen(MODAL_PARAMS_KEYS.SIGN_UP)}>
|
||||
Sign up
|
||||
</Button>
|
||||
|
@ -11,6 +11,7 @@ import { Input } from '@/shared/Input/Input'
|
||||
import { Button } from '@/shared/Button/Button'
|
||||
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'
|
||||
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
export const ModalLogin = () => {
|
||||
const { getModalOpened, handleClose } = useModalSearchParams()
|
||||
@ -19,6 +20,8 @@ export const ModalLogin = () => {
|
||||
|
||||
const notify = useEnqueueSnackbar()
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const [enteredUsername, setEnteredUsername] = useState('')
|
||||
const [enteredPassword, setEnteredPassword] = useState('')
|
||||
const [isPasswordShown, setIsPasswordShown] = useState(false)
|
||||
@ -37,9 +40,9 @@ export const ModalLogin = () => {
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
const user = enteredUsername.split('@')[0]
|
||||
const [username, domain] = enteredUsername.split('@')
|
||||
const response = await fetch(
|
||||
'https://domain.com/.well-known/nostr.json?name=' + user,
|
||||
`https://${domain}/.well-known/nostr.json?name=${username}`,
|
||||
)
|
||||
const getNpub: {
|
||||
names: {
|
||||
@ -47,13 +50,13 @@ export const ModalLogin = () => {
|
||||
}
|
||||
} = await response.json()
|
||||
|
||||
const pubkey = getNpub.names[user]
|
||||
const pubkey = getNpub.names[username]
|
||||
const npub = nip19.npubEncode(pubkey)
|
||||
const passphrase = enteredPassword
|
||||
console.log('fetch', npub, passphrase)
|
||||
|
||||
const k: any = await swicCall('fetchKey', npub, passphrase)
|
||||
notify(`Fetched ${k.npub}`, 'success')
|
||||
navigate(`/key/${k.npub}`)
|
||||
} catch (error: any) {
|
||||
notify(error.message, 'error')
|
||||
}
|
||||
|
@ -15,9 +15,15 @@ import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined
|
||||
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'
|
||||
import { ChangeEvent, useState } from 'react'
|
||||
import { Checkbox } from '@/shared/Checkbox/Checkbox'
|
||||
import { useEnqueueSnackbar } from '@/hooks/useEnqueueSnackbar'
|
||||
import { swicCall } from '@/modules/swic'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
export const ModalSettings = () => {
|
||||
const { getModalOpened, handleClose } = useModalSearchParams()
|
||||
const { npub = '' } = useParams<{ npub: string }>()
|
||||
|
||||
const notify = useEnqueueSnackbar()
|
||||
|
||||
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SETTINGS)
|
||||
const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.SETTINGS)
|
||||
@ -27,22 +33,16 @@ export const ModalSettings = () => {
|
||||
const [isPasswordInvalid, setIsPasswordInvalid] = useState(false)
|
||||
const [isPasswordSynched, setIsPasswordSynched] = useState(false)
|
||||
|
||||
const [isChecked, setIsChecked] = useState(false)
|
||||
|
||||
const handlePasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setIsPasswordInvalid(false)
|
||||
setEnteredPassword(e.target.value)
|
||||
}
|
||||
|
||||
const handlePasswordTypeChange = () =>
|
||||
setIsPasswordShown((prevState) => !prevState)
|
||||
|
||||
const handleSync = () => {
|
||||
setIsPasswordInvalid(false)
|
||||
|
||||
if (enteredPassword.trim().length < 6) {
|
||||
return setIsPasswordInvalid(true)
|
||||
}
|
||||
setIsPasswordSynched(true)
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
handleCloseModal()
|
||||
setEnteredPassword('')
|
||||
@ -50,10 +50,33 @@ export const ModalSettings = () => {
|
||||
setIsPasswordSynched(false)
|
||||
}
|
||||
|
||||
const handleChangeCheckbox = (e: unknown, checked: boolean) => {
|
||||
setIsChecked(checked)
|
||||
}
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setIsPasswordInvalid(false)
|
||||
setIsPasswordSynched(false)
|
||||
|
||||
if (enteredPassword.trim().length < 6) {
|
||||
return setIsPasswordInvalid(true)
|
||||
}
|
||||
try {
|
||||
await swicCall('saveKey', npub, enteredPassword)
|
||||
notify('Key saved', 'success')
|
||||
setIsPasswordInvalid(false)
|
||||
setIsPasswordSynched(true)
|
||||
} catch (error) {
|
||||
setIsPasswordInvalid(false)
|
||||
setIsPasswordSynched(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal open={isModalOpened} onClose={onClose} title='Settings'>
|
||||
<Stack gap={'1rem'}>
|
||||
<StyledSettingContainer>
|
||||
<StyledSettingContainer onSubmit={handleSubmit}>
|
||||
<Stack direction={'row'} justifyContent={'space-between'}>
|
||||
<SectionTitle>Cloud sync</SectionTitle>
|
||||
{isPasswordSynched && (
|
||||
@ -63,7 +86,10 @@ export const ModalSettings = () => {
|
||||
)}
|
||||
</Stack>
|
||||
<Box>
|
||||
<Checkbox />
|
||||
<Checkbox
|
||||
onChange={handleChangeCheckbox}
|
||||
checked={isChecked}
|
||||
/>
|
||||
<Typography variant='caption'>
|
||||
Use this login on multiple devices
|
||||
</Typography>
|
||||
@ -82,7 +108,7 @@ export const ModalSettings = () => {
|
||||
)}
|
||||
</IconButton>
|
||||
}
|
||||
type={isPasswordShown ? 'password' : 'text'}
|
||||
type={isPasswordShown ? 'text' : 'password'}
|
||||
onChange={handlePasswordChange}
|
||||
value={enteredPassword}
|
||||
helperText={isPasswordInvalid ? 'Invalid password' : ''}
|
||||
@ -94,8 +120,9 @@ export const ModalSettings = () => {
|
||||
},
|
||||
},
|
||||
}}
|
||||
disabled={!isChecked}
|
||||
/>
|
||||
<StyledButton type='button' fullWidth onClick={handleSync}>
|
||||
<StyledButton type='submit' fullWidth disabled={!isChecked}>
|
||||
Sync
|
||||
</StyledButton>
|
||||
</StyledSettingContainer>
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
} from '@mui/material'
|
||||
|
||||
export const StyledSettingContainer = styled((props: StackProps) => (
|
||||
<Stack {...props} gap={'1rem'} />
|
||||
<Stack gap={'1rem'} component={'form'} {...props} />
|
||||
))(({ theme }) => ({
|
||||
padding: '0.75rem',
|
||||
borderRadius: '1rem',
|
||||
|
@ -8,16 +8,18 @@ import { StyledAppLogo } from './styled'
|
||||
import { Input } from '@/shared/Input/Input'
|
||||
import { Button } from '@/shared/Button/Button'
|
||||
import { CheckmarkIcon } from '@/assets'
|
||||
import { swicCall } from '@/modules/swic'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
export const ModalSignUp = () => {
|
||||
const { getModalOpened, handleClose } = useModalSearchParams()
|
||||
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SIGN_UP)
|
||||
const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.SIGN_UP)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const notify = useEnqueueSnackbar()
|
||||
const theme = useTheme()
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const [enteredValue, setEnteredValue] = useState('')
|
||||
|
||||
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
@ -36,6 +38,13 @@ export const ModalSignUp = () => {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
const k: any = await swicCall('generateKey')
|
||||
notify(`New key ${k.npub}`, 'success')
|
||||
navigate(`/key/${k.npub}`)
|
||||
} catch (error: any) {
|
||||
notify(error.message, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@ -77,7 +86,9 @@ export const ModalSignUp = () => {
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Button fullWidth>Sign up</Button>
|
||||
<Button fullWidth type='submit'>
|
||||
Sign up
|
||||
</Button>
|
||||
</Stack>
|
||||
</Modal>
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import { MetaEvent } from '@/types/meta-event'
|
||||
import { fetchProfile } from '@/modules/nostr'
|
||||
import { ProfileMenu } from './components/ProfileMenu'
|
||||
import { getShortenNpub } from '@/utils/helpers'
|
||||
import { getShortenNpub } from '@/utils/helpers/helpers'
|
||||
|
||||
export const Header = () => {
|
||||
const { npub = '' } = useParams<{ npub: string }>()
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DbKey } from '@/modules/db'
|
||||
import { getShortenNpub } from '@/utils/helpers'
|
||||
import { getShortenNpub } from '@/utils/helpers/helpers'
|
||||
import {
|
||||
Avatar,
|
||||
ListItemIcon,
|
||||
|
@ -2,6 +2,7 @@ import { Menu as MuiMenu } from '@mui/material'
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode'
|
||||
import LightModeIcon from '@mui/icons-material/LightMode'
|
||||
import LoginIcon from '@mui/icons-material/Login'
|
||||
import PersonAddAltRoundedIcon from '@mui/icons-material/PersonAddAltRounded'
|
||||
import { setThemeMode } from '@/store/reducers/ui.slice'
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks/redux'
|
||||
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||
@ -10,13 +11,16 @@ import { MenuButton } from './styled'
|
||||
import { useOpenMenu } from '@/hooks/useOpenMenu'
|
||||
import { MenuItem } from './MenuItem'
|
||||
import MenuRoundedIcon from '@mui/icons-material/MenuRounded'
|
||||
import { selectKeys } from '@/store'
|
||||
|
||||
export const Menu = () => {
|
||||
const themeMode = useAppSelector((state) => state.ui.themeMode)
|
||||
const keys = useAppSelector(selectKeys)
|
||||
const dispatch = useAppDispatch()
|
||||
const { handleOpen } = useModalSearchParams()
|
||||
|
||||
const isDarkMode = themeMode === 'dark'
|
||||
const isNoKeys = !keys || keys.length === 0
|
||||
|
||||
const {
|
||||
anchorEl,
|
||||
@ -53,9 +57,11 @@ export const Menu = () => {
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
Icon={<LoginIcon />}
|
||||
Icon={
|
||||
isNoKeys ? <LoginIcon /> : <PersonAddAltRoundedIcon />
|
||||
}
|
||||
onClick={handleNavigateToAuth}
|
||||
title='Sign up'
|
||||
title={isNoKeys ? 'Sign up' : 'Add account'}
|
||||
/>
|
||||
<MenuItem
|
||||
Icon={themeIcon}
|
||||
|
@ -8,6 +8,7 @@ import { useNavigate } from 'react-router-dom'
|
||||
import LoginIcon from '@mui/icons-material/Login'
|
||||
import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'
|
||||
import HomeRoundedIcon from '@mui/icons-material/HomeRounded'
|
||||
import PersonAddAltRoundedIcon from '@mui/icons-material/PersonAddAltRounded'
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks/redux'
|
||||
import { selectKeys } from '@/store'
|
||||
import { setThemeMode } from '@/store/reducers/ui.slice'
|
||||
@ -26,6 +27,7 @@ export const ProfileMenu = () => {
|
||||
const { handleOpen } = useModalSearchParams()
|
||||
|
||||
const keys = useAppSelector(selectKeys)
|
||||
const isNoKeys = !keys || keys.length === 0
|
||||
const themeMode = useAppSelector((state) => state.ui.themeMode)
|
||||
const isDarkMode = themeMode === 'dark'
|
||||
|
||||
@ -84,9 +86,11 @@ export const ProfileMenu = () => {
|
||||
title='Home'
|
||||
/>
|
||||
<MenuItem
|
||||
Icon={<LoginIcon />}
|
||||
Icon={
|
||||
isNoKeys ? <LoginIcon /> : <PersonAddAltRoundedIcon />
|
||||
}
|
||||
onClick={handleNavigateToAuth}
|
||||
title='Sign up'
|
||||
title={isNoKeys ? 'Sign up' : 'Add account'}
|
||||
/>
|
||||
<MenuItem
|
||||
Icon={themeIcon}
|
||||
|
@ -10,7 +10,7 @@ import NDK, {
|
||||
} from '@nostr-dev-kit/ndk'
|
||||
import { NOAUTHD_URL, WEB_PUSH_PUBKEY, NIP46_RELAYS } from '../utils/consts'
|
||||
import { Nip04 } from './nip04'
|
||||
import { getReqPerm, isPackagePerm } from '@/utils/helpers'
|
||||
import { getReqPerm, isPackagePerm } from '@/utils/helpers/helpers'
|
||||
//import { PrivateKeySigner } from './signer'
|
||||
|
||||
//const PERF_TEST = false
|
||||
@ -139,7 +139,6 @@ class EventHandlingStrategyWrapper implements IEventHandlingStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class NoauthBackend {
|
||||
readonly swg: ServiceWorkerGlobalScope
|
||||
private keysModule: Keys
|
||||
@ -443,18 +442,15 @@ export class NoauthBackend {
|
||||
private getPerm(req: DbPending): string {
|
||||
const reqPerm = getReqPerm(req)
|
||||
const appPerms = this.perms.filter(
|
||||
(p) =>
|
||||
p.npub === req.npub &&
|
||||
p.appNpub === req.appNpub
|
||||
(p) => p.npub === req.npub && p.appNpub === req.appNpub,
|
||||
)
|
||||
|
||||
// exact match first
|
||||
let perm = appPerms.find((p) => p.perm === reqPerm)
|
||||
// non-exact next
|
||||
if (!perm)
|
||||
perm = appPerms.find((p) => isPackagePerm(p.perm, reqPerm))
|
||||
if (!perm) perm = appPerms.find((p) => isPackagePerm(p.perm, reqPerm))
|
||||
|
||||
console.log("req", req, "perm", reqPerm, "value", perm, appPerms);
|
||||
console.log('req', req, 'perm', reqPerm, 'value', perm, appPerms)
|
||||
return perm?.value || ''
|
||||
}
|
||||
|
||||
@ -489,7 +485,7 @@ export class NoauthBackend {
|
||||
manual: boolean,
|
||||
allow: boolean,
|
||||
remember: boolean,
|
||||
options?: any
|
||||
options?: any,
|
||||
) => {
|
||||
// confirm
|
||||
console.log(
|
||||
@ -535,10 +531,8 @@ export class NoauthBackend {
|
||||
if (index >= 0) self.confirmBuffer.splice(index, 1)
|
||||
|
||||
if (remember) {
|
||||
|
||||
let perm = getReqPerm(req)
|
||||
if (allow && options && options.perm)
|
||||
perm = options.perm
|
||||
if (allow && options && options.perm) perm = options.perm
|
||||
|
||||
await dbi.addPerm({
|
||||
id: req.id,
|
||||
@ -554,10 +548,15 @@ export class NoauthBackend {
|
||||
const otherReqs = self.confirmBuffer.filter(
|
||||
(r) => r.req.appNpub === req.appNpub,
|
||||
)
|
||||
console.log("updated perms", this.perms, "otherReqs", otherReqs)
|
||||
console.log(
|
||||
'updated perms',
|
||||
this.perms,
|
||||
'otherReqs',
|
||||
otherReqs,
|
||||
)
|
||||
for (const r of otherReqs) {
|
||||
const perm = this.getPerm(r.req);
|
||||
// if (r.req.method === req.method) {
|
||||
const perm = this.getPerm(r.req)
|
||||
// if (r.req.method === req.method) {
|
||||
if (perm) {
|
||||
r.cb(perm === '1', false)
|
||||
}
|
||||
@ -590,7 +589,8 @@ export class NoauthBackend {
|
||||
// put to a list of pending requests
|
||||
this.confirmBuffer.push({
|
||||
req,
|
||||
cb: (allow, remember, options) => onAllow(true, allow, remember, options),
|
||||
cb: (allow, remember, options) =>
|
||||
onAllow(true, allow, remember, options),
|
||||
})
|
||||
|
||||
// show notifs
|
||||
@ -753,7 +753,12 @@ export class NoauthBackend {
|
||||
return k
|
||||
}
|
||||
|
||||
private async confirm(id: string, allow: boolean, remember: boolean, options?: any) {
|
||||
private async confirm(
|
||||
id: string,
|
||||
allow: boolean,
|
||||
remember: boolean,
|
||||
options?: any,
|
||||
) {
|
||||
const req = this.confirmBuffer.find((r) => r.req.id === id)
|
||||
if (!req) {
|
||||
console.log('req ', id, 'not found')
|
||||
|
@ -1,7 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
const AppPage = () => {
|
||||
return <div>AppPage</div>
|
||||
}
|
||||
|
||||
export default AppPage
|
95
src/pages/AppPage/App.Page.tsx
Normal file
95
src/pages/AppPage/App.Page.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
import { useLiveQuery } from 'dexie-react-hooks'
|
||||
import { DbHistory, db } from '@/modules/db'
|
||||
import { useParams } from 'react-router'
|
||||
import { useAppSelector } from '@/store/hooks/redux'
|
||||
import { selectAppByAppNpub, selectPermsByNpubAndAppNpub } from '@/store'
|
||||
import { Navigate } from 'react-router-dom'
|
||||
import { formatTimestampDate } from '@/utils/helpers/date'
|
||||
import { Avatar, Box, Stack, Typography } from '@mui/material'
|
||||
import { SectionTitle } from '@/shared/SectionTitle/SectionTitle'
|
||||
import { getShortenNpub } from '@/utils/helpers/helpers'
|
||||
import { PermissionMenuButton } from './styled'
|
||||
import { PermissionsMenu } from './components/PermissionsMenu'
|
||||
import { useOpenMenu } from '@/hooks/useOpenMenu'
|
||||
import { ActivityList } from './components/ActivityList'
|
||||
|
||||
const getAppHistoryQuery = (appNpub: string) =>
|
||||
db.history.where('appNpub').equals(appNpub).toArray()
|
||||
|
||||
const AppPage = () => {
|
||||
const { appNpub = '', npub = '' } = useParams()
|
||||
const perms = useAppSelector((state) =>
|
||||
selectPermsByNpubAndAppNpub(state, npub, appNpub),
|
||||
)
|
||||
const currentApp = useAppSelector((state) =>
|
||||
selectAppByAppNpub(state, appNpub),
|
||||
)
|
||||
const history = useLiveQuery(
|
||||
() => {
|
||||
if (!appNpub.trim().length) return []
|
||||
return getAppHistoryQuery(appNpub)
|
||||
},
|
||||
[],
|
||||
[] as DbHistory[],
|
||||
)
|
||||
|
||||
const { anchorEl, handleClose, handleOpen, open } = useOpenMenu()
|
||||
const connectPerm = perms.find((perm) => perm.perm === 'connect')
|
||||
|
||||
if (!currentApp) {
|
||||
return <Navigate to={`/key/${npub}`} />
|
||||
}
|
||||
|
||||
const { icon = '', name = '' } = currentApp || {}
|
||||
const appName = name || getShortenNpub(appNpub)
|
||||
const { timestamp } = connectPerm || {}
|
||||
const connectedOn =
|
||||
connectPerm && timestamp
|
||||
? `Connected at ${formatTimestampDate(timestamp)}`
|
||||
: 'Not connected'
|
||||
|
||||
return (
|
||||
<Stack maxHeight={'100%'} overflow={'auto'}>
|
||||
<Stack
|
||||
marginBottom={'1rem'}
|
||||
direction={'row'}
|
||||
gap={'1rem'}
|
||||
width={'100%'}
|
||||
>
|
||||
<Avatar
|
||||
src={icon}
|
||||
sx={{
|
||||
width: 70,
|
||||
height: 70,
|
||||
}}
|
||||
variant='rounded'
|
||||
/>
|
||||
<Box flex={'1'} overflow={'hidden'}>
|
||||
<Typography variant='h4' noWrap>
|
||||
{appName}
|
||||
</Typography>
|
||||
<Typography variant='body2' noWrap>
|
||||
{connectedOn}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Box marginBottom={'1rem'}>
|
||||
<SectionTitle marginBottom={'0.5rem'}>Permissions</SectionTitle>
|
||||
<PermissionMenuButton onClick={handleOpen}>
|
||||
Basic/Advanced/Custom {perms.length}
|
||||
</PermissionMenuButton>
|
||||
<PermissionsMenu
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
perms={perms}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<ActivityList history={history} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppPage
|
53
src/pages/AppPage/components/ActivityList.tsx
Normal file
53
src/pages/AppPage/components/ActivityList.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import React, { FC } from 'react'
|
||||
import { DbHistory } from '@/modules/db'
|
||||
import { SectionTitle } from '@/shared/SectionTitle/SectionTitle'
|
||||
import { Box, IconButton, Stack, Typography } from '@mui/material'
|
||||
import MoreIcon from '@mui/icons-material/MoreVert'
|
||||
import { ACTIONS } from '@/components/Modal/ModalConfirmEvent/ModalConfirmEvent'
|
||||
import { formatTimestampDate } from '@/utils/helpers/date'
|
||||
import { StyledButton } from './styled'
|
||||
|
||||
type ActivityListProps = {
|
||||
history: DbHistory[]
|
||||
}
|
||||
|
||||
export const ActivityList: FC<ActivityListProps> = ({ history = [] }) => {
|
||||
return (
|
||||
<>
|
||||
<SectionTitle marginBottom={'0.5rem'}>Activity</SectionTitle>
|
||||
<Box
|
||||
flex={1}
|
||||
overflow={'auto'}
|
||||
display={'flex'}
|
||||
flexDirection={'column'}
|
||||
gap={'0.5rem'}
|
||||
>
|
||||
{history.map((h) => {
|
||||
return (
|
||||
<Stack>
|
||||
<Box
|
||||
width={'100%'}
|
||||
display={'flex'}
|
||||
gap={'0.5rem'}
|
||||
alignItems={'center'}
|
||||
>
|
||||
<Typography flex={1} fontWeight={700}>
|
||||
{ACTIONS[h.method] || h.method}
|
||||
</Typography>
|
||||
<StyledButton>
|
||||
{h.allowed ? 'allow' : 'disallow'}
|
||||
</StyledButton>
|
||||
<IconButton>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography variant='caption'>
|
||||
{formatTimestampDate(h.timestamp)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
37
src/pages/AppPage/components/ItemPermissionMenu.tsx
Normal file
37
src/pages/AppPage/components/ItemPermissionMenu.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { FC } from 'react'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { DbPerm } from '@/modules/db'
|
||||
import { ACTIONS } from '@/components/Modal/ModalConfirmEvent/ModalConfirmEvent'
|
||||
import { StyledMenuItem } from './styled'
|
||||
import { formatTimestampDate } from '@/utils/helpers/date'
|
||||
|
||||
type ItemPermissionMenuProps = {
|
||||
permission: DbPerm
|
||||
}
|
||||
|
||||
export const ItemPermissionMenu: FC<ItemPermissionMenuProps> = ({
|
||||
permission,
|
||||
}) => {
|
||||
const { perm, value, timestamp } = permission || {}
|
||||
|
||||
return (
|
||||
<StyledMenuItem>
|
||||
<Box
|
||||
width={'100%'}
|
||||
display={'flex'}
|
||||
gap={'0.5rem'}
|
||||
alignItems={'center'}
|
||||
>
|
||||
<Typography flex={1} fontWeight={700}>
|
||||
{ACTIONS[perm] || perm}
|
||||
</Typography>
|
||||
<Typography textTransform={'capitalize'} variant='body2'>
|
||||
{value === '1' ? 'allow' : 'disallow'}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant='body2'>
|
||||
{formatTimestampDate(timestamp)}
|
||||
</Typography>
|
||||
</StyledMenuItem>
|
||||
)
|
||||
}
|
26
src/pages/AppPage/components/PermissionsMenu.tsx
Normal file
26
src/pages/AppPage/components/PermissionsMenu.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { DbPerm } from '@/modules/db'
|
||||
import { Menu, MenuItem, MenuProps } from '@mui/material'
|
||||
import { FC } from 'react'
|
||||
import { ItemPermissionMenu } from './ItemPermissionMenu'
|
||||
|
||||
type PermissionsMenuProps = {
|
||||
perms: DbPerm[]
|
||||
} & MenuProps
|
||||
|
||||
export const PermissionsMenu: FC<PermissionsMenuProps> = ({
|
||||
perms,
|
||||
open,
|
||||
anchorEl,
|
||||
onClose,
|
||||
}) => {
|
||||
const isNoPerms = perms.length === 0
|
||||
return (
|
||||
<Menu open={open} anchorEl={anchorEl} onClose={onClose}>
|
||||
{isNoPerms && <MenuItem>No permissions</MenuItem>}
|
||||
{!isNoPerms &&
|
||||
perms.map((perm) => (
|
||||
<ItemPermissionMenu permission={perm} key={perm.id} />
|
||||
))}
|
||||
</Menu>
|
||||
)
|
||||
}
|
18
src/pages/AppPage/components/styled.tsx
Normal file
18
src/pages/AppPage/components/styled.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Button } from '@/shared/Button/Button'
|
||||
import { MenuItem, MenuItemProps, styled } from '@mui/material'
|
||||
|
||||
export const StyledMenuItem = styled((props: MenuItemProps) => (
|
||||
<MenuItem {...props} />
|
||||
))(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
gap: '0.5rem',
|
||||
'&:not(:first-of-type)': {
|
||||
borderTop: '1px solid ' + theme.palette.secondary.main,
|
||||
},
|
||||
}))
|
||||
|
||||
export const StyledButton = styled(Button)({
|
||||
textTransform: 'capitalize',
|
||||
})
|
6
src/pages/AppPage/styled.tsx
Normal file
6
src/pages/AppPage/styled.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import { AppButtonProps, Button } from '@/shared/Button/Button'
|
||||
import { styled } from '@mui/material'
|
||||
|
||||
export const PermissionMenuButton = styled((props: AppButtonProps) => (
|
||||
<Button {...props} variant='outlined' fullWidth />
|
||||
))(() => ({}))
|
@ -1,19 +1,43 @@
|
||||
import { useAppSelector } from '../../store/hooks/redux'
|
||||
import { selectKeys } from '../../store'
|
||||
import { Fragment } from 'react'
|
||||
import { ItemKey } from './components/ItemKey'
|
||||
import { Stack } from '@mui/material'
|
||||
import { SectionTitle } from '../../shared/SectionTitle/SectionTitle'
|
||||
import { Box, Stack, Typography } from '@mui/material'
|
||||
import { AddAccountButton } from './styled'
|
||||
import { useAppSelector } from '@/store/hooks/redux'
|
||||
import { selectKeys } from '@/store'
|
||||
import { SectionTitle } from '@/shared/SectionTitle/SectionTitle'
|
||||
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||
import { MODAL_PARAMS_KEYS } from '@/types/modal'
|
||||
|
||||
const HomePage = () => {
|
||||
const keys = useAppSelector(selectKeys)
|
||||
const isNoKeys = !keys || keys.length === 0
|
||||
|
||||
const { handleOpen } = useModalSearchParams()
|
||||
const handleClickAddAccount = () => handleOpen(MODAL_PARAMS_KEYS.INITIAL)
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<SectionTitle marginBottom={'0.5rem'}>Keys:</SectionTitle>
|
||||
<Stack gap={'0.5rem'}>
|
||||
{keys.map((key) => (
|
||||
<ItemKey {...key} key={key.npub} />
|
||||
))}
|
||||
<Stack maxHeight={'100%'} overflow={'auto'}>
|
||||
<SectionTitle marginBottom={'0.5rem'}>
|
||||
{isNoKeys ? 'Welcome!' : 'Keys:'}
|
||||
</SectionTitle>
|
||||
<Stack gap={'0.5rem'} overflow={'auto'}>
|
||||
{isNoKeys && (
|
||||
<Typography textAlign={'center'} variant='h5'>
|
||||
Hello, this is a key storage app for Nostr
|
||||
</Typography>
|
||||
)}
|
||||
{!isNoKeys && (
|
||||
<Fragment>
|
||||
<Box flex={1} overflow={'auto'} borderRadius={'8px'}>
|
||||
{keys.map((key) => (
|
||||
<ItemKey {...key} key={key.npub} />
|
||||
))}
|
||||
</Box>
|
||||
<AddAccountButton onClick={handleClickAddAccount}>
|
||||
Add account
|
||||
</AddAccountButton>
|
||||
</Fragment>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, useRef } from 'react'
|
||||
import { FC } from 'react'
|
||||
import { DbKey } from '../../../modules/db'
|
||||
import {
|
||||
Avatar,
|
||||
@ -8,8 +8,7 @@ import {
|
||||
TypographyProps,
|
||||
styled,
|
||||
} from '@mui/material'
|
||||
import { call, getShortenNpub, log } from '../../../utils/helpers'
|
||||
import { swicCall } from '../../../modules/swic'
|
||||
import { getShortenNpub } from '../../../utils/helpers/helpers'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
type ItemKeyProps = DbKey
|
||||
@ -18,17 +17,6 @@ export const ItemKey: FC<ItemKeyProps> = (props) => {
|
||||
const { npub, profile } = props
|
||||
const navigate = useNavigate()
|
||||
|
||||
const passPhraseInputRef = useRef<HTMLInputElement | null>(null)
|
||||
|
||||
// eslint-disable-next-line
|
||||
async function saveKey(npub: string) {
|
||||
call(async () => {
|
||||
const passphrase = passPhraseInputRef.current?.value
|
||||
await swicCall('saveKey', npub, passphrase)
|
||||
log('Key saved')
|
||||
})
|
||||
}
|
||||
|
||||
const handleNavigate = () => {
|
||||
navigate('/key/' + npub)
|
||||
}
|
||||
|
10
src/pages/HomePage/styled.tsx
Normal file
10
src/pages/HomePage/styled.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { AppButtonProps, Button } from '@/shared/Button/Button'
|
||||
import { styled } from '@mui/material'
|
||||
import PersonAddAltRoundedIcon from '@mui/icons-material/PersonAddAltRounded'
|
||||
|
||||
export const AddAccountButton = styled((props: AppButtonProps) => (
|
||||
<Button {...props} startIcon={<PersonAddAltRoundedIcon />} />
|
||||
))(() => ({
|
||||
alignSelf: 'center',
|
||||
padding: '0.35rem 1rem',
|
||||
}))
|
@ -1,10 +1,12 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { SectionTitle } from '../../shared/SectionTitle/SectionTitle'
|
||||
import { useAppSelector } from '../../store/hooks/redux'
|
||||
import { askNotificationPermission, getShortenNpub } from '../../utils/helpers'
|
||||
import {
|
||||
askNotificationPermission,
|
||||
getShortenNpub,
|
||||
} from '../../utils/helpers/helpers'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { fetchProfile } from '../../modules/nostr'
|
||||
import { nip19 } from 'nostr-tools'
|
||||
import { Badge, Box, CircularProgress, Stack } from '@mui/material'
|
||||
import { StyledIconButton } from './styled'
|
||||
import { SettingsIcon, ShareIcon } from '@/assets'
|
||||
@ -53,7 +55,10 @@ const KeyPage = () => {
|
||||
const notify = useEnqueueSnackbar()
|
||||
|
||||
const [profile, setProfile] = useState<MetaEvent | null>(null)
|
||||
const userName = profile?.info?.name || profile?.info?.display_name || getShortenNpub(npub)
|
||||
const userName =
|
||||
profile?.info?.name ||
|
||||
profile?.info?.display_name ||
|
||||
getShortenNpub(npub)
|
||||
const userNameWithPrefix = userName + '@nsec.app'
|
||||
|
||||
const [showWarning, setShowWarning] = useState(false)
|
||||
|
@ -5,7 +5,7 @@ import { Box, Stack, Typography } from '@mui/material'
|
||||
import { FC } from 'react'
|
||||
import { StyledEmptyAppsBox } from '../styled'
|
||||
import { Button } from '@/shared/Button/Button'
|
||||
import { call } from '@/utils/helpers'
|
||||
import { call } from '@/utils/helpers/helpers'
|
||||
import { swicCall } from '@/modules/swic'
|
||||
import { useEnqueueSnackbar } from '@/hooks/useEnqueueSnackbar'
|
||||
import { ItemApp } from './ItemApp'
|
||||
|
@ -3,7 +3,7 @@ import { Avatar, Stack, Typography } from '@mui/material'
|
||||
import { FC } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
// import ImageOutlinedIcon from '@mui/icons-material/ImageOutlined'
|
||||
import { getShortenNpub } from '@/utils/helpers'
|
||||
import { getShortenNpub } from '@/utils/helpers/helpers'
|
||||
import { StyledItemAppContainer } from './styled'
|
||||
|
||||
type ItemAppProps = DbApp
|
||||
|
@ -7,7 +7,7 @@ import { CircularProgress, Stack } from '@mui/material'
|
||||
|
||||
const KeyPage = lazy(() => import('../pages/KeyPage/Key.Page'))
|
||||
const ConfirmPage = lazy(() => import('../pages/Confirm.Page'))
|
||||
const AppPage = lazy(() => import('../pages/App.Page'))
|
||||
const AppPage = lazy(() => import('../pages/AppPage/App.Page'))
|
||||
|
||||
const LoadingSpinner = () => (
|
||||
<Stack height={'100%'} justifyContent={'center'} alignItems={'center'}>
|
||||
|
@ -1,9 +1,17 @@
|
||||
import { DialogProps, Slide } from '@mui/material'
|
||||
import { DialogProps, IconButton, Slide } from '@mui/material'
|
||||
import { TransitionProps } from '@mui/material/transitions'
|
||||
import { FC, forwardRef } from 'react'
|
||||
import { StyledDialog, StyledDialogContent, StyledDialogTitle } from './styled'
|
||||
import {
|
||||
StyledCloseButtonWrapper,
|
||||
StyledDialog,
|
||||
StyledDialogContent,
|
||||
StyledDialogTitle,
|
||||
} from './styled'
|
||||
import CloseRoundedIcon from '@mui/icons-material/CloseRounded'
|
||||
|
||||
type ModalProps = DialogProps
|
||||
type ModalProps = DialogProps & {
|
||||
withCloseButton?: boolean
|
||||
}
|
||||
|
||||
const Transition = forwardRef(function Transition(
|
||||
props: TransitionProps & {
|
||||
@ -14,9 +22,29 @@ const Transition = forwardRef(function Transition(
|
||||
return <Slide direction='up' ref={ref} {...props} />
|
||||
})
|
||||
|
||||
export const Modal: FC<ModalProps> = ({ children, title, ...props }) => {
|
||||
export const Modal: FC<ModalProps> = ({
|
||||
children,
|
||||
title,
|
||||
onClose,
|
||||
withCloseButton = true,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<StyledDialog {...props} TransitionComponent={Transition}>
|
||||
<StyledDialog
|
||||
{...props}
|
||||
onClose={onClose}
|
||||
TransitionComponent={Transition}
|
||||
>
|
||||
{withCloseButton && (
|
||||
<StyledCloseButtonWrapper>
|
||||
<IconButton
|
||||
onClick={() => onClose && onClose({}, 'backdropClick')}
|
||||
className='close_btn'
|
||||
>
|
||||
<CloseRoundedIcon />
|
||||
</IconButton>
|
||||
</StyledCloseButtonWrapper>
|
||||
)}
|
||||
{title && <StyledDialogTitle>{title}</StyledDialogTitle>}
|
||||
<StyledDialogContent>{children}</StyledDialogContent>
|
||||
</StyledDialog>
|
||||
|
@ -1,4 +1,6 @@
|
||||
import {
|
||||
Box,
|
||||
BoxProps,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogContentProps,
|
||||
@ -56,3 +58,17 @@ export const StyledDialogContent = styled((props: DialogContentProps) => (
|
||||
padding: '0 1rem 1rem',
|
||||
}
|
||||
})
|
||||
|
||||
export const StyledCloseButtonWrapper = styled((props: BoxProps) => (
|
||||
<Box {...props} />
|
||||
))(() => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '0.5rem 1rem',
|
||||
position: 'relative',
|
||||
'& > .close_btn': {
|
||||
position: 'absolute',
|
||||
transform: 'translateY(50%)',
|
||||
},
|
||||
}))
|
||||
|
@ -55,9 +55,25 @@ export const selectPermsByNpub = memoizeOne(
|
||||
isDeepEqual,
|
||||
)
|
||||
|
||||
export const selectPermsByNpubAndAppNpub = memoizeOne(
|
||||
(state: RootState, npub: string, appNpub: string) => {
|
||||
return state.content.perms.filter(
|
||||
(perm) => perm.npub === npub && perm.appNpub === appNpub,
|
||||
)
|
||||
},
|
||||
isDeepEqual,
|
||||
)
|
||||
|
||||
export const selectPendingsByNpub = memoizeOne(
|
||||
(state: RootState, npub: string) => {
|
||||
return state.content.pending.filter((pending) => pending.npub === npub)
|
||||
},
|
||||
isDeepEqual,
|
||||
)
|
||||
|
||||
export const selectAppByAppNpub = memoizeOne(
|
||||
(state: RootState, appNpub: string) => {
|
||||
return state.content.apps.find((app) => app.appNpub === appNpub)
|
||||
},
|
||||
isDeepEqual,
|
||||
)
|
||||
|
11
src/utils/helpers/date.ts
Normal file
11
src/utils/helpers/date.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { format } from 'date-fns'
|
||||
|
||||
export const formatTimestampDate = (timestamp: number) => {
|
||||
try {
|
||||
const date = new Date(timestamp)
|
||||
const formattedDate = format(date, "HH:mm',' dd-MM-yyyy")
|
||||
return formattedDate
|
||||
} catch (error) {
|
||||
return ''
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { nip19 } from 'nostr-tools'
|
||||
import { ACTION_TYPE, NIP46_RELAYS } from './consts'
|
||||
import { ACTION_TYPE, NIP46_RELAYS } from '../consts'
|
||||
import { DbPending } from '@/modules/db'
|
||||
|
||||
export async function log(s: string) {
|
||||
@ -52,8 +52,7 @@ export function getSignReqKind(req: DbPending): number | undefined {
|
||||
export function getReqPerm(req: DbPending): string {
|
||||
if (req.method === 'sign_event') {
|
||||
const kind = getSignReqKind(req)
|
||||
if (kind !== undefined)
|
||||
return `${req.method}:${kind}`
|
||||
if (kind !== undefined) return `${req.method}:${kind}`
|
||||
}
|
||||
return req.method
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user