47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
"use client";
|
||
|
||
import type { ReactNode } from "react";
|
||
|
||
import { NetworkStatusBanner } from "@/components/network-status-banner";
|
||
import { PlayerBottomNav } from "@/components/layout/player-bottom-nav";
|
||
import { PullToRefreshIndicator } from "@/components/pull-to-refresh-indicator";
|
||
import { usePullToRefresh } from "@/hooks/use-pull-to-refresh";
|
||
|
||
type PlayerAppShellProps = {
|
||
children: ReactNode;
|
||
};
|
||
|
||
/**
|
||
* 玩家端外壳:顶栏(品牌 + 会话)+ 主体 + **底部 Tab 导航**(H5)。
|
||
* 底部栏留白:{@link PlayerBottomNav} 作为 sticky 元素放在底部,main 只需要基础 padding。
|
||
*
|
||
* 注意:全局离线横幅和服务器错误覆盖层已在 ErrorProvider 中处理
|
||
* 这里的 NetworkStatusBanner 仅用于 WebSocket 状态显示
|
||
*/
|
||
export function PlayerAppShell({ children }: PlayerAppShellProps): ReactNode {
|
||
const { pullDistance, isRefreshing } = usePullToRefresh({
|
||
onRefresh: () => {
|
||
window.dispatchEvent(new CustomEvent("lottery-hall-refresh"));
|
||
window.dispatchEvent(new CustomEvent("lottery-wallet-refresh"));
|
||
},
|
||
threshold: 70,
|
||
});
|
||
|
||
return (
|
||
<div className="flex h-full flex-col bg-white text-foreground overflow-hidden">
|
||
<PullToRefreshIndicator
|
||
pullDistance={pullDistance}
|
||
isRefreshing={isRefreshing}
|
||
threshold={70}
|
||
/>
|
||
<NetworkStatusBanner />
|
||
<main id="player-scroll-container" className="flex w-full flex-1 flex-col pb-1 overflow-y-auto overscroll-y-contain">
|
||
{children}
|
||
</main>
|
||
<div className="shrink-0 w-full relative z-50">
|
||
<PlayerBottomNav />
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|