fix(web): remove undefined setHyperliquidWalletAddr call in ExchangeConfigModal (#525)

This commit is contained in:
Ember
2025-11-05 19:35:57 +08:00
committed by GitHub
parent 096bb6a262
commit 713c560ca5

View File

@@ -660,50 +660,52 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
</div> </div>
{/* 信号源配置警告 */} {/* 信号源配置警告 */}
{traders && traders.some(t => (t.use_coin_pool || t.use_oi_top)) && {traders &&
(!userSignalSource.coinPoolUrl && !userSignalSource.oiTopUrl) && ( traders.some((t) => t.use_coin_pool || t.use_oi_top) &&
<div !userSignalSource.coinPoolUrl &&
className="rounded-lg px-4 py-3 flex items-start gap-3 animate-slide-in" !userSignalSource.oiTopUrl && (
style={{ <div
background: 'rgba(246, 70, 93, 0.1)', className="rounded-lg px-4 py-3 flex items-start gap-3 animate-slide-in"
border: '1px solid rgba(246, 70, 93, 0.3)', style={{
}} background: 'rgba(246, 70, 93, 0.1)',
> border: '1px solid rgba(246, 70, 93, 0.3)',
<AlertTriangle }}
size={20} >
className="flex-shrink-0 mt-0.5" <AlertTriangle
style={{ color: '#F6465D' }} size={20}
/> className="flex-shrink-0 mt-0.5"
<div className="flex-1"> style={{ color: '#F6465D' }}
<div className="font-semibold mb-1" style={{ color: '#F6465D' }}> />
{t('signalSourceNotConfigured', language)} <div className="flex-1">
<div className="font-semibold mb-1" style={{ color: '#F6465D' }}>
{t('signalSourceNotConfigured', language)}
</div>
<div className="text-sm" style={{ color: '#848E9C' }}>
<p className="mb-2">
{t('signalSourceWarningMessage', language)}
</p>
<p>
<strong>{t('solutions', language)}</strong>
</p>
<ul className="list-disc list-inside space-y-1 ml-2 mt-1">
<li>"📡 {t('signalSource', language)}"API地址</li>
<li>"使用币种池""使用OI Top"</li>
<li></li>
</ul>
</div>
<button
onClick={() => setShowSignalSourceModal(true)}
className="mt-3 px-3 py-1.5 rounded text-sm font-semibold transition-all hover:scale-105"
style={{
background: '#F0B90B',
color: '#000',
}}
>
{t('configureSignalSourceNow', language)}
</button>
</div> </div>
<div className="text-sm" style={{ color: '#848E9C' }}>
<p className="mb-2">
{t('signalSourceWarningMessage', language)}
</p>
<p>
<strong>{t('solutions', language)}</strong>
</p>
<ul className="list-disc list-inside space-y-1 ml-2 mt-1">
<li>"📡 {t('signalSource', language)}"API地址</li>
<li>"使用币种池""使用OI Top"</li>
<li></li>
</ul>
</div>
<button
onClick={() => setShowSignalSourceModal(true)}
className="mt-3 px-3 py-1.5 rounded text-sm font-semibold transition-all hover:scale-105"
style={{
background: '#F0B90B',
color: '#000',
}}
>
{t('configureSignalSourceNow', language)}
</button>
</div> </div>
</div> )}
)}
{/* Configuration Status */} {/* Configuration Status */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 md:gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 md:gap-6">
@@ -1576,22 +1578,23 @@ function ExchangeConfigModal({
onClose: () => void onClose: () => void
language: Language language: Language
}) { }) {
const [selectedExchangeId, setSelectedExchangeId] = useState(editingExchangeId || ''); const [selectedExchangeId, setSelectedExchangeId] = useState(
const [apiKey, setApiKey] = useState(''); editingExchangeId || ''
const [secretKey, setSecretKey] = useState(''); )
const [passphrase, setPassphrase] = useState(''); const [apiKey, setApiKey] = useState('')
const [testnet, setTestnet] = useState(false); const [secretKey, setSecretKey] = useState('')
const [showGuide, setShowGuide] = useState(false); const [passphrase, setPassphrase] = useState('')
const [testnet, setTestnet] = useState(false)
const [showGuide, setShowGuide] = useState(false)
const [serverIP, setServerIP] = useState<{ const [serverIP, setServerIP] = useState<{
public_ip: string; public_ip: string
message: string; message: string
} | null>(null); } | null>(null)
const [loadingIP, setLoadingIP] = useState(false); const [loadingIP, setLoadingIP] = useState(false)
const [copiedIP, setCopiedIP] = useState(false); const [copiedIP, setCopiedIP] = useState(false)
// 币安配置指南展开状态 // 币安配置指南展开状态
const [showBinanceGuide, setShowBinanceGuide] = useState(false); const [showBinanceGuide, setShowBinanceGuide] = useState(false)
// Aster 特定字段 // Aster 特定字段
const [asterUser, setAsterUser] = useState('') const [asterUser, setAsterUser] = useState('')
@@ -1611,9 +1614,6 @@ function ExchangeConfigModal({
setPassphrase('') // Don't load existing passphrase for security setPassphrase('') // Don't load existing passphrase for security
setTestnet(selectedExchange.testnet || false) setTestnet(selectedExchange.testnet || false)
// Hyperliquid 字段
setHyperliquidWalletAddr(selectedExchange.hyperliquidWalletAddr || '')
// Aster 字段 // Aster 字段
setAsterUser(selectedExchange.asterUser || '') setAsterUser(selectedExchange.asterUser || '')
setAsterSigner(selectedExchange.asterSigner || '') setAsterSigner(selectedExchange.asterSigner || '')
@@ -1624,26 +1624,27 @@ function ExchangeConfigModal({
// 加载服务器IP当选择binance时 // 加载服务器IP当选择binance时
useEffect(() => { useEffect(() => {
if (selectedExchangeId === 'binance' && !serverIP) { if (selectedExchangeId === 'binance' && !serverIP) {
setLoadingIP(true); setLoadingIP(true)
api.getServerIP() api
.then(data => { .getServerIP()
setServerIP(data); .then((data) => {
setServerIP(data)
}) })
.catch(err => { .catch((err) => {
console.error('Failed to load server IP:', err); console.error('Failed to load server IP:', err)
}) })
.finally(() => { .finally(() => {
setLoadingIP(false); setLoadingIP(false)
}); })
} }
}, [selectedExchangeId]); }, [selectedExchangeId])
const handleCopyIP = (ip: string) => { const handleCopyIP = (ip: string) => {
navigator.clipboard.writeText(ip).then(() => { navigator.clipboard.writeText(ip).then(() => {
setCopiedIP(true); setCopiedIP(true)
setTimeout(() => setCopiedIP(false), 2000); setTimeout(() => setCopiedIP(false), 2000)
}); })
}; }
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault() e.preventDefault()
@@ -1789,104 +1790,151 @@ function ExchangeConfigModal({
{selectedExchange && ( {selectedExchange && (
<> <>
{/* Binance 和其他 CEX 交易所的字段 */} {/* Binance 和其他 CEX 交易所的字段 */}
{(selectedExchange.id === 'binance' || selectedExchange.type === 'cex') && selectedExchange.id !== 'hyperliquid' && selectedExchange.id !== 'aster' && ( {(selectedExchange.id === 'binance' ||
<> selectedExchange.type === 'cex') &&
{/* 币安用户配置提示 (D1 方案) */} selectedExchange.id !== 'hyperliquid' &&
{selectedExchange.id === 'binance' && ( selectedExchange.id !== 'aster' && (
<div <>
className="mb-4 p-3 rounded cursor-pointer transition-colors" {/* 币安用户配置提示 (D1 方案) */}
style={{ {selectedExchange.id === 'binance' && (
background: '#1a3a52', <div
border: '1px solid #2b5278', className="mb-4 p-3 rounded cursor-pointer transition-colors"
}} style={{
onClick={() => setShowBinanceGuide(!showBinanceGuide)} background: '#1a3a52',
> border: '1px solid #2b5278',
<div className="flex items-center justify-between"> }}
<div className="flex items-center gap-2"> onClick={() => setShowBinanceGuide(!showBinanceGuide)}
<span style={{ color: '#58a6ff' }}></span> >
<span className="text-sm font-medium" style={{ color: '#EAECEF' }}> <div className="flex items-center justify-between">
<strong></strong> <div className="flex items-center gap-2">
使API API <span style={{ color: '#58a6ff' }}></span>
<span
className="text-sm font-medium"
style={{ color: '#EAECEF' }}
>
<strong></strong>
使API API
</span>
</div>
<span style={{ color: '#8b949e' }}>
{showBinanceGuide ? '▲' : '▼'}
</span> </span>
</div> </div>
<span style={{ color: '#8b949e' }}>
{showBinanceGuide ? '▲' : '▼'}
</span>
</div>
{/* 展开的详细说明 */} {/* 展开的详细说明 */}
{showBinanceGuide && ( {showBinanceGuide && (
<div <div
className="mt-3 pt-3" className="mt-3 pt-3"
style={{ style={{
borderTop: '1px solid #2b5278', borderTop: '1px solid #2b5278',
fontSize: '0.875rem', fontSize: '0.875rem',
color: '#c9d1d9' color: '#c9d1d9',
}} }}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
>
<p className="mb-2" style={{ color: '#8b949e' }}>
<strong></strong> API
</p>
<p className="font-semibold mb-1" style={{ color: '#EAECEF' }}>
</p>
<ol className="list-decimal list-inside space-y-1 mb-3" style={{ paddingLeft: '0.5rem' }}>
<li> <strong>API </strong></li>
<li> API <strong> API </strong></li>
<li><strong></strong><span style={{ color: '#f85149' }}></span></li>
<li>IP <strong></strong> IP</li>
</ol>
<p className="mb-2 p-2 rounded" style={{ background: '#3d2a00', border: '1px solid #9e6a03' }}>
💡 <strong></strong>
使
</p>
<a
href="https://www.binance.com/zh-CN/support/faq/how-to-create-api-keys-on-binance-360002502072"
target="_blank"
rel="noopener noreferrer"
className="inline-block text-sm hover:underline"
style={{ color: '#58a6ff' }}
> >
📖 <p className="mb-2" style={{ color: '#8b949e' }}>
</a> <strong></strong> API
</div>
)} </p>
<p
className="font-semibold mb-1"
style={{ color: '#EAECEF' }}
>
</p>
<ol
className="list-decimal list-inside space-y-1 mb-3"
style={{ paddingLeft: '0.5rem' }}
>
<li>
<strong>API </strong>
</li>
<li>
API
<strong> API </strong>
</li>
<li>
<strong></strong>
<span style={{ color: '#f85149' }}>
</span>
</li>
<li>
IP <strong></strong>
IP
</li>
</ol>
<p
className="mb-2 p-2 rounded"
style={{
background: '#3d2a00',
border: '1px solid #9e6a03',
}}
>
💡 <strong></strong>
使
</p>
<a
href="https://www.binance.com/zh-CN/support/faq/how-to-create-api-keys-on-binance-360002502072"
target="_blank"
rel="noopener noreferrer"
className="inline-block text-sm hover:underline"
style={{ color: '#58a6ff' }}
>
📖
</a>
</div>
)}
</div>
)}
<div>
<label
className="block text-sm font-semibold mb-2"
style={{ color: '#EAECEF' }}
>
{t('apiKey', language)}
</label>
<input
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder={t('enterAPIKey', language)}
className="w-full px-3 py-2 rounded"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
required
/>
</div> </div>
)}
<div> <div>
<label className="block text-sm font-semibold mb-2" style={{ color: '#EAECEF' }}> <label
{t('apiKey', language)} className="block text-sm font-semibold mb-2"
</label> style={{ color: '#EAECEF' }}
<input >
type="password" {t('secretKey', language)}
value={apiKey} </label>
onChange={(e) => setApiKey(e.target.value)} <input
placeholder={t('enterAPIKey', language)} type="password"
className="w-full px-3 py-2 rounded" value={secretKey}
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }} onChange={(e) => setSecretKey(e.target.value)}
required placeholder={t('enterSecretKey', language)}
/> className="w-full px-3 py-2 rounded"
</div> style={{
background: '#0B0E11',
<div> border: '1px solid #2B3139',
<label className="block text-sm font-semibold mb-2" style={{ color: '#EAECEF' }}> color: '#EAECEF',
{t('secretKey', language)} }}
</label> required
<input />
type="password" </div>
value={secretKey}
onChange={(e) => setSecretKey(e.target.value)}
placeholder={t('enterSecretKey', language)}
className="w-full px-3 py-2 rounded"
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
/>
</div>
<div> <div>
<label <label
@@ -1934,37 +1982,62 @@ function ExchangeConfigModal({
</div> </div>
)} )}
{/* Binance 白名单IP提示 */} {/* Binance 白名单IP提示 */}
{selectedExchange.id === 'binance' && ( {selectedExchange.id === 'binance' && (
<div className="p-4 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', border: '1px solid rgba(240, 185, 11, 0.2)' }}> <div
<div className="text-sm font-semibold mb-2" style={{ color: '#F0B90B' }}> className="p-4 rounded"
{t('whitelistIP', language)} style={{
</div> background: 'rgba(240, 185, 11, 0.1)',
<div className="text-xs mb-3" style={{ color: '#848E9C' }}> border: '1px solid rgba(240, 185, 11, 0.2)',
{t('whitelistIPDesc', language)} }}
</div> >
<div
className="text-sm font-semibold mb-2"
style={{ color: '#F0B90B' }}
>
{t('whitelistIP', language)}
</div>
<div
className="text-xs mb-3"
style={{ color: '#848E9C' }}
>
{t('whitelistIPDesc', language)}
</div>
{loadingIP ? ( {loadingIP ? (
<div className="text-xs" style={{ color: '#848E9C' }}> <div className="text-xs" style={{ color: '#848E9C' }}>
{t('loadingServerIP', language)} {t('loadingServerIP', language)}
</div> </div>
) : serverIP && serverIP.public_ip ? ( ) : serverIP && serverIP.public_ip ? (
<div className="flex items-center gap-2 p-2 rounded" style={{ background: '#0B0E11' }}> <div
<code className="flex-1 text-sm font-mono" style={{ color: '#F0B90B' }}>{serverIP.public_ip}</code> className="flex items-center gap-2 p-2 rounded"
<button style={{ background: '#0B0E11' }}
type="button"
onClick={() => handleCopyIP(serverIP.public_ip)}
className="px-3 py-1 rounded text-xs font-semibold transition-all hover:scale-105"
style={{ background: 'rgba(240, 185, 11, 0.2)', color: '#F0B90B' }}
> >
{copiedIP ? t('ipCopied', language) : t('copyIP', language)} <code
</button> className="flex-1 text-sm font-mono"
</div> style={{ color: '#F0B90B' }}
) : null} >
</div> {serverIP.public_ip}
)} </code>
</> <button
)} type="button"
onClick={() => handleCopyIP(serverIP.public_ip)}
className="px-3 py-1 rounded text-xs font-semibold transition-all hover:scale-105"
style={{
background: 'rgba(240, 185, 11, 0.2)',
color: '#F0B90B',
}}
>
{copiedIP
? t('ipCopied', language)
: t('copyIP', language)}
</button>
</div>
) : null}
</div>
)}
</>
)}
{/* Hyperliquid 交易所的字段 */} {/* Hyperliquid 交易所的字段 */}
{selectedExchange.id === 'hyperliquid' && ( {selectedExchange.id === 'hyperliquid' && (