feat: 添加 CLOB 2.0 迁移弹窗提醒与账户页迁移引导
登录后首次进入显示迁移提醒弹窗,引导用户前往账户页; 账户页首次进入时显示 Alert 提示 USDC.e → pUSD 迁移按钮; 移动端账户卡片补充 swap 操作按钮。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,7 @@ import { wsManager } from './services/websocket'
|
||||
import type { OrderPushMessage } from './types'
|
||||
import { apiService } from './services/api'
|
||||
import { hasToken } from './utils'
|
||||
import ClobMigrationModal, { STORAGE_KEY as CLOB_MIGRATION_KEY } from './components/ClobMigrationModal'
|
||||
|
||||
/**
|
||||
* 路由保护组件
|
||||
@@ -64,6 +65,7 @@ function App() {
|
||||
const { t, i18n } = useTranslation()
|
||||
const [isFirstUse, setIsFirstUse] = useState<boolean | null>(null)
|
||||
const [checking, setChecking] = useState(true)
|
||||
const [clobMigrationVisible, setClobMigrationVisible] = useState(false)
|
||||
|
||||
// 根据当前语言设置 Ant Design 的 locale
|
||||
const getAntdLocale = () => {
|
||||
@@ -199,6 +201,13 @@ function App() {
|
||||
wsManager.disconnect()
|
||||
}
|
||||
}, [checking, isFirstUse])
|
||||
|
||||
// 已登录且未查看过 CLOB V2 迁移提醒时显示弹窗
|
||||
useEffect(() => {
|
||||
if (!checking && isFirstUse === false && hasToken() && !localStorage.getItem(CLOB_MIGRATION_KEY)) {
|
||||
setClobMigrationVisible(true)
|
||||
}
|
||||
}, [checking, isFirstUse])
|
||||
|
||||
// 订阅订单推送并显示全局通知
|
||||
useEffect(() => {
|
||||
@@ -284,6 +293,7 @@ function App() {
|
||||
{/* 默认重定向到登录页 */}
|
||||
<Route path="*" element={<Navigate to="/login" replace />} />
|
||||
</Routes>
|
||||
<ClobMigrationModal open={clobMigrationVisible} onClose={() => setClobMigrationVisible(false)} />
|
||||
</BrowserRouter>
|
||||
</ConfigProvider>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Modal, Button, Space } from 'antd'
|
||||
import { SwapOutlined } from '@ant-design/icons'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const STORAGE_KEY = 'clob_v2_migration_dismissed'
|
||||
|
||||
interface ClobMigrationModalProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const ClobMigrationModal: React.FC<ClobMigrationModalProps> = ({ open, onClose }) => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handleClose = (dontRemind = false) => {
|
||||
if (dontRemind) {
|
||||
localStorage.setItem(STORAGE_KEY, 'true')
|
||||
}
|
||||
onClose()
|
||||
}
|
||||
|
||||
const handleGoToAccounts = () => {
|
||||
localStorage.setItem(STORAGE_KEY, 'true')
|
||||
onClose()
|
||||
navigate('/accounts')
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<SwapOutlined style={{ color: '#fa8c16' }} />
|
||||
<span>{t('clobMigration.title')}</span>
|
||||
</div>
|
||||
}
|
||||
open={open}
|
||||
onCancel={() => handleClose()}
|
||||
footer={
|
||||
<Space>
|
||||
<Button onClick={() => handleClose(true)}>
|
||||
{t('clobMigration.dontRemind')}
|
||||
</Button>
|
||||
<Button onClick={() => handleClose()}>
|
||||
{t('common.later')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={handleGoToAccounts}>
|
||||
{t('clobMigration.goToAccounts')}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
closable
|
||||
maskClosable={false}
|
||||
>
|
||||
<p style={{ fontSize: 14, lineHeight: 1.8, color: 'rgba(0, 0, 0, 0.65)' }}>
|
||||
{t('clobMigration.description')}
|
||||
</p>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ClobMigrationModal
|
||||
export { STORAGE_KEY }
|
||||
@@ -1801,5 +1801,14 @@
|
||||
"maxSizeUpdated": "Updated to max size",
|
||||
"periodChanged": "Period has changed, popup closed"
|
||||
}
|
||||
},
|
||||
"clobMigration": {
|
||||
"title": "CLOB 2.0 Migration Notice",
|
||||
"description": "Polymarket CLOB has been upgraded to V2. Please go to the Accounts page to complete the USDC migration to ensure trading functions properly.",
|
||||
"goToAccounts": "Go to Accounts",
|
||||
"dontRemind": "Don't remind again",
|
||||
"accountGuide": "CLOB V2 requires pUSD for trading. Click the USDC.e → pUSD button in the account actions to complete the migration.",
|
||||
"accountGuideButton": "USDC.e → pUSD",
|
||||
"dismissGuide": "Got it"
|
||||
}
|
||||
}
|
||||
@@ -1801,5 +1801,14 @@
|
||||
"maxSizeUpdated": "已更新为最大数量",
|
||||
"periodChanged": "周期已切换,弹窗已关闭"
|
||||
}
|
||||
},
|
||||
"clobMigration": {
|
||||
"title": "CLOB 2.0 迁移提醒",
|
||||
"description": "Polymarket CLOB 已升级至 V2 版本,您需要前往账户页完成 USDC 迁移操作,以确保交易功能正常使用。",
|
||||
"goToAccounts": "前往账户页",
|
||||
"dontRemind": "不再提醒",
|
||||
"accountGuide": "CLOB V2 需要 pUSD 进行交易,请点击账户操作栏中的 USDC.e → pUSD 按钮完成迁移。",
|
||||
"accountGuideButton": "USDC.e → pUSD",
|
||||
"dismissGuide": "知道了"
|
||||
}
|
||||
}
|
||||
@@ -1801,5 +1801,14 @@
|
||||
"maxSizeUpdated": "已更新為最大數量",
|
||||
"periodChanged": "週期已切換,彈窗已關閉"
|
||||
}
|
||||
},
|
||||
"clobMigration": {
|
||||
"title": "CLOB 2.0 遷移提醒",
|
||||
"description": "Polymarket CLOB 已升級至 V2 版本,您需要前往帳戶頁完成 USDC 遷移操作,以確保交易功能正常使用。",
|
||||
"goToAccounts": "前往帳戶頁",
|
||||
"dontRemind": "不再提醒",
|
||||
"accountGuide": "CLOB V2 需要 pUSD 進行交易,請點擊帳戶操作欄中的 USDC.e → pUSD 按鈕完成遷移。",
|
||||
"accountGuideButton": "USDC.e → pUSD",
|
||||
"dismissGuide": "知道了"
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,9 @@ const AccountList: React.FC = () => {
|
||||
const [accountImportModalVisible, setAccountImportModalVisible] = useState(false)
|
||||
const [accountImportForm] = Form.useForm()
|
||||
const [wrapLoading, setWrapLoading] = useState<Record<number, boolean>>({})
|
||||
const [migrationGuideVisible, setMigrationGuideVisible] = useState(false)
|
||||
|
||||
const ACCOUNT_GUIDE_KEY = 'clob_v2_account_guide_dismissed'
|
||||
const handleWrapToPusd = async (account: Account) => {
|
||||
try {
|
||||
setWrapLoading(prev => ({ ...prev, [account.id]: true }))
|
||||
@@ -70,6 +72,13 @@ const AccountList: React.FC = () => {
|
||||
fetchAccounts()
|
||||
}, [fetchAccounts])
|
||||
|
||||
// 首次进入且有账户时显示迁移引导
|
||||
useEffect(() => {
|
||||
if (!loading && accounts.length > 0 && !localStorage.getItem(ACCOUNT_GUIDE_KEY)) {
|
||||
setMigrationGuideVisible(true)
|
||||
}
|
||||
}, [loading, accounts.length])
|
||||
|
||||
const handleAccountImportSuccess = async () => {
|
||||
message.success(t('accountImport.importSuccess'))
|
||||
setAccountImportModalVisible(false)
|
||||
@@ -363,7 +372,7 @@ const AccountList: React.FC = () => {
|
||||
}
|
||||
const balanceObj = balanceMap[record.id]
|
||||
const balance = balanceObj?.total || record.balance || '-'
|
||||
return balance && balance !== '-' && typeof balance === 'string' ? `${formatUSDC(balance)} USDC` : '-'
|
||||
return balance && balance !== '-' && typeof balance === 'string' ? `$${formatUSDC(balance)}` : '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -496,6 +505,38 @@ const AccountList: React.FC = () => {
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{migrationGuideVisible && !loading && accounts.length > 0 && (
|
||||
<Alert
|
||||
message={t('clobMigration.accountGuide')}
|
||||
type="warning"
|
||||
showIcon
|
||||
icon={<SwapOutlined />}
|
||||
closable
|
||||
onClose={() => {
|
||||
localStorage.setItem(ACCOUNT_GUIDE_KEY, 'true')
|
||||
setMigrationGuideVisible(false)
|
||||
}}
|
||||
afterClose={() => {
|
||||
localStorage.setItem(ACCOUNT_GUIDE_KEY, 'true')
|
||||
setMigrationGuideVisible(false)
|
||||
}}
|
||||
style={{ marginBottom: 16, ...(isMobile ? { margin: '0 8px 12px' } : {}) }}
|
||||
action={
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
danger
|
||||
onClick={() => {
|
||||
localStorage.setItem(ACCOUNT_GUIDE_KEY, 'true')
|
||||
setMigrationGuideVisible(false)
|
||||
}}
|
||||
>
|
||||
{t('clobMigration.dismissGuide')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Card style={{
|
||||
margin: isMobile ? '0 -8px' : '0',
|
||||
borderRadius: isMobile ? '0' : undefined
|
||||
@@ -562,7 +603,7 @@ const AccountList: React.FC = () => {
|
||||
{t('accountList.totalBalance')}
|
||||
</div>
|
||||
<div style={{ fontSize: '14px', fontWeight: '600', color: '#52c41a' }}>
|
||||
{balance?.total && balance.total !== '-' ? `${formatUSDC(balance.total)} USDC` : '- USDC'}
|
||||
{balance?.total && balance.total !== '-' ? `$${formatUSDC(balance.total)}` : '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
@@ -626,6 +667,16 @@ const AccountList: React.FC = () => {
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title={t('clobMigration.accountGuideButton')}>
|
||||
<div
|
||||
onClick={() => handleWrapToPusd(account)}
|
||||
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', cursor: wrapLoading[account.id] ? 'wait' : 'pointer', padding: '4px 8px' }}
|
||||
>
|
||||
<SwapOutlined style={{ fontSize: '18px', color: '#fa8c16' }} spin={wrapLoading[account.id]} />
|
||||
<span style={{ fontSize: '10px', color: '#8c8c8c', marginTop: '2px' }}>{t('clobMigration.accountGuideButton')}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<Popconfirm
|
||||
title={t('accountList.deleteConfirm')}
|
||||
description={
|
||||
@@ -786,7 +837,7 @@ const AccountList: React.FC = () => {
|
||||
<Spin size="small" />
|
||||
) : detailBalance ? (
|
||||
<span style={{ fontWeight: 'bold', color: '#1890ff', fontSize: '16px' }}>
|
||||
{formatUSDC(detailBalance.total)} USDC
|
||||
${formatUSDC(detailBalance.total)}
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ color: '#999' }}>-</span>
|
||||
@@ -797,7 +848,7 @@ const AccountList: React.FC = () => {
|
||||
<Spin size="small" />
|
||||
) : detailBalance ? (
|
||||
<span style={{ color: '#52c41a' }}>
|
||||
{formatUSDC(detailBalance.available)} USDC
|
||||
${formatUSDC(detailBalance.available)}
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ color: '#999' }}>-</span>
|
||||
@@ -808,7 +859,7 @@ const AccountList: React.FC = () => {
|
||||
<Spin size="small" />
|
||||
) : detailBalance ? (
|
||||
<span style={{ color: '#1890ff' }}>
|
||||
{formatUSDC(detailBalance.position)} USDC
|
||||
${formatUSDC(detailBalance.position)}
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ color: '#999' }}>-</span>
|
||||
@@ -864,7 +915,7 @@ const AccountList: React.FC = () => {
|
||||
fontWeight: 'bold',
|
||||
color: detailAccount.totalPnl && detailAccount.totalPnl.startsWith('-') ? '#ff4d4f' : '#52c41a'
|
||||
}}>
|
||||
{formatUSDC(detailAccount.totalPnl)} USDC
|
||||
${formatUSDC(detailAccount.totalPnl)}
|
||||
</span>
|
||||
</Descriptions.Item>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user