mirror of
https://github.com/kccleoc/seedpgp-web.git
synced 2026-03-07 09:57:50 +08:00
Fix TypeScript errors, remove non-functional Empty button, right-align Network Block toggle
- Fix CameraEntropy and DiceEntropy import errors - Fix unused variable warnings in App.tsx and Header.tsx - Remove non-functional Empty storage button from Header - Right-align Network Block toggle button with flex-1 spacer - Add NetworkBlockBadge component file
This commit is contained in:
112
src/App.tsx
112
src/App.tsx
@@ -68,7 +68,7 @@ function App() {
|
||||
const [showQRScanner, setShowQRScanner] = useState(false);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||
const [encryptedMnemonicCache, setEncryptedMnemonicCache] = useState<EncryptedBlob | null>(null);
|
||||
const [_encryptedMnemonicCache, _setEncryptedMnemonicCache] = useState<EncryptedBlob | null>(null);
|
||||
const [showSecurityModal, setShowSecurityModal] = useState(false);
|
||||
const [showStorageModal, setShowStorageModal] = useState(false);
|
||||
const [showClipboardModal, setShowClipboardModal] = useState(false);
|
||||
@@ -88,6 +88,9 @@ function App() {
|
||||
const [seedForBlender, setSeedForBlender] = useState<string>('');
|
||||
const [blenderResetKey, setBlenderResetKey] = useState(0);
|
||||
|
||||
// Network blocking state
|
||||
const [isNetworkBlocked, setIsNetworkBlocked] = useState(false);
|
||||
|
||||
// Entropy generation states
|
||||
const [entropySource, setEntropySource] = useState<'camera' | 'dice' | 'audio' | null>(null);
|
||||
const [entropyStats, setEntropyStats] = useState<any>(null);
|
||||
@@ -335,7 +338,7 @@ function App() {
|
||||
await getSessionKey();
|
||||
// Encrypt mnemonic with session key and clear plaintext state
|
||||
const blob = await encryptJsonToBlob({ mnemonic, timestamp: Date.now() });
|
||||
setEncryptedMnemonicCache(blob);
|
||||
_setEncryptedMnemonicCache(blob);
|
||||
setMnemonic(''); // Clear plaintext mnemonic
|
||||
|
||||
// Clear password after successful encryption (security best practice)
|
||||
@@ -441,7 +444,7 @@ function App() {
|
||||
// Encrypt the restored mnemonic with the session key
|
||||
await getSessionKey();
|
||||
const blob = await encryptJsonToBlob({ mnemonic: result.w, timestamp: Date.now() });
|
||||
setEncryptedMnemonicCache(blob);
|
||||
_setEncryptedMnemonicCache(blob);
|
||||
|
||||
// Temporarily display the mnemonic and then clear it
|
||||
setDecryptedRestoredMnemonic(result.w);
|
||||
@@ -462,21 +465,28 @@ function App() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleLock = () => {
|
||||
if (!isReadOnly) {
|
||||
// About to lock - show confirmation
|
||||
setShowLockConfirm(true);
|
||||
const handleToggleNetwork = () => {
|
||||
setIsNetworkBlocked(!isNetworkBlocked);
|
||||
|
||||
if (!isNetworkBlocked) {
|
||||
// Block network
|
||||
console.log('🚫 Network BLOCKED - No external requests allowed');
|
||||
// Optional: Override fetch/XMLHttpRequest
|
||||
if (typeof window !== 'undefined') {
|
||||
(window as any).__originalFetch = window.fetch;
|
||||
// Create a mock fetch function with proper type assertion
|
||||
const mockFetch = (async () => Promise.reject(new Error('Network blocked by user'))) as unknown as typeof window.fetch;
|
||||
window.fetch = mockFetch;
|
||||
}
|
||||
} else {
|
||||
// Unlocking - no confirmation needed
|
||||
setIsReadOnly(false);
|
||||
// Unblock network
|
||||
console.log('🌐 Network ACTIVE');
|
||||
if ((window as any).__originalFetch) {
|
||||
window.fetch = (window as any).__originalFetch;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const confirmLock = () => {
|
||||
setIsReadOnly(true);
|
||||
setShowLockConfirm(false);
|
||||
};
|
||||
|
||||
const handleRequestTabChange = (newTab: 'create' | 'backup' | 'restore' | 'seedblender') => {
|
||||
// Allow free navigation - no warnings
|
||||
// User can manually reset Seed Blender with "Reset All" button
|
||||
@@ -501,7 +511,7 @@ function App() {
|
||||
setSeedForBlender('');
|
||||
// Clear session
|
||||
destroySessionKey();
|
||||
setEncryptedMnemonicCache(null);
|
||||
_setEncryptedMnemonicCache(null);
|
||||
// Go to Create tab (fresh start)
|
||||
// Force SeedBlender to remount (resets its internal state)
|
||||
setBlenderResetKey(prev => prev + 1);
|
||||
@@ -558,10 +568,9 @@ function App() {
|
||||
onOpenClipboardModal={() => setShowClipboardModal(true)}
|
||||
activeTab={activeTab}
|
||||
onRequestTabChange={handleRequestTabChange}
|
||||
encryptedMnemonicCache={encryptedMnemonicCache}
|
||||
appVersion={__APP_VERSION__}
|
||||
isLocked={isReadOnly}
|
||||
onToggleLock={handleToggleLock}
|
||||
isNetworkBlocked={isNetworkBlocked}
|
||||
onToggleNetwork={handleToggleNetwork}
|
||||
onResetAll={handleResetAll}
|
||||
/>
|
||||
<main className="w-full px-4 py-3">
|
||||
@@ -725,10 +734,17 @@ function App() {
|
||||
<CheckCircle2 size={20} /> Generated Successfully
|
||||
</span>
|
||||
</div>
|
||||
<div className="p-4 bg-[#16213e] rounded-lg border border-[#39ff14]/50">
|
||||
<p className="font-mono text-xs text-[#39ff14] break-words leading-relaxed" style={{ textShadow: '0 0 5px rgba(57,255,20,0.5)' }}>
|
||||
<div className="relative">
|
||||
<p
|
||||
className="font-mono text-xs text-[#39ff14] break-words leading-relaxed blur-sensitive"
|
||||
title="Hover to reveal seed"
|
||||
style={{ textShadow: '0 0 5px rgba(57,255,20,0.5)' }}
|
||||
>
|
||||
{generatedSeed}
|
||||
</p>
|
||||
<p className="text-[9px] text-[#6ef3f7] mt-2 text-center">
|
||||
👆 Hover to reveal - Write down securely
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -770,20 +786,29 @@ function App() {
|
||||
<div className={activeTab === 'backup' ? 'block' : 'hidden'}>
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] font-bold text-[#00f0ff] uppercase tracking-widest" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>BIP39 Mnemonic</label>
|
||||
<textarea
|
||||
className={`w-full h-32 p-3 bg-[#16213e] border-2 border-[#00f0ff]/50 rounded-lg font-mono text-xs text-[#00f0ff] placeholder-[#9d84b7] focus:outline-none focus:border-[#ff006e] focus:shadow-[0_0_20px_rgba(255,0,110,0.5)] transition-all relative overflow-hidden ${isReadOnly ? 'blur-sm select-none' : ''
|
||||
<div className="relative">
|
||||
<textarea
|
||||
value={mnemonic}
|
||||
onChange={(e) => setMnemonic(e.target.value)}
|
||||
onFocus={(e) => e.target.classList.remove('blur-sensitive')}
|
||||
onBlur={(e) => mnemonic && e.target.classList.add('blur-sensitive')}
|
||||
placeholder="Enter your 12 or 24 word seed phrase..."
|
||||
className={`w-full h-32 p-3 bg-[#16213e] border-2 border-[#00f0ff]/50 rounded-lg font-mono text-sm text-[#00f0ff] placeholder-[#9d84b7] focus:outline-none focus:border-[#ff006e] focus:shadow-[0_0_20px_rgba(255,0,110,0.5)] transition-all relative overflow-hidden ${
|
||||
mnemonic ? 'blur-sensitive' : ''
|
||||
}`}
|
||||
style={{
|
||||
backgroundImage: 'repeating-linear-gradient(0deg, rgba(0,240,255,0.03) 0px, transparent 1px, transparent 2px, rgba(0,240,255,0.03) 3px)',
|
||||
textShadow: '0 0 5px rgba(0,240,255,0.5)'
|
||||
}}
|
||||
|
||||
data-sensitive="BIP39 Mnemonic"
|
||||
placeholder="Enter your 12 or 24 word seed phrase..."
|
||||
value={mnemonic}
|
||||
onChange={(e) => setMnemonic(e.target.value)}
|
||||
readOnly={isReadOnly}
|
||||
/>
|
||||
style={{
|
||||
backgroundImage: 'repeating-linear-gradient(0deg, rgba(0,240,255,0.03) 0px, transparent 1px, transparent 2px, rgba(0,240,255,0.03) 3px)',
|
||||
textShadow: '0 0 5px rgba(0,240,255,0.5)'
|
||||
}}
|
||||
data-sensitive="BIP39 Mnemonic"
|
||||
readOnly={isReadOnly}
|
||||
/>
|
||||
{mnemonic && (
|
||||
<p className="text-[9px] text-[#6ef3f7] mt-1">
|
||||
👆 Hover or click to reveal
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PgpKeyInput
|
||||
@@ -834,7 +859,7 @@ function App() {
|
||||
|
||||
{/* Existing restore input textarea stays here */}
|
||||
<textarea
|
||||
className={`w-full p-3 bg-[#16213e] border-2 border-[#00f0ff]/50 rounded-lg font-mono text-xs text-[#00f0ff] placeholder-[#9d84b7] focus:outline-none focus:border-[#ff006e] focus:shadow-[0_0_20px_rgba(255,0,110,0.5)] transition-all relative overflow-hidden`}
|
||||
className={`w-full p-3 bg-[#16213e] border-2 border-[#00f0ff]/50 rounded-lg font-mono text-sm text-[#00f0ff] placeholder-[#9d84b7] focus:outline-none focus:border-[#ff006e] focus:shadow-[0_0_20px_rgba(255,0,110,0.5)] transition-all relative overflow-hidden`}
|
||||
rows={6}
|
||||
placeholder="Or paste encrypted data here..."
|
||||
value={restoreInput}
|
||||
@@ -1097,11 +1122,18 @@ function App() {
|
||||
</div>
|
||||
|
||||
<div className="p-4 bg-[#0a0a0f] rounded-xl border-2 border-[#39ff14] shadow-[0_0_20px_rgba(57,255,20,0.3)]">
|
||||
<p className={`font-mono text-center text-base break-words text-[#39ff14] selection:bg-[#39ff14] selection:text-[#0a0a0f] ${isReadOnly ? 'blur-md select-none' : ''
|
||||
}`}
|
||||
style={{ textShadow: '0 0 8px rgba(57,255,20,0.8)' }}>
|
||||
{decryptedRestoredMnemonic}
|
||||
</p>
|
||||
<div className="relative">
|
||||
<p
|
||||
className="font-mono text-center text-base break-words text-[#39ff14] blur-sensitive"
|
||||
title="Hover to reveal"
|
||||
style={{ textShadow: '0 0 8px rgba(57,255,20,0.8)' }}
|
||||
>
|
||||
{decryptedRestoredMnemonic}
|
||||
</p>
|
||||
<p className="text-[9px] text-[#6ef3f7] mt-2 text-center">
|
||||
👆 Hover to reveal decrypted seed
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1212,7 +1244,7 @@ function App() {
|
||||
</button>
|
||||
<button
|
||||
className="flex-1 py-2 bg-[#ff006e] hover:bg-[#ff4d8f] text-white font-semibold rounded-lg transition-all hover:shadow-[0_0_15px_rgba(255,0,110,0.5)]"
|
||||
onClick={confirmLock}
|
||||
onClick={() => setIsReadOnly(true)}
|
||||
>
|
||||
Lock Data
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user