"use client"; import React, { useState } from 'react'; import { useKeystore } from '../../../contexts/keystore'; import { readKeystoreFromFile, saveKeystoreCredentialToFile } from '../../../utils/keystore'; import { DecryptedCredentials } from '@waku/rln'; import { useAppState } from '../../../contexts/AppStateContext'; import { TerminalWindow } from '../../ui/terminal-window'; import { Button } from '../../ui/button'; import { Copy, Eye, Download, Trash2, ArrowDownToLine } from 'lucide-react'; import { KeystoreExporter } from '../../KeystoreExporter'; import { keystoreManagement, type ContentSegment } from '../../../content/index'; export function KeystoreManagement() { const { hasStoredCredentials, storedCredentialsHashes, error, exportCredential, importKeystore, removeCredential, getDecryptedCredential } = useKeystore(); const { setGlobalError } = useAppState(); const [exportPassword, setExportPassword] = useState(''); const [selectedCredential, setSelectedCredential] = useState(null); const [viewPassword, setViewPassword] = useState(''); const [viewingCredential, setViewingCredential] = useState(null); const [decryptedInfo, setDecryptedInfo] = useState(null); const [isDecrypting, setIsDecrypting] = useState(false); const [copiedHash, setCopiedHash] = useState(null); React.useEffect(() => { if (error) { setGlobalError(error); } }, [error, setGlobalError]); const handleExportKeystoreCredential = async (hash: string) => { try { if (!exportPassword) { setGlobalError('Please enter your keystore password to export'); return; } const keystore = await exportCredential(hash, exportPassword); saveKeystoreCredentialToFile(keystore); setExportPassword(''); setSelectedCredential(null); } catch (err) { setGlobalError(err instanceof Error ? err.message : 'Failed to export credential'); } }; const handleImportKeystore = async () => { try { const keystore = await readKeystoreFromFile(); const success = importKeystore(keystore); if (!success) { setGlobalError('Failed to import keystore'); } } catch (err) { setGlobalError(err instanceof Error ? err.message : 'Failed to import keystore'); } }; const handleRemoveCredential = (hash: string) => { try { removeCredential(hash); } catch (err) { setGlobalError(err instanceof Error ? err.message : 'Failed to remove credential'); } }; const handleViewCredential = async (hash: string) => { if (!viewPassword) { setGlobalError('Please enter your keystore password to view credential'); return; } setIsDecrypting(true); try { const credential = await getDecryptedCredential(hash, viewPassword); setIsDecrypting(false); if (credential) { setDecryptedInfo(credential); } else { setGlobalError('Could not decrypt credential. Please check your password and try again.'); } } catch (err) { setIsDecrypting(false); setGlobalError(err instanceof Error ? err.message : 'Failed to decrypt credential'); } }; // Reset view state when changing credentials React.useEffect(() => { if (viewingCredential !== selectedCredential) { setDecryptedInfo(null); } }, [viewingCredential, selectedCredential]); // Add a function to copy text to clipboard with visual feedback const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text) .then(() => { setCopiedHash(text); setTimeout(() => setCopiedHash(null), 2000); }) .catch(err => { console.error('Failed to copy: ', err); }); }; return (

{keystoreManagement.title}

{/* Warning - RLN not initialized */} {!hasStoredCredentials && (

⚠️ {keystoreManagement.noCredentialsWarning}

)} {/* About Section */}
{">"}

{keystoreManagement.infoHeader}

{keystoreManagement.about.map((paragraph: ContentSegment[], i: number) => (

{paragraph.map((segment: ContentSegment, j: number) => ( segment.type === 'link' ? ( {segment.content} ) : ( {segment.content} ) ))}

))}
{/* Resources Section */}

{keystoreManagement.resources.title}

{keystoreManagement.resources.links.map((link: { name: string; url: string }, i: number) => ( {link.name} ))}
{/* Stored Credentials */}

{keystoreManagement.storedCredentialsTitle}

{hasStoredCredentials ? (
{storedCredentialsHashes.map((hash) => (
{hash.slice(0, 10)}...{hash.slice(-6)} {copiedHash === hash && ( Copied! )}
{/* View Credential Section */} {viewingCredential === hash && (
setViewPassword(e.target.value)} placeholder="Enter credential password" className="flex-1 px-3 py-2 border border-terminal-border rounded-md bg-terminal-background text-foreground font-mono focus:ring-1 focus:ring-accent focus:border-accent text-sm" disabled={isDecrypting} />
{/* Decrypted Information Display */} {decryptedInfo && (
{">"}

Credential Details

ID Commitment:
{decryptedInfo.identity.IDCommitment}
ID Nullifier:
{decryptedInfo.identity.IDNullifier}
Membership Details:
Chain ID:
{decryptedInfo.membership.chainId}
Rate Limit:
{decryptedInfo.membership.rateLimit}
)}
)} {/* Export Credential Section */} {selectedCredential === hash && (
setExportPassword(e.target.value)} placeholder="Enter keystore password" className="w-full px-3 py-2 border border-terminal-border rounded-md bg-terminal-background text-foreground font-mono focus:ring-1 focus:ring-primary focus:border-primary text-sm" />
)}
))}
) : (
No credentials stored
)}
); }