dgb-wallet/packages/web-ui/src/SettingsModal.tsx
Digibyte.X d50286a9a5 DigiByte.X Wallet — recovery-first web wallet monorepo
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.
2026-09-20 20:16:52 +02:00

137 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
}