rwadurian/backend/mpc-system/services/service-party-app/src/components/Layout.tsx

214 lines
8.1 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 { ReactNode, useEffect, useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useAppStore, getStatusColor } from '../stores/appStore';
import styles from './Layout.module.css';
interface LayoutProps {
children: ReactNode;
}
const navItems = [
{ path: '/', label: '我的钱包', icon: '🔐' },
{ path: '/create', label: '创建钱包', icon: '' },
{ path: '/join', label: '加入创建', icon: '🤝' },
{ path: '/cosign/join', label: '参与签名', icon: '🔥' },
{ path: '/settings', label: '设置', icon: '⚙️' },
];
export default function Layout({ children }: LayoutProps) {
const location = useLocation();
const [isRefreshing, setIsRefreshing] = useState(false);
const [kavaNetwork, setKavaNetwork] = useState<'mainnet' | 'testnet'>('mainnet');
const { environment, operation, checkAllServices, appReady } = useAppStore();
// 启动时检测环境和获取网络
useEffect(() => {
checkAllServices();
// 获取当前 Kava 网络
const loadNetwork = () => {
// 优先从 localStorage 读取(与 transaction.ts 保持一致)
const storedNetwork = localStorage.getItem('kava_network') as 'mainnet' | 'testnet' | null;
if (storedNetwork) {
setKavaNetwork(storedNetwork);
} else {
// 后备:从 Electron API 读取
window.electronAPI?.kava.getNetwork().then(result => {
setKavaNetwork(result.network);
});
}
};
loadNetwork();
// 监听 localStorage 变化(当其他标签页切换网络时触发)
const handleStorageChange = (e: StorageEvent) => {
if (e.key === 'kava_network' && e.newValue) {
setKavaNetwork(e.newValue as 'mainnet' | 'testnet');
}
};
// 监听同一窗口的自定义事件Settings 页面切换网络时触发)
const handleCustomNetworkChange = (e: Event) => {
const customEvent = e as CustomEvent<{ network: 'mainnet' | 'testnet' }>;
setKavaNetwork(customEvent.detail.network);
};
window.addEventListener('storage', handleStorageChange);
window.addEventListener('kava-network-change', handleCustomNetworkChange);
return () => {
window.removeEventListener('storage', handleStorageChange);
window.removeEventListener('kava-network-change', handleCustomNetworkChange);
};
}, [checkAllServices]);
const handleRefresh = async () => {
setIsRefreshing(true);
await checkAllServices();
setIsRefreshing(false);
};
const getOperationTitle = () => {
if (operation.type === 'keygen') return '密钥生成中';
if (operation.type === 'sign') return '签名进行中';
return '操作进行中';
};
return (
<div className={styles.layout}>
<nav className={styles.sidebar}>
<div className={styles.logo}>
<span className={styles.logoIcon}>🍈</span>
<span className={styles.logoText}>绿</span>
</div>
<ul className={styles.navList}>
{navItems.map((item) => (
<li key={item.path}>
<Link
to={item.path}
className={`${styles.navItem} ${
location.pathname === item.path ? styles.navItemActive : ''
}`}
>
<span className={styles.navIcon}>{item.icon}</span>
<span className={styles.navLabel}>{item.label}</span>
</Link>
</li>
))}
</ul>
<div className={styles.footer}>
{/* 状态面板 */}
<div className={styles.statusPanel}>
{/* 消息路由 */}
<div className={styles.statusRow}>
<span className={styles.statusLabel}></span>
<span className={styles.statusValue}>
<span
className={styles.statusDot}
style={{ backgroundColor: getStatusColor(environment.messageRouter.status) }}
/>
<span className={styles.statusText} title={environment.messageRouter.message}>
{environment.messageRouter.status === 'connected'
? '已连接'
: environment.messageRouter.status === 'checking'
? '检测中...'
: environment.messageRouter.status === 'error'
? '失败'
: '未连接'}
</span>
</span>
</div>
{/* Kava API */}
<div className={styles.statusRow}>
<span className={styles.statusLabel}>
Kava API
<span className={kavaNetwork === 'testnet' ? styles.networkBadgeTestnet : styles.networkBadgeMainnet}>
{kavaNetwork === 'testnet' ? '测试网' : '主网'}
</span>
</span>
<span className={styles.statusValue}>
<span
className={styles.statusDot}
style={{ backgroundColor: getStatusColor(environment.kavaApi.status) }}
/>
<span className={styles.statusText} title={environment.kavaApi.message}>
{environment.kavaApi.status === 'connected'
? '已连接'
: environment.kavaApi.status === 'checking'
? '检测中...'
: environment.kavaApi.status === 'error'
? '失败'
: '未连接'}
</span>
</span>
</div>
{/* 本地存储 */}
<div className={styles.statusRow}>
<span className={styles.statusLabel}></span>
<span className={styles.statusValue}>
<span
className={styles.statusDot}
style={{ backgroundColor: getStatusColor(environment.database.status) }}
/>
<span className={styles.statusText} title={environment.database.message}>
{environment.database.status === 'connected'
? environment.database.message
: environment.database.status === 'checking'
? '检测中...'
: environment.database.status === 'error'
? '错误'
: '未知'}
</span>
</span>
</div>
{/* 刷新按钮 */}
<div className={styles.statusRow}>
<span className={styles.statusLabel}>
{appReady === 'ready' ? '✅ 就绪' : appReady === 'error' ? '⚠️ 部分异常' : '🔄 初始化中'}
</span>
<button
className={`${styles.refreshButton} ${isRefreshing ? styles.spinning : ''}`}
onClick={handleRefresh}
disabled={isRefreshing}
title="重新检测"
>
🔄
</button>
</div>
</div>
{/* 操作进度面板 */}
{(operation.status === 'connecting' || operation.status === 'in_progress') && (
<div className={styles.operationPanel}>
<div className={styles.operationHeader}>
<span className={styles.operationSpinner} />
<span>{getOperationTitle()}</span>
</div>
{operation.currentStep !== undefined && operation.totalSteps !== undefined && (
<>
<div className={styles.operationProgress}>
<div
className={styles.operationProgressBar}
style={{
width: `${(operation.currentStep / operation.totalSteps) * 100}%`,
}}
/>
</div>
<div className={styles.operationStep}>
{operation.stepDescription || `步骤 ${operation.currentStep}/${operation.totalSteps}`}
</div>
</>
)}
</div>
)}
</div>
</nav>
<main className={styles.main}>{children}</main>
</div>
);
}