@@ -75,6 +75,7 @@ import {
|
||||
selectGameCreationAppReadyTasks,
|
||||
} from '../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import type {
|
||||
ProfileDashboardSummary,
|
||||
ProfileMudPointBalance,
|
||||
ProfileRechargeCenterResponse,
|
||||
ProfileRechargeProduct,
|
||||
@@ -5359,8 +5360,8 @@ export function WorkspaceLauncher({
|
||||
const [runtimeConfigOpen, setRuntimeConfigOpen] = useState(false);
|
||||
const [pendingNonEmptyProject, setPendingNonEmptyProject] =
|
||||
useState<PendingNonEmptyProject | null>(null);
|
||||
const [walletBalance, setWalletBalance] = useState<number | null>(null);
|
||||
const walletBalanceRevisionRef = useRef(0);
|
||||
const [profileDashboard, setProfileDashboard] =
|
||||
useState<ProfileDashboardSummary | null>(null);
|
||||
const [mudPointBalance, setMudPointBalance] =
|
||||
useState<ProfileMudPointBalance | null>(null);
|
||||
const [mudPointBalanceStatus, setMudPointBalanceStatus] = useState<
|
||||
@@ -5829,25 +5830,18 @@ export function WorkspaceLauncher({
|
||||
|
||||
useEffect(() => {
|
||||
let disposed = false;
|
||||
const walletBalanceRevision = walletBalanceRevisionRef.current;
|
||||
void getClientProfileDashboard()
|
||||
.then((dashboard) => {
|
||||
if (
|
||||
disposed ||
|
||||
walletBalanceRevisionRef.current !== walletBalanceRevision
|
||||
) {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
setWalletBalance(dashboard.walletBalance);
|
||||
setProfileDashboard(dashboard);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (
|
||||
disposed ||
|
||||
walletBalanceRevisionRef.current !== walletBalanceRevision
|
||||
) {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
setWalletBalance(null);
|
||||
setProfileDashboard(null);
|
||||
setMudPointBalanceError(
|
||||
error instanceof Error ? error.message : '泥点读取失败',
|
||||
);
|
||||
@@ -6204,8 +6198,9 @@ export function WorkspaceLauncher({
|
||||
try {
|
||||
const center = await getClientProfileRechargeCenter();
|
||||
setMudPointBalance(center.mudPointBalance ?? null);
|
||||
walletBalanceRevisionRef.current += 1;
|
||||
setWalletBalance(center.walletBalance);
|
||||
setProfileDashboard((current) =>
|
||||
current ? { ...current, walletBalance: center.walletBalance } : current,
|
||||
);
|
||||
setMudPointBalanceStatus('ready');
|
||||
} catch (error) {
|
||||
setMudPointBalance(null);
|
||||
@@ -6241,8 +6236,9 @@ export function WorkspaceLauncher({
|
||||
setMudPointBalance(center.mudPointBalance ?? null);
|
||||
setMudPointBalanceStatus('ready');
|
||||
setMudPointBalanceError('');
|
||||
walletBalanceRevisionRef.current += 1;
|
||||
setWalletBalance(center.walletBalance);
|
||||
setProfileDashboard((current) =>
|
||||
current ? { ...current, walletBalance: center.walletBalance } : current,
|
||||
);
|
||||
}
|
||||
|
||||
async function loadRechargeCenter() {
|
||||
@@ -8686,7 +8682,7 @@ export function WorkspaceLauncher({
|
||||
|
||||
<div className="launcher-account-bar" aria-label="账户资产">
|
||||
<PlatformMudPointWalletEntry
|
||||
balance={walletBalance}
|
||||
balance={profileDashboard?.walletBalance ?? null}
|
||||
breakdown={mudPointBalance}
|
||||
isLoading={mudPointBalanceStatus === 'loading'}
|
||||
error={mudPointBalanceError || null}
|
||||
@@ -9472,7 +9468,7 @@ export function WorkspaceLauncher({
|
||||
<PlatformProfileWalletLedgerModal
|
||||
ledger={walletLedger}
|
||||
fallbackBalance={
|
||||
walletBalance ?? mudPointBalance?.totalPoints ?? 0
|
||||
profileDashboard?.walletBalance ?? mudPointBalance?.totalPoints ?? 0
|
||||
}
|
||||
isLoading={walletLedgerLoading}
|
||||
error={walletLedgerError}
|
||||
|
||||
@@ -1585,6 +1585,15 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
it('opens the shared recharge modal from the wallet and sidebar entries', async () => {
|
||||
const rechargeCenter = {
|
||||
walletBalance: 120,
|
||||
mudPointBalance: {
|
||||
totalPoints: 120,
|
||||
permanentPoints: 100,
|
||||
limitedPoints: 20,
|
||||
limitedExpiresAt: '2026-08-01T00:00:00Z',
|
||||
dailyFreePoints: 0,
|
||||
dailyFreeResetPoints: 20,
|
||||
dailyFreeResetsAt: '2026-07-18T00:00:00Z',
|
||||
},
|
||||
membership: {
|
||||
status: 'inactive',
|
||||
tier: 'normal',
|
||||
@@ -1622,14 +1631,13 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
};
|
||||
let rechargeOrderRequestCount = 0;
|
||||
let releaseLateRechargeOrder: (() => void) | null = null;
|
||||
let resolveInitialDashboard: ((response: Response) => void) | null = null;
|
||||
const fetchSpy = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockImplementation(async (input: RequestInfo | URL) => {
|
||||
const url = String(input);
|
||||
if (url === '/api/profile/dashboard') {
|
||||
return new Promise<Response>((resolve) => {
|
||||
resolveInitialDashboard = resolve;
|
||||
return new Response(JSON.stringify({ walletBalance: 120 }), {
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
if (url === '/api/profile/recharge-center') {
|
||||
@@ -1690,13 +1698,9 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
...rechargeCenter,
|
||||
walletBalance: 180,
|
||||
mudPointBalance: {
|
||||
...rechargeCenter.mudPointBalance,
|
||||
totalPoints: 180,
|
||||
permanentPoints: 160,
|
||||
limitedPoints: 20,
|
||||
limitedExpiresAt: '2026-08-01T00:00:00Z',
|
||||
dailyFreePoints: 0,
|
||||
dailyFreeResetPoints: 20,
|
||||
dailyFreeResetsAt: '2026-07-18T00:00:00Z',
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -1713,7 +1717,6 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
await screen.findByRole('dialog', { name: '购买更多泥点' }),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText('当前余额 120 泥点')).not.toBeNull();
|
||||
expect(screen.getByRole('button', { name: '泥点 120' })).not.toBeNull();
|
||||
fireEvent.click(screen.getByRole('button', { name: '关闭购买更多泥点' }));
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '我的' }));
|
||||
@@ -1745,14 +1748,6 @@ describe('AI 游戏创作 App 界面边界', () => {
|
||||
expect(screen.queryByRole('dialog', { name: '微信扫码支付' })).toBeNull();
|
||||
});
|
||||
expect(screen.getByText('当前余额 180 泥点')).not.toBeNull();
|
||||
expect(screen.getByRole('button', { name: '泥点 180' })).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
resolveInitialDashboard?.(
|
||||
new Response(JSON.stringify({ walletBalance: 40 }), { status: 200 }),
|
||||
);
|
||||
});
|
||||
expect(screen.getByRole('button', { name: '泥点 180' })).not.toBeNull();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /60泥点.*购买/ }));
|
||||
await waitFor(() => {
|
||||
|
||||
Reference in New Issue
Block a user