feat: 集成网络连接管理与降级轮询功能
- 在 PlayerAppShell 中引入 NetworkStatusBanner 组件以显示网络状态 - 在 HallBettingGrid 中实现下注后触发钱包轮询 - 在 HallWalletStrip 中添加网络连接状态管理与定期刷新逻辑 - 在 useHallDrawLive 中集成 WebSocket 连接状态与降级轮询机制,确保在断开时自动切换到轮询模式
This commit is contained in:
148
src/stores/network-connection-store.ts
Normal file
148
src/stores/network-connection-store.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
"use client";
|
||||
|
||||
import { create } from "zustand";
|
||||
|
||||
export type NetworkMode = "websocket" | "polling" | "offline";
|
||||
|
||||
export type NetworkConnectionState = {
|
||||
/** 当前网络模式:WebSocket 正常 / 降级轮询 / 离线 */
|
||||
mode: NetworkMode;
|
||||
/** WebSocket 是否已连接 */
|
||||
isWebSocketConnected: boolean;
|
||||
/** 是否正在尝试重连 */
|
||||
isReconnecting: boolean;
|
||||
/** 重连尝试次数 */
|
||||
reconnectAttempts: number;
|
||||
/** 上次连接成功时间 */
|
||||
lastConnectedAt: number | null;
|
||||
/** 上次断开连接时间 */
|
||||
lastDisconnectedAt: number | null;
|
||||
/** 当前活跃的画作数据轮询计时器ID */
|
||||
drawPollingIntervalId: number | null;
|
||||
/** 当前活跃的钱包余额轮询计时器ID */
|
||||
walletPollingIntervalId: number | null;
|
||||
/** 钱包余额轮询过期时间戳(用于bet后的限时轮询) */
|
||||
walletPollingExpiryAt: number | null;
|
||||
|
||||
// Actions
|
||||
setMode: (mode: NetworkMode) => void;
|
||||
setWebSocketConnected: (connected: boolean) => void;
|
||||
setReconnecting: (reconnecting: boolean) => void;
|
||||
incrementReconnectAttempts: () => void;
|
||||
resetReconnectAttempts: () => void;
|
||||
setLastConnectedAt: (timestamp: number | null) => void;
|
||||
setLastDisconnectedAt: (timestamp: number | null) => void;
|
||||
setDrawPollingIntervalId: (id: number | null) => void;
|
||||
setWalletPollingIntervalId: (id: number | null) => void;
|
||||
setWalletPollingExpiryAt: (timestamp: number | null) => void;
|
||||
/** 切换到降级模式(WebSocket断开时调用) */
|
||||
switchToPollingMode: () => void;
|
||||
/** 切换回WebSocket模式(WebSocket重连成功时调用) */
|
||||
switchToWebSocketMode: () => void;
|
||||
/** 清除所有轮询计时器 */
|
||||
clearAllPollingIntervals: () => void;
|
||||
/** 停止钱包轮询 */
|
||||
clearWalletPolling: () => void;
|
||||
/** 停止画作数据轮询 */
|
||||
clearDrawPolling: () => void;
|
||||
};
|
||||
|
||||
export const useNetworkConnectionStore = create<NetworkConnectionState>(
|
||||
(set, get) => ({
|
||||
mode: "websocket",
|
||||
isWebSocketConnected: false,
|
||||
isReconnecting: false,
|
||||
reconnectAttempts: 0,
|
||||
lastConnectedAt: null,
|
||||
lastDisconnectedAt: null,
|
||||
drawPollingIntervalId: null,
|
||||
walletPollingIntervalId: null,
|
||||
walletPollingExpiryAt: null,
|
||||
|
||||
setMode: (mode) => set({ mode }),
|
||||
|
||||
setWebSocketConnected: (connected) =>
|
||||
set({ isWebSocketConnected: connected }),
|
||||
|
||||
setReconnecting: (reconnecting) => set({ isReconnecting: reconnecting }),
|
||||
|
||||
incrementReconnectAttempts: () =>
|
||||
set((state) => ({ reconnectAttempts: state.reconnectAttempts + 1 })),
|
||||
|
||||
resetReconnectAttempts: () => set({ reconnectAttempts: 0 }),
|
||||
|
||||
setLastConnectedAt: (timestamp) => set({ lastConnectedAt: timestamp }),
|
||||
|
||||
setLastDisconnectedAt: (timestamp) =>
|
||||
set({ lastDisconnectedAt: timestamp }),
|
||||
|
||||
setDrawPollingIntervalId: (id) => set({ drawPollingIntervalId: id }),
|
||||
|
||||
setWalletPollingIntervalId: (id) => set({ walletPollingIntervalId: id }),
|
||||
|
||||
setWalletPollingExpiryAt: (timestamp) =>
|
||||
set({ walletPollingExpiryAt: timestamp }),
|
||||
|
||||
switchToPollingMode: () =>
|
||||
set({
|
||||
mode: "polling",
|
||||
isWebSocketConnected: false,
|
||||
lastDisconnectedAt: Date.now(),
|
||||
}),
|
||||
|
||||
switchToWebSocketMode: () => {
|
||||
const { drawPollingIntervalId, walletPollingIntervalId } = get();
|
||||
// 清除轮询计时器
|
||||
if (drawPollingIntervalId) {
|
||||
window.clearInterval(drawPollingIntervalId);
|
||||
}
|
||||
if (walletPollingIntervalId) {
|
||||
window.clearInterval(walletPollingIntervalId);
|
||||
}
|
||||
set({
|
||||
mode: "websocket",
|
||||
isWebSocketConnected: true,
|
||||
lastConnectedAt: Date.now(),
|
||||
drawPollingIntervalId: null,
|
||||
walletPollingIntervalId: null,
|
||||
walletPollingExpiryAt: null,
|
||||
reconnectAttempts: 0,
|
||||
isReconnecting: false,
|
||||
});
|
||||
},
|
||||
|
||||
clearAllPollingIntervals: () => {
|
||||
const { drawPollingIntervalId, walletPollingIntervalId } = get();
|
||||
if (drawPollingIntervalId) {
|
||||
window.clearInterval(drawPollingIntervalId);
|
||||
}
|
||||
if (walletPollingIntervalId) {
|
||||
window.clearInterval(walletPollingIntervalId);
|
||||
}
|
||||
set({
|
||||
drawPollingIntervalId: null,
|
||||
walletPollingIntervalId: null,
|
||||
walletPollingExpiryAt: null,
|
||||
});
|
||||
},
|
||||
|
||||
clearWalletPolling: () => {
|
||||
const { walletPollingIntervalId } = get();
|
||||
if (walletPollingIntervalId) {
|
||||
window.clearInterval(walletPollingIntervalId);
|
||||
}
|
||||
set({
|
||||
walletPollingIntervalId: null,
|
||||
walletPollingExpiryAt: null,
|
||||
});
|
||||
},
|
||||
|
||||
clearDrawPolling: () => {
|
||||
const { drawPollingIntervalId } = get();
|
||||
if (drawPollingIntervalId) {
|
||||
window.clearInterval(drawPollingIntervalId);
|
||||
}
|
||||
set({ drawPollingIntervalId: null });
|
||||
},
|
||||
}),
|
||||
);
|
||||
Reference in New Issue
Block a user