Packages: core (BIP39/32/44/49/84/86 derivation, WIF), keystore (PBKDF2 + AES-GCM), psbt, p2p (BIP157/158 SPV daemon, Dandelion++ broadcast with relay-echo verification), blockbook, rpc, electrum, net, bns, web-ui (React/Vite), mobile (Expo). ops/ holds the nginx vhost, systemd unit and push.sh deploy script for digibyte.x/web.
137 lines
4.8 KiB
TypeScript
137 lines
4.8 KiB
TypeScript
import { useEffect } from 'react';
|
||
import { useActiveSeed } from './ActiveSeed.js';
|
||
import { useBackendConfig } from './backend-config.js';
|
||
import { BackendMatrix } from './BackendMatrix.js';
|
||
|
||
// A real Settings modal — replaces the previous "gear toggles the
|
||
// classic tab bar" behaviour. Structured into sections the way
|
||
// Trust/MetaMask do: Wallet, Backends, Display, Advanced.
|
||
|
||
export function SettingsModal({
|
||
onClose,
|
||
onOpenAdvanced,
|
||
}: {
|
||
onClose: () => void;
|
||
onOpenAdvanced: () => void;
|
||
}): JSX.Element {
|
||
const { active, setActive } = useActiveSeed();
|
||
const { config, toggle: toggleBackend, reset: resetBackend } = useBackendConfig();
|
||
|
||
useEffect(() => {
|
||
const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
|
||
window.addEventListener('keydown', onKey);
|
||
return () => window.removeEventListener('keydown', onKey);
|
||
}, [onClose]);
|
||
|
||
return (
|
||
<div className="modal-backdrop" onClick={onClose}>
|
||
<div className="modal modal-lg" role="dialog" aria-modal="true" aria-label="Settings" onClick={(e) => e.stopPropagation()}>
|
||
<div className="modal-header">
|
||
<h2>Settings</h2>
|
||
<button className="modal-close" onClick={onClose} aria-label="Close">×</button>
|
||
</div>
|
||
|
||
<div className="settings-body">
|
||
<SettingsSection title="Wallet">
|
||
{active ? (
|
||
<SettingsRow
|
||
label="Lock wallet"
|
||
hint="Wipes the in-memory seed. Encrypted keystore stays saved."
|
||
actionLabel="Lock"
|
||
actionKind="danger"
|
||
onAction={() => { setActive(null); onClose(); }}
|
||
/>
|
||
) : (
|
||
<SettingsRow
|
||
label="Import or unlock"
|
||
hint="No wallet is currently loaded."
|
||
actionLabel="Import"
|
||
onAction={() => { onOpenAdvanced(); onClose(); }}
|
||
/>
|
||
)}
|
||
<SettingsRow
|
||
label="Manage saved wallets"
|
||
hint="Add, unlock, or delete encrypted keystore entries."
|
||
actionLabel="Open"
|
||
onAction={() => { onOpenAdvanced(); onClose(); }}
|
||
/>
|
||
</SettingsSection>
|
||
|
||
<SettingsSection title="Backends" hint="Which servers this wallet talks to. Privacy-first defaults use the P2P daemon only; enabling Blockbook adds full history at the cost of revealing addresses to the indexer operator.">
|
||
<BackendMatrix
|
||
config={config}
|
||
toggle={toggleBackend}
|
||
caps={['scan', 'broadcast'] as const}
|
||
/>
|
||
<button className="text-btn danger" onClick={resetBackend}>
|
||
Reset to privacy-first defaults
|
||
</button>
|
||
</SettingsSection>
|
||
|
||
<SettingsSection title="Display">
|
||
<SettingsRow
|
||
label="Fiat price feed"
|
||
hint="Off. Enabling would fetch DGB→USD from a public price API, revealing that this wallet is checking prices. Coming with an opt-in toggle."
|
||
actionLabel="Coming soon"
|
||
disabled
|
||
/>
|
||
</SettingsSection>
|
||
|
||
<SettingsSection title="Advanced">
|
||
<SettingsRow
|
||
label="Classic tabs"
|
||
hint="The old Wallets / Balance (deep) / Send (classic) interface — with the year-picker for deep historical scans."
|
||
actionLabel="Open"
|
||
onAction={() => { onOpenAdvanced(); onClose(); }}
|
||
/>
|
||
<SettingsRow
|
||
label="About"
|
||
hint={`DigiByte.X Wallet · recovery-first · privacy by default. Loaded from ${location.origin}. Source lives at packages/web-ui in the dgb-wallet monorepo.`}
|
||
/>
|
||
</SettingsSection>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function SettingsSection({
|
||
title, hint, children,
|
||
}: { title: string; hint?: string; children: React.ReactNode }): JSX.Element {
|
||
return (
|
||
<section className="settings-section">
|
||
<h3>{title}</h3>
|
||
{hint && <p className="settings-hint">{hint}</p>}
|
||
<div className="settings-rows">{children}</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
function SettingsRow({
|
||
label, hint, actionLabel, actionKind, onAction, disabled,
|
||
}: {
|
||
label: string;
|
||
hint?: string;
|
||
actionLabel?: string;
|
||
actionKind?: 'danger';
|
||
onAction?: () => void;
|
||
disabled?: boolean;
|
||
}): JSX.Element {
|
||
return (
|
||
<div className="settings-row">
|
||
<div className="settings-row-body">
|
||
<div className="settings-row-label">{label}</div>
|
||
{hint && <div className="settings-row-hint">{hint}</div>}
|
||
</div>
|
||
{actionLabel && (
|
||
<button
|
||
className={`settings-row-btn ${actionKind === 'danger' ? 'danger' : ''}`}
|
||
onClick={onAction}
|
||
disabled={disabled || !onAction}
|
||
>
|
||
{actionLabel}
|
||
</button>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|