year 1 日 前
コミット
f7c897b420

BIN
public/img/update_coin.webp


BIN
public/logo6.webp


+ 5 - 0
public/scripts/pwa.js

@@ -0,0 +1,5 @@
+window.addEventListener("beforeinstallprompt", (evt) => {
+    evt.preventDefault();
+    window.PwaEvent = evt;
+    sessionStorage.setItem("can_installPwa", "true");
+});

BIN
public_original/img/update_coin.png


BIN
public_original/logo6.png


+ 5 - 0
public_original/scripts/pwa.js

@@ -0,0 +1,5 @@
+window.addEventListener("beforeinstallprompt", (evt) => {
+    evt.preventDefault();
+    window.PwaEvent = evt;
+    sessionStorage.setItem("can_installPwa", "true");
+});

+ 104 - 4
src/components/Card/Card.tsx

@@ -2,15 +2,22 @@
 import { Category, GameListRep, GameRequest, toggleFavorite } from "@/api/home";
 import { userInfoApi } from "@/api/login";
 import CustomImage from "@/components/Image";
+import { WrapHocPosition } from "@/enums";
+import feedback from "@/feedback";
 import useGame from "@/hooks/useGame";
 import { useRouter } from "@/i18n/routing";
+import { useSystemStore } from "@/stores/useSystemStore";
 import { useWalletStore } from "@/stores/useWalletStore";
+import { isPWAorAPK } from "@/utils";
 import { getToken } from "@/utils/Cookies";
+import { isIOS } from "@/utils/methods";
 import { Button, Popup, Toast } from "antd-mobile";
 import clsx from "clsx";
 import { useTranslations } from "next-intl";
 import { FC, PropsWithChildren, ReactNode, useEffect, useRef, useState } from "react";
 import { shallow } from "zustand/shallow";
+import CustomButton from "../CustomButton";
+import InstallAPK from "../InstallApk";
 import TipsModal, { ModalProps } from "../TipsModal";
 import styles from "./style.module.scss";
 
@@ -25,7 +32,7 @@ export interface CardProps {
 
 const Card: FC<PropsWithChildren<CardProps>> = (props) => {
     const { render, item, groupType, className } = props;
-
+    const systemStore = useSystemStore();
     const { getGameUrl, getCoinType } = useGame();
     const t = useTranslations("Game");
     const brandRef = useRef<GameListRep | null>(null);
@@ -36,6 +43,7 @@ const Card: FC<PropsWithChildren<CardProps>> = (props) => {
             point: state.wallet?.point,
             free_score: state.wallet?.free_score,
             lose_score: state.wallet?.lose_score,
+            deposit: state.wallet?.deposit,
         };
     }, shallow);
     // 判断是否有未结算的对局
@@ -48,9 +56,102 @@ const Card: FC<PropsWithChildren<CardProps>> = (props) => {
     const router = useRouter();
     const token = getToken();
     // 点击检测弹窗
-    const handler = (game: GameListRep) => {
+    const handler = async (game: GameListRep) => {
+        if (!token) {
+            router.push("/login");
+            return;
+        }
+        const res: any = await systemStore.setupConfig();
+        if (!res?.recharge_enable) {
+            if (wallet.deposit <= 0) {
+                const feedRes = await feedback.showModal({
+                    content: (modalProps: any) => (
+                        <>
+                            <div className="relative h-[.5rem] rounded-[.1rem_.1rem_0_0] bg-[var(--primary3)]">
+                                <div className="absolute bottom-0 left-[50%] flex h-[.5rem] w-[.5rem] translate-x-[-50%] translate-y-[50%] items-center justify-center rounded-[50%] bg-[var(--primary3)]">
+                                    <i className="iconfont icon-Group5 text-[.24rem] text-[#fff]"></i>
+                                </div>
+                            </div>
+                            <div className="flex flex-col items-center px-[.15rem] pt-[.4rem] text-center text-[.15rem] font-bold">
+                                <div className="front-black mb-[.3rem] text-[.16rem]">
+                                    Recarregue e desbloqueie!
+                                </div>
+                                <div className="text-[.12rem] font-[400] text-[var(--adm-color-weak)]">
+                                    Sua recompensa exclusiva será ativada automaticamente após o
+                                    depósito. Jogue agora e aproveite prêmios incríveis!
+                                </div>
+                            </div>
+                            <div className="p-[.15rem]">
+                                <CustomButton
+                                    onClick={() => {
+                                        router.push("/deposit");
+                                        modalProps?.doClose("confirm");
+                                    }}
+                                    className="!w-full"
+                                >
+                                    Depósito
+                                </CustomButton>
+                            </div>
+                        </>
+                    ),
+                    useDefaultFooter: false,
+                    containerClassName: "p-0",
+                    width: "70%",
+                });
+                // dialogManage.showDialog("AlertDialog", {
+                //     content: (
+                //         <div className="flex flex-col items-center px-[.15rem] pt-[.5rem] text-center text-[.15rem] font-bold">
+                //             <img src="/home/gold.webp" className="mb-[.15rem] w-[1rem]" alt="" />
+                //             <div className="mb-[.1rem]">
+                //                 A recompensa para novos usuários será ativada após o depósito
+                //             </div>
+                //             <div className="text-[#11de68]">podendo jogar em seguida.</div>
+                //         </div>
+                //     ),
+                //     footerText: "Depósito",
+                //     onConfirm: () => {
+                //         dialogManage.hideDialog("AlertDialog");
+                //         router.push("/deposit");
+                //     },
+                // });
+                console.log(feedRes);
+                return;
+            }
+        }
+        // if (!wallet?.score || wallet?.score <= 0) {
+        //     router.push("/deposit");
+        //     return;
+        // }
+
+        if (!res?.browser_enable) {
+            if (!isPWAorAPK()) {
+                if (isIOS()) {
+                    await feedback.showImage({
+                        imgUrl: "/store/ios.webp",
+                    });
+                    return;
+                }
+
+                await feedback.showModal({
+                    content: () => {
+                        return <InstallAPK></InstallAPK>;
+                    },
+                    position: WrapHocPosition.bottom,
+                    showClose: false,
+                    containerClassName: styles.installApk,
+                    useDefaultFooter: false,
+                    maskClose: true,
+                });
+
+                // dialogManage.showDialog("InstallAPK");
+                return;
+            }
+        }
+
         setVisible(true);
         brandRef.current = game;
+        // setVisible(true);
+        // brandRef.current = game;
     };
     useEffect(() => {
         element.current = document.getElementById("app");
@@ -189,8 +290,7 @@ const Card: FC<PropsWithChildren<CardProps>> = (props) => {
                         height={"100%"}
                         className={"h-[100%] w-[100%]"}
                     ></CustomImage>
-                    {/* && item?.online_user */}
-                    {props.isShowOnline && (
+                    {props.isShowOnline && item?.online_user && (
                         <div className={styles.cardOnline}>
                             <i className="iconfont icon-fangke2 text-[.08rem] text-[#11de68]"></i>
                             <span className="relative top-[1px] ml-[.04rem] text-[.1rem]">

+ 3 - 0
src/components/Card/style.module.scss

@@ -42,6 +42,9 @@
     //     top: -0.02rem;
     // }
 }
+.installApk {
+    border-radius: 0.1rem 0.1rem 0 0 !important;
+}
 .favorite {
     position: absolute;
     right: 0.1rem;

+ 0 - 0
src/components/InstallApk/index.module.scss


+ 199 - 0
src/components/InstallApk/index.tsx

@@ -0,0 +1,199 @@
+"use client";
+
+import { ModalProps } from "@/components/TipsModal";
+import useDesktop from "@/hooks/useDesktop";
+import { useSystemStore } from "@/stores/useSystemStore";
+import { isPWAorAPK } from "@/utils";
+import { server } from "@/utils/client";
+import { isAndroid } from "@/utils/methods";
+import React from "react";
+import CustomButton from "../CustomButton";
+interface AppInfoTypes {
+    /**
+     * 创建时间
+     */
+    created_at: number;
+    /**
+     * ID 编号
+     */
+    id: number;
+    /**
+     * 名称
+     */
+    name: string;
+    /**
+     * 文件大小
+     */
+    size: number;
+    /**
+     * 更新时间
+     */
+    updated_at: number;
+    /**
+     * 下载地址
+     */
+    url: string;
+    /**
+     * 版本号
+     */
+    version: string;
+}
+
+const getAppInfoApi = async () => {
+    return server.request<AppInfoTypes>({
+        url: "/v1/api/front/app/info",
+        method: "POST",
+    });
+};
+
+const InstallAPK = ({ ...props }) => {
+    const { downloadHandler } = useDesktop("components");
+    const keyName = "InstallAPK";
+    const [data, setData] = React.useState<AppInfoTypes | null>(null);
+    const [errorType, setErrorType] = React.useState<number>(0);
+    const { service } = useSystemStore((state) => ({
+        isCollapse: state.isCollapse,
+        setCollapse: state.setCollapse,
+        service: state.service,
+        show_free_game: state.show_free_game,
+        show_again_game: state.show_again_game,
+    }));
+    const dialogRef = React.useRef<ModalProps>(null);
+    React.useEffect(() => {
+        let checkInterval: any = null;
+        if (isAndroid()) {
+            getApkInfo();
+        }
+
+        if (!("serviceWorker" in navigator)) {
+            setErrorType(1);
+            return;
+        }
+        checkInterval = setInterval(() => {
+            if (isPWAorAPK()) {
+                props.doClose();
+                clearInterval(checkInterval);
+            }
+        }, 1000);
+        return () => {
+            clearInterval(checkInterval);
+        };
+    }, []);
+    const getApkInfo = async () => {
+        const res = await getAppInfoApi();
+        if (res?.code === 200) {
+            setData(res.data);
+        }
+    };
+
+    const serviceUrl: string = React.useMemo(() => {
+        if (!service) return "";
+        const result = service.filter((item) => item.status === 1);
+        return result[0]?.url || "";
+    }, [service]);
+
+    const doInstallPWA = async () => {
+        if (!("serviceWorker" in navigator)) {
+            setErrorType(1);
+            return;
+        }
+
+        const canInstallPwa = sessionStorage.getItem("can_installPwa");
+        if (canInstallPwa) {
+            downloadHandler();
+            return;
+        }
+        setErrorType(2);
+    };
+
+    return (
+        <div className="px-[.05rem] py-[.1rem]">
+            <div className="flex items-center">
+                <img src="/logo6.webp" alt="" className="h-[68px] w-[68px]" />
+                <div className="ml-[.1rem] flex-1 leading-[1]">
+                    <div className="text-[20px] font-black">8g.game</div>
+                    <div className="mt-[.06rem] text-[12px] text-[var(--textColor2)]">
+                        Mais bônus e benefícios distribuídos. Interface do sistema e velocidade de
+                        acesso otimizadas.
+                    </div>
+                </div>
+            </div>
+            <div className="mt-[.3rem] flex w-full items-center justify-center text-[20px] font-black">
+                Descobrir uma nova versão
+            </div>
+            <div className="mt-[3%] flex w-full flex-col items-center justify-center gap-[6px]">
+                <div className="flex w-full flex-row items-center justify-center gap-[5px] break-all text-[14px] text-[var(--textColor2)]">
+                    A sua conta 78911411415 recarga foi recebida. Atualize para o aplicativo e
+                    aproveite o jogo
+                </div>
+                <div className="forceinstall-hdialog-content-reward flex w-full flex-row items-center justify-center gap-[5px] text-[14px]">
+                    <img
+                        src="/img/update_coin.webp"
+                        alt=""
+                        loading="eager"
+                        decoding="async"
+                        className="logo h-[14px]"
+                    />
+                    <span>Mais jogos adicionados.</span>
+                </div>
+                <div className="forceinstall-hdialog-content-reward flex w-full flex-row items-center justify-center gap-[5px] text-[14px]">
+                    <img
+                        data-v-2f3b467d=""
+                        src="/img/update_coin.webp"
+                        alt=""
+                        loading="eager"
+                        decoding="async"
+                        className="logo h-[16px]"
+                    />
+                    <span>Mais bônus e benefícios distribuídos.</span>
+                </div>
+                <div className="forceinstall-hdialog-content-reward flex w-full flex-row items-center justify-center gap-[5px] text-[14px]">
+                    <img
+                        data-v-2f3b467d=""
+                        src="/img/update_coin.webp"
+                        alt=""
+                        loading="eager"
+                        decoding="async"
+                        className="logo h-[16px]"
+                    />
+                    <span>Interface do sistema e velocidade de</span>
+                </div>
+                <div className="forceinstall-hdialog-content-reward flex w-full flex-row items-center justify-center gap-[5px] text-[14px]">
+                    <span>acesso otimizadas.</span>
+                </div>
+            </div>
+            <div className="flex justify-center px-[.3rem] pb-[.1rem] pt-[.15rem]">
+                {isAndroid() && (
+                    <a
+                        href={data?.url}
+                        className="!block w-full"
+                        download={data?.name}
+                        target="_blank"
+                    >
+                        <CustomButton className="!w-full !rounded-[.3rem] !bg-gradient-to-r !from-[#fe7919] !to-[#ab3fe7] !text-[var(--textColor1)]">
+                            Actualizar Agora
+                        </CustomButton>
+                    </a>
+                )}
+                {!isAndroid() && (
+                    <CustomButton
+                        onClick={doInstallPWA}
+                        className="!w-full !rounded-[.3rem] !bg-gradient-to-r !from-[#fe7919] !to-[#ab3fe7] !text-[var(--textColor1)]"
+                    >
+                        Actualizar Agora
+                    </CustomButton>
+                )}
+            </div>
+            {errorType !== 0 && (
+                <div className="pb-[.1rem] text-center text-[.1rem] leading-[1] text-[red]">
+                    {errorType === 2 &&
+                        "Se você já tiver o aplicativo instalado, jogue no aplicativo. Se não estiver instalado, tente atualizar a interface ou fechar a guia para voltar a instalar."}
+                    {errorType === 1 &&
+                        "O navegador atual não suporta a instalação de PWA, por favor, use a nova versão do navegador Chrome"}
+                </div>
+            )}
+        </div>
+    );
+};
+
+export default InstallAPK;

+ 8 - 0
src/enums/index.tsx

@@ -1,3 +1,11 @@
+export enum WrapHocPosition {
+    top = "top",
+    bottom = "bottom",
+    center = "center",
+    left = "left",
+    right = "right",
+}
+
 export enum BtnTypeEnum {
     "DISABLED",
     "DEPOSITE",

+ 2 - 9
src/feedback/WrapHoc/index.tsx

@@ -1,15 +1,8 @@
+import { WrapHocPosition } from "@/enums";
 import clsx from "clsx";
 import React from "react";
 import styles from "./index.module.scss";
 
-export enum WrapHocPosition {
-    top = "top",
-    bottom = "bottom",
-    center = "center",
-    left = "left",
-    right = "right",
-}
-
 interface Props {
     children: React.ReactNode;
     position?: WrapHocPosition;
@@ -55,6 +48,7 @@ const WrapHoc: React.FC<Props> = ({
 
     const newProps = {
         ...props,
+        position,
         close,
         show,
     };
@@ -67,7 +61,6 @@ const WrapHoc: React.FC<Props> = ({
                 <div
                     className={clsx(styles.container, styles[position])}
                     onClick={() => {
-                        console.log(123123, maskClose);
                         if (maskClose) {
                             doClose("cancel");
                         }

+ 5 - 1
src/feedback/dialog/component.module.scss

@@ -3,9 +3,13 @@
     flex-direction: column;
     align-items: center;
     position: relative;
-    transform: translateY(-50px) scale(1);
+    transform: translateY(-50px) scale(0.98);
     transition: all 0.2s ease-in-out;
     opacity: 0.2;
+    &.bottom {
+        transform: translateY(100%) scale(1);
+    }
+
     &.showed {
         transform: translateY(0) scale(1);
         opacity: 1;

+ 4 - 0
src/feedback/dialog/component.tsx

@@ -1,5 +1,6 @@
 import CustomButton from "@/components/CustomButton";
 import SvgIcon from "@/components/SvgIcon";
+import { WrapHocPosition } from "@/enums";
 import clsx from "clsx";
 import React from "react";
 import styles from "./component.module.scss";
@@ -15,6 +16,7 @@ interface Props {
     useDefaultFooter?: boolean;
     confirmText?: string;
     showClose?: boolean;
+    position?: WrapHocPosition;
     [key: string]: any;
 }
 
@@ -27,6 +29,7 @@ const Components: React.FC<Props> = ({
     containerClassName,
     useDefaultFooter = true,
     showClose = true,
+    position = WrapHocPosition.center,
     callback,
     ...props
 }) => {
@@ -49,6 +52,7 @@ const Components: React.FC<Props> = ({
         <div
             className={clsx(styles.modalBox, className, {
                 [styles.showed]: animateType === 1,
+                [styles.bottom]: position === WrapHocPosition.bottom,
             })}
             style={{
                 width: typeof props.width === "number" ? `${props.width}px` : props.width,

+ 4 - 2
src/feedback/dialog/index.tsx

@@ -1,3 +1,4 @@
+import { WrapHocPosition } from "@/enums";
 import feedbackStatus from "@/feedback/status";
 import RootSiblings from "../sibling";
 import WrapHoc from "../WrapHoc";
@@ -5,7 +6,6 @@ import ModalComponent from "./component";
 
 const moduleName = "Modal";
 const siblingKey = "Component";
-
 export interface ModalState {
     title?: string;
     content?: ((p?: any) => React.ReactNode) | React.ReactNode;
@@ -27,6 +27,7 @@ export interface ModalState {
     containerClassName?: string;
     showClose?: boolean;
     maskClose?: boolean;
+    position?: WrapHocPosition;
     [key: string]: any;
 }
 
@@ -49,10 +50,11 @@ export const showModal = (opts: ModalState = {}): Promise<ModalBoxResType> => {
             cancelBgColor: "#fff",
             cancelColor: "#999",
             confirmColor: "#fff",
-            width: 500,
+            width: "100%",
             showCancel: true,
             showClose: true,
             maskClose: false,
+            position: WrapHocPosition.center,
         };
         const useProps = {
             ...defaultProps,

+ 7 - 4
src/hooks/useDesktop.tsx

@@ -1,7 +1,6 @@
 /**
  * @description 自定义pwa 安装hooks
  */
-import { useRouter } from "@/i18n/routing";
 import { isIOS } from "@/utils/methods";
 import { Dialog } from "antd-mobile";
 import { useEffect, useRef, useState } from "react";
@@ -9,7 +8,7 @@ import { useEffect, useRef, useState } from "react";
 const useDesktop = (source: "page" | "components") => {
     const prompt = useRef<Event | null>(null);
 
-    const router = useRouter();
+    // const router = useRouter();
     const [isHasDesktop, setHasDesktop] = useState(false);
 
     const downloadHandler = () => {
@@ -41,7 +40,7 @@ const useDesktop = (source: "page" | "components") => {
         // 有pwa 则不会触发
         prompt.current = e;
         //sessionStorage.getItem("pwa_install") === null
-        if (source === "page") {
+        if (source === "page" && !window?.isShowInstallAPK) {
             setHasDesktop(true);
         }
     };
@@ -53,14 +52,18 @@ const useDesktop = (source: "page" | "components") => {
     };
 
     useEffect(() => {
+        if (window?.PwaEvent) {
+            prompt.current = window.PwaEvent;
+        }
         window.addEventListener("beforeinstallprompt", (evt) => {
             evt.preventDefault();
             initDesktop(evt);
+            sessionStorage.setItem("can_installPwa", "true");
         });
         // @ts-ignore
         window.onappinstalled = function (ev) {
             // 安装完成
-            router.replace("/");
+            window.nextRouter.replace("/");
         };
         return () => window.removeEventListener("beforeinstallprompt", initDesktop);
     }, []);

+ 5 - 3
src/stores/useSystemStore.ts

@@ -47,11 +47,13 @@ export const useSystemStore = create<State & Action>()((set, get) => {
         setService(value) {
             set((state) => ({ ...state, service: value }));
         },
-        setupConfig: () => {
+        setupConfig: async () => {
             try {
-                getConfigApi().then((config) => {
+                const config = await getConfigApi();
+                if (config) {
                     set((state) => ({ ...state, ...config }));
-                });
+                }
+                return config;
             } catch (e) {}
         },
         reset: () => set(initialState),

+ 2 - 0
src/types/global.d.ts

@@ -12,6 +12,8 @@ declare global {
         var isAndroidAPK: boolean;
         var pomelo: any;
         var nextRouter: Router;
+        var isShowInstallAPK: boolean;
+        var PwaEvent: any;
     }
 
     namespace window {}

+ 12 - 0
src/utils/index.ts

@@ -170,3 +170,15 @@ export const cryptoStr = (str: string | string) => {
     let masked = toStr.slice(0, 3) + "***" + toStr.slice(-2);
     return masked;
 };
+export const isPWAorAPK = (): boolean => {
+    const displayModes = ["fullscreen", "standalone", "minimal-ui"];
+    const matchesPwa = displayModes.some(
+        (displayMode) => window.matchMedia("(display-mode: " + displayMode + ")").matches
+    );
+    return (
+        matchesPwa ||
+        (window.navigator as any)?.standalone ||
+        document.referrer.includes("android-app://") ||
+        window?.isAndroidAPK
+    );
+};