mirror of
https://github.com/kccleoc/seedpgp-web.git
synced 2026-03-06 17:37:51 +08:00
fix(blender): display detailed validation errors
Improves the user experience by providing clear, inline error messages when mnemonic validation fails. - The validation logic now captures the error message from `mnemonicToEntropy`. - The UI displays this message directly below the invalid input field. - This addresses the ambiguity where an input was marked as invalid (red border) without explaining why, as seen in the user-provided screenshot.
This commit is contained in:
@@ -96,18 +96,23 @@ export function SeedBlender({ onDirtyStateChange, setMnemonicForBackup, requestT
|
||||
const validMnemonics = entries.map(e => e.decryptedMnemonic).filter((m): m is string => m !== null && m.length > 0);
|
||||
|
||||
const validityPromises = entries.map(async (entry) => {
|
||||
if (!entry.rawInput.trim()) return null;
|
||||
if (entry.isEncrypted && !entry.decryptedMnemonic) return null; // Cannot validate until decrypted
|
||||
if (!entry.rawInput.trim()) return { isValid: null, error: null };
|
||||
if (entry.isEncrypted && !entry.decryptedMnemonic) return { isValid: null, error: null };
|
||||
|
||||
const textToValidate = entry.decryptedMnemonic || entry.rawInput;
|
||||
try {
|
||||
await mnemonicToEntropy(textToValidate.trim());
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
return { isValid: true, error: null };
|
||||
} catch (e: any) {
|
||||
return { isValid: false, error: e.message || "Invalid mnemonic" };
|
||||
}
|
||||
});
|
||||
const newValidity = await Promise.all(validityPromises);
|
||||
setEntries(currentEntries => currentEntries.map((e, i) => ({ ...e, isValid: newValidity[i] })));
|
||||
const newValidationResults = await Promise.all(validityPromises);
|
||||
setEntries(currentEntries => currentEntries.map((e, i) => ({
|
||||
...e,
|
||||
isValid: newValidationResults[i]?.isValid ?? e.isValid,
|
||||
error: newValidationResults[i]?.error ?? e.error,
|
||||
})));
|
||||
|
||||
if (validMnemonics.length > 0) {
|
||||
try {
|
||||
@@ -223,16 +228,19 @@ export function SeedBlender({ onDirtyStateChange, setMnemonicForBackup, requestT
|
||||
{entry.error && <p className="text-xs text-red-400">{entry.error}</p>}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col sm:flex-row items-start gap-2">
|
||||
<div className="relative w-full">
|
||||
<textarea value={entry.rawInput} onChange={(e) => updateEntry(index, { rawInput: e.target.value, decryptedMnemonic: e.target.value, isValid: null })} placeholder={`Mnemonic #${index + 1} (12 or 24 words)`} className={`w-full h-28 sm:h-24 p-3 pr-10 bg-slate-50 border-2 rounded-lg text-sm font-mono text-slate-900 placeholder:text-slate-400 ${getBorderColor(entry.isValid)}`} />
|
||||
{entry.isValid === true && <CheckCircle2 className="absolute top-3 right-3 text-green-500" />}
|
||||
{entry.isValid === false && <AlertTriangle className="absolute top-3 right-3 text-red-500" />}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<button onClick={() => handleScan(index)} className="p-3 h-full bg-purple-600/20 text-purple-300 hover:bg-purple-600/50 hover:text-white rounded-md"><QrCode size={20} /></button>
|
||||
<button onClick={() => handleRemoveEntry(entry.id)} className="p-3 h-full bg-red-600/20 text-red-400 hover:bg-red-600/50 hover:text-white rounded-md"><X size={20} /></button>
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col sm:flex-row items-start gap-2">
|
||||
<div className="relative w-full">
|
||||
<textarea value={entry.rawInput} onChange={(e) => updateEntry(index, { rawInput: e.target.value, decryptedMnemonic: e.target.value, isValid: null, error: null })} placeholder={`Mnemonic #${index + 1} (12 or 24 words)`} className={`w-full h-28 sm:h-24 p-3 pr-10 bg-slate-50 border-2 rounded-lg text-sm font-mono text-slate-900 placeholder:text-slate-400 ${getBorderColor(entry.isValid)}`} />
|
||||
{entry.isValid === true && <CheckCircle2 className="absolute top-3 right-3 text-green-500" />}
|
||||
{entry.isValid === false && <AlertTriangle className="absolute top-3 right-3 text-red-500" />}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<button onClick={() => handleScan(index)} className="p-3 h-full bg-purple-600/20 text-purple-300 hover:bg-purple-600/50 hover:text-white rounded-md"><QrCode size={20} /></button>
|
||||
<button onClick={() => handleRemoveEntry(entry.id)} className="p-3 h-full bg-red-600/20 text-red-400 hover:bg-red-600/50 hover:text-white rounded-md"><X size={20} /></button>
|
||||
</div>
|
||||
</div>
|
||||
{entry.error && <p className="text-xs text-red-400 px-1">{entry.error}</p>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user