add close button in modals & add app details page
This commit is contained in:
32
package-lock.json
generated
32
package-lock.json
generated
@@ -23,7 +23,9 @@
|
|||||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||||
"@types/react-dom": "^18.2.17",
|
"@types/react-dom": "^18.2.17",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
|
"date-fns": "^3.3.1",
|
||||||
"dexie": "^3.2.4",
|
"dexie": "^3.2.4",
|
||||||
|
"dexie-react-hooks": "^1.1.7",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
"nostr-tools": "^1.17.0",
|
"nostr-tools": "^1.17.0",
|
||||||
@@ -7245,6 +7247,15 @@
|
|||||||
"node": ">=10"
|
"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": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
@@ -7465,6 +7476,16 @@
|
|||||||
"node": ">=6.0"
|
"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": {
|
"node_modules/didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
@@ -23335,6 +23356,11 @@
|
|||||||
"whatwg-url": "^8.0.0"
|
"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": {
|
"debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/dexie/-/dexie-3.2.4.tgz",
|
||||||
"integrity": "sha512-VKoTQRSv7+RnffpOJ3Dh6ozknBqzWw/F3iqMdsZg958R0AS8AnY9x9d1lbwENr0gzeGJHXKcGhAMRaqys6SxqA=="
|
"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": {
|
"didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"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-copy-to-clipboard": "^5.0.7",
|
||||||
"@types/react-dom": "^18.2.17",
|
"@types/react-dom": "^18.2.17",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
|
"date-fns": "^3.3.1",
|
||||||
"dexie": "^3.2.4",
|
"dexie": "^3.2.4",
|
||||||
|
"dexie-react-hooks": "^1.1.7",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
"nostr-tools": "^1.17.0",
|
"nostr-tools": "^1.17.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||||
import { Modal } from '@/shared/Modal/Modal'
|
import { Modal } from '@/shared/Modal/Modal'
|
||||||
import { MODAL_PARAMS_KEYS } from '@/types/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 { Avatar, Box, Stack, Typography } from '@mui/material'
|
||||||
import { useParams, useSearchParams } from 'react-router-dom'
|
import { useParams, useSearchParams } from 'react-router-dom'
|
||||||
import { useAppSelector } from '@/store/hooks/redux'
|
import { useAppSelector } from '@/store/hooks/redux'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||||
import { Modal } from '@/shared/Modal/Modal'
|
import { Modal } from '@/shared/Modal/Modal'
|
||||||
import { MODAL_PARAMS_KEYS } from '@/types/modal'
|
import { MODAL_PARAMS_KEYS } from '@/types/modal'
|
||||||
import { call, getShortenNpub } from '@/utils/helpers'
|
import { call, getShortenNpub } from '@/utils/helpers/helpers'
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
@@ -47,6 +47,7 @@ type ModalConfirmEventProps = {
|
|||||||
export const ACTIONS: { [type: string]: string } = {
|
export const ACTIONS: { [type: string]: string } = {
|
||||||
get_public_key: 'Get public key',
|
get_public_key: 'Get public key',
|
||||||
sign_event: 'Sign event',
|
sign_event: 'Sign event',
|
||||||
|
connect: 'Connect',
|
||||||
}
|
}
|
||||||
|
|
||||||
type PendingRequest = DbPending & { checked: boolean }
|
type PendingRequest = DbPending & { checked: boolean }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Input } from '@/shared/Input/Input'
|
|||||||
import { InputCopyButton } from '@/shared/InputCopyButton/InputCopyButton'
|
import { InputCopyButton } from '@/shared/InputCopyButton/InputCopyButton'
|
||||||
import { Modal } from '@/shared/Modal/Modal'
|
import { Modal } from '@/shared/Modal/Modal'
|
||||||
import { MODAL_PARAMS_KEYS } from '@/types/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 { Stack, Typography } from '@mui/material'
|
||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import { useParams } from 'react-router-dom'
|
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 { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||||
import { Button } from '@/shared/Button/Button'
|
import { Button } from '@/shared/Button/Button'
|
||||||
import { Modal } from '@/shared/Modal/Modal'
|
import { Modal } from '@/shared/Modal/Modal'
|
||||||
@@ -18,9 +18,17 @@ export const ModalInitial = () => {
|
|||||||
setShowAdvancedContent(true)
|
setShowAdvancedContent(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (isModalOpened) {
|
||||||
|
setShowAdvancedContent(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isModalOpened])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={isModalOpened} onClose={handleCloseModal}>
|
<Modal open={isModalOpened} onClose={handleCloseModal}>
|
||||||
<Stack paddingTop={'1rem'} gap={'1rem'}>
|
<Stack paddingTop={'2.5rem'} gap={'1rem'}>
|
||||||
<Button onClick={() => handleOpen(MODAL_PARAMS_KEYS.SIGN_UP)}>
|
<Button onClick={() => handleOpen(MODAL_PARAMS_KEYS.SIGN_UP)}>
|
||||||
Sign up
|
Sign up
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { Input } from '@/shared/Input/Input'
|
|||||||
import { Button } from '@/shared/Button/Button'
|
import { Button } from '@/shared/Button/Button'
|
||||||
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'
|
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'
|
||||||
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'
|
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
export const ModalLogin = () => {
|
export const ModalLogin = () => {
|
||||||
const { getModalOpened, handleClose } = useModalSearchParams()
|
const { getModalOpened, handleClose } = useModalSearchParams()
|
||||||
@@ -19,6 +20,8 @@ export const ModalLogin = () => {
|
|||||||
|
|
||||||
const notify = useEnqueueSnackbar()
|
const notify = useEnqueueSnackbar()
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const [enteredUsername, setEnteredUsername] = useState('')
|
const [enteredUsername, setEnteredUsername] = useState('')
|
||||||
const [enteredPassword, setEnteredPassword] = useState('')
|
const [enteredPassword, setEnteredPassword] = useState('')
|
||||||
const [isPasswordShown, setIsPasswordShown] = useState(false)
|
const [isPasswordShown, setIsPasswordShown] = useState(false)
|
||||||
@@ -37,9 +40,9 @@ export const ModalLogin = () => {
|
|||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
try {
|
try {
|
||||||
const user = enteredUsername.split('@')[0]
|
const [username, domain] = enteredUsername.split('@')
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
'https://domain.com/.well-known/nostr.json?name=' + user,
|
`https://${domain}/.well-known/nostr.json?name=${username}`,
|
||||||
)
|
)
|
||||||
const getNpub: {
|
const getNpub: {
|
||||||
names: {
|
names: {
|
||||||
@@ -47,13 +50,13 @@ export const ModalLogin = () => {
|
|||||||
}
|
}
|
||||||
} = await response.json()
|
} = await response.json()
|
||||||
|
|
||||||
const pubkey = getNpub.names[user]
|
const pubkey = getNpub.names[username]
|
||||||
const npub = nip19.npubEncode(pubkey)
|
const npub = nip19.npubEncode(pubkey)
|
||||||
const passphrase = enteredPassword
|
const passphrase = enteredPassword
|
||||||
console.log('fetch', npub, passphrase)
|
console.log('fetch', npub, passphrase)
|
||||||
|
|
||||||
const k: any = await swicCall('fetchKey', npub, passphrase)
|
const k: any = await swicCall('fetchKey', npub, passphrase)
|
||||||
notify(`Fetched ${k.npub}`, 'success')
|
notify(`Fetched ${k.npub}`, 'success')
|
||||||
|
navigate(`/key/${k.npub}`)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
notify(error.message, 'error')
|
notify(error.message, 'error')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,15 @@ import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined
|
|||||||
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'
|
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'
|
||||||
import { ChangeEvent, useState } from 'react'
|
import { ChangeEvent, useState } from 'react'
|
||||||
import { Checkbox } from '@/shared/Checkbox/Checkbox'
|
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 = () => {
|
export const ModalSettings = () => {
|
||||||
const { getModalOpened, handleClose } = useModalSearchParams()
|
const { getModalOpened, handleClose } = useModalSearchParams()
|
||||||
|
const { npub = '' } = useParams<{ npub: string }>()
|
||||||
|
|
||||||
|
const notify = useEnqueueSnackbar()
|
||||||
|
|
||||||
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SETTINGS)
|
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SETTINGS)
|
||||||
const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.SETTINGS)
|
const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.SETTINGS)
|
||||||
@@ -27,22 +33,16 @@ export const ModalSettings = () => {
|
|||||||
const [isPasswordInvalid, setIsPasswordInvalid] = useState(false)
|
const [isPasswordInvalid, setIsPasswordInvalid] = useState(false)
|
||||||
const [isPasswordSynched, setIsPasswordSynched] = useState(false)
|
const [isPasswordSynched, setIsPasswordSynched] = useState(false)
|
||||||
|
|
||||||
|
const [isChecked, setIsChecked] = useState(false)
|
||||||
|
|
||||||
const handlePasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
|
const handlePasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setIsPasswordInvalid(false)
|
||||||
setEnteredPassword(e.target.value)
|
setEnteredPassword(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePasswordTypeChange = () =>
|
const handlePasswordTypeChange = () =>
|
||||||
setIsPasswordShown((prevState) => !prevState)
|
setIsPasswordShown((prevState) => !prevState)
|
||||||
|
|
||||||
const handleSync = () => {
|
|
||||||
setIsPasswordInvalid(false)
|
|
||||||
|
|
||||||
if (enteredPassword.trim().length < 6) {
|
|
||||||
return setIsPasswordInvalid(true)
|
|
||||||
}
|
|
||||||
setIsPasswordSynched(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
handleCloseModal()
|
handleCloseModal()
|
||||||
setEnteredPassword('')
|
setEnteredPassword('')
|
||||||
@@ -50,10 +50,33 @@ export const ModalSettings = () => {
|
|||||||
setIsPasswordSynched(false)
|
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 (
|
return (
|
||||||
<Modal open={isModalOpened} onClose={onClose} title='Settings'>
|
<Modal open={isModalOpened} onClose={onClose} title='Settings'>
|
||||||
<Stack gap={'1rem'}>
|
<Stack gap={'1rem'}>
|
||||||
<StyledSettingContainer>
|
<StyledSettingContainer onSubmit={handleSubmit}>
|
||||||
<Stack direction={'row'} justifyContent={'space-between'}>
|
<Stack direction={'row'} justifyContent={'space-between'}>
|
||||||
<SectionTitle>Cloud sync</SectionTitle>
|
<SectionTitle>Cloud sync</SectionTitle>
|
||||||
{isPasswordSynched && (
|
{isPasswordSynched && (
|
||||||
@@ -63,7 +86,10 @@ export const ModalSettings = () => {
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Box>
|
<Box>
|
||||||
<Checkbox />
|
<Checkbox
|
||||||
|
onChange={handleChangeCheckbox}
|
||||||
|
checked={isChecked}
|
||||||
|
/>
|
||||||
<Typography variant='caption'>
|
<Typography variant='caption'>
|
||||||
Use this login on multiple devices
|
Use this login on multiple devices
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -82,7 +108,7 @@ export const ModalSettings = () => {
|
|||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
type={isPasswordShown ? 'password' : 'text'}
|
type={isPasswordShown ? 'text' : 'password'}
|
||||||
onChange={handlePasswordChange}
|
onChange={handlePasswordChange}
|
||||||
value={enteredPassword}
|
value={enteredPassword}
|
||||||
helperText={isPasswordInvalid ? 'Invalid password' : ''}
|
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
|
Sync
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</StyledSettingContainer>
|
</StyledSettingContainer>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
|
|
||||||
export const StyledSettingContainer = styled((props: StackProps) => (
|
export const StyledSettingContainer = styled((props: StackProps) => (
|
||||||
<Stack {...props} gap={'1rem'} />
|
<Stack gap={'1rem'} component={'form'} {...props} />
|
||||||
))(({ theme }) => ({
|
))(({ theme }) => ({
|
||||||
padding: '0.75rem',
|
padding: '0.75rem',
|
||||||
borderRadius: '1rem',
|
borderRadius: '1rem',
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ import { StyledAppLogo } from './styled'
|
|||||||
import { Input } from '@/shared/Input/Input'
|
import { Input } from '@/shared/Input/Input'
|
||||||
import { Button } from '@/shared/Button/Button'
|
import { Button } from '@/shared/Button/Button'
|
||||||
import { CheckmarkIcon } from '@/assets'
|
import { CheckmarkIcon } from '@/assets'
|
||||||
|
import { swicCall } from '@/modules/swic'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
export const ModalSignUp = () => {
|
export const ModalSignUp = () => {
|
||||||
const { getModalOpened, handleClose } = useModalSearchParams()
|
const { getModalOpened, handleClose } = useModalSearchParams()
|
||||||
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SIGN_UP)
|
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SIGN_UP)
|
||||||
const handleCloseModal = handleClose(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 notify = useEnqueueSnackbar()
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const [enteredValue, setEnteredValue] = useState('')
|
const [enteredValue, setEnteredValue] = useState('')
|
||||||
|
|
||||||
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
@@ -36,6 +38,13 @@ export const ModalSignUp = () => {
|
|||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
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 (
|
return (
|
||||||
@@ -77,7 +86,9 @@ export const ModalSignUp = () => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button fullWidth>Sign up</Button>
|
<Button fullWidth type='submit'>
|
||||||
|
Sign up
|
||||||
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useCallback, useEffect, useState } from 'react'
|
|||||||
import { MetaEvent } from '@/types/meta-event'
|
import { MetaEvent } from '@/types/meta-event'
|
||||||
import { fetchProfile } from '@/modules/nostr'
|
import { fetchProfile } from '@/modules/nostr'
|
||||||
import { ProfileMenu } from './components/ProfileMenu'
|
import { ProfileMenu } from './components/ProfileMenu'
|
||||||
import { getShortenNpub } from '@/utils/helpers'
|
import { getShortenNpub } from '@/utils/helpers/helpers'
|
||||||
|
|
||||||
export const Header = () => {
|
export const Header = () => {
|
||||||
const { npub = '' } = useParams<{ npub: string }>()
|
const { npub = '' } = useParams<{ npub: string }>()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DbKey } from '@/modules/db'
|
import { DbKey } from '@/modules/db'
|
||||||
import { getShortenNpub } from '@/utils/helpers'
|
import { getShortenNpub } from '@/utils/helpers/helpers'
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Menu as MuiMenu } from '@mui/material'
|
|||||||
import DarkModeIcon from '@mui/icons-material/DarkMode'
|
import DarkModeIcon from '@mui/icons-material/DarkMode'
|
||||||
import LightModeIcon from '@mui/icons-material/LightMode'
|
import LightModeIcon from '@mui/icons-material/LightMode'
|
||||||
import LoginIcon from '@mui/icons-material/Login'
|
import LoginIcon from '@mui/icons-material/Login'
|
||||||
|
import PersonAddAltRoundedIcon from '@mui/icons-material/PersonAddAltRounded'
|
||||||
import { setThemeMode } from '@/store/reducers/ui.slice'
|
import { setThemeMode } from '@/store/reducers/ui.slice'
|
||||||
import { useAppDispatch, useAppSelector } from '@/store/hooks/redux'
|
import { useAppDispatch, useAppSelector } from '@/store/hooks/redux'
|
||||||
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||||
@@ -10,13 +11,16 @@ import { MenuButton } from './styled'
|
|||||||
import { useOpenMenu } from '@/hooks/useOpenMenu'
|
import { useOpenMenu } from '@/hooks/useOpenMenu'
|
||||||
import { MenuItem } from './MenuItem'
|
import { MenuItem } from './MenuItem'
|
||||||
import MenuRoundedIcon from '@mui/icons-material/MenuRounded'
|
import MenuRoundedIcon from '@mui/icons-material/MenuRounded'
|
||||||
|
import { selectKeys } from '@/store'
|
||||||
|
|
||||||
export const Menu = () => {
|
export const Menu = () => {
|
||||||
const themeMode = useAppSelector((state) => state.ui.themeMode)
|
const themeMode = useAppSelector((state) => state.ui.themeMode)
|
||||||
|
const keys = useAppSelector(selectKeys)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { handleOpen } = useModalSearchParams()
|
const { handleOpen } = useModalSearchParams()
|
||||||
|
|
||||||
const isDarkMode = themeMode === 'dark'
|
const isDarkMode = themeMode === 'dark'
|
||||||
|
const isNoKeys = !keys || keys.length === 0
|
||||||
|
|
||||||
const {
|
const {
|
||||||
anchorEl,
|
anchorEl,
|
||||||
@@ -53,9 +57,11 @@ export const Menu = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Icon={<LoginIcon />}
|
Icon={
|
||||||
|
isNoKeys ? <LoginIcon /> : <PersonAddAltRoundedIcon />
|
||||||
|
}
|
||||||
onClick={handleNavigateToAuth}
|
onClick={handleNavigateToAuth}
|
||||||
title='Sign up'
|
title={isNoKeys ? 'Sign up' : 'Add account'}
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Icon={themeIcon}
|
Icon={themeIcon}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useNavigate } from 'react-router-dom'
|
|||||||
import LoginIcon from '@mui/icons-material/Login'
|
import LoginIcon from '@mui/icons-material/Login'
|
||||||
import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'
|
import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'
|
||||||
import HomeRoundedIcon from '@mui/icons-material/HomeRounded'
|
import HomeRoundedIcon from '@mui/icons-material/HomeRounded'
|
||||||
|
import PersonAddAltRoundedIcon from '@mui/icons-material/PersonAddAltRounded'
|
||||||
import { useAppDispatch, useAppSelector } from '@/store/hooks/redux'
|
import { useAppDispatch, useAppSelector } from '@/store/hooks/redux'
|
||||||
import { selectKeys } from '@/store'
|
import { selectKeys } from '@/store'
|
||||||
import { setThemeMode } from '@/store/reducers/ui.slice'
|
import { setThemeMode } from '@/store/reducers/ui.slice'
|
||||||
@@ -26,6 +27,7 @@ export const ProfileMenu = () => {
|
|||||||
const { handleOpen } = useModalSearchParams()
|
const { handleOpen } = useModalSearchParams()
|
||||||
|
|
||||||
const keys = useAppSelector(selectKeys)
|
const keys = useAppSelector(selectKeys)
|
||||||
|
const isNoKeys = !keys || keys.length === 0
|
||||||
const themeMode = useAppSelector((state) => state.ui.themeMode)
|
const themeMode = useAppSelector((state) => state.ui.themeMode)
|
||||||
const isDarkMode = themeMode === 'dark'
|
const isDarkMode = themeMode === 'dark'
|
||||||
|
|
||||||
@@ -84,9 +86,11 @@ export const ProfileMenu = () => {
|
|||||||
title='Home'
|
title='Home'
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Icon={<LoginIcon />}
|
Icon={
|
||||||
|
isNoKeys ? <LoginIcon /> : <PersonAddAltRoundedIcon />
|
||||||
|
}
|
||||||
onClick={handleNavigateToAuth}
|
onClick={handleNavigateToAuth}
|
||||||
title='Sign up'
|
title={isNoKeys ? 'Sign up' : 'Add account'}
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Icon={themeIcon}
|
Icon={themeIcon}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
const AppPage = () => {
|
|
||||||
return <div>AppPage</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AppPage
|
|
||||||
132
src/pages/AppPage/App.Page.tsx
Normal file
132
src/pages/AppPage/App.Page.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
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, IconButton, Stack, Typography } from '@mui/material'
|
||||||
|
import { SectionTitle } from '@/shared/SectionTitle/SectionTitle'
|
||||||
|
import { getShortenNpub } from '@/utils/helpers/helpers'
|
||||||
|
import { StyledButton } from './styled'
|
||||||
|
import { PermissionsMenu } from './components/PermissionsMenu'
|
||||||
|
import { useOpenMenu } from '@/hooks/useOpenMenu'
|
||||||
|
import { ACTIONS } from '@/components/Modal/ModalConfirmEvent/ModalConfirmEvent'
|
||||||
|
import MoreIcon from '@mui/icons-material/MoreVert'
|
||||||
|
|
||||||
|
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>
|
||||||
|
<StyledButton onClick={handleOpen}>
|
||||||
|
Basic/Advanced/Custom {perms.length}
|
||||||
|
</StyledButton>
|
||||||
|
<PermissionsMenu
|
||||||
|
open={open}
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
perms={perms}
|
||||||
|
onClose={handleClose}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<Typography
|
||||||
|
textTransform={'capitalize'}
|
||||||
|
variant='body2'
|
||||||
|
>
|
||||||
|
{h.allowed ? 'allow' : 'disallow'}
|
||||||
|
</Typography>
|
||||||
|
<IconButton>
|
||||||
|
<MoreIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
<Typography variant='caption'>
|
||||||
|
{formatTimestampDate(h.timestamp)}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppPage
|
||||||
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
13
src/pages/AppPage/components/styled.tsx
Normal file
13
src/pages/AppPage/components/styled.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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,
|
||||||
|
},
|
||||||
|
}))
|
||||||
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 StyledButton = styled((props: AppButtonProps) => (
|
||||||
|
<Button {...props} variant='outlined' fullWidth />
|
||||||
|
))(() => ({}))
|
||||||
@@ -1,19 +1,43 @@
|
|||||||
import { useAppSelector } from '../../store/hooks/redux'
|
import { Fragment } from 'react'
|
||||||
import { selectKeys } from '../../store'
|
|
||||||
import { ItemKey } from './components/ItemKey'
|
import { ItemKey } from './components/ItemKey'
|
||||||
import { Stack } from '@mui/material'
|
import { Box, Stack, Typography } from '@mui/material'
|
||||||
import { SectionTitle } from '../../shared/SectionTitle/SectionTitle'
|
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 HomePage = () => {
|
||||||
const keys = useAppSelector(selectKeys)
|
const keys = useAppSelector(selectKeys)
|
||||||
|
const isNoKeys = !keys || keys.length === 0
|
||||||
|
|
||||||
|
const { handleOpen } = useModalSearchParams()
|
||||||
|
const handleClickAddAccount = () => handleOpen(MODAL_PARAMS_KEYS.INITIAL)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack maxHeight={'100%'} overflow={'auto'}>
|
||||||
<SectionTitle marginBottom={'0.5rem'}>Keys:</SectionTitle>
|
<SectionTitle marginBottom={'0.5rem'}>
|
||||||
<Stack gap={'0.5rem'}>
|
{isNoKeys ? 'Welcome!' : 'Keys:'}
|
||||||
{keys.map((key) => (
|
</SectionTitle>
|
||||||
<ItemKey {...key} key={key.npub} />
|
<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>
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FC, useRef } from 'react'
|
import { FC } from 'react'
|
||||||
import { DbKey } from '../../../modules/db'
|
import { DbKey } from '../../../modules/db'
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
@@ -8,8 +8,7 @@ import {
|
|||||||
TypographyProps,
|
TypographyProps,
|
||||||
styled,
|
styled,
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import { call, getShortenNpub, log } from '../../../utils/helpers'
|
import { getShortenNpub } from '../../../utils/helpers/helpers'
|
||||||
import { swicCall } from '../../../modules/swic'
|
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
type ItemKeyProps = DbKey
|
type ItemKeyProps = DbKey
|
||||||
@@ -18,17 +17,6 @@ export const ItemKey: FC<ItemKeyProps> = (props) => {
|
|||||||
const { npub, profile } = props
|
const { npub, profile } = props
|
||||||
const navigate = useNavigate()
|
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 = () => {
|
const handleNavigate = () => {
|
||||||
navigate('/key/' + npub)
|
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 { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { SectionTitle } from '../../shared/SectionTitle/SectionTitle'
|
import { SectionTitle } from '../../shared/SectionTitle/SectionTitle'
|
||||||
import { useAppSelector } from '../../store/hooks/redux'
|
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 { useParams } from 'react-router-dom'
|
||||||
import { fetchProfile } from '../../modules/nostr'
|
import { fetchProfile } from '../../modules/nostr'
|
||||||
import { nip19 } from 'nostr-tools'
|
|
||||||
import { Badge, Box, CircularProgress, Stack } from '@mui/material'
|
import { Badge, Box, CircularProgress, Stack } from '@mui/material'
|
||||||
import { StyledIconButton } from './styled'
|
import { StyledIconButton } from './styled'
|
||||||
import { SettingsIcon, ShareIcon } from '@/assets'
|
import { SettingsIcon, ShareIcon } from '@/assets'
|
||||||
@@ -52,7 +54,10 @@ const KeyPage = () => {
|
|||||||
const notify = useEnqueueSnackbar()
|
const notify = useEnqueueSnackbar()
|
||||||
|
|
||||||
const [profile, setProfile] = useState<MetaEvent | null>(null)
|
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 userNameWithPrefix = userName + '@nsec.app'
|
||||||
|
|
||||||
const [showWarning, setShowWarning] = useState(false)
|
const [showWarning, setShowWarning] = useState(false)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Box, Stack, Typography } from '@mui/material'
|
|||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { StyledEmptyAppsBox } from '../styled'
|
import { StyledEmptyAppsBox } from '../styled'
|
||||||
import { Button } from '@/shared/Button/Button'
|
import { Button } from '@/shared/Button/Button'
|
||||||
import { call } from '@/utils/helpers'
|
import { call } from '@/utils/helpers/helpers'
|
||||||
import { swicCall } from '@/modules/swic'
|
import { swicCall } from '@/modules/swic'
|
||||||
import { useEnqueueSnackbar } from '@/hooks/useEnqueueSnackbar'
|
import { useEnqueueSnackbar } from '@/hooks/useEnqueueSnackbar'
|
||||||
import { ItemApp } from './ItemApp'
|
import { ItemApp } from './ItemApp'
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Avatar, Stack, Typography } from '@mui/material'
|
|||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
// import ImageOutlinedIcon from '@mui/icons-material/ImageOutlined'
|
// import ImageOutlinedIcon from '@mui/icons-material/ImageOutlined'
|
||||||
import { getShortenNpub } from '@/utils/helpers'
|
import { getShortenNpub } from '@/utils/helpers/helpers'
|
||||||
import { StyledItemAppContainer } from './styled'
|
import { StyledItemAppContainer } from './styled'
|
||||||
|
|
||||||
type ItemAppProps = DbApp
|
type ItemAppProps = DbApp
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { CircularProgress, Stack } from '@mui/material'
|
|||||||
|
|
||||||
const KeyPage = lazy(() => import('../pages/KeyPage/Key.Page'))
|
const KeyPage = lazy(() => import('../pages/KeyPage/Key.Page'))
|
||||||
const ConfirmPage = lazy(() => import('../pages/Confirm.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 = () => (
|
const LoadingSpinner = () => (
|
||||||
<Stack height={'100%'} justifyContent={'center'} alignItems={'center'}>
|
<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 { TransitionProps } from '@mui/material/transitions'
|
||||||
import { FC, forwardRef } from 'react'
|
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(
|
const Transition = forwardRef(function Transition(
|
||||||
props: TransitionProps & {
|
props: TransitionProps & {
|
||||||
@@ -14,9 +22,29 @@ const Transition = forwardRef(function Transition(
|
|||||||
return <Slide direction='up' ref={ref} {...props} />
|
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 (
|
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>}
|
{title && <StyledDialogTitle>{title}</StyledDialogTitle>}
|
||||||
<StyledDialogContent>{children}</StyledDialogContent>
|
<StyledDialogContent>{children}</StyledDialogContent>
|
||||||
</StyledDialog>
|
</StyledDialog>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
|
Box,
|
||||||
|
BoxProps,
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogContentProps,
|
DialogContentProps,
|
||||||
@@ -56,3 +58,17 @@ export const StyledDialogContent = styled((props: DialogContentProps) => (
|
|||||||
padding: '0 1rem 1rem',
|
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,
|
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(
|
export const selectPendingsByNpub = memoizeOne(
|
||||||
(state: RootState, npub: string) => {
|
(state: RootState, npub: string) => {
|
||||||
return state.content.pending.filter((pending) => pending.npub === npub)
|
return state.content.pending.filter((pending) => pending.npub === npub)
|
||||||
},
|
},
|
||||||
isDeepEqual,
|
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 { nip19 } from 'nostr-tools'
|
||||||
import { NIP46_RELAYS } from './consts'
|
import { NIP46_RELAYS } from '../consts'
|
||||||
|
|
||||||
export async function log(s: string) {
|
export async function log(s: string) {
|
||||||
const log = document.getElementById('log')
|
const log = document.getElementById('log')
|
||||||
Reference in New Issue
Block a user