A complete blockchain implementation featuring: - synord: Full node with GHOSTDAG consensus - explorer-web: Modern React blockchain explorer with 3D DAG visualization - CLI wallet and tools - Smart contract SDK and example contracts (DEX, NFT, token) - WASM crypto library for browser/mobile
192 lines
6.3 KiB
TypeScript
192 lines
6.3 KiB
TypeScript
import { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useWalletStore, type Network } from '../store/wallet';
|
|
|
|
export default function SettingsPage() {
|
|
const {
|
|
network,
|
|
rpcEndpoint,
|
|
setNetwork,
|
|
setRpcEndpoint,
|
|
deleteWallet,
|
|
} = useWalletStore();
|
|
const navigate = useNavigate();
|
|
|
|
const [customEndpoint, setCustomEndpoint] = useState(rpcEndpoint);
|
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
const [deleteConfirmText, setDeleteConfirmText] = useState('');
|
|
|
|
const handleNetworkChange = (newNetwork: Network) => {
|
|
setNetwork(newNetwork);
|
|
// Update default endpoint based on network
|
|
const defaultEndpoints: Record<Network, string> = {
|
|
mainnet: 'https://rpc.synor.cc',
|
|
testnet: 'https://testnet-rpc.synor.cc',
|
|
devnet: 'http://localhost:16110',
|
|
};
|
|
setRpcEndpoint(defaultEndpoints[newNetwork]);
|
|
setCustomEndpoint(defaultEndpoints[newNetwork]);
|
|
};
|
|
|
|
const handleSaveEndpoint = () => {
|
|
setRpcEndpoint(customEndpoint);
|
|
};
|
|
|
|
const handleDeleteWallet = () => {
|
|
if (deleteConfirmText === 'DELETE') {
|
|
deleteWallet();
|
|
navigate('/');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-2xl">
|
|
<h1 className="text-2xl font-bold mb-6">Settings</h1>
|
|
|
|
{/* Network Selection */}
|
|
<div className="card mb-6">
|
|
<h2 className="text-lg font-semibold mb-4">Network</h2>
|
|
<div className="grid grid-cols-3 gap-3">
|
|
{(['mainnet', 'testnet', 'devnet'] as Network[]).map((net) => (
|
|
<button
|
|
key={net}
|
|
onClick={() => handleNetworkChange(net)}
|
|
className={`p-4 rounded-lg border-2 transition-colors ${
|
|
network === net
|
|
? 'border-synor-500 bg-synor-900/30'
|
|
: 'border-slate-700 hover:border-slate-600'
|
|
}`}
|
|
>
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<span
|
|
className={`w-2 h-2 rounded-full ${
|
|
net === 'mainnet'
|
|
? 'bg-green-500'
|
|
: net === 'testnet'
|
|
? 'bg-yellow-500'
|
|
: 'bg-blue-500'
|
|
}`}
|
|
></span>
|
|
<span className="font-medium capitalize">{net}</span>
|
|
</div>
|
|
<div className="text-xs text-slate-500">
|
|
{net === 'mainnet' && 'Production network'}
|
|
{net === 'testnet' && 'Test with fake SYNOR'}
|
|
{net === 'devnet' && 'Local development'}
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
{network === 'mainnet' && (
|
|
<div className="mt-4 p-3 bg-yellow-900/30 border border-yellow-700 rounded-lg text-yellow-300 text-sm">
|
|
Mainnet is not yet launched. Switch to testnet or devnet.
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* RPC Endpoint */}
|
|
<div className="card mb-6">
|
|
<h2 className="text-lg font-semibold mb-4">RPC Endpoint</h2>
|
|
<div className="flex gap-3">
|
|
<input
|
|
type="text"
|
|
value={customEndpoint}
|
|
onChange={(e) => setCustomEndpoint(e.target.value)}
|
|
className="input flex-1"
|
|
placeholder="http://localhost:16110"
|
|
/>
|
|
<button
|
|
onClick={handleSaveEndpoint}
|
|
disabled={customEndpoint === rpcEndpoint}
|
|
className="btn btn-secondary"
|
|
>
|
|
Save
|
|
</button>
|
|
</div>
|
|
<p className="text-xs text-slate-500 mt-2">
|
|
Connect to a custom Synor node
|
|
</p>
|
|
</div>
|
|
|
|
{/* Security */}
|
|
<div className="card mb-6">
|
|
<h2 className="text-lg font-semibold mb-4">Security</h2>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between p-3 bg-slate-900 rounded-lg">
|
|
<div>
|
|
<div className="font-medium">Auto-lock</div>
|
|
<div className="text-sm text-slate-500">
|
|
Lock wallet after inactivity
|
|
</div>
|
|
</div>
|
|
<select className="bg-slate-800 border border-slate-700 rounded px-3 py-2">
|
|
<option value="5">5 minutes</option>
|
|
<option value="15">15 minutes</option>
|
|
<option value="30">30 minutes</option>
|
|
<option value="60">1 hour</option>
|
|
<option value="0">Never</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Danger Zone */}
|
|
<div className="card border-red-900">
|
|
<h2 className="text-lg font-semibold text-red-400 mb-4">Danger Zone</h2>
|
|
|
|
{!showDeleteConfirm ? (
|
|
<button
|
|
onClick={() => setShowDeleteConfirm(true)}
|
|
className="btn btn-danger"
|
|
>
|
|
Delete Wallet
|
|
</button>
|
|
) : (
|
|
<div className="space-y-4">
|
|
<div className="p-3 bg-red-900/30 border border-red-700 rounded-lg text-red-300 text-sm">
|
|
<strong>Warning:</strong> This will permanently delete your wallet
|
|
from this device. Make sure you have your recovery phrase backed up.
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm text-slate-400 mb-2">
|
|
Type "DELETE" to confirm
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={deleteConfirmText}
|
|
onChange={(e) => setDeleteConfirmText(e.target.value)}
|
|
className="input"
|
|
placeholder="DELETE"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex gap-3">
|
|
<button
|
|
onClick={() => {
|
|
setShowDeleteConfirm(false);
|
|
setDeleteConfirmText('');
|
|
}}
|
|
className="btn btn-secondary flex-1"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
onClick={handleDeleteWallet}
|
|
disabled={deleteConfirmText !== 'DELETE'}
|
|
className="btn btn-danger flex-1"
|
|
>
|
|
Confirm Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Version */}
|
|
<div className="mt-6 text-center text-sm text-slate-500">
|
|
Synor Web Wallet v0.1.0
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|