184 lines
5.1 KiB
TypeScript
184 lines
5.1 KiB
TypeScript
import { Button, Card, ErrorState, Textarea } from '@/components/ui'
|
|
import { t } from '@/lib/i18n'
|
|
|
|
export function RecoveryWarning(): React.ReactElement {
|
|
return (
|
|
<Card variant="default" className="bg-yellow-900/20 border-yellow-400/50 mb-6">
|
|
<p className="text-yellow-400 font-semibold mb-2">{t('account.create.recovery.warning.title')}</p>
|
|
<p className="text-yellow-300/90 text-sm" dangerouslySetInnerHTML={{ __html: t('account.create.recovery.warning.part1') }} />
|
|
<p className="text-yellow-300/90 text-sm mt-2" dangerouslySetInnerHTML={{ __html: t('account.create.recovery.warning.part2') }} />
|
|
<p className="text-yellow-300/90 text-sm mt-2">{t('account.create.recovery.warning.part3')}</p>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
export function RecoveryPhraseDisplay({
|
|
recoveryPhrase,
|
|
copied,
|
|
onCopy,
|
|
}: {
|
|
recoveryPhrase: string[]
|
|
copied: boolean
|
|
onCopy: () => void
|
|
}): React.ReactElement {
|
|
const recoveryItems = buildRecoveryPhraseItems(recoveryPhrase)
|
|
return (
|
|
<Card variant="default" className="bg-cyber-darker mb-6">
|
|
<div className="grid grid-cols-2 gap-4 mb-4">
|
|
{recoveryItems.map((item, index) => (
|
|
<Card
|
|
key={item.key}
|
|
variant="default"
|
|
className="bg-cyber-dark p-3 text-center font-mono text-lg"
|
|
>
|
|
<span className="text-cyber-accent/70 text-sm mr-2">{index + 1}.</span>
|
|
<span className="font-semibold text-neon-cyan">{item.word}</span>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
<Button
|
|
onClick={() => {
|
|
void onCopy()
|
|
}}
|
|
variant="secondary"
|
|
size="small"
|
|
className="w-full"
|
|
>
|
|
{copied ? t('account.create.recovery.copied') : t('account.create.recovery.copy')}
|
|
</Button>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
function buildRecoveryPhraseItems(recoveryPhrase: string[]): { key: string; word: string }[] {
|
|
const counts = new Map<string, number>()
|
|
return recoveryPhrase.map((word) => {
|
|
const nextCount = (counts.get(word) ?? 0) + 1
|
|
counts.set(word, nextCount)
|
|
return { key: `${word}-${nextCount}`, word }
|
|
})
|
|
}
|
|
|
|
export function PublicKeyDisplay({ npub }: { npub: string }): React.ReactElement {
|
|
return (
|
|
<Card variant="default" className="bg-neon-blue/10 border-neon-blue/30 mb-6">
|
|
<p className="text-neon-blue font-semibold mb-2">{t('account.create.publicKey')}</p>
|
|
<p className="text-neon-cyan text-sm font-mono break-all">{npub}</p>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
export function ImportKeyForm({
|
|
importKey,
|
|
setImportKey,
|
|
error,
|
|
}: {
|
|
importKey: string
|
|
setImportKey: (key: string) => void
|
|
error: string | null
|
|
}): React.ReactElement {
|
|
return (
|
|
<>
|
|
<Textarea
|
|
id="importKey"
|
|
label={t('account.create.importKey.label')}
|
|
value={importKey}
|
|
onChange={(e) => setImportKey(e.target.value)}
|
|
placeholder={t('account.create.importKey.placeholder')}
|
|
className="font-mono text-sm text-neon-cyan bg-cyber-darker"
|
|
rows={4}
|
|
helperText={t('account.create.importKey.help')}
|
|
/>
|
|
{error && <ErrorState message={error} className="mb-4" />}
|
|
</>
|
|
)
|
|
}
|
|
|
|
export function ImportStepButtons({ loading, onImport, onBack }: { loading: boolean; onImport: () => void; onBack: () => void }): React.ReactElement {
|
|
return (
|
|
<div className="flex gap-4">
|
|
<Button
|
|
onClick={onBack}
|
|
variant="secondary"
|
|
className="flex-1"
|
|
>
|
|
{t('account.create.back')}
|
|
</Button>
|
|
<Button
|
|
onClick={() => {
|
|
void onImport()
|
|
}}
|
|
disabled={loading}
|
|
loading={loading}
|
|
variant="primary"
|
|
className="flex-1"
|
|
>
|
|
{loading ? t('import.loading') : t('import.button')}
|
|
</Button>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function GenerateButton({ loading, onGenerate }: { loading: boolean; onGenerate: () => void }): React.ReactElement {
|
|
return (
|
|
<Button
|
|
onClick={() => {
|
|
void onGenerate()
|
|
}}
|
|
disabled={loading}
|
|
loading={loading}
|
|
variant="primary"
|
|
size="large"
|
|
className="w-full"
|
|
>
|
|
{loading ? t('account.create.importing') : t('account.create.generateButton')}
|
|
</Button>
|
|
)
|
|
}
|
|
|
|
function ImportButton({ loading, onImport }: { loading: boolean; onImport: () => void }): React.ReactElement {
|
|
return (
|
|
<Button
|
|
onClick={onImport}
|
|
disabled={loading}
|
|
variant="secondary"
|
|
size="large"
|
|
className="w-full"
|
|
>
|
|
{t('account.create.importButton')}
|
|
</Button>
|
|
)
|
|
}
|
|
|
|
function CancelButton({ onClose }: { onClose: () => void }): React.ReactElement {
|
|
return (
|
|
<Button
|
|
onClick={onClose}
|
|
variant="ghost"
|
|
className="w-full"
|
|
>
|
|
{t('account.create.cancel')}
|
|
</Button>
|
|
)
|
|
}
|
|
|
|
export function ChooseStepButtons({
|
|
loading,
|
|
onGenerate,
|
|
onImport,
|
|
onClose,
|
|
}: {
|
|
loading: boolean
|
|
onGenerate: () => void
|
|
onImport: () => void
|
|
onClose: () => void
|
|
}): React.ReactElement {
|
|
return (
|
|
<div className="flex flex-col gap-4">
|
|
<GenerateButton loading={loading} onGenerate={onGenerate} />
|
|
<ImportButton loading={loading} onImport={onImport} />
|
|
<CancelButton onClose={onClose} />
|
|
</div>
|
|
)
|
|
}
|