diff --git a/.env b/.env index b1322fbf..5a3736dc 100644 --- a/.env +++ b/.env @@ -1,3 +1,6 @@ VITE_PROJECT_ID=aa1989b48f2f9f37e9e29811f97d58af VITE_IS_MAINTENANCE = 0 VITE_MAINTENANCE_TIPS = 'The website is currently undergoing maintenance to add support for Phase II Upgrading.' + +VITE_SITE_KEY="6LcfgBYqAAAAAMWveYEvnQQFNg33AU71GBj60Rwl" +VITE_SECRET_KEY="6LcfgBYqAAAAAHCFhPK_oiBKNts8JXKRMh9auxxr" diff --git a/index.html b/index.html index 7000c977..b04603b4 100644 --- a/index.html +++ b/index.html @@ -46,6 +46,10 @@ gtag("js", new Date()); gtag("config", "G-LHZKFW0MBZ"); + + + diff --git a/package.json b/package.json index 1eedc789..ddb2e585 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "react": "^18.2.0", "react-device-detect": "^2.2.3", "react-dom": "^18.2.0", + "react-google-recaptcha": "^3.1.0", "react-hot-toast": "^2.4.1", "react-icons": "^5.0.1", "react-loadable": "^5.5.0", @@ -55,6 +56,7 @@ "@types/qs": "^6.9.12", "@types/react": "^18.2.56", "@types/react-dom": "^18.2.19", + "@types/react-google-recaptcha": "^2.1.9", "@typescript-eslint/eslint-plugin": "^7.0.2", "@typescript-eslint/parser": "^7.0.2", "@vitejs/plugin-react": "^4.2.1", diff --git a/src/components/DashboardS2/DailyRoulette/DailyBox.tsx b/src/components/DashboardS2/DailyRoulette/DailyBox.tsx index b72bc4cd..c423b1d8 100644 --- a/src/components/DashboardS2/DailyRoulette/DailyBox.tsx +++ b/src/components/DashboardS2/DailyRoulette/DailyBox.tsx @@ -1,11 +1,13 @@ import styled from "styled-components"; -import { useCallback, useMemo } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import timezone from "dayjs/plugin/timezone"; import DailyDrawModal from "./DailyDrawModal"; import { useDisclosure } from "@nextui-org/react"; import { dailyOpen } from "@/api"; +import { useReCaptchaStore } from "@/hooks/useReCaptchaStore"; +import GoogleRecaptcha from "@/components/GoogleRecaptcha"; dayjs.extend(utc); dayjs.extend(timezone); export enum BoxType { @@ -24,6 +26,14 @@ interface DailyBoxProps { remain?: number; } +const RecaptchaContainer = styled.div` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 9999; +`; + const DailyBox = (props: DailyBoxProps) => { const modal = useDisclosure(); const { type, weekday, date, amount, index, remain, onDrawed } = props; @@ -52,11 +62,39 @@ const DailyBox = (props: DailyBoxProps) => { return `Exactly in ${hours + (index - 5) * 24} Hours`; } }, [index]); + + const { reCaptchaTs, reCaptchaValue } = useReCaptchaStore(); + + const [isShowRecaptcha, setIsShowRecaptcha] = useState(false); + + useEffect(() => { + console.log("isShowRecaptcha", isShowRecaptcha); + }, [isShowRecaptcha]); + const handleClaim = useCallback(async () => { + console.log("handleClaim"); + if (type === BoxType.Active) { + const ts = Date.now(); + + if ( + reCaptchaTs === 0 || + ts - reCaptchaTs > 1000 * 60 * 60 * 12 || + !reCaptchaValue + ) { + setIsShowRecaptcha(true); + return; + } modal.onOpen(); } - }, [type, modal]); + }, [type, modal, reCaptchaValue, reCaptchaTs]); + + const handleReCaptchaSuccess = () => { + console.log("handleReCaptchaSuccess"); + setIsShowRecaptcha(false); + modal.onOpen(); + }; + return (

{weekday}

@@ -100,6 +138,12 @@ const DailyBox = (props: DailyBoxProps) => { onDrawed={onDrawed} remain={remain} /> + + {isShowRecaptcha && ( + + + + )}
); }; diff --git a/src/components/GoogleRecaptcha.tsx b/src/components/GoogleRecaptcha.tsx new file mode 100644 index 00000000..57d0bfb3 --- /dev/null +++ b/src/components/GoogleRecaptcha.tsx @@ -0,0 +1,50 @@ +import { useReCaptchaStore } from "@/hooks/useReCaptchaStore"; +import { useEffect, useState } from "react"; + +interface GoogleRecaptchaProps { + onSuccess?: () => void; +} + +export default function GoogleRecaptcha({ onSuccess }: GoogleRecaptchaProps) { + const [grecaptchaId, setGrecaptchaId] = useState(null); + const [isRecaptchaLoad, setIsRecaptchaLoad] = useState(true); + + const { setReCaptchaTs, setReCaptchaValue } = useReCaptchaStore(); + + const onChange = (value: string) => { + console.log("Captcha value:", value); + const ts = Date.now(); + setReCaptchaValue(value); + setReCaptchaTs(ts); + onSuccess?.(); + }; + + useEffect(() => { + setTimeout(() => { + const grecaptcha = (window as any)?.grecaptcha?.render("robot", { + sitekey: "6LcfgBYqAAAAAMWveYEvnQQFNg33AU71GBj60Rwl", + theme: "light", + size: "normal", + callback: onChange, + }); + // console.log('grecaptcha', grecaptcha) + setGrecaptchaId(grecaptcha); + if (!grecaptcha && grecaptcha !== 0) { + setIsRecaptchaLoad(false); + } else { + setIsRecaptchaLoad(true); + } + }, 1000); + }, []); + return ( +
+
+ {!isRecaptchaLoad && ( +
+ Due to your internet cannot access Google reCAPTCHA service, we can't + verify if you're human or bot. Please check your internet setting. +
+ )} +
+ ); +} diff --git a/src/hooks/useReCaptchaStore.tsx b/src/hooks/useReCaptchaStore.tsx new file mode 100644 index 00000000..7543f299 --- /dev/null +++ b/src/hooks/useReCaptchaStore.tsx @@ -0,0 +1,27 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +export type ReCaptchaStore = { + reCaptchaValue: string; + reCaptchaTs: number; + setReCaptchaValue: (reCaptchaValue: string) => void; + setReCaptchaTs: (reCaptchaTs: number) => void; +}; + +export const useReCaptchaStore = create()( + persist( + (set, get) => ({ + reCaptchaValue: "", + reCaptchaTs: 0, + setReCaptchaValue: (reCaptchaValue: string) => { + set({ reCaptchaValue }); + }, + setReCaptchaTs: (reCaptchaTs: number) => { + set({ reCaptchaTs }); + }, + }), + { + name: "ReCaptchaStore", + } + ) +); diff --git a/src/pages/DashboardS2/index2.tsx b/src/pages/DashboardS2/index2.tsx index b77c4b44..89f20a08 100644 --- a/src/pages/DashboardS2/index2.tsx +++ b/src/pages/DashboardS2/index2.tsx @@ -28,6 +28,7 @@ import { epochList } from "@/constants/epoch"; import PremiusAd from "@/components/DashboardS2/PremiusAd"; import ZKLClaimAd from "@/components/DashboardS2/ZKLClaimAd"; import MysteryBoxIII from "@/components/Dashboard/MysteryBoxIII"; +import GoogleRecaptcha from "@/components/GoogleRecaptcha"; export type TotalTvlItem = { symbol: string; tokenAddress: string; @@ -601,6 +602,7 @@ export default function Dashboard() {
+ {/* */}
diff --git a/vite.config.ts b/vite.config.ts index 80419bc0..9bc30499 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -47,8 +47,8 @@ export default defineConfig({ port: 3001, proxy: { "/app-api": { - // target: "https://goerli.app.zklink.io", - target: "https://app-api.zklink.io", + target: "https://goerli.app.zklink.io", + // target: "https://app-api.zklink.io", changeOrigin: true, rewrite: (path) => path.replace(/^\/app-api/, ""), }, diff --git a/yarn.lock b/yarn.lock index b8d45d2b..eaa45e41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3480,6 +3480,13 @@ dependencies: "@types/react" "*" +"@types/react-google-recaptcha@^2.1.9": + version "2.1.9" + resolved "https://registry.yarnpkg.com/@types/react-google-recaptcha/-/react-google-recaptcha-2.1.9.tgz#cd1ffe571fe738473b66690a86dad6c9d3648427" + integrity sha512-nT31LrBDuoSZJN4QuwtQSF3O89FVHC4jLhM+NtKEmVF5R1e8OY0Jo4//x2Yapn2aNHguwgX5doAq8Zo+Ehd0ug== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18.2.56": version "18.2.67" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.67.tgz#96b7af0b5e79c756f4bdd981de2ca28472c858e5" @@ -5350,7 +5357,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.3.1: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -6587,6 +6594,14 @@ radix3@^1.1.0: resolved "https://registry.yarnpkg.com/radix3/-/radix3-1.1.1.tgz#60a56876ffec62c88a22396a6a1c4c7efe9eb4b1" integrity sha512-yUUd5VTiFtcMEx0qFUxGAv5gbMc1un4RvEO1JZdP7ZUl/RHygZK6PknIKntmQRZxnMY3ZXD2ISaw1ij8GYW1yg== +react-async-script@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/react-async-script/-/react-async-script-1.2.0.tgz#ab9412a26f0b83f5e2e00de1d2befc9400834b21" + integrity sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q== + dependencies: + hoist-non-react-statics "^3.3.0" + prop-types "^15.5.0" + react-device-detect@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.3.tgz#97a7ae767cdd004e7c3578260f48cf70c036e7ca" @@ -6602,6 +6617,14 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" +react-google-recaptcha@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz#44aaab834495d922b9d93d7d7a7fb2326315b4ab" + integrity sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg== + dependencies: + prop-types "^15.5.0" + react-async-script "^1.2.0" + react-hot-toast@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.1.tgz#df04295eda8a7b12c4f968e54a61c8d36f4c0994"