rwadurian/backend/mpc-system/services/service-party-app/src/pages/Settings.tsx

356 lines
13 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 { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import styles from './Settings.module.css';
interface Settings {
messageRouterUrl: string;
accountServiceUrl: string;
autoBackup: boolean;
backupPath: string;
}
export default function Settings() {
const navigate = useNavigate();
const [settings, setSettings] = useState<Settings>({
messageRouterUrl: 'mpc-grpc.szaiai.com:443', // 生产环境默认地址
accountServiceUrl: 'https://rwaapi.szaiai.com', // Account 服务默认地址
autoBackup: false,
backupPath: '',
});
const [kavaNetwork, setKavaNetwork] = useState<'mainnet' | 'testnet'>('mainnet');
const [isLoading, setIsLoading] = useState(true);
const [isSaving, setIsSaving] = useState(false);
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
useEffect(() => {
loadSettings();
}, []);
const loadSettings = async () => {
try {
const result = await window.electronAPI.storage.getSettings();
const accountUrl = await window.electronAPI.account.getUrl();
const networkResult = await window.electronAPI.kava.getNetwork();
if (result) {
setSettings({
...result,
accountServiceUrl: accountUrl || 'https://rwaapi.szaiai.com',
});
}
setKavaNetwork(networkResult.network);
} catch (err) {
console.error('Failed to load settings:', err);
} finally {
setIsLoading(false);
}
};
const handleSave = async () => {
setIsSaving(true);
setMessage(null);
try {
await window.electronAPI.storage.saveSettings(settings);
await window.electronAPI.account.updateUrl(settings.accountServiceUrl);
setMessage({ type: 'success', text: '设置已保存' });
} catch (err) {
setMessage({ type: 'error', text: '保存设置失败' });
} finally {
setIsSaving(false);
}
};
const handleTestConnection = async () => {
setMessage(null);
try {
const result = await window.electronAPI.grpc.testConnection(settings.messageRouterUrl);
if (result.success) {
setMessage({ type: 'success', text: 'Message Router 连接成功' });
} else {
setMessage({ type: 'error', text: result.error || 'Message Router 连接失败' });
}
} catch (err) {
setMessage({ type: 'error', text: 'Message Router 连接测试失败' });
}
};
const handleTestAccountConnection = async () => {
setMessage(null);
try {
// 先更新 URL
await window.electronAPI.account.updateUrl(settings.accountServiceUrl);
const result = await window.electronAPI.account.testConnection();
if (result.success) {
setMessage({ type: 'success', text: 'Account 服务连接成功' });
} else {
setMessage({ type: 'error', text: result.error || 'Account 服务连接失败' });
}
} catch (err) {
setMessage({ type: 'error', text: 'Account 服务连接测试失败' });
}
};
const handleSelectBackupPath = async () => {
try {
const path = await window.electronAPI.dialog.selectDirectory();
if (path) {
setSettings(prev => ({ ...prev, backupPath: path }));
}
} catch (err) {
console.error('Failed to select directory:', err);
}
};
if (isLoading) {
return (
<div className={styles.container}>
<div className={styles.loading}>
<div className={styles.spinner}></div>
<p>...</p>
</div>
</div>
);
}
return (
<div className={styles.container}>
<div className={styles.header}>
<h1 className={styles.title}></h1>
</div>
<div className={styles.content}>
{/* 连接设置 */}
<section className={styles.section}>
<h2 className={styles.sectionTitle}></h2>
<div className={styles.card}>
<div className={styles.field}>
<label className={styles.label}>Message Router </label>
<div className={styles.inputWithButton}>
<input
type="text"
value={settings.messageRouterUrl}
onChange={(e) => setSettings(prev => ({ ...prev, messageRouterUrl: e.target.value }))}
placeholder="mpc-grpc.szaiai.com:443"
className={styles.input}
/>
<button
className={styles.testButton}
onClick={handleTestConnection}
>
</button>
</div>
<p className={styles.hint}>
Message Router gRPC (生产环境: mpc-grpc.szaiai.com:443)
</p>
</div>
<div className={styles.field}>
<label className={styles.label}>Account </label>
<div className={styles.inputWithButton}>
<input
type="text"
value={settings.accountServiceUrl}
onChange={(e) => setSettings(prev => ({ ...prev, accountServiceUrl: e.target.value }))}
placeholder="https://rwaapi.szaiai.com"
className={styles.input}
/>
<button
className={styles.testButton}
onClick={handleTestAccountConnection}
>
</button>
</div>
<p className={styles.hint}>
Account HTTP (生产环境: https://rwaapi.szaiai.com)
</p>
</div>
</div>
</section>
{/* 区块链网络设置 */}
<section className={styles.section}>
<h2 className={styles.sectionTitle}></h2>
<div className={styles.card}>
<div className={styles.field}>
<label className={styles.label}>Kava </label>
<div className={styles.networkToggle}>
<button
className={`${styles.networkButton} ${kavaNetwork === 'testnet' ? styles.networkButtonActive : ''}`}
onClick={async () => {
const result = await window.electronAPI.kava.switchNetwork('testnet');
if (result.success) {
setKavaNetwork('testnet');
// 同步到 localStorage 供前端工具函数使用
localStorage.setItem('kava_network', 'testnet');
// 触发自定义事件通知 Layout 更新网络状态显示
window.dispatchEvent(new CustomEvent('kava-network-change', { detail: { network: 'testnet' } }));
setMessage({ type: 'success', text: '已切换到 Kava 测试网' });
}
}}
>
</button>
<button
className={`${styles.networkButton} ${kavaNetwork === 'mainnet' ? styles.networkButtonActive : ''}`}
onClick={async () => {
const result = await window.electronAPI.kava.switchNetwork('mainnet');
if (result.success) {
setKavaNetwork('mainnet');
// 同步到 localStorage 供前端工具函数使用
localStorage.setItem('kava_network', 'mainnet');
// 触发自定义事件通知 Layout 更新网络状态显示
window.dispatchEvent(new CustomEvent('kava-network-change', { detail: { network: 'mainnet' } }));
setMessage({ type: 'success', text: '已切换到 Kava 主网' });
}
}}
>
</button>
</div>
<p className={styles.hint}>
{kavaNetwork === 'testnet'
? '当前使用测试网 (kava_2221-16000),适合开发测试'
: '当前使用主网 (kava_2222-10),请谨慎操作'}
</p>
</div>
</div>
</section>
{/* 备份设置 */}
<section className={styles.section}>
<h2 className={styles.sectionTitle}></h2>
<div className={styles.card}>
<div className={styles.field}>
<div className={styles.checkboxField}>
<input
type="checkbox"
id="autoBackup"
checked={settings.autoBackup}
onChange={(e) => setSettings(prev => ({ ...prev, autoBackup: e.target.checked }))}
className={styles.checkbox}
/>
<label htmlFor="autoBackup" className={styles.checkboxLabel}>
</label>
</div>
<p className={styles.hint}>
</p>
</div>
{settings.autoBackup && (
<div className={styles.field}>
<label className={styles.label}></label>
<div className={styles.inputWithButton}>
<input
type="text"
value={settings.backupPath}
onChange={(e) => setSettings(prev => ({ ...prev, backupPath: e.target.value }))}
placeholder="选择备份目录"
className={styles.input}
readOnly
/>
<button
className={styles.browseButton}
onClick={handleSelectBackupPath}
>
</button>
</div>
</div>
)}
</div>
</section>
{/* 数据管理 */}
<section className={styles.section}>
<h2 className={styles.sectionTitle}></h2>
<div className={styles.card}>
<div className={styles.field}>
<label className={styles.label}></label>
<p className={styles.hint}>
</p>
<button className={styles.actionButton}>
</button>
</div>
<div className={styles.divider}></div>
<div className={styles.field}>
<label className={styles.label}></label>
<p className={styles.hint}>
</p>
<button className={styles.actionButton}>
</button>
</div>
<div className={styles.divider}></div>
<div className={styles.field}>
<label className={styles.labelDanger}></label>
<p className={styles.hint}>
</p>
<button className={styles.dangerButton}>
</button>
</div>
</div>
</section>
{/* 关于 */}
<section className={styles.section}>
<h2 className={styles.sectionTitle}></h2>
<div className={styles.card}>
<div className={styles.aboutInfo}>
<div className={styles.aboutItem}>
<span className={styles.aboutLabel}></span>
<span className={styles.aboutValue}>Service Party</span>
</div>
<div className={styles.aboutItem}>
<span className={styles.aboutLabel}></span>
<span className={styles.aboutValue}>1.0.0</span>
</div>
<div className={styles.aboutItem}>
<span className={styles.aboutLabel}></span>
<span className={styles.aboutValue}>RWADurian MPC System</span>
</div>
</div>
</div>
</section>
{message && (
<div className={`${styles.message} ${styles[message.type]}`}>
{message.text}
</div>
)}
<div className={styles.actions}>
<button
className={styles.secondaryButton}
onClick={() => navigate('/')}
>
</button>
<button
className={styles.primaryButton}
onClick={handleSave}
disabled={isSaving}
>
{isSaving ? '保存中...' : '保存设置'}
</button>
</div>
</div>
</div>
);
}