feat: 集成网络连接管理与降级轮询功能

- 在 PlayerAppShell 中引入 NetworkStatusBanner 组件以显示网络状态
- 在 HallBettingGrid 中实现下注后触发钱包轮询
- 在 HallWalletStrip 中添加网络连接状态管理与定期刷新逻辑
- 在 useHallDrawLive 中集成 WebSocket 连接状态与降级轮询机制,确保在断开时自动切换到轮询模式
This commit is contained in:
2026-05-13 14:44:58 +08:00
parent 377e03e167
commit 1e7a06dc86
9 changed files with 974 additions and 16 deletions

View File

@@ -1,6 +1,7 @@
import Link from "next/link";
import type { ReactNode } from "react";
import { NetworkStatusBanner } from "@/components/network-status-banner";
import { PlayerBottomNav } from "@/components/layout/player-bottom-nav";
import { PlayerSessionBar } from "@/features/player/player-session-bar";
@@ -15,6 +16,7 @@ type PlayerAppShellProps = {
export function PlayerAppShell({ children }: PlayerAppShellProps): ReactNode {
return (
<div className="flex min-h-dvh flex-col bg-background text-foreground">
<NetworkStatusBanner />
<header className="sticky top-0 z-40 shrink-0 border-b border-border bg-background/95 backdrop-blur-sm supports-[backdrop-filter]:bg-background/80">
<div className="mx-auto flex h-12 max-w-lg items-center gap-2 px-4">
<Link

View File

@@ -0,0 +1,96 @@
"use client";
import { WifiOff, RefreshCw, AlertCircle } from "lucide-react";
import { useCallback } from "react";
import { useWebSocketManager } from "@/hooks/use-websocket-manager";
import { cn } from "@/lib/utils";
// 颜色常量(来自用户需求)
const COLORS = {
success: "#52c41a",
error: "#ff4d4f",
warning: "#faad14",
neutral: "#d9d9d9",
};
/**
* 网络状态横幅组件
* 当 WebSocket 断开时显示降级模式提示
*/
export function NetworkStatusBanner(): React.ReactElement | null {
const { showDegradedBanner, mode, reconnect } = useWebSocketManager();
const handleReconnectClick = useCallback(() => {
reconnect();
}, [reconnect]);
// 只有在降级模式或离线模式下才显示
if (!showDegradedBanner) {
return null;
}
const isOffline = mode === "offline";
return (
<div
className={cn(
"sticky top-0 z-50 flex items-center justify-center gap-2 px-4 py-2 text-sm",
"animate-in slide-in-from-top-2 duration-200",
)}
style={{
backgroundColor: isOffline ? COLORS.error : COLORS.warning,
color: "#fff",
}}
role="status"
aria-live="polite"
>
{isOffline ? (
<>
<WifiOff className="size-4 shrink-0" aria-hidden />
<span className="font-medium"></span>
<button
onClick={handleReconnectClick}
className="ml-2 flex items-center gap-1 rounded px-2 py-0.5 text-xs font-medium hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50"
aria-label="尝试重连"
>
<RefreshCw className="size-3" aria-hidden />
</button>
</>
) : (
<>
<AlertCircle className="size-4 shrink-0" aria-hidden />
<span className="font-medium">
...
</span>
<button
onClick={handleReconnectClick}
className="ml-2 flex items-center gap-1 rounded px-2 py-0.5 text-xs font-medium hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50"
aria-label="尝试恢复 WebSocket 连接"
>
<RefreshCw className="size-3" aria-hidden />
</button>
</>
)}
</div>
);
}
/**
* 带网络状态横幅的页面包装器
* 将横幅插入到页面顶部
*/
export function WithNetworkStatusBanner({
children,
}: {
children: React.ReactNode;
}): React.ReactElement {
return (
<>
<NetworkStatusBanner />
{children}
</>
);
}