Remove CreateAccountModal for generation, generate account directly and show recovery step then unlock modal
This commit is contained in:
parent
7cfd235a00
commit
107571c378
@ -4,6 +4,8 @@ import { useAuthorPresentation } from '@/hooks/useAuthorPresentation'
|
|||||||
import { ArticleField } from './ArticleField'
|
import { ArticleField } from './ArticleField'
|
||||||
import { ArticleFormButtons } from './ArticleFormButtons'
|
import { ArticleFormButtons } from './ArticleFormButtons'
|
||||||
import { CreateAccountModal } from './CreateAccountModal'
|
import { CreateAccountModal } from './CreateAccountModal'
|
||||||
|
import { RecoveryStep } from './CreateAccountModalSteps'
|
||||||
|
import { UnlockAccountModal } from './UnlockAccountModal'
|
||||||
import { ImageUploadField } from './ImageUploadField'
|
import { ImageUploadField } from './ImageUploadField'
|
||||||
import { PresentationFormHeader } from './PresentationFormHeader'
|
import { PresentationFormHeader } from './PresentationFormHeader'
|
||||||
import { t } from '@/lib/i18n'
|
import { t } from '@/lib/i18n'
|
||||||
@ -254,17 +256,39 @@ function NoAccountActionButtons({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function NoAccountView() {
|
function NoAccountView() {
|
||||||
const [showCreateModal, setShowCreateModal] = useState(false)
|
const [showImportModal, setShowImportModal] = useState(false)
|
||||||
const [modalStep, setModalStep] = useState<'choose' | 'import'>('choose')
|
const [showRecoveryStep, setShowRecoveryStep] = useState(false)
|
||||||
|
const [showUnlockModal, setShowUnlockModal] = useState(false)
|
||||||
|
const [recoveryPhrase, setRecoveryPhrase] = useState<string[]>([])
|
||||||
|
const [npub, setNpub] = useState('')
|
||||||
|
const [generating, setGenerating] = useState(false)
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
const handleOpenModal = (step: 'choose' | 'import') => {
|
const handleGenerate = async () => {
|
||||||
setModalStep(step)
|
setGenerating(true)
|
||||||
setShowCreateModal(true)
|
setError(null)
|
||||||
|
try {
|
||||||
|
const { nostrAuthService } = await import('@/lib/nostrAuth')
|
||||||
|
const result = await nostrAuthService.createAccount()
|
||||||
|
setRecoveryPhrase(result.recoveryPhrase)
|
||||||
|
setNpub(result.npub)
|
||||||
|
setShowRecoveryStep(true)
|
||||||
|
} catch (e) {
|
||||||
|
setError(e instanceof Error ? e.message : 'Failed to create account')
|
||||||
|
} finally {
|
||||||
|
setGenerating(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleModalClose = () => {
|
const handleRecoveryContinue = () => {
|
||||||
setShowCreateModal(false)
|
setShowRecoveryStep(false)
|
||||||
setModalStep('choose')
|
setShowUnlockModal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUnlockSuccess = () => {
|
||||||
|
setShowUnlockModal(false)
|
||||||
|
setRecoveryPhrase([])
|
||||||
|
setNpub('')
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -273,15 +297,35 @@ function NoAccountView() {
|
|||||||
<p className="text-center text-cyber-accent mb-2">
|
<p className="text-center text-cyber-accent mb-2">
|
||||||
Créez un compte ou importez votre clé secrète pour commencer
|
Créez un compte ou importez votre clé secrète pour commencer
|
||||||
</p>
|
</p>
|
||||||
|
{error && <p className="text-sm text-red-400">{error}</p>}
|
||||||
<NoAccountActionButtons
|
<NoAccountActionButtons
|
||||||
onGenerate={() => handleOpenModal('choose')}
|
onGenerate={handleGenerate}
|
||||||
onImport={() => handleOpenModal('import')}
|
onImport={() => setShowImportModal(true)}
|
||||||
/>
|
/>
|
||||||
{showCreateModal && (
|
{generating && (
|
||||||
|
<p className="text-cyber-accent text-sm">Génération du compte...</p>
|
||||||
|
)}
|
||||||
|
{showImportModal && (
|
||||||
<CreateAccountModal
|
<CreateAccountModal
|
||||||
onSuccess={handleModalClose}
|
onSuccess={() => {
|
||||||
onClose={handleModalClose}
|
setShowImportModal(false)
|
||||||
initialStep={modalStep}
|
setShowUnlockModal(true)
|
||||||
|
}}
|
||||||
|
onClose={() => setShowImportModal(false)}
|
||||||
|
initialStep="import"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showRecoveryStep && (
|
||||||
|
<RecoveryStep
|
||||||
|
recoveryPhrase={recoveryPhrase}
|
||||||
|
npub={npub}
|
||||||
|
onContinue={handleRecoveryContinue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showUnlockModal && (
|
||||||
|
<UnlockAccountModal
|
||||||
|
onSuccess={handleUnlockSuccess}
|
||||||
|
onClose={() => setShowUnlockModal(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useNostrAuth } from '@/hooks/useNostrAuth'
|
import { useNostrAuth } from '@/hooks/useNostrAuth'
|
||||||
import { ConnectedUserMenu } from './ConnectedUserMenu'
|
import { ConnectedUserMenu } from './ConnectedUserMenu'
|
||||||
import { CreateAccountModal } from './CreateAccountModal'
|
import { RecoveryStep } from './CreateAccountModalSteps'
|
||||||
import { UnlockAccountModal } from './UnlockAccountModal'
|
import { UnlockAccountModal } from './UnlockAccountModal'
|
||||||
import type { NostrProfile } from '@/types/nostr'
|
import type { NostrProfile } from '@/types/nostr'
|
||||||
|
|
||||||
@ -37,12 +37,12 @@ function ConnectForm({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function useAutoConnect(accountExists: boolean | null, pubkey: string | null, showCreateModal: boolean, showUnlockModal: boolean, connect: () => Promise<void>) {
|
function useAutoConnect(accountExists: boolean | null, pubkey: string | null, showRecoveryStep: boolean, showUnlockModal: boolean, connect: () => Promise<void>) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (accountExists === true && !pubkey && !showCreateModal && !showUnlockModal) {
|
if (accountExists === true && !pubkey && !showRecoveryStep && !showUnlockModal) {
|
||||||
void connect()
|
void connect()
|
||||||
}
|
}
|
||||||
}, [accountExists, pubkey, showCreateModal, showUnlockModal, connect])
|
}, [accountExists, pubkey, showRecoveryStep, showUnlockModal, connect])
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConnectedState({ pubkey, profile, loading, disconnect }: { pubkey: string; profile: NostrProfile | null; loading: boolean; disconnect: () => Promise<void> }) {
|
function ConnectedState({ pubkey, profile, loading, disconnect }: { pubkey: string; profile: NostrProfile | null; loading: boolean; disconnect: () => Promise<void> }) {
|
||||||
@ -72,28 +72,28 @@ function UnlockState({ loading, error, onUnlock, onClose }: { loading: boolean;
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function DisconnectedModals({
|
|
||||||
showCreateModal,
|
function DisconnectedState({
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
showUnlockModal,
|
showUnlockModal,
|
||||||
setShowCreateModal,
|
|
||||||
setShowUnlockModal,
|
setShowUnlockModal,
|
||||||
|
onCreateAccount,
|
||||||
}: {
|
}: {
|
||||||
showCreateModal: boolean
|
loading: boolean
|
||||||
|
error: string | null
|
||||||
showUnlockModal: boolean
|
showUnlockModal: boolean
|
||||||
setShowCreateModal: (show: boolean) => void
|
|
||||||
setShowUnlockModal: (show: boolean) => void
|
setShowUnlockModal: (show: boolean) => void
|
||||||
|
onCreateAccount: () => void
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{showCreateModal && (
|
<ConnectForm
|
||||||
<CreateAccountModal
|
onCreateAccount={onCreateAccount}
|
||||||
onSuccess={() => {
|
onUnlock={() => setShowUnlockModal(true)}
|
||||||
setShowCreateModal(false)
|
loading={loading}
|
||||||
setShowUnlockModal(true)
|
error={error}
|
||||||
}}
|
|
||||||
onClose={() => setShowCreateModal(false)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{showUnlockModal && (
|
{showUnlockModal && (
|
||||||
<UnlockAccountModal
|
<UnlockAccountModal
|
||||||
onSuccess={() => setShowUnlockModal(false)}
|
onSuccess={() => setShowUnlockModal(false)}
|
||||||
@ -104,45 +104,43 @@ function DisconnectedModals({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function DisconnectedState({
|
|
||||||
loading,
|
|
||||||
error,
|
|
||||||
showCreateModal,
|
|
||||||
showUnlockModal,
|
|
||||||
setShowCreateModal,
|
|
||||||
setShowUnlockModal,
|
|
||||||
}: {
|
|
||||||
loading: boolean
|
|
||||||
error: string | null
|
|
||||||
showCreateModal: boolean
|
|
||||||
showUnlockModal: boolean
|
|
||||||
setShowCreateModal: (show: boolean) => void
|
|
||||||
setShowUnlockModal: (show: boolean) => void
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ConnectForm
|
|
||||||
onCreateAccount={() => setShowCreateModal(true)}
|
|
||||||
onUnlock={() => setShowUnlockModal(true)}
|
|
||||||
loading={loading}
|
|
||||||
error={error}
|
|
||||||
/>
|
|
||||||
<DisconnectedModals
|
|
||||||
showCreateModal={showCreateModal}
|
|
||||||
showUnlockModal={showUnlockModal}
|
|
||||||
setShowCreateModal={setShowCreateModal}
|
|
||||||
setShowUnlockModal={setShowUnlockModal}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ConnectButton() {
|
export function ConnectButton() {
|
||||||
const { connected, pubkey, profile, loading, error, connect, disconnect, accountExists, isUnlocked } = useNostrAuth()
|
const { connected, pubkey, profile, loading, error, connect, disconnect, accountExists, isUnlocked } = useNostrAuth()
|
||||||
const [showCreateModal, setShowCreateModal] = useState(false)
|
const [showRecoveryStep, setShowRecoveryStep] = useState(false)
|
||||||
const [showUnlockModal, setShowUnlockModal] = useState(false)
|
const [showUnlockModal, setShowUnlockModal] = useState(false)
|
||||||
|
const [recoveryPhrase, setRecoveryPhrase] = useState<string[]>([])
|
||||||
|
const [npub, setNpub] = useState('')
|
||||||
|
const [creatingAccount, setCreatingAccount] = useState(false)
|
||||||
|
const [createError, setCreateError] = useState<string | null>(null)
|
||||||
|
|
||||||
useAutoConnect(accountExists, pubkey, showCreateModal, showUnlockModal, connect)
|
useAutoConnect(accountExists, pubkey, false, showUnlockModal, connect)
|
||||||
|
|
||||||
|
const handleCreateAccount = async () => {
|
||||||
|
setCreatingAccount(true)
|
||||||
|
setCreateError(null)
|
||||||
|
try {
|
||||||
|
const { nostrAuthService } = await import('@/lib/nostrAuth')
|
||||||
|
const result = await nostrAuthService.createAccount()
|
||||||
|
setRecoveryPhrase(result.recoveryPhrase)
|
||||||
|
setNpub(result.npub)
|
||||||
|
setShowRecoveryStep(true)
|
||||||
|
} catch (e) {
|
||||||
|
setCreateError(e instanceof Error ? e.message : 'Failed to create account')
|
||||||
|
} finally {
|
||||||
|
setCreatingAccount(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRecoveryContinue = () => {
|
||||||
|
setShowRecoveryStep(false)
|
||||||
|
setShowUnlockModal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUnlockSuccess = () => {
|
||||||
|
setShowUnlockModal(false)
|
||||||
|
setRecoveryPhrase([])
|
||||||
|
setNpub('')
|
||||||
|
}
|
||||||
|
|
||||||
if (connected && pubkey && isUnlocked) {
|
if (connected && pubkey && isUnlocked) {
|
||||||
return <ConnectedState pubkey={pubkey} profile={profile} loading={loading} disconnect={disconnect} />
|
return <ConnectedState pubkey={pubkey} profile={profile} loading={loading} disconnect={disconnect} />
|
||||||
@ -160,13 +158,27 @@ export function ConnectButton() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<DisconnectedState
|
<DisconnectedState
|
||||||
loading={loading}
|
loading={loading || creatingAccount}
|
||||||
error={error}
|
error={error || createError}
|
||||||
showCreateModal={showCreateModal}
|
|
||||||
showUnlockModal={showUnlockModal}
|
showUnlockModal={showUnlockModal}
|
||||||
setShowCreateModal={setShowCreateModal}
|
|
||||||
setShowUnlockModal={setShowUnlockModal}
|
setShowUnlockModal={setShowUnlockModal}
|
||||||
|
onCreateAccount={handleCreateAccount}
|
||||||
/>
|
/>
|
||||||
|
{showRecoveryStep && (
|
||||||
|
<RecoveryStep
|
||||||
|
recoveryPhrase={recoveryPhrase}
|
||||||
|
npub={npub}
|
||||||
|
onContinue={handleRecoveryContinue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showUnlockModal && (
|
||||||
|
<UnlockAccountModal
|
||||||
|
onSuccess={handleUnlockSuccess}
|
||||||
|
onClose={() => setShowUnlockModal(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
|
|
||||||
export function RecoveryWarning() {
|
export function RecoveryWarning() {
|
||||||
return (
|
return (
|
||||||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
|
<div className="bg-yellow-900/20 border border-yellow-400/50 rounded-lg p-4 mb-6">
|
||||||
<p className="text-yellow-800 font-semibold mb-2">⚠️ Important</p>
|
<p className="text-yellow-400 font-semibold mb-2">⚠️ Important</p>
|
||||||
<p className="text-yellow-700 text-sm">
|
<p className="text-yellow-300/90 text-sm">
|
||||||
Ces <strong className="font-bold">4 mots-clés</strong> sont votre seule façon de récupérer votre compte.
|
Ces <strong className="font-bold">4 mots-clés</strong> sont votre seule façon de récupérer votre compte.
|
||||||
<strong className="font-bold"> Ils ne seront jamais affichés à nouveau.</strong>
|
<strong className="font-bold"> Ils ne seront jamais affichés à nouveau.</strong>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-yellow-700 text-sm mt-2">
|
<p className="text-yellow-300/90 text-sm mt-2">
|
||||||
Ces mots-clés (dictionnaire BIP39) sont utilisés avec <strong>PBKDF2</strong> pour chiffrer une clé de chiffrement (KEK) stockée dans l'API Credentials du navigateur. Cette KEK chiffre ensuite votre clé privée stockée dans IndexedDB (système à deux niveaux).
|
Ces mots-clés (dictionnaire BIP39) sont utilisés avec <strong>PBKDF2</strong> pour chiffrer une clé de chiffrement (KEK) stockée dans l'API Credentials du navigateur. Cette KEK chiffre ensuite votre clé privée stockée dans IndexedDB (système à deux niveaux).
|
||||||
</p>
|
</p>
|
||||||
<p className="text-yellow-700 text-sm mt-2">
|
<p className="text-yellow-300/90 text-sm mt-2">
|
||||||
Notez-les dans un endroit sûr. Sans ces mots-clés, vous perdrez définitivement l'accès à votre compte.
|
Notez-les dans un endroit sûr. Sans ces mots-clés, vous perdrez définitivement l'accès à votre compte.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -27,15 +27,15 @@ export function RecoveryPhraseDisplay({
|
|||||||
onCopy: () => void
|
onCopy: () => void
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-50 border border-gray-300 rounded-lg p-6 mb-6">
|
<div className="bg-cyber-darker border border-neon-cyan/30 rounded-lg p-6 mb-6">
|
||||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||||
{recoveryPhrase.map((word, index) => (
|
{recoveryPhrase.map((word, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="bg-white border border-gray-300 rounded-lg p-3 text-center font-mono text-lg"
|
className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-3 text-center font-mono text-lg"
|
||||||
>
|
>
|
||||||
<span className="text-gray-500 text-sm mr-2">{index + 1}.</span>
|
<span className="text-cyber-accent/70 text-sm mr-2">{index + 1}.</span>
|
||||||
<span className="font-semibold">{word}</span>
|
<span className="font-semibold text-neon-cyan">{word}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -43,7 +43,7 @@ export function RecoveryPhraseDisplay({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
void onCopy()
|
void onCopy()
|
||||||
}}
|
}}
|
||||||
className="w-full py-2 px-4 bg-gray-200 hover:bg-gray-300 rounded-lg text-sm font-medium transition-colors"
|
className="w-full py-2 px-4 bg-cyber-light border border-neon-cyan/30 hover:border-neon-cyan/50 hover:bg-cyber-dark text-cyber-accent hover:text-neon-cyan rounded-lg text-sm font-medium transition-colors"
|
||||||
>
|
>
|
||||||
{copied ? '✓ Copié!' : 'Copier les mots-clés'}
|
{copied ? '✓ Copié!' : 'Copier les mots-clés'}
|
||||||
</button>
|
</button>
|
||||||
@ -53,9 +53,9 @@ export function RecoveryPhraseDisplay({
|
|||||||
|
|
||||||
export function PublicKeyDisplay({ npub }: { npub: string }) {
|
export function PublicKeyDisplay({ npub }: { npub: string }) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
|
<div className="bg-neon-blue/10 border border-neon-blue/30 rounded-lg p-4 mb-6">
|
||||||
<p className="text-blue-800 font-semibold mb-2">Votre clé publique (npub)</p>
|
<p className="text-neon-blue font-semibold mb-2">Votre clé publique (npub)</p>
|
||||||
<p className="text-blue-700 text-sm font-mono break-all">{npub}</p>
|
<p className="text-neon-cyan text-sm font-mono break-all">{npub}</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ export function ImportKeyForm({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label htmlFor="importKey" className="block text-sm font-medium text-gray-700 mb-2">
|
<label htmlFor="importKey" className="block text-sm font-medium text-cyber-accent mb-2">
|
||||||
Clé privée (nsec ou hex)
|
Clé privée (nsec ou hex)
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
@ -80,15 +80,15 @@ export function ImportKeyForm({
|
|||||||
value={importKey}
|
value={importKey}
|
||||||
onChange={(e) => setImportKey(e.target.value)}
|
onChange={(e) => setImportKey(e.target.value)}
|
||||||
placeholder="nsec1..."
|
placeholder="nsec1..."
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg font-mono text-sm"
|
className="w-full px-3 py-2 bg-cyber-darker border border-neon-cyan/30 rounded-lg font-mono text-sm text-neon-cyan"
|
||||||
rows={4}
|
rows={4}
|
||||||
/>
|
/>
|
||||||
<p className="text-sm text-gray-600 mt-2">
|
<p className="text-sm text-cyber-accent/70 mt-2">
|
||||||
Après l'import, vous recevrez <strong>4 mots-clés de récupération</strong> (dictionnaire BIP39) pour sécuriser votre compte.
|
Après l'import, vous recevrez <strong>4 mots-clés de récupération</strong> (dictionnaire BIP39) pour sécuriser votre compte.
|
||||||
Ces mots-clés chiffrent une clé de chiffrement (KEK) stockée dans l'API Credentials, qui chiffre ensuite votre clé privée.
|
Ces mots-clés chiffrent une clé de chiffrement (KEK) stockée dans l'API Credentials, qui chiffre ensuite votre clé privée.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{error && <p className="text-sm text-red-600 mb-4">{error}</p>}
|
{error && <p className="text-sm text-red-400 mb-4">{error}</p>}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ export function ImportStepButtons({ loading, onImport, onBack }: { loading: bool
|
|||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<button
|
<button
|
||||||
onClick={onBack}
|
onClick={onBack}
|
||||||
className="flex-1 py-2 px-4 bg-gray-200 hover:bg-gray-300 rounded-lg font-medium transition-colors"
|
className="flex-1 py-2 px-4 bg-cyber-light border border-neon-cyan/30 hover:border-neon-cyan/50 hover:bg-cyber-dark text-cyber-accent hover:text-neon-cyan rounded-lg font-medium transition-colors"
|
||||||
>
|
>
|
||||||
Retour
|
Retour
|
||||||
</button>
|
</button>
|
||||||
@ -140,13 +140,13 @@ export function ChooseStepButtons({
|
|||||||
<button
|
<button
|
||||||
onClick={onImport}
|
onClick={onImport}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full py-3 px-6 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg font-medium transition-colors disabled:opacity-50"
|
className="w-full py-3 px-6 bg-cyber-light border border-neon-cyan/30 hover:border-neon-cyan/50 hover:bg-cyber-dark text-cyber-accent hover:text-neon-cyan rounded-lg font-medium transition-colors disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Importer une clé existante
|
Importer une clé existante
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="w-full py-2 px-4 text-gray-500 hover:text-gray-700 font-medium transition-colors"
|
className="w-full py-2 px-4 text-cyber-accent/70 hover:text-neon-cyan font-medium transition-colors"
|
||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -22,8 +22,8 @@ export function RecoveryStep({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
<div className="bg-white rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
<div className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto shadow-glow-cyan">
|
||||||
<h2 className="text-2xl font-bold mb-4">Sauvegardez vos 4 mots-clés de récupération</h2>
|
<h2 className="text-2xl font-bold mb-4 text-neon-cyan">Sauvegardez vos 4 mots-clés de récupération</h2>
|
||||||
<RecoveryWarning />
|
<RecoveryWarning />
|
||||||
<RecoveryPhraseDisplay recoveryPhrase={recoveryPhrase} copied={copied} onCopy={handleCopy} />
|
<RecoveryPhraseDisplay recoveryPhrase={recoveryPhrase} copied={copied} onCopy={handleCopy} />
|
||||||
<PublicKeyDisplay npub={npub} />
|
<PublicKeyDisplay npub={npub} />
|
||||||
@ -57,8 +57,8 @@ export function ImportStep({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
<div className="bg-white rounded-lg p-6 max-w-md w-full mx-4">
|
<div className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-6 max-w-md w-full mx-4 shadow-glow-cyan">
|
||||||
<h2 className="text-2xl font-bold mb-4">Importer une clé privée</h2>
|
<h2 className="text-2xl font-bold mb-4 text-neon-cyan">Importer une clé privée</h2>
|
||||||
<ImportKeyForm importKey={importKey} setImportKey={setImportKey} error={error} />
|
<ImportKeyForm importKey={importKey} setImportKey={setImportKey} error={error} />
|
||||||
<ImportStepButtons loading={loading} onImport={onImport} onBack={onBack} />
|
<ImportStepButtons loading={loading} onImport={onImport} onBack={onBack} />
|
||||||
</div>
|
</div>
|
||||||
@ -81,12 +81,12 @@ export function ChooseStep({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
<div className="bg-white rounded-lg p-6 max-w-md w-full mx-4">
|
<div className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-6 max-w-md w-full mx-4 shadow-glow-cyan">
|
||||||
<h2 className="text-2xl font-bold mb-4">Créer un compte</h2>
|
<h2 className="text-2xl font-bold mb-4 text-neon-cyan">Créer un compte</h2>
|
||||||
<p className="text-gray-600 mb-6">
|
<p className="text-cyber-accent/70 mb-6">
|
||||||
Créez un nouveau compte Nostr ou importez une clé privée existante.
|
Créez un nouveau compte Nostr ou importez une clé privée existante.
|
||||||
</p>
|
</p>
|
||||||
{error && <p className="text-sm text-red-600 mb-4">{error}</p>}
|
{error && <p className="text-sm text-red-400 mb-4">{error}</p>}
|
||||||
<ChooseStepButtons loading={loading} onGenerate={onGenerate} onImport={onImport} onClose={onClose} />
|
<ChooseStepButtons loading={loading} onGenerate={onGenerate} onImport={onImport} onClose={onClose} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user