Merge pull request #18 from nostrband/develop
Implement connectApp logic, add app url and icon
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
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, getAppIconTitle, getShortenNpub } from '@/utils/helpers/helpers'
|
import { call, getAppIconTitle, getDomain, 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 { useNavigate, useParams, useSearchParams } from 'react-router-dom'
|
||||||
import { useAppSelector } from '@/store/hooks/redux'
|
import { useAppSelector } from '@/store/hooks/redux'
|
||||||
import { selectAppsByNpub } from '@/store'
|
import { selectAppsByNpub } from '@/store'
|
||||||
import { StyledButton, StyledToggleButtonsGroup } from './styled'
|
import { StyledButton, StyledToggleButtonsGroup } from './styled'
|
||||||
@@ -11,25 +11,33 @@ import { ActionToggleButton } from './сomponents/ActionToggleButton'
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { swicCall } from '@/modules/swic'
|
import { swicCall } from '@/modules/swic'
|
||||||
import { ACTION_TYPE } from '@/utils/consts'
|
import { ACTION_TYPE } from '@/utils/consts'
|
||||||
|
import { useEnqueueSnackbar } from '@/hooks/useEnqueueSnackbar'
|
||||||
|
|
||||||
export const ModalConfirmConnect = () => {
|
export const ModalConfirmConnect = () => {
|
||||||
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams()
|
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams()
|
||||||
|
const notify = useEnqueueSnackbar()
|
||||||
|
const navigate = useNavigate()
|
||||||
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_CONNECT)
|
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_CONNECT)
|
||||||
|
|
||||||
const { npub = '' } = useParams<{ npub: string }>()
|
const [searchParams] = useSearchParams()
|
||||||
|
const paramNpub = searchParams.get('npub') || ''
|
||||||
|
const { npub = paramNpub } = useParams<{ npub: string }>()
|
||||||
const apps = useAppSelector((state) => selectAppsByNpub(state, npub))
|
const apps = useAppSelector((state) => selectAppsByNpub(state, npub))
|
||||||
|
|
||||||
const [selectedActionType, setSelectedActionType] = useState<ACTION_TYPE>(ACTION_TYPE.BASIC)
|
const [selectedActionType, setSelectedActionType] = useState<ACTION_TYPE>(ACTION_TYPE.BASIC)
|
||||||
|
|
||||||
const [searchParams] = useSearchParams()
|
|
||||||
const appNpub = searchParams.get('appNpub') || ''
|
const appNpub = searchParams.get('appNpub') || ''
|
||||||
const pendingReqId = searchParams.get('reqId') || ''
|
const pendingReqId = searchParams.get('reqId') || ''
|
||||||
const isPopup = searchParams.get('popup') === 'true'
|
const isPopup = searchParams.get('popup') === 'true'
|
||||||
|
const token = searchParams.get('token') || ''
|
||||||
|
|
||||||
const triggerApp = apps.find((app) => app.appNpub === appNpub)
|
const triggerApp = apps.find((app) => app.appNpub === appNpub)
|
||||||
const { name, icon = '' } = triggerApp || {}
|
const { name, url = '', icon = '' } = triggerApp || {}
|
||||||
const appName = name || getShortenNpub(appNpub)
|
const appUrl = url || searchParams.get('appUrl') || ''
|
||||||
const appAvatarTitle = getAppIconTitle(name, appNpub)
|
const appDomain = getDomain(appUrl)
|
||||||
|
const appName = name || appDomain || getShortenNpub(appNpub)
|
||||||
|
const appAvatarTitle = getAppIconTitle(name || appDomain, appNpub)
|
||||||
|
const appIcon = icon || (appDomain ? `https://${appDomain}/favicon.ico` : '')
|
||||||
|
|
||||||
const handleActionTypeChange = (_: any, value: ACTION_TYPE | null) => {
|
const handleActionTypeChange = (_: any, value: ACTION_TYPE | null) => {
|
||||||
if (!value) return undefined
|
if (!value) return undefined
|
||||||
@@ -47,6 +55,9 @@ export const ModalConfirmConnect = () => {
|
|||||||
onClose: (sp) => {
|
onClose: (sp) => {
|
||||||
sp.delete('appNpub')
|
sp.delete('appNpub')
|
||||||
sp.delete('reqId')
|
sp.delete('reqId')
|
||||||
|
sp.delete('popup')
|
||||||
|
sp.delete('npub')
|
||||||
|
sp.delete('appUrl')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -59,16 +70,57 @@ export const ModalConfirmConnect = () => {
|
|||||||
if (isPopup) window.close()
|
if (isPopup) window.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
const allow = () => {
|
const allow = async () => {
|
||||||
const options: any = {}
|
let perms = ['connect','get_public_key'];
|
||||||
if (selectedActionType === ACTION_TYPE.BASIC) options.perms = [ACTION_TYPE.BASIC]
|
if (selectedActionType === ACTION_TYPE.BASIC) perms = [ACTION_TYPE.BASIC]
|
||||||
// else
|
|
||||||
// options.perms = ['connect','get_public_key'];
|
if (pendingReqId) {
|
||||||
confirmPending(pendingReqId, true, true, options)
|
const options = { perms }
|
||||||
|
await confirmPending(pendingReqId, true, true, options)
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await swicCall('enablePush')
|
||||||
|
console.log('enablePush done')
|
||||||
|
} catch (e: any) {
|
||||||
|
console.log('error', e)
|
||||||
|
notify('Please enable Notifications in website settings!', 'error')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await swicCall('connectApp', { npub, appNpub, appUrl, perms })
|
||||||
|
console.log('connectApp done', npub, appNpub, appUrl, perms)
|
||||||
|
} catch (e: any) {
|
||||||
|
notify(e.toString(), 'error')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
try {
|
||||||
|
await swicCall('redeemToken', npub, token)
|
||||||
|
console.log('redeemToken done')
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error", e);
|
||||||
|
notify('App did not reply. Please try to log in now.', 'error')
|
||||||
|
navigate(`/key/${npub}`, { replace: true })
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notify('App connected! Closing...', 'success')
|
||||||
|
|
||||||
|
if (isPopup)
|
||||||
|
setTimeout(() => window.close(), 3000);
|
||||||
|
else
|
||||||
|
navigate(`/key/${npub}`, { replace: true })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const disallow = () => {
|
const disallow = () => {
|
||||||
|
if (pendingReqId)
|
||||||
confirmPending(pendingReqId, false, true)
|
confirmPending(pendingReqId, false, true)
|
||||||
|
else
|
||||||
|
closeModalAfterRequest()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPopup) {
|
if (isPopup) {
|
||||||
@@ -91,7 +143,7 @@ export const ModalConfirmConnect = () => {
|
|||||||
width: 56,
|
width: 56,
|
||||||
height: 56,
|
height: 56,
|
||||||
}}
|
}}
|
||||||
src={icon}
|
src={appIcon}
|
||||||
>
|
>
|
||||||
{appAvatarTitle}
|
{appAvatarTitle}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|||||||
@@ -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, getAppIconTitle, getReqActionName, getShortenNpub, getSignReqKind } from '@/utils/helpers/helpers'
|
import { call, getAppIconTitle, getReqActionName, getShortenNpub } from '@/utils/helpers/helpers'
|
||||||
import { Avatar, Box, List, ListItem, ListItemIcon, ListItemText, Stack, Typography } from '@mui/material'
|
import { Avatar, Box, List, ListItem, ListItemIcon, ListItemText, 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'
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ export class NoauthBackend {
|
|||||||
const url = `${NOAUTHD_URL}/name?npub=${npub}`
|
const url = `${NOAUTHD_URL}/name?npub=${npub}`
|
||||||
const r = await fetch(url)
|
const r = await fetch(url)
|
||||||
const d = await r.json()
|
const d = await r.json()
|
||||||
return d?.names?.length ? d.names[0] as string : ''
|
return d?.names?.length ? (d.names[0] as string) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sendPost({ url, method, headers, body }: { url: string; method: string; headers: any; body: string }) {
|
private async sendPost({ url, method, headers, body }: { url: string; method: string; headers: any; body: string }) {
|
||||||
@@ -407,6 +407,23 @@ export class NoauthBackend {
|
|||||||
throw new Error('Too many requests, retry later')
|
throw new Error('Too many requests, retry later')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async sendTokenToServer(npub: string, token: string) {
|
||||||
|
const body = JSON.stringify({
|
||||||
|
npub,
|
||||||
|
token,
|
||||||
|
})
|
||||||
|
|
||||||
|
const method = 'POST'
|
||||||
|
const url = `${NOAUTHD_URL}/created`
|
||||||
|
|
||||||
|
return this.sendPostAuthd({
|
||||||
|
npub,
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private notify() {
|
private notify() {
|
||||||
// FIXME collect info from accessBuffer and confirmBuffer
|
// FIXME collect info from accessBuffer and confirmBuffer
|
||||||
// and update the notifications
|
// and update the notifications
|
||||||
@@ -550,6 +567,50 @@ export class NoauthBackend {
|
|||||||
return perm?.value || ''
|
return perm?.value || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async connectApp({
|
||||||
|
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(),
|
||||||
|
name: appName,
|
||||||
|
icon: appIcon,
|
||||||
|
url: appUrl,
|
||||||
|
})
|
||||||
|
|
||||||
|
// reload
|
||||||
|
this.apps = await dbi.listApps()
|
||||||
|
|
||||||
|
// write new perms confirmed by user
|
||||||
|
for (const p of perms) {
|
||||||
|
await dbi.addPerm({
|
||||||
|
id: Math.random().toString(36).substring(7),
|
||||||
|
npub: npub,
|
||||||
|
appNpub: appNpub,
|
||||||
|
perm: p,
|
||||||
|
value: '1',
|
||||||
|
timestamp: Date.now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// reload
|
||||||
|
this.perms = await dbi.listPerms()
|
||||||
|
}
|
||||||
|
|
||||||
private async allowPermitCallback({
|
private async allowPermitCallback({
|
||||||
backend,
|
backend,
|
||||||
npub,
|
npub,
|
||||||
@@ -566,7 +627,7 @@ export class NoauthBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const appNpub = nip19.npubEncode(remotePubkey)
|
const appNpub = nip19.npubEncode(remotePubkey)
|
||||||
const connected = !!this.apps.find(a => a.appNpub === appNpub)
|
const connected = !!this.apps.find((a) => a.appNpub === appNpub)
|
||||||
if (!connected && method !== 'connect') {
|
if (!connected && method !== 'connect') {
|
||||||
console.log('ignoring request before connect', method, id, appNpub, npub)
|
console.log('ignoring request before connect', method, id, appNpub, npub)
|
||||||
return false
|
return false
|
||||||
@@ -790,6 +851,11 @@ export class NoauthBackend {
|
|||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async redeemToken(npub: string, token: string) {
|
||||||
|
console.log('redeeming token', npub, token)
|
||||||
|
await this.sendTokenToServer(npub, token)
|
||||||
|
}
|
||||||
|
|
||||||
private async importKey(name: string, nsec: string) {
|
private async importKey(name: string, nsec: string) {
|
||||||
const k = await this.addKey({ name, nsec })
|
const k = await this.addKey({ name, nsec })
|
||||||
this.updateUI()
|
this.updateUI()
|
||||||
@@ -829,7 +895,7 @@ export class NoauthBackend {
|
|||||||
const npubName = await this.fetchNpubName(npub)
|
const npubName = await this.fetchNpubName(npub)
|
||||||
if (npubName) {
|
if (npubName) {
|
||||||
// already have name for this npub
|
// already have name for this npub
|
||||||
console.log("existing npub name", npub, npubName)
|
console.log('existing npub name', npub, npubName)
|
||||||
name = npubName
|
name = npubName
|
||||||
} else if (nip05.includes('@')) {
|
} else if (nip05.includes('@')) {
|
||||||
// no name for them?
|
// no name for them?
|
||||||
@@ -837,7 +903,7 @@ export class NoauthBackend {
|
|||||||
if (domain === DOMAIN) {
|
if (domain === DOMAIN) {
|
||||||
// wtf? how did we learn their npub if
|
// wtf? how did we learn their npub if
|
||||||
// it's the name on our server but we can't fetch it?
|
// it's the name on our server but we can't fetch it?
|
||||||
console.log("existing name", nip05name)
|
console.log('existing name', nip05name)
|
||||||
name = nip05name
|
name = nip05name
|
||||||
} else {
|
} else {
|
||||||
// try to take same name on our domain
|
// try to take same name on our domain
|
||||||
@@ -850,13 +916,13 @@ export class NoauthBackend {
|
|||||||
takenName = await fetchNip05(`${name}@${DOMAIN}`)
|
takenName = await fetchNip05(`${name}@${DOMAIN}`)
|
||||||
}
|
}
|
||||||
if (takenName) {
|
if (takenName) {
|
||||||
console.log("All names taken, leave without a name?")
|
console.log('All names taken, leave without a name?')
|
||||||
name = ''
|
name = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("fetch", { name, existingName })
|
console.log('fetch', { name, existingName })
|
||||||
|
|
||||||
// add new key
|
// add new key
|
||||||
const nsec = await this.keysModule.decryptKeyPass({
|
const nsec = await this.keysModule.decryptKeyPass({
|
||||||
@@ -926,6 +992,8 @@ export class NoauthBackend {
|
|||||||
let result = undefined
|
let result = undefined
|
||||||
if (method === 'generateKey') {
|
if (method === 'generateKey') {
|
||||||
result = await this.generateKey(args[0])
|
result = await this.generateKey(args[0])
|
||||||
|
} else if (method === 'redeemToken') {
|
||||||
|
result = await this.redeemToken(args[0], args[1])
|
||||||
} else if (method === 'importKey') {
|
} else if (method === 'importKey') {
|
||||||
result = await this.importKey(args[0], args[1])
|
result = await this.importKey(args[0], args[1])
|
||||||
} else if (method === 'saveKey') {
|
} else if (method === 'saveKey') {
|
||||||
@@ -934,6 +1002,8 @@ export class NoauthBackend {
|
|||||||
result = await this.fetchKey(args[0], args[1], args[2])
|
result = await this.fetchKey(args[0], args[1], args[2])
|
||||||
} else if (method === 'confirm') {
|
} else if (method === 'confirm') {
|
||||||
result = await this.confirm(args[0], args[1], args[2], args[3])
|
result = await this.confirm(args[0], args[1], args[2], args[3])
|
||||||
|
} else if (method === 'connectApp') {
|
||||||
|
result = await this.connectApp(args[0])
|
||||||
} else if (method === 'deleteApp') {
|
} else if (method === 'deleteApp') {
|
||||||
result = await this.deleteApp(args[0])
|
result = await this.deleteApp(args[0])
|
||||||
} else if (method === 'deletePerm') {
|
} else if (method === 'deletePerm') {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type ItemPermissionProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ItemPermission: FC<ItemPermissionProps> = ({ permission }) => {
|
export const ItemPermission: FC<ItemPermissionProps> = ({ permission }) => {
|
||||||
const { perm, value, timestamp, id } = permission || {}
|
const { value, timestamp, id } = permission || {}
|
||||||
|
|
||||||
const { anchorEl, handleClose, handleOpen, open } = useOpenMenu()
|
const { anchorEl, handleClose, handleOpen, open } = useOpenMenu()
|
||||||
|
|
||||||
|
|||||||
103
src/pages/CreatePage/Create.Page.tsx
Normal file
103
src/pages/CreatePage/Create.Page.tsx
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { Stack, Typography } from '@mui/material'
|
||||||
|
import { GetStartedButton, LearnMoreButton } from './styled'
|
||||||
|
import { DOMAIN } from '@/utils/consts'
|
||||||
|
import { useSearchParams } from 'react-router-dom'
|
||||||
|
import { swicCall } from '@/modules/swic'
|
||||||
|
import { useEnqueueSnackbar } from '@/hooks/useEnqueueSnackbar'
|
||||||
|
import { ModalConfirmConnect } from '@/components/Modal/ModalConfirmConnect/ModalConfirmConnect'
|
||||||
|
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
|
||||||
|
import { MODAL_PARAMS_KEYS } from '@/types/modal'
|
||||||
|
|
||||||
|
const CreatePage = () => {
|
||||||
|
const notify = useEnqueueSnackbar()
|
||||||
|
const { handleOpen } = useModalSearchParams()
|
||||||
|
|
||||||
|
const [searchParams] = useSearchParams()
|
||||||
|
|
||||||
|
const name = searchParams.get('name') || ''
|
||||||
|
const token = searchParams.get('token') || ''
|
||||||
|
const appNpub = searchParams.get('appNpub') || ''
|
||||||
|
const isValid = name && token && appNpub
|
||||||
|
|
||||||
|
const nip05 = `${name}@${DOMAIN}`
|
||||||
|
|
||||||
|
const handleLearnMore = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
window.open(`https://${DOMAIN}`, '_blank').focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClickAddAccount = async () => {
|
||||||
|
try {
|
||||||
|
const key: any = await swicCall('generateKey', name)
|
||||||
|
|
||||||
|
let appUrl = ''
|
||||||
|
if (window.document.referrer) {
|
||||||
|
try {
|
||||||
|
const u = new URL(window.document.referrer)
|
||||||
|
appUrl = u.origin
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Created', key.npub, 'app', appUrl)
|
||||||
|
|
||||||
|
handleOpen(MODAL_PARAMS_KEYS.CONFIRM_CONNECT, {
|
||||||
|
search: {
|
||||||
|
npub: key.npub,
|
||||||
|
appNpub,
|
||||||
|
appUrl,
|
||||||
|
token,
|
||||||
|
// needed for this screen itself
|
||||||
|
name,
|
||||||
|
// will close after all done
|
||||||
|
popup: 'true'
|
||||||
|
},
|
||||||
|
replace: true
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
notify(error.message || error.toString(), 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
return (
|
||||||
|
<Stack maxHeight={'100%'} overflow={'auto'}>
|
||||||
|
<Typography textAlign={'center'} variant="h6" paddingTop="1em">
|
||||||
|
Bad parameters.
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack maxHeight={'100%'} overflow={'auto'}>
|
||||||
|
<Typography textAlign={'center'} variant="h4" paddingTop="0.5em">
|
||||||
|
Welcome to Nostr!
|
||||||
|
</Typography>
|
||||||
|
<Stack gap={'0.5rem'} overflow={'auto'}>
|
||||||
|
<Typography textAlign={'left'} variant="h6" paddingTop="0.5em">
|
||||||
|
Chosen name: <b>{nip05}</b>
|
||||||
|
</Typography>
|
||||||
|
<GetStartedButton onClick={handleClickAddAccount}>Create account</GetStartedButton>
|
||||||
|
|
||||||
|
<Typography textAlign={'left'} variant="h5" paddingTop="1em">
|
||||||
|
What you need to know:
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<ol style={{ marginLeft: '1em' }}>
|
||||||
|
<li>Nostr accounts are based on cryptographic keys.</li>
|
||||||
|
<li>All your actions on Nostr will be signed by your keys.</li>
|
||||||
|
<li>Nsec.app is one of many services to manage Nostr keys.</li>
|
||||||
|
<li>When you create an account, a new key will be created.</li>
|
||||||
|
<li>This key can later be used with other Nostr websites.</li>
|
||||||
|
</ol>
|
||||||
|
<LearnMoreButton onClick={handleLearnMore}>Learn more</LearnMoreButton>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
<ModalConfirmConnect />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreatePage
|
||||||
26
src/pages/CreatePage/styled.tsx
Normal file
26
src/pages/CreatePage/styled.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { AppButtonProps, Button } from '@/shared/Button/Button'
|
||||||
|
import { styled } from '@mui/material'
|
||||||
|
import PersonAddAltRoundedIcon from '@mui/icons-material/PersonAddAltRounded'
|
||||||
|
import PlayArrowOutlinedIcon from '@mui/icons-material/PlayArrowOutlined'
|
||||||
|
import HelpOutlineOutlinedIcon from '@mui/icons-material/HelpOutlineOutlined'
|
||||||
|
|
||||||
|
export const AddAccountButton = styled((props: AppButtonProps) => (
|
||||||
|
<Button {...props} startIcon={<PersonAddAltRoundedIcon />} />
|
||||||
|
))(() => ({
|
||||||
|
alignSelf: 'center',
|
||||||
|
padding: '0.35rem 1rem',
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const GetStartedButton = styled((props: AppButtonProps) => (
|
||||||
|
<Button {...props} startIcon={<PlayArrowOutlinedIcon />} />
|
||||||
|
))(() => ({
|
||||||
|
alignSelf: 'left',
|
||||||
|
padding: '0.35rem 1rem',
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const LearnMoreButton = styled((props: AppButtonProps) => (
|
||||||
|
<Button {...props} startIcon={<HelpOutlineOutlinedIcon />} />
|
||||||
|
))(() => ({
|
||||||
|
alignSelf: 'left',
|
||||||
|
padding: '0.35rem 1rem',
|
||||||
|
}))
|
||||||
@@ -18,7 +18,7 @@ const HomePage = () => {
|
|||||||
|
|
||||||
const handleLearnMore = () => {
|
const handleLearnMore = () => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.open(`https://info.${DOMAIN}`, '_blank').focus()
|
window.open(`https://${DOMAIN}`, '_blank').focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type AppsProps = {
|
|||||||
npub: string
|
npub: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Apps: FC<AppsProps> = ({ apps = [], npub = '' }) => {
|
export const Apps: FC<AppsProps> = ({ apps = [] }) => {
|
||||||
const notify = useEnqueueSnackbar()
|
const notify = useEnqueueSnackbar()
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ import { DbApp } from '@/modules/db'
|
|||||||
import { Avatar, Stack, Typography } from '@mui/material'
|
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 { getAppIconTitle, getDomain, getShortenNpub } from '@/utils/helpers/helpers'
|
||||||
import { getAppIconTitle, getShortenNpub } from '@/utils/helpers/helpers'
|
|
||||||
import { StyledItemAppContainer } from './styled'
|
import { StyledItemAppContainer } from './styled'
|
||||||
|
|
||||||
type ItemAppProps = DbApp
|
type ItemAppProps = DbApp
|
||||||
|
|
||||||
export const ItemApp: FC<ItemAppProps> = ({ npub, appNpub, icon, name }) => {
|
export const ItemApp: FC<ItemAppProps> = ({ npub, appNpub, icon, name, url }) => {
|
||||||
const appName = name || getShortenNpub(appNpub)
|
const appDomain = getDomain(url)
|
||||||
const appAvatarTitle = getAppIconTitle(name, appNpub)
|
const appName = name || appDomain || getShortenNpub(appNpub)
|
||||||
|
const appIcon = icon || `https://${appDomain}/favicon.ico`
|
||||||
|
const appAvatarTitle = getAppIconTitle(name || appDomain, appNpub)
|
||||||
return (
|
return (
|
||||||
<StyledItemAppContainer
|
<StyledItemAppContainer
|
||||||
direction={'row'}
|
direction={'row'}
|
||||||
@@ -23,8 +24,8 @@ export const ItemApp: FC<ItemAppProps> = ({ npub, appNpub, icon, name }) => {
|
|||||||
<Avatar
|
<Avatar
|
||||||
variant="rounded"
|
variant="rounded"
|
||||||
sx={{ width: 56, height: 56 }}
|
sx={{ width: 56, height: 56 }}
|
||||||
src={icon}
|
src={appIcon}
|
||||||
alt={name}
|
alt={appName}
|
||||||
>
|
>
|
||||||
{appAvatarTitle}
|
{appAvatarTitle}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import HomePage from '../pages/HomePage/Home.Page'
|
|||||||
import WelcomePage from '../pages/Welcome.Page'
|
import WelcomePage from '../pages/Welcome.Page'
|
||||||
import { Layout } from '../layout/Layout'
|
import { Layout } from '../layout/Layout'
|
||||||
import { CircularProgress, Stack } from '@mui/material'
|
import { CircularProgress, Stack } from '@mui/material'
|
||||||
|
import CreatePage from '@/pages/CreatePage/Create.Page'
|
||||||
|
|
||||||
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'))
|
||||||
@@ -26,6 +27,7 @@ const AppRoutes = () => {
|
|||||||
<Route path="/key/:npub" element={<KeyPage />} />
|
<Route path="/key/:npub" element={<KeyPage />} />
|
||||||
<Route path="/key/:npub/app/:appNpub" element={<AppPage />} />
|
<Route path="/key/:npub/app/:appNpub" element={<AppPage />} />
|
||||||
<Route path="/key/:npub/:req_id" element={<ConfirmPage />} />
|
<Route path="/key/:npub/:req_id" element={<ConfirmPage />} />
|
||||||
|
<Route path="/create" element={<CreatePage />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="*" element={<Navigate to={'/home'} />} />
|
<Route path="*" element={<Navigate to={'/home'} />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@@ -3,11 +3,20 @@ import { ACTIONS, ACTION_TYPE, NIP46_RELAYS } from '../consts'
|
|||||||
import { DbPending, DbPerm } from '@/modules/db'
|
import { DbPending, DbPerm } from '@/modules/db'
|
||||||
import { MetaEvent } from '@/types/meta-event'
|
import { MetaEvent } from '@/types/meta-event'
|
||||||
|
|
||||||
export async function call(cb: () => any) {
|
export async function call(cb: () => any, err?: (e: string) => void) {
|
||||||
try {
|
try {
|
||||||
return await cb()
|
return await cb()
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
console.log(`Error: ${e}`)
|
console.log(`Error: ${e}`)
|
||||||
|
err?.(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getDomain = (url: string) => {
|
||||||
|
try {
|
||||||
|
return new URL(url).hostname
|
||||||
|
} catch {
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user