156 lines
5.5 KiB
TypeScript
156 lines
5.5 KiB
TypeScript
|
||
export function RecoveryWarning() {
|
||
return (
|
||
<div className="bg-yellow-900/20 border border-yellow-400/50 rounded-lg p-4 mb-6">
|
||
<p className="text-yellow-400 font-semibold mb-2">⚠️ Important</p>
|
||
<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.
|
||
<strong className="font-bold"> Ils ne seront jamais affichés à nouveau.</strong>
|
||
</p>
|
||
<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).
|
||
</p>
|
||
<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.
|
||
</p>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export function RecoveryPhraseDisplay({
|
||
recoveryPhrase,
|
||
copied,
|
||
onCopy,
|
||
}: {
|
||
recoveryPhrase: string[]
|
||
copied: boolean
|
||
onCopy: () => void
|
||
}) {
|
||
return (
|
||
<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">
|
||
{recoveryPhrase.map((word, index) => (
|
||
<div
|
||
key={index}
|
||
className="bg-cyber-dark border border-neon-cyan/30 rounded-lg 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">{word}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<button
|
||
onClick={() => {
|
||
void onCopy()
|
||
}}
|
||
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'}
|
||
</button>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export function PublicKeyDisplay({ npub }: { npub: string }) {
|
||
return (
|
||
<div className="bg-neon-blue/10 border border-neon-blue/30 rounded-lg p-4 mb-6">
|
||
<p className="text-neon-blue font-semibold mb-2">Votre clé publique (npub)</p>
|
||
<p className="text-neon-cyan text-sm font-mono break-all">{npub}</p>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export function ImportKeyForm({
|
||
importKey,
|
||
setImportKey,
|
||
error,
|
||
}: {
|
||
importKey: string
|
||
setImportKey: (key: string) => void
|
||
error: string | null
|
||
}) {
|
||
return (
|
||
<>
|
||
<div className="mb-4">
|
||
<label htmlFor="importKey" className="block text-sm font-medium text-cyber-accent mb-2">
|
||
Clé privée (nsec ou hex)
|
||
</label>
|
||
<textarea
|
||
id="importKey"
|
||
value={importKey}
|
||
onChange={(e) => setImportKey(e.target.value)}
|
||
placeholder="nsec1..."
|
||
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}
|
||
/>
|
||
<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.
|
||
Ces mots-clés chiffrent une clé de chiffrement (KEK) stockée dans l'API Credentials, qui chiffre ensuite votre clé privée.
|
||
</p>
|
||
</div>
|
||
{error && <p className="text-sm text-red-400 mb-4">{error}</p>}
|
||
</>
|
||
)
|
||
}
|
||
|
||
export function ImportStepButtons({ loading, onImport, onBack }: { loading: boolean; onImport: () => void; onBack: () => void }) {
|
||
return (
|
||
<div className="flex gap-4">
|
||
<button
|
||
onClick={onBack}
|
||
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
|
||
</button>
|
||
<button
|
||
onClick={() => {
|
||
void onImport()
|
||
}}
|
||
disabled={loading}
|
||
className="flex-1 py-2 px-4 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan disabled:opacity-50"
|
||
>
|
||
{loading ? 'Importation...' : 'Importer'}
|
||
</button>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export function ChooseStepButtons({
|
||
loading,
|
||
onGenerate,
|
||
onImport,
|
||
onClose,
|
||
}: {
|
||
loading: boolean
|
||
onGenerate: () => void
|
||
onImport: () => void
|
||
onClose: () => void
|
||
}) {
|
||
return (
|
||
<div className="flex flex-col gap-4">
|
||
<button
|
||
onClick={() => {
|
||
void onGenerate()
|
||
}}
|
||
disabled={loading}
|
||
className="w-full py-3 px-6 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan disabled:opacity-50"
|
||
>
|
||
{loading ? 'Génération...' : 'Générer un nouveau compte'}
|
||
</button>
|
||
<button
|
||
onClick={onImport}
|
||
disabled={loading}
|
||
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
|
||
</button>
|
||
<button
|
||
onClick={onClose}
|
||
className="w-full py-2 px-4 text-cyber-accent/70 hover:text-neon-cyan font-medium transition-colors"
|
||
>
|
||
Annuler
|
||
</button>
|
||
</div>
|
||
)
|
||
}
|