zhejiushizhuce преди 2 години
родител
ревизия
90a7a38074
променени са 35 файла, в които са добавени 1552 реда и са изтрити 58 реда
  1. 1 0
      package.json
  2. 41 11
      src/App.js
  3. 3 0
      src/Global.js
  4. 1 0
      src/api/request.js
  5. BIN
      src/assets/share/Twitter.png
  6. BIN
      src/assets/share/WhatsApp.png
  7. BIN
      src/assets/share/email.png
  8. BIN
      src/assets/share/facebook.png
  9. BIN
      src/assets/share/sms.png
  10. BIN
      src/assets/share/telegram.png
  11. 0 0
      src/assets/share/whatsappsvg.svg
  12. 14 3
      src/components/AutoBanner/index.jsx
  13. 5 1
      src/constants/localkey.js
  14. 5 1
      src/contexts/themeContext.js
  15. 21 1
      src/features/api.js
  16. 24 1
      src/features/mainSlice.js
  17. 76 3
      src/features/turnplateSlice.js
  18. 5 1
      src/layout/Layout.jsx
  19. 3 1
      src/layout/Sidebar/index.jsx
  20. 29 1
      src/locales/languages/en_US.json
  21. 3 1
      src/pages/index.jsx
  22. 6 0
      src/utils/helpers.js
  23. 93 0
      src/widgets/WCashWheel/WCashWheelHeader.jsx
  24. 45 0
      src/widgets/WCashWheel/WHallWheel/WHallWheel.jsx
  25. 28 0
      src/widgets/WCashWheel/WHallWheel/styles.module.scss
  26. 157 0
      src/widgets/WCashWheel/WPopInfo/WPopInfo.jsx
  27. 158 0
      src/widgets/WCashWheel/WPopInfo/styles.module.scss
  28. 92 0
      src/widgets/WCashWheel/WRetainPop/WRetainPop.jsx
  29. 111 0
      src/widgets/WCashWheel/WRetainPop/styles.module.scss
  30. 212 0
      src/widgets/WCashWheel/WShare/WShare.jsx
  31. 116 0
      src/widgets/WCashWheel/WShare/styles.module.scss
  32. 170 26
      src/widgets/WCashWheel/WWheel/WWheel.jsx
  33. 5 0
      src/widgets/WCashWheel/WWheel/styles.module.scss
  34. 41 7
      src/widgets/WCashWheel/index.jsx
  35. 87 0
      src/widgets/WCashWheel/styles.module.scss

+ 1 - 0
package.json

@@ -20,6 +20,7 @@
     "@emotion/cache": "^11.10.5",
     "@emotion/react": "^11.10.6",
     "@emotion/styled": "^11.10.5",
+    "@fingerprintjs/fingerprintjs-pro": "^3.9.5",
     "@loadable/component": "^5.15.3",
     "@mui/base": "^5.0.0-beta.19",
     "@mui/material": "^5.11.4",

+ 41 - 11
src/App.js

@@ -33,7 +33,7 @@ import useAuthRoute from '@hooks/useAuthRoute';
 // utils
 import {StyleSheetManager} from 'styled-components';
 import {ThemeProvider as MuiThemeProvider, createTheme} from '@mui/material/styles';
-import {preventDefault} from '@utils/helpers';
+import {getUrlParam, localStorageSet, preventDefault} from '@utils/helpers';
 import rtlPlugin from 'stylis-plugin-rtl';
 import {CacheProvider} from '@emotion/react';
 import createCache from '@emotion/cache';
@@ -41,7 +41,7 @@ import createCache from '@emotion/cache';
 import {BrowserRouter, HashRouter, Route, Routes, useLocation} from 'react-router-dom';
 import {ToastContainer} from 'react-toastify';
 import Layout from "@layout/Layout";
-import {getFetchStatus, getUserID, getUserInfo} from '@features/mainSlice';
+import {getBFP, getFetchStatus, getUserID, getUserInfo} from '@features/mainSlice';
 import {useDispatch, useSelector} from "react-redux";
 import {REQ_STATUS} from "@api/config";
 import {reqCustomService, reqGetRoutes, reqTurnplateIndex, reqWithdrawBaseInfo} from "@features/api";
@@ -51,13 +51,17 @@ import {connectHall, disconnectHall} from "@features/actions/hallClient";
 import * as toast from "react-toastify";
 import {useBlocker} from "react-router";
 import FontFaceObserver from 'fontfaceobserver';
+import {getTurnplate} from "@features/turnplateSlice";
+import {LOCAL_WHEEL_INVITE_CODE} from "@constants/localkey";
 const App = () => {
     const dispatch = useDispatch();
     const fetchStatus=useSelector(getFetchStatus);
     const userInfo=useSelector(getUserInfo);
     const userID=useSelector(getUserID);
+    const turnplate=useSelector(getTurnplate);
     const appRef = useRef(null);
-    const {theme, direction} = useThemeProvider();
+    const {theme, direction,setIsShowWheel} = useThemeProvider();
+    const {pathname, search, hash} = useLocation();
     const gaKey = process.env.REACT_APP_PUBLIC_GA;
     gaKey && ReactGA.initialize(gaKey);
     // auto RTL support for Material-UI components and styled-components
@@ -73,16 +77,29 @@ const App = () => {
         stylisPlugins: plugins,
     });
     const startReqRoutes=()=>{
-        dispatch(reqGetRoutes());
+        //先请求指纹id
+        dispatch(getBFP()).then((result)=>{
+            dispatch(reqGetRoutes());
+        });
     }
     const loadFont=()=>{
-        let swisscbt=new FontFaceObserver("SWISSC-BT");
-        swisscbt.load().then(() => {
-
-        })
+        // let swisscbt=new FontFaceObserver("SWISSC-BT");
+        // swisscbt.load().then(() => {
+        //
+        // })
+        new FontFaceObserver("SWISSC-BT").load();
         new FontFaceObserver("Roboto").load();
         new FontFaceObserver("Roboto-1").load();
     }
+    //检查邀请码
+    const checkInviteCode=()=>{
+        let name="act";
+        let code=getUrlParam(name);
+        if (code!=null){
+            localStorageSet(LOCAL_WHEEL_INVITE_CODE,code)
+        }
+    }
+    //初始化
     useEffect(() => {
         // scroll to top on route change
         loadFont();
@@ -90,6 +107,7 @@ const App = () => {
         startReqRoutes();
         appRef.current && appRef.current.scrollTo(0, 0);
         preventDefault();
+        checkInviteCode();
     }, []);
     useEffect(()=>{
         if (userID!=0){
@@ -97,7 +115,7 @@ const App = () => {
             //withdraw
             dispatch(reqWithdrawBaseInfo());
             //turnplate
-            dispatch(reqTurnplateIndex());
+            // dispatch(reqTurnplateIndex());
         }
         else
         {
@@ -107,9 +125,21 @@ const App = () => {
         reqPayConfig(dispatch,userInfo);
         //customer
         dispatch(reqCustomService());
-        // var client = new Colyseus.Client('wss://wstest.ouro777.com:10000');
-        // console.log(client.joinOrCreate)
+        dispatch(reqTurnplateIndex());
     },[userID])
+    //转盘
+    useEffect(()=>{
+        if(turnplate){
+            let times=turnplate.times;
+            if (times>0){
+                if (pathname==="/"){
+                    if(hash==null||hash===""){
+                        setIsShowWheel(true)
+                    }
+                }
+            }
+        }
+    },[turnplate])
     const renderLayout=()=>{
         return (
             <>

+ 3 - 0
src/Global.js

@@ -18,3 +18,6 @@ global.authLockPath=null;
 global.device="mobile";
 global.width=100;
 global.height=100;
+global.isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
+//游客id 指纹
+global.BFP=null;

+ 1 - 0
src/api/request.js

@@ -13,6 +13,7 @@ const getSign=()=>{
 }
 const getComParams=()=>{
     let params={};
+    if (global.BFP!=null)params.bfp=global.BFP;
     let sign=getSign();
     if (sign!=null&&sign!="")params.sign=sign;
     params.device=global.device;

BIN
src/assets/share/Twitter.png


BIN
src/assets/share/WhatsApp.png


BIN
src/assets/share/email.png


BIN
src/assets/share/facebook.png


BIN
src/assets/share/sms.png


BIN
src/assets/share/telegram.png


Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
src/assets/share/whatsappsvg.svg


+ 14 - 3
src/components/AutoBanner/index.jsx

@@ -23,13 +23,15 @@ import Spring from "@components/Spring";
 import {StyledSwiper} from "@components/AutoBanner/style";
 import BannerControls from "@components/AutoBanner/BannerControls";
 import BannerPagination from "@components/AutoBanner/BannerPagination";
-import {NavLink} from "react-router-dom";
+import {NavLink, useNavigate} from "react-router-dom";
 import styles from './styles.module.scss';
+import {ROUTE_PATH} from "@constants/routes";
 
 
 const AutoBanner = ({data,perview=3,spaceBetween=15,delay=3000,speed=1000}) => {
     const [swiper, setSwiper] = useState(null);
     const {isMobile} = useThemeProvider();
+    const navigate=useNavigate();
     if (isMobile())perview=1;
     const [activeIndex,setActiveIndex]=useState(0);
     useEffect(() => {
@@ -37,6 +39,13 @@ const AutoBanner = ({data,perview=3,spaceBetween=15,delay=3000,speed=1000}) => {
             swiper.update();
         }
     }, [swiper]);
+    const onClickNav=(item)=>{
+        let link=item.link;
+        if (item.link_game!=null&&item.link_game>0){
+            link=ROUTE_PATH.GAME+"/"+item.link_game;
+        }
+        navigate(link);
+    }
     const renderControls=()=>{
         let controls=(<></>);
         if (!isMobile()){
@@ -79,10 +88,12 @@ const AutoBanner = ({data,perview=3,spaceBetween=15,delay=3000,speed=1000}) => {
                         <SwiperSlide className="d-flex flex-row w-100" style={{gap:`${spaceBetween}px`}} key={index}>
                             {
                                 items.map((item,iIndex)=>(
-                                    <NavLink key={iIndex} className="img-box w-100" to={item.link}>
+                                    <button key={iIndex} className="img-box w-100" onClick={()=>{
+                                        onClickNav(item);
+                                    }}>
                                         <LazyImage style={{borderRadius:"var(--border-radius)"}} className="img-box-content" src={item.img} alt={item.alt}>
                                         </LazyImage>
-                                    </NavLink>
+                                    </button>
                                 ))
                             }
                         </SwiperSlide>

+ 5 - 1
src/constants/localkey.js

@@ -1,7 +1,11 @@
 const BASE="SG_";
+//设备id
+const LOCAL_VISTOR_ID="LOCAL_VISTOR_ID";
 const LOCAL_USER_INFO="USER_INFO";
 const LOCAL_IS_LOGIN="IS_LOGIN";
 const LOCAL_SIGN="G_SIGN";
 const LOCAL_GAME_URL="LOCAL_GAME_URL_";
 const LOCAL_CUSTOM_LOCK="LOCAL_CUSTOM_LOCK";
-export {LOCAL_USER_INFO,LOCAL_IS_LOGIN,LOCAL_SIGN,LOCAL_GAME_URL,LOCAL_CUSTOM_LOCK};
+const LOCAL_WHEEL_RETAIN="LOCAL_WHEEL_RETAIN";
+const LOCAL_WHEEL_INVITE_CODE="LOCAL_WHEEL_INVITE_CODE";
+export {LOCAL_VISTOR_ID,LOCAL_USER_INFO,LOCAL_IS_LOGIN,LOCAL_SIGN,LOCAL_GAME_URL,LOCAL_CUSTOM_LOCK,LOCAL_WHEEL_RETAIN,LOCAL_WHEEL_INVITE_CODE};

+ 5 - 1
src/contexts/themeContext.js

@@ -45,6 +45,8 @@ export const ThemeProvider = ({children}) => {
     //show pass prompt
     const [isShowPassPrompt,setIsShowPassPrompt]=useState(false);
 
+    //show wheel
+    const [isShowWheel,setIsShowWheel]=useState(false);
     const isLogin=useSelector(isLogined);
     const user=useSelector(getUserInfo);
     const initWindowSize=()=>{
@@ -153,7 +155,9 @@ export const ThemeProvider = ({children}) => {
                 isShowPassPrompt,
                 setIsShowPassPrompt,
                 //检查路由是否允许跳转
-                checkRoute
+                checkRoute,
+                isShowWheel,
+                setIsShowWheel
             }}>
             {children}
         </ThemeContext.Provider>

+ 21 - 1
src/features/api.js

@@ -16,7 +16,7 @@ import {
     APP_PAYMENT_ORDER,
     APP_RECHARGE_CONFIG,
     APP_REGISTER,
-    APP_ROUTES, APP_TURNPLATE_INDEX,
+    APP_ROUTES, APP_TURNPLATE_BONUS, APP_TURNPLATE_INDEX, APP_TURNPLATE_KAN,
     APP_WITHDRAW_BASE_INFO,
     APP_WITHDRAW_INFO,
     APP_WITHDRAW_TAX,
@@ -359,4 +359,24 @@ export const reqTurnplateIndex=createAsyncThunk(
         return response;
     }
 )
+export const reqTurnplateKan=createAsyncThunk(
+    APP_TURNPLATE_KAN,
+    async (arg,thunkAPI)=>{
+        let params={
+            url:APP_TURNPLATE_KAN
+        }
+        const response=await Http.get(params);
+        return response;
+    }
+)
+export const reqTurnplateBonus=createAsyncThunk(
+    APP_TURNPLATE_BONUS,
+    async (arg,thunkAPI)=>{
+        let params={
+            url:APP_TURNPLATE_BONUS
+        }
+        const response=await Http.get(params);
+        return response;
+    }
+)
 //api

+ 24 - 1
src/features/mainSlice.js

@@ -19,8 +19,9 @@ import {
 } from "@features/api";
 import {toast} from 'react-toastify';
 import {getHttpMsg, localStorageGet, localStorageSet} from "@utils/helpers";
-import {LOCAL_CUSTOM_LOCK, LOCAL_IS_LOGIN, LOCAL_SIGN, LOCAL_USER_INFO} from "@constants/localkey";
+import {LOCAL_CUSTOM_LOCK, LOCAL_IS_LOGIN, LOCAL_SIGN, LOCAL_USER_INFO, LOCAL_VISTOR_ID} from "@constants/localkey";
 import {reqPayConfig} from "@features/paySlice";
+import FingerprintJS from '@fingerprintjs/fingerprintjs-pro'
 const i18n=global.i18n;
 const generateNavs=(routes)=>{
     const navs=[];
@@ -509,3 +510,25 @@ export const getCustomStatus=(state)=>{
 //数据获取
 export default Main.reducer;
 
+export const getBFP=()=>{
+    return async (dispatch, getState) => {
+        let bfp="";
+        bfp=localStorageGet(LOCAL_VISTOR_ID,"");
+        if (bfp!=null&&bfp!==""){
+            global.BFP=bfp;
+            return bfp;
+        }
+        const fpPromise = FingerprintJS.load({
+            apiKey: "W7zWifGvJhjacPekHi64"
+        })
+        let fp=await fpPromise;
+        let result=await fp.get();
+        if (result!=null){
+            bfp=result.visitorId;
+            localStorageSet(LOCAL_VISTOR_ID,bfp);
+            global.BFP=bfp;
+        }
+        return bfp;
+    }
+}
+

+ 76 - 3
src/features/turnplateSlice.js

@@ -1,5 +1,5 @@
 import {createSlice} from "@reduxjs/toolkit";
-import {buildReqReducer, reqGameList, reqTurnplateIndex} from "@features/api";
+import {buildReqReducer, reqGameList, reqTurnplateBonus, reqTurnplateIndex, reqTurnplateKan} from "@features/api";
 import {REQ_STATUS} from "@api/config";
 const reducerTurnplateIndex=(state,action,arg)=>{
     let payload=action.payload;
@@ -14,6 +14,36 @@ const reducerTurnplateIndex=(state,action,arg)=>{
         }
     }
 }
+const reducerTurnplateKan=(state,action,arg)=>{
+    let payload=action.payload;
+    // payload={
+    //     "data": {
+    //         "type": 2,
+    //         "bonus": 9414,
+    //         "times": 0,
+    //         "collect_amount": 9414
+    //     },
+    //     "result": "",
+    //     "msg": "success",
+    //     "code": 200
+    // }
+    if (payload&&payload.code===200){
+        let callback=arg;
+        if (callback)callback(payload.data);
+    }
+}
+const turnplateKanStatus=(state,status,arg)=>{
+    state.status.TURNPLATE_KAN=status;
+}
+const reducerTurnplateBonus=(state,action,arg)=>{
+    let payload=action.payload;
+    if (payload&&payload.code===200){
+
+    }
+}
+const turnplateBonusStatus=(state,status,arg)=>{
+    state.status.TURNPLATE_BONUS=status;
+}
 export const Turnplate=createSlice({
     name: 'turnplate',
     initialState: {
@@ -21,15 +51,35 @@ export const Turnplate=createSlice({
         record:[],
         link:null,
         numbers:[],
-        myRecord:[]
+        myRecord:[],
+        status:{
+            TURNPLATE_KAN:REQ_STATUS.NONE,
+            TURNPLATE_BONUS:REQ_STATUS.NONE
+        }
     },
     reducers: {
+        updateTimes:((state, action) => {
+            let payload=action.payload;
+            if (payload){
+                let times=payload.times;
+                state.turnplate.times=times;
+            }
+        }),
+        updateCollect:((state, action) => {
+            let payload=action.payload;
+            if (payload){
+                let collect_amount=payload.collect_amount;
+                state.turnplate.collect_amount=collect_amount;
+            }
+        }),
     },
     extraReducers: (builder) => {
         buildReqReducer(builder,reqTurnplateIndex,reducerTurnplateIndex,null);
+        buildReqReducer(builder,reqTurnplateKan,reducerTurnplateKan,turnplateKanStatus);
+        buildReqReducer(builder,reqTurnplateBonus,reducerTurnplateBonus,turnplateBonusStatus);
     },
 })
-export const {} = Turnplate.actions;
+export const {updateTimes,updateCollect} = Turnplate.actions;
 export const getTurnplate=(state)=>{
     return state.turnplate.turnplate;
 }
@@ -39,4 +89,27 @@ export const getRecord=(state)=>{
 export const getMyRecord=(state)=>{
     return state.turnplate.myRecord;
 }
+export const getShareUrl=(state)=>{
+    return state.turnplate.link;
+}
+export const getTotalAmount=(state)=>{
+    if (state.turnplate.turnplate){
+        return state.turnplate.turnplate.total_amount;
+    }
+    else return 0;
+}
+export const getNumbers=(state)=>{
+    if (state.turnplate.numbers){
+        let numbers=state.turnplate.numbers;
+        let temp=numbers.slice(0);
+        let random=[];
+        for (let i = 0; i <20 ; i++) {
+            let rand=Math.floor(Math.random()*temp.length);
+            random.push(temp[rand]);
+            temp.splice(rand,1);
+        }
+        return random;
+    }
+    return [];
+}
 export default Turnplate.reducer;

+ 5 - 1
src/layout/Layout.jsx

@@ -22,12 +22,13 @@ import WLangSelect from "@widgets/WLangSelect";
 import MsgBox from "@components/MsgBox";
 import WDPassPrompt from "@widgets/WWithdraw/PassPrompt/WDPassPrompt";
 import useAuthLockRoute from "@hooks/useAuthLockRoute";
+import WHallWheel from "@widgets/WCashWheel/WHallWheel/WHallWheel";
 const PageNotFound = lazy(() => import('@pages/PageNotFound'));
 
 const Layout=()=>{
     const appRef = useRef(null);
     const msgBoxRef=useRef(null);
-    const {isMobile} = useThemeProvider();
+    const {isMobile,isShowWheel, setIsShowWheel} = useThemeProvider();
     const {pathname, search, hash} = useLocation();
     const isFull = useSelector(isFullRoute(pathname));
     const inRoutes=useSelector(routesIn);
@@ -100,6 +101,9 @@ const Layout=()=>{
                 <WLangSelect/>
                 <MsgBox ref={msgBoxRef}/>
                 <WDPassPrompt/>
+                <WHallWheel open={isShowWheel} onClose={()=>{
+                    setIsShowWheel(false);
+                }}></WHallWheel>
             </>
         )
     }

+ 3 - 1
src/layout/Sidebar/index.jsx

@@ -366,7 +366,9 @@ const Sidebar = () => {
                             </button>
                         </div>
                         <div className="bonus-item li-spin">
-                            <button className="bonus-item-btn">
+                            <button className="bonus-item-btn" onClick={()=>{
+                                navigate(ROUTE_PATH.CASH_WHEEL);
+                            }}>
                                 <div className="spin-anim active">
                                     <img className="img-spinbg" src={spin_1} alt={"spin-bg"}/>
                                     <img className="img-turntable" src={spin_2} alt={"spin-turntable"}/>

+ 29 - 1
src/locales/languages/en_US.json

@@ -302,7 +302,7 @@
     "rule": {
       "title": "Rules",
       "rule_1": "1. You can start withdrawing money when the accumulated amount reaches 100BRL (maximum reward amount is 1,000BRL, there is no limit on times).",
-      "rule_2": "2. Inviting friends to register on 6TT.CC will help you withdraw money quickly.",
+      "rule_2": "2. Inviting friends to register on 24680.com will help you withdraw money quickly.",
       "rule_3": "3. Each user will receive one free spin per day.",
       "rule_4": "4. Each user's activity is valid for 3 days and can participate in the activity multiple times.",
       "rule_5": "5. If you get an extra bonus, you can play for free and win more real money.",
@@ -316,6 +316,34 @@
       "step_1": "You still need {0} to make the withdrawal",
       "step_2": "{0} R$ will be paid into your Pix account",
       "btn_invite": "Invite friends to help with"
+    },
+    "share_box": {
+      "title_1": "1. Invite friends to help with withdrawals",
+      "btn_more": "More",
+      "btn_facebook": "Facebook",
+      "btn_whatsapp": "WhatsApp",
+      "btn_telegram": "Telegram",
+      "btn_twitter": "Twitter",
+      "btn_email": "Email",
+      "share_text": "Receive {0} {1} for free, Pix QUICK WITHDRAWAL",
+      "share_title": "Receive {0} {1} for free",
+      "btn_copy": "Copy",
+      "title_2": "2. Send invitation to random player to help",
+      "send_title": "Send Message",
+      "send_on": "on",
+      "btn_sms": "SMS"
+    },
+    "pop_info": {
+      "title": "Receive R${0} for free",
+      "title_1": "QUICK WITHDRAWAL",
+      "tips": "Congratulations on your good luck, you won",
+      "btn_invite": "Claim more to cash out"
+    },
+    "retain_pop": {
+      "title": "You're so lucky!",
+      "tips": "Invite friends to help with withdrawals",
+      "btn_share": "Share with friends",
+      "btn_giveup": "Give up the withdrawal of {0} BRL"
     }
   }
 }

+ 3 - 1
src/pages/index.jsx

@@ -1,6 +1,7 @@
 import {lazy} from "react";
 import DepositHeader from "@widgets/WDeposit/DepositHeader";
 import WithdrawHeader from "@widgets/WWithdraw/WithdrawHeader";
+import WCashWheelHeader from "@widgets/WCashWheel/WCashWheelHeader";
 const ModulePage=lazy(()=>import('@pages/ModulePage'));
 const Home=lazy(()=>import('@pages/Home'));
 const Login=lazy(()=>import("@pages/Login"));
@@ -35,5 +36,6 @@ export default {
 }
 export const Header={
     deposit: DepositHeader,
-    withdraw:WithdrawHeader
+    withdraw:WithdrawHeader,
+    cashwheel:WCashWheelHeader
 }

+ 6 - 0
src/utils/helpers.js

@@ -231,3 +231,9 @@ export const formatCash=(cash,fixed=2)=>{
     cash=cash/global.CASH_BASE;
     return cash.toFixed(fixed);
 }
+export const getUrlParam=(name)=>{
+    if (window.location==null||window.location.href==null)return null;
+    let url = window.location.href;
+
+    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(url) || [, ""])[1].replace(/\+/g, '%20')) || null;
+}

+ 93 - 0
src/widgets/WCashWheel/WCashWheelHeader.jsx

@@ -0,0 +1,93 @@
+import {useSelector} from "react-redux";
+import {getUserInfo, isLogined} from "@features/mainSlice";
+import styles from "./styles.module.scss";
+import SvgIcon from "@components/SvgIcon";
+import React, {useState} from "react";
+import WRetainPop from "@widgets/WCashWheel/WRetainPop/WRetainPop";
+import WShare from "@widgets/WCashWheel/WShare/WShare";
+import {getTurnplate} from "@features/turnplateSlice";
+import {localStorageGet, localStorageSet} from "@utils/helpers";
+import {LOCAL_WHEEL_RETAIN} from "@constants/localkey";
+const i18n=global.i18n;
+const WCashWheelHeader=({...props})=>{
+    const route=props.route;
+    const close=props.close;
+    const user=useSelector(getUserInfo);
+    const isLogin=useSelector(isLogined);
+    const turnplate=useSelector(getTurnplate);
+    const [openRetain,setOpenRetain]=useState(false);
+    const [openShare,setOpenShare]=useState(false);
+    const onCheckClose=()=>{
+        let isclose=true;
+        if (isLogin){
+            if (turnplate!=null){
+                let collect_amount=turnplate.collect_amount;
+                let total_amount=turnplate.total_amount;
+                if (collect_amount>0&&collect_amount<total_amount){
+                    let lsttime=localStorageGet(LOCAL_WHEEL_RETAIN,"0");
+                    lsttime=Number(lsttime);
+                    let now=Date.now();
+                    let dis=now-lsttime;
+                    if (dis>86400000){
+                        setOpenRetain(true);
+                        isclose=false;
+                    }
+                }
+            }
+        }
+        if (isclose){
+            close();
+        }
+    }
+    const renderHeader=()=>{
+        let title=route?route.title:"";
+        return (
+            <div className={styles.header}>
+                <div className={styles.hcontent}>
+                    {i18n.t(title)}
+                </div>
+                <div className={styles.top}>
+                    <div className={styles.left}>
+                        <button className={styles.close} onClick={()=>{
+                            // close();
+                            onCheckClose();
+                        }}>
+                            <SvgIcon className={styles.arrow} herf={"icon_Arrow"}/>
+                        </button>
+                    </div>
+                    <div className={styles.right}>
+
+                    </div>
+                </div>
+            </div>
+        )
+    }
+    const renderShare=()=>{
+        return (
+            <WShare open={openShare} onClose={()=>{
+                setOpenShare(false);
+            }}></WShare>
+        )
+    }
+    const renderRetainPop=()=>{
+        return (
+            <WRetainPop open={openRetain} onClose={()=>{
+                setOpenRetain(false);
+            }} openShare={openShareBox} closeP={()=>{
+                localStorageSet(LOCAL_WHEEL_RETAIN,Date.now())
+                close();
+            }}></WRetainPop>
+        )
+    }
+    const openShareBox=()=>{
+        setOpenShare(true)
+    }
+    return (
+        <div id={props.id} className={props.className}>
+            {renderHeader()}
+            {renderShare()}
+            {renderRetainPop()}
+        </div>
+    )
+}
+export default WCashWheelHeader;

+ 45 - 0
src/widgets/WCashWheel/WHallWheel/WHallWheel.jsx

@@ -0,0 +1,45 @@
+import Fade from "@mui/material/Fade";
+import {ClickAwayListener} from "@mui/base/ClickAwayListener";
+import Grow from "@mui/material/Grow";
+import WWheel from "@widgets/WCashWheel/WWheel/WWheel";
+import Modal from "@mui/material/Modal";
+import styles from "./styles.module.scss";
+import WShare from "@widgets/WCashWheel/WShare/WShare";
+import React, {useState} from "react";
+
+const WHallWheel=({open,onClose})=>{
+    const [openShare,setOpenShare]=useState(false);
+    const renderShare=()=>{
+        return (
+            <WShare open={openShare} onClose={()=>{
+                setOpenShare(false);
+            }}></WShare>
+        )
+    }
+    const openShareBox=()=>{
+        setOpenShare(true);
+    }
+    return (
+        <>
+            <Modal open={open} onClose={onClose} closeAfterTransition>
+                <Fade in={open} timeout={500}>
+                    <div className={styles.container}>
+                        <ClickAwayListener onClickAway={onClose}>
+                            <Grow in={open} timeout={500}>
+                                <div className={styles.content}>
+                                    <button className={styles.close} onClick={onClose} aria-label="Close popup">
+                                        <i className="icon-xmark"/>
+                                    </button>
+                                    <WWheel showShare={false} openShare={openShareBox} onClose={onClose}></WWheel>
+                                </div>
+                            </Grow>
+                        </ClickAwayListener>
+                    </div>
+                </Fade>
+            </Modal>
+            {renderShare()}
+        </>
+    )
+
+}
+export default WHallWheel;

+ 28 - 0
src/widgets/WCashWheel/WHallWheel/styles.module.scss

@@ -0,0 +1,28 @@
+.container{
+  width: 100%;
+  height: 100%;
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+.content{
+  width: 100%;
+  //height: 100%;
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  & .close {
+    position: absolute;
+    top: 1.5rem;
+    right: 1.5rem;
+    z-index: 10;
+    i{
+      font-size: 1.4rem;
+      color: var(--header);
+    }
+  }
+}

+ 157 - 0
src/widgets/WCashWheel/WPopInfo/WPopInfo.jsx

@@ -0,0 +1,157 @@
+import Popup from "@components/Popup";
+import React from "react";
+import styles from "./styles.module.scss"
+import {useSelector} from "react-redux";
+import {getRecord, getTurnplate} from "@features/turnplateSlice";
+import icon_cash from "@assets/wheel/cash_2.png";
+import icon_coin from "@assets/wheel/pix_2.png";
+import {formatCash, stringFormat} from "@utils/helpers";
+import cash_2 from "@assets/wheel/cash_2.png";
+import CountUp from "react-countup";
+import coin from "@assets/wheel/pix_2.png";
+import {Autoplay} from "swiper";
+import {Swiper, SwiperSlide} from "swiper/react";
+import styled from 'styled-components/macro';
+const i18n=global.i18n;
+const StyledSwiper=styled(Swiper)`
+.swiper-wrapper{
+    transition-timing-function: cubic-bezier(1.00, 1.00, 0.00, 0.00);
+ }
+`
+const WPopInfo=({open,onClose,openShare})=>{
+    let turnplate=useSelector(getTurnplate);
+    if (turnplate==null)turnplate={total_amount:10000,collect_amount:0}
+    const record=useSelector(getRecord);
+    const formatPhone=(phone)=>{
+        let tstr=phone.substring(1,phone.length-2);
+        phone=phone.replace(tstr,'*'.repeat(tstr.length));
+        if (!phone.startsWith("55"))phone="55"+phone;
+        return phone;
+    }
+    const renderMain=()=>{
+        let collect=turnplate.collect_amount;
+        let total=turnplate.total_amount;
+        let left=total-collect;
+        let isEnableCash=collect>0;
+        let progress=((collect/total)*100).toFixed(2);
+        return (
+            <div className={styles.cash_main_con}>
+                <div className={styles.cash_main}>
+                    <div className={styles.have_cash}>
+                        <img className={styles.cash_img} src={cash_2}/>
+                        <CountUp
+                            start={0}
+                            end={Number(formatCash(collect))}
+                            duration={1}
+                            separator=" "
+                            decimals={2}
+                            prefix={global.DOLLAR+" "}
+                            startOnMount
+
+                        >
+                            {({ countUpRef, start }) => {
+                                if (countUpRef.current){
+                                    let current=countUpRef.current;
+                                    if (current.value!==collect){
+                                        start();
+                                        current.value=collect;
+                                    }
+                                }
+                                return (
+                                    <div ref={countUpRef} className={styles.cash_text}/>
+                                )
+                            }}
+                        </CountUp>
+                        {/*<div className={styles.cash_text}>{global.DOLLAR+" "+formatCash(collect)}</div>*/}
+                    </div>
+                    <div className={styles.progress}>
+                        <div>{progress+"%"}</div>
+                        <div className={styles.bar}>
+                            <span className={styles.dot} style={{width:progress+"%"}}>
+                                <div className={styles.custor}></div>
+                            </span>
+                        </div>
+                    </div>
+                    <div className={styles.need_cash}>
+                        <div>{i18n.t("turnplate.need_tip")}</div>
+                        <div className={styles.need_txt}>{formatCash(left)}</div>
+                        <div>{i18n.t("turnplate.need_tip_1")}</div>
+                    </div>
+                    <button className={styles.btn_invite} onClick={()=>{
+                        if (onClose)onClose();
+                        if(openShare)openShare();
+                    }}>
+                        {i18n.t("turnplate.pop_info.btn_invite")}
+                    </button>
+                </div>
+            </div>
+        )
+    }
+    const renderRecord=()=>{
+        let list=record;
+        return (
+            <div className={styles.record_con}>
+                <div className={styles.record}>
+                    {
+                        list==null||list.length===0?(<></>):(
+                            <StyledSwiper
+                                className={styles.swiper_list}
+                                direction={"vertical"}
+                                spaceBetween={0}
+                                slidesPerView={4}
+                                loop={true}
+                                autoplay={{delay: 0, disableOnInteraction: false}}
+                                effect="fade"
+                                fadeEffect={{crossFade: true}}
+                                modules={[Autoplay]}
+                                speed={800}
+                            >
+                                {
+                                    list.map((item,index)=>{
+                                        let phone=formatPhone(item.phone);
+                                        let name=item.name;
+                                        let amount=item.amount;
+                                        return (
+                                            <SwiperSlide key={index}>
+                                                <div className={styles.swiper_item}>
+                                                    <div>{phone}</div>
+                                                    <div className={styles.name}>{name}</div>
+                                                    <div className={styles.coins_con}>
+                                                        {"+"+amount}
+                                                        <div className={styles.unit}>{global.DOLLAR}</div>
+                                                    </div>
+                                                </div>
+                                            </SwiperSlide>
+                                        )
+                                    })
+                                }
+                            </StyledSwiper>
+                        )
+                    }
+                </div>
+            </div>
+        )
+    }
+    return (
+        <Popup open={open} onClose={()=>{
+            if (onClose)onClose();
+        }}>
+            <div className={styles.container}>
+                <div className={styles.title_con}>
+                    <img className={styles.icon} src={icon_cash}/>
+                    {stringFormat(i18n.t("turnplate.pop_info.title"),[formatCash(turnplate.total_amount,0)])}
+                </div>
+                <div className={styles.sub_title_con}>
+                    <img className={styles.icon} src={icon_coin}/>
+                    {i18n.t("turnplate.pop_info.title_1")}
+                </div>
+                <div className={styles.tips}>
+                    {i18n.t("turnplate.pop_info.tips")}
+                </div>
+                {renderMain()}
+                {renderRecord()}
+            </div>
+        </Popup>
+    )
+}
+export default WPopInfo;

+ 158 - 0
src/widgets/WCashWheel/WPopInfo/styles.module.scss

@@ -0,0 +1,158 @@
+.container{
+  display: flex;
+  flex-direction: column;
+  gap: 0.5rem;
+  & .title_con{
+    color: var(--header);
+    font-size: 1rem;
+    display: flex;
+    gap: 0.5rem;
+    & .icon{
+      width: 1.4rem;
+      height: auto;
+    }
+  }
+  & .sub_title_con{
+    color: var(--text);
+    font-size: 1rem;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 0.5rem;
+    & .icon{
+      width: 1.4rem;
+      height: auto;
+    }
+  }
+  & .tips{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #6ddf39;
+  }
+}
+.cash_main_con{
+  width: 100%;
+}
+.cash_main{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  border-radius: 0.8rem;
+  gap: 0.5rem;
+  & .have_cash{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    & .cash_img{
+      width: 4rem;
+      height: auto;
+    }
+    & .cash_text{
+      font-size: 2.2rem;
+      font-family: "Roboto";
+      color: var(--header);
+    }
+  }
+  & .progress{
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: flex-end;
+    width: 100%;
+    font-size: 1rem;
+    font-weight: 600;
+    color: var(--header);
+    gap: 0.25rem;
+    & .bar{
+      width: 100%;
+      height: 0.6rem;
+      background: #6ddf39;
+      border-radius: 1rem;
+      position: relative;
+      & .dot{
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-end;
+        align-items: center;
+        height: 100%;
+        & .custor{
+          background: #fff;
+          border-radius: 50%;
+          width: 100%;
+          max-width: 0.6rem;
+          aspect-ratio:1;
+        }
+      }
+    }
+  }
+  & .need_cash{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 0.5rem;
+    //font-size: 1rem;
+    color: var(--header);
+    & .need_txt{
+      color: #6ddf39;
+      font-family: "SWISSC-BT";
+    }
+  }
+  & .btn_invite{
+    background: rgb(109, 223, 57);
+    color: rgb(255, 255, 255);
+    padding: 0.6rem 2rem;
+    width: 90%;
+    border-radius: var(--border-radius);
+    font-size: 1rem;
+  }
+}
+.record_con{
+  width: 100%;
+}
+.record{
+  display: flex;
+  flex-direction: column;
+  gap: 0.5rem;
+  position: relative;
+  height: 8rem;
+  & .swiper_list{
+    display: flex;
+    flex-direction: column;
+    gap: 1rem;
+    width: 100%;
+    & .swiper_item{
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      color: var(--text);
+      //font-size: 1rem;
+      font-weight: 500;
+      & .name{
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        max-width: 8rem;
+      }
+      & .coins_con{
+        display: flex;
+        align-items: center;
+        color: #6ddf39;
+        gap: 0.5rem;
+        & .unit{
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          background-color: #6ddf39;
+          width: 1.5rem;
+          height: 1.5rem;
+          border-radius: 50%;
+          font-family: "Roboto";
+          color: var(--header);
+        }
+      }
+    }
+  }
+}

+ 92 - 0
src/widgets/WCashWheel/WRetainPop/WRetainPop.jsx

@@ -0,0 +1,92 @@
+import Popup from "@components/Popup";
+import React from "react";
+import styles from "./styles.module.scss"
+import cash_2 from "@assets/wheel/cash_2.png";
+import CountUp from "react-countup";
+import {formatCash, stringFormat} from "@utils/helpers";
+import {useSelector} from "react-redux";
+import {getTurnplate} from "@features/turnplateSlice";
+const i18n=global.i18n;
+const WRetainPop=({open,onClose,openShare,closeP})=>{
+    let turnplate=useSelector(getTurnplate);
+    if (turnplate==null)turnplate={total_amount:10000,collect_amount:0}
+    const renderMain=()=>{
+        let collect=turnplate.collect_amount;
+        let total=turnplate.total_amount;
+        let left=total-collect;
+        let isEnableCash=collect>0;
+        let progress=((collect/total)*100).toFixed(2);
+        return (
+            <div className={styles.cash_main_con}>
+                <div className={styles.cash_main}>
+                    <div className={styles.have_cash}>
+                        <img className={styles.cash_img} src={cash_2}/>
+                        <CountUp
+                            start={!open?Number(formatCash(collect)):0}
+                            end={Number(formatCash(collect))}
+                            duration={1}
+                            separator=" "
+                            decimals={2}
+                            prefix={global.DOLLAR+" "}
+                            startOnMount
+
+                        >
+                            {({ countUpRef, start }) => {
+                                return (
+                                    <div ref={(ref)=>{
+                                        countUpRef.current=ref;
+                                        if (open){
+                                            start();
+                                        }
+                                    }} className={styles.cash_text}/>
+                                )
+                            }}
+                        </CountUp>
+                        {/*<div className={styles.cash_text}>{global.DOLLAR+" "+formatCash(collect)}</div>*/}
+                    </div>
+                    <div className={styles.progress}>
+                        <div>{progress+"%"}</div>
+                        <div className={styles.bar}>
+                            <span className={styles.dot} style={{width:progress+"%"}}>
+                                <div className={styles.custor}></div>
+                            </span>
+                        </div>
+                    </div>
+                    <div className={styles.need_cash}>
+                        <div>{i18n.t("turnplate.need_tip")}</div>
+                        <div className={styles.need_txt}>{formatCash(left)}</div>
+                        <div>{i18n.t("turnplate.need_tip_1")}</div>
+                    </div>
+                </div>
+            </div>
+        )
+    }
+    const renderBtns=()=>{
+        let collect=turnplate.collect_amount;
+        return (
+            <div className={styles.btn_con}>
+                <button className={styles.share_btn} onClick={()=>{
+                    if(onClose)onClose();
+                    if (openShare)openShare()
+                }}>{i18n.t("turnplate.retain_pop.btn_share")}</button>
+                <button onClick={()=>{
+                    if (onClose)onClose();
+                    if(closeP)closeP();
+                }} className={styles.giveup_btn}>{stringFormat(i18n.t("turnplate.retain_pop.btn_giveup"),[formatCash(collect)])}</button>
+            </div>
+        )
+    }
+    return (
+        <Popup open={open} onClose={()=>{
+            if (onClose)onClose();
+        }}>
+            <div className={styles.container}>
+                <div className={styles.title}>{i18n.t("turnplate.retain_pop.title")}</div>
+                <div className={styles.tips}>{i18n.t("turnplate.retain_pop.tips")}</div>
+                {renderMain()}
+                {renderBtns()}
+            </div>
+        </Popup>
+    )
+}
+export default WRetainPop;

+ 111 - 0
src/widgets/WCashWheel/WRetainPop/styles.module.scss

@@ -0,0 +1,111 @@
+.container{
+  display: flex;
+  flex-direction: column;
+  gap: 1rem;
+  padding: 1rem;
+  & .title{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #6ddf39;
+    font-size: 1.3rem;
+  }
+  & .tips{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 1rem;
+    color: var(--header);
+  }
+}
+.cash_main_con{
+  width: 100%;
+  margin-top: 1.5rem;
+  margin-bottom: 1.5rem;
+}
+.cash_main{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  border-radius: 0.8rem;
+  gap: 0.5rem;
+  & .have_cash{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    & .cash_img{
+      width: 4rem;
+      height: auto;
+    }
+    & .cash_text{
+      font-size: 2.2rem;
+      font-family: "Roboto";
+      color: var(--header);
+    }
+  }
+  & .progress{
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: flex-end;
+    width: 100%;
+    font-size: 1rem;
+    font-weight: 600;
+    color: var(--header);
+    gap: 0.25rem;
+    & .bar{
+      width: 100%;
+      height: 0.6rem;
+      background: #6ddf39;
+      border-radius: 1rem;
+      position: relative;
+      & .dot{
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-end;
+        align-items: center;
+        height: 100%;
+        & .custor{
+          background: #fff;
+          border-radius: 50%;
+          width: 100%;
+          max-width: 0.6rem;
+          aspect-ratio:1;
+        }
+      }
+    }
+  }
+  & .need_cash{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 0.5rem;
+    //font-size: 1rem;
+    color: var(--header);
+    & .need_txt{
+      color: #6ddf39;
+      font-family: "SWISSC-BT";
+    }
+  }
+}
+.btn_con{
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  gap: 1rem;
+  & .share_btn{
+    width: 100%;
+    padding: 0.6rem;
+    font-size: 1.1rem;
+    color: #fff;
+    background: rgb(109, 223, 57);
+    border-radius: var(--border-radius);
+  }
+  & .giveup_btn{
+    color:var(--text);
+    width: 100%;
+    //font-size: 1rem;
+  }
+}

+ 212 - 0
src/widgets/WCashWheel/WShare/WShare.jsx

@@ -0,0 +1,212 @@
+import MuiDrawer from '@mui/material/Drawer';
+import styles from "./styles.module.scss";
+import {ReactComponent as ShareIcon} from '@assets/singlesvg/share.svg';
+import icon_fb from "@assets/share/facebook.png";
+import icon_ws from "@assets/share/WhatsApp.png";
+import icon_tg from "@assets/share/telegram.png";
+import icon_twitter from "@assets/share/Twitter.png";
+import icon_email from "@assets/share/email.png";
+import icon_svg_ws from "@assets/share/whatsappsvg.svg";
+import icon_sms from "@assets/share/sms.png";
+import {useSelector} from "react-redux";
+import {getNumbers, getShareUrl, getTotalAmount} from "@features/turnplateSlice";
+import {formatCash, stringFormat} from "@utils/helpers";
+import {CopyToClipboard} from "react-copy-to-clipboard/lib/Component";
+import {toast} from "react-toastify";
+import {useState} from "react";
+const i18n=global.i18n;
+const WShare=({open,onClose})=>{
+    const shareUrl=useSelector(getShareUrl);
+    const amount=useSelector(getTotalAmount);
+    const numbers=useSelector(getNumbers);
+    const shareTitle=stringFormat(i18n.t("turnplate.share_box.share_title"),[global.DOLLAR,formatCash(amount,0)]);
+    const shareText=stringFormat(i18n.t("turnplate.share_box.share_text"),[global.DOLLAR,formatCash(amount,0)]);
+    const [waPhoneIndex,setWaPhoneIndex]=useState(-1);
+    const shareMore=()=>{
+        try {
+            navigator.share ? navigator.share({
+                title: shareTitle,
+                url: shareUrl,
+                text: shareText
+            }) : shareFb()
+        } catch (t) {
+            shareFb()
+        }
+    }
+    const shareFb=()=>{
+        if (global.isIOS){
+            let url="fb://share/?link="+shareUrl;
+            window.open(url);
+        }
+        else
+        {
+            let url="https://www.facebook.com/sharer/sharer.php?u="+shareUrl+"&t=" + encodeURIComponent(shareText);
+            window.open(url);
+        }
+    }
+    const shareWa=()=>{
+        let url="whatsapp://send?text=" + encodeURIComponent(shareText) + encodeURIComponent("\n\n" + shareUrl) + "&via=lopscoop";
+        window.open(url);
+    }
+    const shareTG=()=>{
+        let url="https://t.me/share/url?url=" + encodeURIComponent(shareUrl) + "&text=" + encodeURIComponent(shareText);
+        window.open(url);
+    }
+    const shareTw=()=>{
+        let url="https://twitter.com/intent/tweet?text" + encodeURIComponent(shareText) + "&url=" + encodeURIComponent(shareUrl);
+        window.open(url);
+    }
+    const shareEmail=()=>{
+        let url="mailto:?body=" + encodeURIComponent(shareText + " " + shareUrl);
+        window.open(url);
+    }
+    const shareWaMore=()=>{
+        if (numbers.length>0){
+            let windex=waPhoneIndex+1;
+            if (windex>=numbers.length)windex=0;
+            setWaPhoneIndex(windex);
+            let phone=numbers[windex];
+            let url="whatsapp://send?phone=+" + phone + "&text=" + encodeURIComponent("Responda OK,".concat(shareText, ".")) + encodeURIComponent("\n\n" + shareUrl) + "&via=lopscoop";
+            window.open(url);
+        }
+    }
+    const shareSms=()=>{
+        let text="";
+        let smsCode="";
+        if (global.isIOS){
+            text="&body="+shareText+" "+shareUrl;
+            smsCode="/open?addresses="+numbers.join(", ");
+        }
+        else
+        {
+            text="?body=" + encodeURIComponent(shareText) + " " + encodeURIComponent(shareUrl);
+            smsCode=numbers.join(", ")+ ";";
+        }
+        let url="sms:" + smsCode + text;
+        window.open(url);
+    }
+    const renderIcons=()=>{
+        return (
+            <div className={styles.share_icon_con}>
+                <button className={styles.icon_btn} onClick={shareMore}>
+                    <div className={`${styles.icon} ${styles.more}`}>
+                        <ShareIcon className={styles.s_icon}></ShareIcon>
+                    </div>
+                    <div>{i18n.t("turnplate.share_box.btn_more")}</div>
+                </button>
+                <button className={styles.icon_btn} onClick={shareFb}>
+                    <div className={`${styles.icon}`}>
+                        <img src={icon_fb}/>
+                    </div>
+                    <div>{i18n.t("turnplate.share_box.btn_facebook")}</div>
+                </button>
+                <button className={styles.icon_btn} onClick={shareWa}>
+                    <div className={`${styles.icon}`}>
+                        <img src={icon_ws}/>
+                    </div>
+                    <div>{i18n.t("turnplate.share_box.btn_whatsapp")}</div>
+                </button>
+                <button className={styles.icon_btn} onClick={shareTG}>
+                    <div className={`${styles.icon}`}>
+                        <img src={icon_tg}/>
+                    </div>
+                    <div>{i18n.t("turnplate.share_box.btn_telegram")}</div>
+                </button>
+                <button className={styles.icon_btn} onClick={shareTw}>
+                    <div className={`${styles.icon}`}>
+                        <img src={icon_twitter}/>
+                    </div>
+                    <div>{i18n.t("turnplate.share_box.btn_twitter")}</div>
+                </button>
+                <button className={styles.icon_btn} onClick={shareEmail}>
+                    <div className={`${styles.icon}`}>
+                        <img src={icon_email}/>
+                    </div>
+                    <div>{i18n.t("turnplate.share_box.btn_email")}</div>
+                </button>
+            </div>
+        )
+    }
+    const renderShareUrl=()=>{
+        return (
+            <CopyToClipboard text={shareUrl} onCopy={()=>{
+                toast.success(i18n.t("copy_success"));
+            }}>
+                <button className={styles.share_url_con}>
+                    {shareUrl}
+                    <div className={styles.copy}>{i18n.t("turnplate.share_box.btn_copy")}</div>
+                </button>
+            </CopyToClipboard>
+        )
+    }
+    const renderRandomPlayers=()=>{
+        return (
+            <div className={styles.share_random_con}>
+                <div className={styles.content}>
+                    {
+                        numbers.map((item,index)=>{
+                            return (
+                                <div key={index} className={`${styles.item} ${index<=waPhoneIndex?styles.active:''}`}>{item}</div>
+                            )
+                        })
+                    }
+                </div>
+                <div className={styles.btn_con}>
+                    <button className={styles.btn_send} onClick={shareWaMore}>
+                        <div className={styles.text_con}>
+                            <div>{i18n.t("turnplate.share_box.send_title")}</div>
+                            <div className={styles.text_app}>
+                                <div>{i18n.t("turnplate.share_box.send_on")}</div>
+                                <div className={styles.name}>{i18n.t("turnplate.share_box.btn_whatsapp")}</div>
+                            </div>
+                        </div>
+                        <img className={styles.icon} src={icon_svg_ws}/>
+                    </button>
+                    <button className={styles.btn_send} onClick={shareSms}>
+                        <img className={styles.icon} src={icon_sms}/>
+                        <div className={styles.text_con}>
+                            <div>{i18n.t("turnplate.share_box.send_title")}</div>
+                            <div className={styles.text_app}>
+                                <div className={styles.name}>{i18n.t("turnplate.share_box.btn_sms")}</div>
+                            </div>
+                        </div>
+                    </button>
+                </div>
+            </div>
+        )
+    }
+    const renderShare=()=>{
+        return (
+            <div className={styles.container}>
+                <div className={styles.title}>{i18n.t("turnplate.share_box.title_1")}</div>
+                {renderIcons()}
+                {renderShareUrl()}
+                <div className={styles.title}>{i18n.t("turnplate.share_box.title_2")}</div>
+                {renderRandomPlayers()}
+            </div>
+        )
+    }
+    return (
+        <MuiDrawer open={open}
+                   anchor={"bottom"}
+                   sx={{
+                       zIndex:"1400",
+                       "& .MuiDrawer-paper": {
+                           height: "60%",
+                           boxSizing: "border-box",
+                           overflow:'visible !important',
+                           backgroundColor:'var(--standard);',
+                           boxShadow:'var(--widget-shadow);',
+                           borderTopLeftRadius:'1rem',
+                           borderTopRightRadius:'1rem'
+                       },
+                   }}
+                   variant="temporary"
+                   onClose={()=>{
+                       if (onClose)onClose();
+                   }}>
+            {renderShare()}
+        </MuiDrawer>
+    )
+}
+export default WShare;

+ 116 - 0
src/widgets/WCashWheel/WShare/styles.module.scss

@@ -0,0 +1,116 @@
+.container{
+  display: flex;
+  flex-direction: column;
+  position: relative;
+  width: 100%;
+  height: 100%;
+  padding: 1rem;
+  gap: 1rem;
+  & .title{
+    color: var(--header);
+    font-size: 1rem;
+  }
+  & .share_icon_con{
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: space-around;
+    & .icon_btn{
+      display: flex;
+      flex-direction: column;
+      gap: 0.5rem;
+      align-items: center;
+      justify-content: center;
+      color: var(--header);
+      font-size: 0.75rem;
+      & .icon{
+        width: 3rem;
+        height: 3rem;
+        border-radius: 50%;
+        background-color: #fff;
+      }
+      & .more{
+        background-color: #1a9276;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        & .s_icon{
+          width: 1.6rem;
+          height: 1.6rem;
+          fill: #fff;
+        }
+      }
+    }
+  }
+  & .share_url_con{
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 100%;
+    padding:0.5rem 1rem;
+    color: var(--header);
+    border-radius: var(--border-radius);
+    background-color: rgba(0, 0, 0, 0.1);
+    & .copy{
+      color: #40a1de;
+    }
+  }
+  & .share_random_con{
+    display: flex;
+    flex-direction: column;
+    background-color: #000;
+    border-radius: var(--border-radius);
+    padding: 1rem;
+    gap: 0.5rem;
+    & .content{
+      display: flex;
+      flex-wrap: wrap;
+      & .item{
+        padding: 0 .25rem;
+        color: var(--text);
+        font-size: 0.75rem;
+        &.active{
+          color: #555;
+        }
+      }
+    }
+    & .btn_con{
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      gap: 1.5rem;
+      & .btn_send{
+        width: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        background-color: #fff;
+        border-radius: var(--border-radius);
+        padding: 0.3rem 0.8rem;
+        & .text_con{
+          display: flex;
+          flex-direction: column;
+          align-items: flex-start;
+          gap: 0.5rem;
+          color: #000;
+          font-weight: 600;
+        }
+        & .text_app{
+          display: flex;
+          flex-direction: row;
+          gap: 0.25rem;
+          align-items: center;
+          & .name{
+            font-weight: 700;
+            font-family: "SWISSC-BT";
+            font-size: 0.9rem;
+          }
+        }
+        & .icon{
+          width: 2.8rem;
+          height: 2.8rem;
+        }
+      }
+    }
+  }
+}

+ 170 - 26
src/widgets/WCashWheel/WWheel/WWheel.jsx

@@ -6,17 +6,23 @@ import light2 from "@assets/wheel/light2.png";
 import spinBg from "@assets/wheel/spinBg.png";
 import pointer from "@assets/wheel/pointer.png"
 import styled from 'styled-components/macro';
-import {useEffect, useRef, useState} from "react";
+import React, {useEffect, useRef, useState} from "react";
 import spin_1 from "@assets/wheel/spin-1.png";
 import spin_2 from "@assets/wheel/spin-2.png";
 import spin_4 from "@assets/wheel/spin-4.png";
 import spin_5 from "@assets/wheel/spin-5.png";
 import draw from "@assets/wheel/draw.png";
-import {useSelector} from "react-redux";
-import {getTurnplate} from "@features/turnplateSlice";
-import {formatTime} from "@utils/helpers";
+import {useDispatch, useSelector} from "react-redux";
+import {getTurnplate, updateCollect, updateTimes} from "@features/turnplateSlice";
+import {formatCash, formatTime} from "@utils/helpers";
 import SvgIcon from "@components/SvgIcon";
 import {ReactComponent as ShareIcon} from '@assets/singlesvg/share.svg';
+import {isLogined} from "@features/mainSlice";
+import {useNavigate} from "react-router";
+import {ROUTE_PATH} from "@constants/routes";
+import {reqTurnplateKan} from "@features/api";
+import CountUp from "react-countup";
+import WPopInfo from "@widgets/WCashWheel/WPopInfo/WPopInfo";
 const i18n=global.i18n;
 const spinImgSrc=[
     null,
@@ -33,9 +39,13 @@ const StyledCanvas=styled.canvas`
 const turnObj={
     timer:null,
     cdTimer:null,
-    count:0
+    count:0,
+    result:null,
+    resTimer:null,
+    flashTimer:null,
+    resetTimer:null
 };
-const WWheel=()=>{
+const WWheel=({openShare,showShare=true,onClose=null})=>{
     let turnplate=useSelector(getTurnplate);
     if(turnplate==null)turnplate={times:0};
     const data=[
@@ -55,6 +65,14 @@ const WWheel=()=>{
     const startRotateDegreeDynamic=3240;
     const [drawStyle,setDrawStyle]=useState({});
     const [countdown,setCountdown]=useState(turnplate?turnplate.countdown:0);
+    const isLogin=useSelector(isLogined);
+    const navigate=useNavigate();
+    const dispatch = useDispatch();
+    const stopduring=5.5;
+    const resetduring=2;
+    const [collect,setCollect]=useState(0);
+    const [isLock,setIsLock]=useState(false);
+    const [openPopInfo,setOpenPopInfo]=useState(false);
     const loadImgs=()=>{
         let totalCount=spinImgSrc.length;
         let loadedCount=0;
@@ -87,13 +105,6 @@ const WWheel=()=>{
             turnObj.cdTimer=null;
         }
     }
-    const updateCD=()=>{
-        if (countdown<=0){
-            clearCDTimer();
-            return;
-        }
-        setCountdown(countdown-1);
-    }
     const checkCountdown=()=>{
         if (countdown>0){
             clearCDTimer();
@@ -109,9 +120,28 @@ const WWheel=()=>{
             },1000);
         }
     }
+    const onTurnResult=(data)=>{
+        let collect_amount=data.collect_amount;
+        let ids=[0,4];
+        let index=Math.floor(Math.random()*ids.length);
+        let id=ids[index];
+        turnObj.result={
+            reward:id,
+            amount:collect_amount
+        };
+    }
     const startSpin=()=>{
-        // drawAnimation();
+        if (!isLogin){
+            if (onClose)onClose();
+            navigate(ROUTE_PATH.SIGN_IN);
+            return;
+        }
+        if (isLock)return;
+        if (turnplate.times<=0)return;
         startTurn();
+        dispatch(reqTurnplateKan(onTurnResult));
+        dispatch(updateTimes({times:(turnplate.times-1)}))
+        setIsLock(true);
     }
     const clearTurnTimer=()=>{
         if (turnObj.timer!=null){
@@ -137,9 +167,10 @@ const WWheel=()=>{
                 transition:`transform ${during}s cubic-bezier(1.00, 1.00, 0.00, 0.00)`,
             }
             setDrawStyle(roteStyle);
-            if(turnCount>3){
-                // drawAnimation(turnCount*360+startRotateDegreeDynamic);
-                stopTurn(1);
+            if(turnCount>2){
+                if (turnObj.result){
+                    stopTurn(turnObj.result.reward);
+                }
             }
         },during*1000);
         turnObj.timer=timer;
@@ -161,11 +192,66 @@ const WWheel=()=>{
         clearTurnTimer();
         let roteStyle={
             transform:"rotate(".concat(angle, "deg)"),
-            transition:"transform 5.5s cubic-bezier(0.250, 0.460, 0.455, 0.995)",
+            transition:`transform ${stopduring}s cubic-bezier(0.250, 0.460, 0.455, 0.995)`,
         };
         setDrawStyle(roteStyle);
+        playRes();
+    }
+    const flashDraw=()=>{
+        let show=false;
+        turnObj.flashTimer=setInterval(()=>{
+            show=!show;
+            let result=show?turnObj.result:null;
+            drawWheel(result)
+        },260)
+    }
+    const playRes=()=>{
+        clearResTimer();
+        let during=(stopduring+0.5)*1000;
+        turnObj.resTimer=setTimeout(()=>{
+            flashDraw();
+            setCollect(turnObj.result.amount);
+            startReset();
+            dispatch(updateCollect({collect_amount:turnObj.result.amount}))
+        },during)
+    }
+    // const
+    const clearResTimer=()=>{
+        if (turnObj.resTimer){
+            clearTimeout(turnObj.resTimer);
+            turnObj.resTimer=null;
+        }
+        if (turnObj.flashTimer){
+            clearInterval(turnObj.flashTimer);
+            turnObj.flashTimer=null;
+        }
     }
-    const drawWheel=()=>{
+    const startReset=()=>{
+        turnObj.resetTimer=setTimeout(()=>{
+            resetWheel();
+        },resetduring*1000)
+    }
+    const resetWheel=()=>{
+        turnObj.result=null;
+        turnObj.count=0;
+        let roteStyle={
+            transform:"rotate(0deg)",
+            transition:``,
+        };
+        setDrawStyle(roteStyle);
+        drawWheel(null);
+        setCollect(0);
+        setIsLock(false);
+        setOpenPopInfo(true);
+        clearResTimer();
+    }
+    const clearResetTimer=()=>{
+        if (turnObj.resetTimer){
+            clearTimeout(turnObj.resetTimer);
+            turnObj.resetTimer=null;
+        }
+    }
+    const drawWheel=(result)=>{
         let draw=drawRef.current;
         if (draw==null)return;
         let colors=["#B90615","#FFD760"];
@@ -190,6 +276,19 @@ const WWheel=()=>{
                 let item=rdat[i];
                 if (item==null)continue;
                 let color=colors[i%color_len];
+                if (result){
+                    if(result.reward===item.id){
+                        c.beginPath();
+                        c.moveTo(0, 0);
+                        c.arc(0, 0, s.width / 2 - 6, -l / 2, l / 2.08);
+                        c.strokeStyle = "#fff";
+                        c.fillStyle = "#fff";
+                        c.lineWidth = 10;
+                        c.lineTo(0, -5);
+                        c.stroke();
+                        c.closePath();
+                    }
+                }
                 c.rotate(l);
                 c.save();
                 c.beginPath();
@@ -216,6 +315,9 @@ const WWheel=()=>{
         return ()=>{
             clearTurnTimer();
             clearCDTimer();
+            clearResTimer();
+            turnObj.result=null;
+            clearResetTimer();
         }
     },[]);
     useEffect(()=>{
@@ -236,8 +338,35 @@ const WWheel=()=>{
                     }}>
                         <img src={draw} className={styles.spin_img}/>
                         <div className={styles.spin_content}>
-                            <div className={styles.spin_num}>{times}</div>
+                            {
+                                collect>0?(
+                                    <CountUp
+                                        start={0}
+                                        end={Number(formatCash(collect))}
+                                        duration={1}
+                                        separator=" "
+                                        decimals={2}
+                                        prefix={"+"}
+                                    >
+                                        {({ countUpRef, start }) => {
+                                            return (
+                                                <div ref={(ref)=>{
+                                                    countUpRef.current=ref;
+                                                    if (countUpRef.collect!==collect){
+                                                        countUpRef.collect=collect;
+                                                        start();
+                                                    }
+                                                }} className={styles.collect_num}/>
+                                            )
+                                        }}
+                                    </CountUp>
+                                ):(
+
+                                    <div className={styles.spin_num}>{times}</div>
+                                )
+                            }
                         </div>
+
                     </button>
                 </div>
             </>
@@ -256,13 +385,27 @@ const WWheel=()=>{
         )
     }
     const renderShareBar=()=>{
+        if (showShare){
+            return (
+                <button className={styles.share_con} onClick={()=>{
+                    if (openShare)openShare();
+                }}>
+                    <div className={styles.share_bar}>
+                        <div className={styles.text}>{i18n.t("turnplate.invite_tips")}</div>
+                        <ShareIcon className={styles.icon}/>
+                    </div>
+                </button>
+            )
+        }
+        else {
+            return (<></>);
+        }
+    }
+    const renderPopInfo=()=>{
         return (
-            <button className={styles.share_con}>
-                <div className={styles.share_bar}>
-                    <div className={styles.text}>{i18n.t("turnplate.invite_tips")}</div>
-                    <ShareIcon className={styles.icon}/>
-                </div>
-            </button>
+            <WPopInfo open={openPopInfo} onClose={()=>{
+                setOpenPopInfo(false);
+            }} openShare={openShare}></WPopInfo>
         )
     }
     return (
@@ -282,6 +425,7 @@ const WWheel=()=>{
             </div>
             {renderCountDown()}
             {renderShareBar()}
+            {renderPopInfo()}
         </div>
     )
 }

+ 5 - 0
src/widgets/WCashWheel/WWheel/styles.module.scss

@@ -107,6 +107,11 @@
               -1.5px  1.5px 0 #953500,
               1.5px  1.5px 0 #953500;
     }
+    & .collect_num{
+      font-family: "Roboto";
+      font-size: 1.2rem;
+      font-weight: 600;
+    }
   }
 }
 .cd_con{

+ 41 - 7
src/widgets/WCashWheel/index.jsx

@@ -12,6 +12,12 @@ import {Swiper, SwiperSlide} from "swiper/react";
 import styled from 'styled-components/macro';
 import CountUp from "react-countup";
 import Popup from "@components/Popup";
+import WShare from "@widgets/WCashWheel/WShare/WShare";
+import {isLogined} from "@features/mainSlice";
+import {useNavigate} from "react-router";
+import {ROUTE_PATH} from "@constants/routes";
+import SmallLoading from "@components/SmallLoading";
+import WRetainPop from "@widgets/WCashWheel/WRetainPop/WRetainPop";
 const i18n=global.i18n;
 const StyledSwiper=styled(Swiper)`
 .swiper-wrapper{
@@ -20,13 +26,16 @@ const StyledSwiper=styled(Swiper)`
 `
 const WCashWheel=({close})=>{
     let turnplate=useSelector(getTurnplate);
-    if(turnplate==null)turnplate={collect_amount:0,total_amount:10000};
+    // if(turnplate==null)turnplate={collect_amount:0,total_amount:10000};
     const record=useSelector(getRecord);
     const myrecord=useSelector(getMyRecord);
     const [recordIndex,setRecordIndex]=useState(0);
     const recordLog=[record,myrecord];
     const [swiper, setSwiper] = useState(null);
     const [openInfoBox,setOpenInfoBox]=useState(false);
+    const [openShare,setOpenShare]=useState(false);
+    const isLogin=useSelector(isLogined);
+    const navigate=useNavigate();
     useEffect(() => {
         if (swiper) {
             swiper.update();
@@ -43,7 +52,7 @@ const WCashWheel=({close})=>{
     }
     const renderCashMain=()=>{
         let collect=turnplate.collect_amount;
-        collect=9556;
+        // collect=9556;
         let total=turnplate.total_amount;
         let left=total-collect;
         let isEnableCash=collect>0;
@@ -178,7 +187,7 @@ const WCashWheel=({close})=>{
     }
     const renderInfoBox=()=>{
         let collect=turnplate.collect_amount;
-        collect=9556;
+        // collect=9556;
         let total=turnplate.total_amount;
         let left=total-collect;
         return (
@@ -219,7 +228,10 @@ const WCashWheel=({close})=>{
                                 <div className={`${styles.step_item} ${collect>=total?styles.active:''}`}>{stringFormat(i18n.t("turnplate.info_box.step_2"),[formatCash(total,0)])}</div>
                             </div>
                         </div>
-                        <button className={styles.invite_btn}>
+                        <button className={styles.invite_btn} onClick={()=>{
+                            setOpenInfoBox(false);
+                            checkOpenShare();
+                        }}>
                             {i18n.t("turnplate.info_box.btn_invite")}
                         </button>
                     </div>
@@ -227,15 +239,37 @@ const WCashWheel=({close})=>{
             </Popup>
         )
     }
-    return (
-        <div className={styles.container}>
+    const renderShare=()=>{
+        return (
+            <WShare open={openShare} onClose={()=>{
+                setOpenShare(false);
+            }}></WShare>
+        )
+    }
+    const checkOpenShare=()=>{
+        if (!isLogin){
+            navigate(ROUTE_PATH.SIGN_IN);
+            return;
+        }
+        setOpenShare(true);
+    }
+    const renderMain=()=>{
+        return (
             <Spring type={"slideUp"} className={styles.content}>
                 {renderCashMain()}
-                <WWheel></WWheel>
+                <WWheel openShare={checkOpenShare}></WWheel>
                 {renderRecord()}
                 {renderHelp()}
                 {renderInfoBox()}
+                {renderShare()}
             </Spring>
+        )
+    }
+    return (
+        <div className={styles.container}>
+            {
+                turnplate==null?(<SmallLoading></SmallLoading>):(renderMain())
+            }
         </div>
     )
 }

+ 87 - 0
src/widgets/WCashWheel/styles.module.scss

@@ -5,6 +5,7 @@
   flex-direction: column;
   position: relative;
   background-color: var(--widget-bg);
+  overflow-x: hidden;
 }
 .content{
   display: flex;
@@ -305,4 +306,90 @@
     }
   }
 }
+.header{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  width:100%;
+  background: var(--widget);
+  border-bottom: 1px solid var(--border);
+  box-shadow:var(--widget-shadow);
+  position:relative;
+  height:3.75rem;
+  padding: 0px 10px;
+  & .top{
+    display: flex;
+    align-items: center;
+    position:fixed;
+    justify-content: space-between;
+    left: 10px;
+    right: 10px;
+    & .left{
+      display: flex;
+      align-items: center;
+      gap: 0.25rem;
+      & .close{
+        display: flex;
+        align-items: center;
+        height: 3.75rem;
+        width: 2rem;
+        & .arrow{
+          width: 1.4rem;
+          height: 1.8em;
+          transform: rotate(-180deg);
+          fill: var(--text);
+        }
+      }
+      & .user{
+        display: flex;
+        align-items: flex-start;
+        flex-direction: column;
+        justify-content: center;
+        & .uid{
+          color: var(--text);
+          font-weight: 600;
+        }
+        & .gold_con{
+          display: flex;
+          align-items: center;
+          gap: 0.25rem;
+          & .icon_golds{
+            width: 1rem;
+            height: 1rem;
+          }
+          & .golds{
+            font-size: 0.875rem;
+            font-weight: 600;
+            color: #edc349;
+          }
+        }
+      }
+    }
+    & .right{
+      display: flex;
+      align-items: center;
+      & .right_btn{
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        height: 2.5rem;
+        width: 2.5rem;
+        & .icon{
+          width: 1.4rem;
+          height: 1.4rem;
+          fill: var(--text);
+        }
+      }
+    }
+  }
+  & .hcontent {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex: 1;
+    font-size: 1rem;
+    font-weight: 600;
+    color: var(--header);
+  }
+}
 

Някои файлове не бяха показани, защото твърде много файлове са промени