feat: generate new account & profile editor

This commit is contained in:
codytseng 2025-01-14 18:09:31 +08:00
parent 3f031da748
commit 78629dd64f
33 changed files with 535 additions and 142 deletions

View file

@ -0,0 +1,59 @@
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { useNostr } from '@/providers/NostrProvider'
import { Check, Copy, RefreshCcw } from 'lucide-react'
import { generateSecretKey } from 'nostr-tools'
import { nsecEncode } from 'nostr-tools/nip19'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
export default function GenerateNewAccount({
back,
onLoginSuccess
}: {
back: () => void
onLoginSuccess: () => void
}) {
const { t } = useTranslation()
const { nsecLogin } = useNostr()
const [nsec, setNsec] = useState(generateNsec())
const [copied, setCopied] = useState(false)
const handleLogin = () => {
nsecLogin(nsec).then(() => onLoginSuccess())
}
return (
<>
<div className="text-orange-400">
{t(
'This is a private key. Do not share it with anyone. Keep it safe and secure. You will not be able to recover it if you lose it.'
)}
</div>
<div className="flex gap-2">
<Input value={nsec} />
<Button variant="secondary" onClick={() => setNsec(generateNsec())}>
<RefreshCcw />
</Button>
<Button
onClick={() => {
navigator.clipboard.writeText(nsec)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}}
>
{copied ? <Check /> : <Copy />}
</Button>
</div>
<Button onClick={handleLogin}>{t('Login')}</Button>
<Button variant="secondary" onClick={back}>
{t('Back')}
</Button>
</>
)
}
function generateNsec() {
const sk = generateSecretKey()
return nsecEncode(sk)
}

View file

@ -1,34 +1,38 @@
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { useNostr } from '@/providers/NostrProvider'
import { TSignerType } from '@/types'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import AccountList from '../AccountList'
import BunkerLogin from './BunkerLogin'
import PrivateKeyLogin from './NsecLogin'
import GenerateNewAccount from './GenerateNewAccount'
type TAccountManagerPage = 'nsec' | 'bunker' | 'generate' | null
export default function AccountManager({ close }: { close?: () => void }) {
const [loginMethod, setLoginMethod] = useState<TSignerType | null>(null)
const [page, setPage] = useState<TAccountManagerPage>(null)
return (
<>
{loginMethod === 'nsec' ? (
<PrivateKeyLogin back={() => setLoginMethod(null)} onLoginSuccess={() => close?.()} />
) : loginMethod === 'bunker' ? (
<BunkerLogin back={() => setLoginMethod(null)} onLoginSuccess={() => close?.()} />
{page === 'nsec' ? (
<PrivateKeyLogin back={() => setPage(null)} onLoginSuccess={() => close?.()} />
) : page === 'bunker' ? (
<BunkerLogin back={() => setPage(null)} onLoginSuccess={() => close?.()} />
) : page === 'generate' ? (
<GenerateNewAccount back={() => setPage(null)} onLoginSuccess={() => close?.()} />
) : (
<AccountManagerNav setLoginMethod={setLoginMethod} close={close} />
<AccountManagerNav setPage={setPage} close={close} />
)}
</>
)
}
function AccountManagerNav({
setLoginMethod,
setPage,
close
}: {
setLoginMethod: (method: TSignerType) => void
setPage: (page: TAccountManagerPage) => void
close?: () => void
}) {
const { t } = useTranslation()
@ -44,12 +48,19 @@ function AccountManagerNav({
{t('Login with Browser Extension')}
</Button>
)}
<Button variant="secondary" onClick={() => setLoginMethod('bunker')} className="w-full">
<Button variant="secondary" onClick={() => setPage('bunker')} className="w-full">
{t('Login with Bunker')}
</Button>
<Button variant="secondary" onClick={() => setLoginMethod('nsec')} className="w-full">
<Button variant="secondary" onClick={() => setPage('nsec')} className="w-full">
{t('Login with Private Key')}
</Button>
<Separator />
<div className="text-center text-muted-foreground text-sm font-semibold">
{t("Don't have an account yet?")}
</div>
<Button variant="secondary" onClick={() => setPage('generate')} className="w-full">
{t('Generate New Account')}
</Button>
{accounts.length > 0 && (
<>
<Separator />