Layout.jsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. import ScrollToTop from "@components/ScrollToTop";
  2. import Sidebar from "@layout/Sidebar";
  3. import Navbar from "@layout/Navbar";
  4. import BottomNav from "@layout/BottomNav";
  5. import AppContainer from "@layout/AppContainer";
  6. import React, { Suspense, useCallback, useEffect, useRef } from "react";
  7. import LoadingScreen from "@components/LoadingScreen";
  8. import { useLocation, useNavigate } from "react-router-dom";
  9. import { useDispatch, useSelector } from "react-redux";
  10. import { getFreeWithdraw, isFullRoute, isLogined, routesIn, routesPop, updateGameRoute } from "@features/mainSlice";
  11. import { preventDefault } from "@utils/helpers";
  12. import { useThemeProvider } from "@contexts/themeContext";
  13. import { CBrowserRouter, CHashRouter } from "@layout/Routes";
  14. import GameSearchPanel from "@layout/GameSearchPanel";
  15. import WLangSelect from "@widgets/WLangSelect";
  16. import MsgBox from "@components/MsgBox";
  17. // import WDPassPrompt from "@widgets/WWithdraw/PassPrompt/WDPassPrompt";
  18. import WHallWheel from "@widgets/WCashWheel/WHallWheel/WHallWheel";
  19. import LoadingIn from "@components/Loading/LoadingIn";
  20. // import WDPassBox from "@widgets/WWithdraw/PassBox/WDPassBox";
  21. import { ROUTE_PATH, ROUTE_TYPE_FULL, ROUTE_TYPE_IN } from "@constants/routes";
  22. import DropCoins from "@components/DropCoins";
  23. import useEdgeGesture from "@hooks/useEdgeGesture";
  24. import MailNew from "@components/MailNew";
  25. import WBindPhone from "@widgets/WBindPhone";
  26. import WInsBrowserGuide from "@widgets/WInsBrowserGuide";
  27. import WProtect from "@widgets/WProtect";
  28. import LoadingCircular from "@components/Loading/LoadingCircular";
  29. import WFirstPay from "@widgets/WFirstPay";
  30. // import WSaveAccount from "@widgets/WSaveAccount";
  31. // import WDepositInfo from "@widgets/WDeposit/WPayInfo/WDepositInfo";
  32. import WNewUserPrompt from "@widgets/WNewUserPrompt";
  33. import WFirstCashoutTips from "@widgets/WFirstCashoutTips";
  34. import WWithdrawLimit from "@widgets/WWithdrawLimit";
  35. import WWithdrawMission from "@widgets/WWithdrawMission";
  36. import WCheckIn from "@widgets/WCheckIn";
  37. import WMusicSettings from "@widgets/WMusicSettings";
  38. import TopDownload from "@widgets/TopDownload";
  39. import WOrientation from "@widgets/WOrientation";
  40. import { useWithdrawMission } from "@contexts/withdrawMissionContext";
  41. import WExclusiveGift from "@widgets/WExclusiveGift";
  42. // import WComeBackBonus from "@widgets/WComeBackBonus";
  43. // import WComeBackWithdraw from "@widgets/WComeBackWithdraw";
  44. import WRecallGiftTips from "@widgets/WRecallGiftTips";
  45. import WRecommendedGames from "@widgets/WRecommendedGames";
  46. import WCommonTextTip from "@widgets/WCommonTextTip";
  47. import WHolidaysAct from "@widgets/WHolidaysAct";
  48. import WFreeBonusGift from "@widgets/WFreeBonusGift";
  49. import WSecurityScan from "@widgets/WInstallBox/WSecurityScan";
  50. import WQuickInstall from "@widgets/WInstallBox/WQuickInstall";
  51. import WDownloadPrompt from "@widgets/WInstallBox/WDownloadPrompt";
  52. import WPopPwaBonus from "@widgets/WInstallBox/WPopPwaBonus";
  53. import WDailyGift from "@widgets/WDailyGift";
  54. import WAllBalance from "@widgets/WAllBalance";
  55. import WWithdrawHelp from "@widgets/WWithdrawHelp";
  56. import WBalanceRule from "@widgets/WBalanceRule";
  57. import WEditUserInfo from "@widgets/WEditUserInfo";
  58. import WEditNickName from "@widgets/WEditNickName";
  59. import WDetailsPopup from "@widgets/WDetailsPopup";
  60. import WRecallGiftInfo from "@widgets/WRecallGiftInfo";
  61. import WSuperBallPopup from "@widgets/WSuperBall/WSuperBallPopup/WSuperBallPopup";
  62. import WOfficialGuide from "@widgets/WOfficialGuide";
  63. import WVipUpgrade from "@widgets/WVipUpgrade";
  64. import WMobileCode from "@widgets/WMobileCode";
  65. import HomeWithdrawPopup from "@widgets/HomeWithdrawPopup";
  66. import HomeWithdrawChoosePopup from "@widgets/HomeWithdrawChoosePopup";
  67. import WPaymentResult from "@widgets/WPaymentResult";
  68. import WEditBindInfo from "@widgets/Userinfo/WBindInfo/WEditBindInfo";
  69. import WKickoutTips from "@widgets/WKickoutTips";
  70. import { ENABLE_SUPERBALL } from "@constants/app";
  71. import WRedeemCode from "@widgets/WRedeemCode";
  72. import WSecurityCheck from "@widgets/WWithdrawalInfo/Security/WSecurityCheck";
  73. import { selectEmailInfo } from "@features/redemptionCodeSlice";
  74. const Layout = ({ ...props }) => {
  75. const appRef = useRef(null);
  76. const mainRef = useRef(null);
  77. const msgBoxRef = useRef(null);
  78. const { isMobile, isShowWheel, setIsShowWheel, isOpenSub, walletPos } = useThemeProvider();
  79. const { pathname, search, hash } = useLocation();
  80. const isFull = useSelector(isFullRoute(pathname));
  81. const inRoutes = useSelector(routesIn);
  82. const popRoutes = useSelector(routesPop);
  83. const isLogin = useSelector(isLogined);
  84. const navigate = useNavigate();
  85. const { getSidedGesture } = useEdgeGesture();
  86. const isGesture = getSidedGesture();
  87. const mobile = isMobile();
  88. const dispatch = useDispatch();
  89. useEffect(() => {
  90. preventDefault();
  91. }, []);
  92. useEffect(() => {
  93. const root = document.documentElement;
  94. let frameId;
  95. const updateViewportHeight = () => {
  96. cancelAnimationFrame(frameId);
  97. frameId = requestAnimationFrame(() => {
  98. const viewportHeight = window.visualViewport?.height || window.innerHeight;
  99. root.style.setProperty("--app-viewport-height", `${Math.round(viewportHeight)}px`);
  100. });
  101. };
  102. const viewport = window.visualViewport;
  103. updateViewportHeight();
  104. window.addEventListener("resize", updateViewportHeight);
  105. window.addEventListener("orientationchange", updateViewportHeight);
  106. viewport?.addEventListener("resize", updateViewportHeight);
  107. viewport?.addEventListener("scroll", updateViewportHeight);
  108. return () => {
  109. cancelAnimationFrame(frameId);
  110. window.removeEventListener("resize", updateViewportHeight);
  111. window.removeEventListener("orientationchange", updateViewportHeight);
  112. viewport?.removeEventListener("resize", updateViewportHeight);
  113. viewport?.removeEventListener("scroll", updateViewportHeight);
  114. root.style.removeProperty("--app-viewport-height");
  115. };
  116. }, []);
  117. useEffect(() => {
  118. const scrollTop = () => {
  119. mainRef.current && mainRef.current.scrollTo(0, 0);
  120. appRef.current && appRef.current.scrollTo(0, 0);
  121. window.scrollTo(0, 0);
  122. document.documentElement.scrollTop = 0;
  123. document.body.scrollTop = 0;
  124. };
  125. scrollTop();
  126. const raf = requestAnimationFrame(scrollTop);
  127. const timer = setTimeout(scrollTop, 0);
  128. return () => {
  129. cancelAnimationFrame(raf);
  130. clearTimeout(timer);
  131. };
  132. }, [pathname, hash]);
  133. useEffect(() => {
  134. const msgBox = msgBoxRef.current;
  135. global.msgBox = msgBox;
  136. return () => {
  137. if (global.msgBox === msgBox) delete global.msgBox;
  138. };
  139. }, []);
  140. useEffect(() => {
  141. let routeType = ROUTE_TYPE_FULL;
  142. if (!mobile) routeType = ROUTE_TYPE_IN;
  143. dispatch(updateGameRoute(routeType));
  144. }, [dispatch, mobile]);
  145. // 通用提现按钮跳转
  146. const withdrawInfo = useSelector(getFreeWithdraw);
  147. const { setOpen: setMissionOpen } = useWithdrawMission();
  148. const onClickNormalWithdraw = useCallback(() => {
  149. if (withdrawInfo?.state === 1) {
  150. navigate(ROUTE_PATH.FREEWITHDRAW);
  151. } else if (withdrawInfo?.state === 2) {
  152. navigate(ROUTE_PATH.FREEWITHDRAW);
  153. } else if (withdrawInfo?.state === 3) {
  154. setMissionOpen(true);
  155. } else if (withdrawInfo?.state === 4) {
  156. setMissionOpen(true);
  157. }
  158. }, [navigate, setMissionOpen, withdrawInfo?.state]);
  159. // 通用邮箱兑换码按钮跳转
  160. const emailInfo = useSelector(selectEmailInfo);
  161. const onClickRedeemCode = useCallback(() => {
  162. if (emailInfo && typeof emailInfo === 'object' && Object.keys(emailInfo).length === 0) return;
  163. const pendingRechargeCount = emailInfo?.pending_recharge_reward_count;
  164. const vipReward = emailInfo?.vip_reward;
  165. const claimableRewardCount = emailInfo?.claimable_reward_count;
  166. const rawMultiplier = Number(vipReward?.multiplier);
  167. const multiplier = Number.isFinite(rawMultiplier) && rawMultiplier > 0 ? rawMultiplier : 1;
  168. // 兑换按钮
  169. if (claimableRewardCount > 0) {//存在充值解锁未领取奖励
  170. const payload = {
  171. grant_status: "ready",
  172. amount: claimableRewardCount,
  173. base_amount: (claimableRewardCount / multiplier),
  174. vip_multiplier: multiplier
  175. };
  176. global.showRedeemResult?.(payload);
  177. } else if (pendingRechargeCount > 0) {//未充值
  178. const payload = {
  179. grant_status: "pending_recharge",
  180. amount: pendingRechargeCount,
  181. base_amount: (pendingRechargeCount / multiplier),
  182. vip_multiplier: multiplier
  183. };
  184. global.showRedeemResult?.(payload);
  185. } else if (emailInfo?.is_bound === false) {//未绑定邮箱
  186. global.openRedeemFirstBind?.();
  187. } else {
  188. global.openRedeemNormal?.();
  189. }
  190. }, [emailInfo]);
  191. useEffect(() => {
  192. global.onClickNormalWithdraw = onClickNormalWithdraw;
  193. global.onClickRedeemCode = onClickRedeemCode;
  194. return () => {
  195. if (global.onClickNormalWithdraw === onClickNormalWithdraw) delete global.onClickNormalWithdraw;
  196. if (global.onClickRedeemCode === onClickRedeemCode) delete global.onClickRedeemCode;
  197. };
  198. }, [onClickNormalWithdraw, onClickRedeemCode]);
  199. const renderIn = () => {
  200. let isShowSub = false;
  201. if (isMobile() && isOpenSub) isShowSub = true;
  202. if (hash == null || hash === "") isShowSub = false;
  203. return (
  204. <>
  205. <div className={`app ${isShowSub ? 'app_openSub' : ''} ${isGesture ? 'app_gestureSub' : ''}`} ref={appRef}>
  206. <ScrollToTop />
  207. <AppContainer>
  208. <div className="app_container-content d-flex flex-column flex-1">
  209. <Suspense fallback={<LoadingIn />}>
  210. <CBrowserRouter isLogin={isLogin} routes={inRoutes} />
  211. </Suspense>
  212. </div>
  213. </AppContainer>
  214. <GameSearchPanel />
  215. </div>
  216. <div className={`app-top ${isShowSub ? 'app_openSub' : ''} ${isGesture ? 'app_gestureSub' : ''}`}>
  217. <Sidebar />
  218. <Navbar />
  219. {isMobile() && <BottomNav />}
  220. </div>
  221. </>
  222. );
  223. };
  224. const renderInFull = () => {
  225. return (
  226. <div className={`app fluid`} ref={appRef}>
  227. <ScrollToTop />
  228. <div className="app_container_full">
  229. <Suspense fallback={<LoadingScreen />}>
  230. <CBrowserRouter isLogin={isLogin} routes={inRoutes} />
  231. </Suspense>
  232. </div>
  233. </div>
  234. );
  235. };
  236. const renderHash = () => {
  237. return (
  238. <CHashRouter isLogin={isLogin} routes={popRoutes} hlocation={{
  239. pathname: pathname,
  240. search: search,
  241. hash: hash
  242. }} basePath={pathname} />
  243. );
  244. };
  245. const renderPop = () => {
  246. return (
  247. <div className="app-pop">
  248. <WLangSelect />
  249. <MsgBox ref={msgBoxRef} />
  250. {/* <WDPassPrompt />
  251. <WDPassBox open={isShowWDPassBox} onClose={() => {
  252. setIsShowWDPassBox(false);
  253. }} /> */}
  254. <WHallWheel open={isShowWheel} onClose={() => {
  255. setIsShowWheel(false);
  256. }}></WHallWheel>
  257. <WBindPhone />
  258. <WNewUserPrompt />
  259. <WProtect />
  260. <WFirstPay />
  261. {/* <WSaveAccount /> */}
  262. {/* <WDepositInfo /> */}
  263. <WFirstCashoutTips />
  264. <WWithdrawLimit />
  265. <WWithdrawMission />
  266. <WMusicSettings />
  267. <WExclusiveGift />
  268. {/* <WComeBackBonus /> */}
  269. {/* <WComeBackWithdraw /> */}
  270. <WRecallGiftTips />
  271. <WCommonTextTip />
  272. <WDetailsPopup />
  273. {
  274. isLogin && <WCheckIn />
  275. }
  276. <WHolidaysAct />
  277. <WFreeBonusGift />
  278. <WRecommendedGames />
  279. <WSecurityScan />
  280. <WQuickInstall />
  281. <WDownloadPrompt />
  282. <WPopPwaBonus />
  283. <WDailyGift />
  284. <WAllBalance />
  285. <WWithdrawHelp />
  286. <WBalanceRule />
  287. <WEditUserInfo />
  288. <WEditNickName />
  289. <WRecallGiftInfo />
  290. <WOrientation />
  291. {
  292. ENABLE_SUPERBALL && <WSuperBallPopup />
  293. }
  294. <WOfficialGuide />
  295. <WInsBrowserGuide />
  296. <WVipUpgrade />
  297. <WMobileCode />
  298. <HomeWithdrawPopup />
  299. <HomeWithdrawChoosePopup />
  300. <WPaymentResult />
  301. <WEditBindInfo />
  302. <WKickoutTips />
  303. <WRedeemCode />
  304. <WSecurityCheck />
  305. </div>
  306. );
  307. };
  308. const renderEffect = () => {
  309. return (
  310. <>
  311. <DropCoins walletPos={walletPos} />
  312. <MailNew />
  313. <LoadingCircular />
  314. </>
  315. );
  316. };
  317. const topRef = useRef(null);
  318. const renderTop = () => {
  319. return (
  320. <div className="appHeader" ref={topRef}>
  321. </div>
  322. );
  323. };
  324. const mainStyles = {
  325. height: `calc(var(--app-viewport-height) - ${topRef.current ? topRef.current?.clientHeight : 0}px)`
  326. };
  327. const renderFloat = () => {
  328. return (
  329. <div className="appFloat">
  330. <TopDownload />
  331. </div>
  332. );
  333. };
  334. return (
  335. <>
  336. {
  337. renderTop()
  338. }
  339. {
  340. renderFloat()
  341. }
  342. <div className={`appMain ${mobile ? "appMain_mobile" : ""}`} style={mainStyles} ref={mainRef}>
  343. {
  344. !isFull ? renderIn() : renderInFull()
  345. }
  346. {
  347. renderHash()
  348. }
  349. {
  350. renderPop()
  351. }
  352. {
  353. renderEffect()
  354. }
  355. </div>
  356. </>
  357. );
  358. };
  359. export default Layout;