From 16932959d5a776197f7f5575f8e3482c24cee141 Mon Sep 17 00:00:00 2001 From: Arseniy Sitnikov Date: Thu, 12 Dec 2024 23:01:22 +0300 Subject: [PATCH] autoclick add & add ranks --- frontend/src/.DS_Store | Bin 6148 -> 6148 bytes .../shared/Clicker/ClickerBtn/ClickerBtn.tsx | 116 ++++++--------- .../shared/Clicker/PointsZoom/PointsZoom.tsx | 90 +++++++----- .../Clicker/SectionsBlock/SectionsBlock.tsx | 33 ++++- .../src/shared/Elements/DevPopup/DevPopup.tsx | 5 +- .../shared/Elements/RatingCard/RatingCard.tsx | 24 ++-- .../Elements/RatingCard/ratingcard.module.css | 4 +- .../shared/Elements/UsersIcons/UsersIcons.tsx | 6 +- .../shared/Pages/ClickerPage/ClickerPage.tsx | 10 +- .../src/shared/Pages/ErrorPage/ErrorPage.tsx | 12 +- .../Pages/ErrorPage/errorpage.module.css | 9 +- .../shared/Pages/RatingPage/RatingPage.tsx | 84 +++++------ .../Pages/RatingPage/ratingpage.module.css | 8 ++ .../src/shared/Pages/RoutePage/RoutePage.tsx | 2 + .../shared/Pages/StoragePage/StoragePage.tsx | 12 +- .../FriendsPageBlock/FriendsPageBlock.tsx | 71 +++++---- .../friendspageblock.module.css | 8 ++ frontend/src/shared/hooks/checkWhiteList.ts | 14 ++ frontend/src/shared/hooks/useFriendsData.ts | 24 ++++ frontend/src/shared/hooks/useRankData.ts | 23 +++ frontend/src/shared/hooks/useTgData.ts | 3 +- frontend/src/store/friends/actions.ts | 97 +++++++++++++ frontend/src/store/friends/reducer.ts | 36 +++++ frontend/src/store/me/actions.ts | 21 ++- frontend/src/store/rank/actions.ts | 135 ++++++++++++++++++ frontend/src/store/rank/reducer.ts | 32 +++++ frontend/src/store/reducer.ts | 30 ++++ frontend/src/utils/isWhiteList.js | 20 +++ frontend/src/utils/verification.js | 14 ++ 29 files changed, 718 insertions(+), 225 deletions(-) create mode 100644 frontend/src/shared/hooks/checkWhiteList.ts create mode 100644 frontend/src/shared/hooks/useFriendsData.ts create mode 100644 frontend/src/shared/hooks/useRankData.ts create mode 100644 frontend/src/store/friends/actions.ts create mode 100644 frontend/src/store/friends/reducer.ts create mode 100644 frontend/src/store/rank/actions.ts create mode 100644 frontend/src/store/rank/reducer.ts create mode 100644 frontend/src/utils/isWhiteList.js diff --git a/frontend/src/.DS_Store b/frontend/src/.DS_Store index 18ab8a0c73904e76a654b40ca82b5216ce59519a..2279b4c3a14607c85eacdbdf6c0b885595769c75 100644 GIT binary patch delta 29 jcmZoMXfc@J&&V<{z#2&O7;n6x&pug!QF1do$6tN`iP{Mm delta 39 rcmZoMXfc@J&&WD4z#2&O81u3*6f>kU6fu;6$c>w=**CLu{N)D#({c*l diff --git a/frontend/src/shared/Clicker/ClickerBtn/ClickerBtn.tsx b/frontend/src/shared/Clicker/ClickerBtn/ClickerBtn.tsx index aca24a4..288d568 100644 --- a/frontend/src/shared/Clicker/ClickerBtn/ClickerBtn.tsx +++ b/frontend/src/shared/Clicker/ClickerBtn/ClickerBtn.tsx @@ -1,27 +1,26 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import styles from './clickerbtn.module.css'; import { ModalWindow } from '../../ModalWindow'; import { ClickerPopup } from '../ClickerPopup'; import { getGradient } from '../../../utils/getGradient'; import { useAppSelector } from '../../hooks/useAppSelector'; import { useDispatch } from 'react-redux'; -import { IUserData, updateEnergyRequestAsync } from '../../../store/me/actions'; -import axios from 'axios'; -import { DevPopup } from '../../Elements/DevPopup'; -import { saveMult } from '../../../store/mult'; -import { Spinner } from '../../Elements/Spinner'; +import { updateEnergyRequestAsync } from '../../../store/me/actions'; interface IClickerBtn { coins: number, setCoins(a: number): void, energy: number, setMult(a: number): void, - setEnergy(a: number): void + setEnergy(a: number): void, + setClickTime(a: number): void, + clickTime: number, + sameCoords: boolean, + setSameCoords(a: boolean): void, + closeAutoClick: boolean } -export function ClickerBtn({ setCoins, energy, setMult, coins, setEnergy }: IClickerBtn) { - const urlClick = useAppSelector(state => state.urlClick); - const token = useAppSelector(state => state.token); +export function ClickerBtn({ setCoins, closeAutoClick, energy, setMult, coins, setEnergy, setClickTime, clickTime, setSameCoords }: IClickerBtn) { const [fill, setFill] = useState(0); const [size, setSize] = useState(240); const circumference = 2 * Math.PI * 125; @@ -32,6 +31,22 @@ export function ClickerBtn({ setCoins, energy, setMult, coins, setEnergy }: ICli let styleIndex = useAppSelector(state => state.styleIndex); const [maxEnergy, setMaxEnergy] = useState(500); const dispatch = useDispatch(); + const [prevClickTime, setPrevClickTime] = useState(0); + const [prevCoords, setPrevCoords] = useState(0); + + useEffect(() => { + if(!closeAutoClick) { + setClose(true); + } + }, [closeAutoClick]); + + + useEffect(() => { + if(clickTime === 0) { + setPrevClickTime(0); + setPrevCoords(0); + } + }, [clickTime]); useEffect(() => { setFill((maxEnergy - energy) / maxEnergy * 100); @@ -50,7 +65,25 @@ export function ClickerBtn({ setCoins, energy, setMult, coins, setEnergy }: ICli setGradient(getGradient()) }, [styleIndex]); - const btnClick = () => { + const btnClick = (event: React.MouseEvent) => { + const x = event.nativeEvent.offsetX; + const y = event.nativeEvent.offsetY; + + const coords = x*y; + if (coords === prevCoords) { + setSameCoords(true); + } else { + setSameCoords(false); + } + setPrevCoords(coords); + + const currentTime = Date.now(); + const clickInterval = currentTime - prevClickTime; + if(prevClickTime != 0) { + setClickTime(clickTime + clickInterval); + } + setPrevClickTime(currentTime); + if (energy != 0) { const newEnergy = energy - 1; const newFill = (maxEnergy - newEnergy) / maxEnergy * 100; @@ -70,70 +103,11 @@ export function ClickerBtn({ setCoins, energy, setMult, coins, setEnergy }: ICli } else { setClose(false); } - //sendClick(); } else { setClose(false); } }; - /*const sendClick = () => { - if (token && !loading) { - setLoading(true); - axios.post(`${urlClick}/api/v1/click/`, - {}, - { - headers: { - "Authorization": `TelegramToken ${token}` - } - } - ).then((resp) => { - //console.log(resp); - if(resp.data) { - setLoading(false); - const click = Number(resp.data.click.value); - // - const encodeMult = btoa(click.toString()); - sessionStorage.setItem('mt', encodeMult); - // - const newEnergy = Number(resp.data.energy); - setMult(Number(click.toFixed(2))) - dispatch(saveMult(Number(click.toFixed(2)))); - const newFill = (maxEnergy - newEnergy) / maxEnergy * 100; - if (newFill <= 100) { - const newCoins = Number(coins + click); - dispatch(updateEnergyRequestAsync(newEnergy)) - setCoins(newCoins); - setEnergy(newEnergy) - setFill(newFill); - } else { - setFill(100); - } - - if (newFill < 100) { - setSize(220); - - const timer = setTimeout(() => { - setSize(240); - clearTimeout(timer); - }, 100); - } else { - setClose(false); - } - // - } - if(error) { - setError(false) - } - }).catch((err) => { - setLoading(false); - setCloseError(false); - setError(true); - console.log(err); - }) - } - };*/ - - const hotCards = [ { title: 'Ты большой молодец', @@ -162,7 +136,7 @@ export function ClickerBtn({ setCoins, energy, setMult, coins, setEnergy }: ICli {gradient} - {!close && } />} diff --git a/frontend/src/shared/Clicker/PointsZoom/PointsZoom.tsx b/frontend/src/shared/Clicker/PointsZoom/PointsZoom.tsx index 7cc96b3..025bae1 100644 --- a/frontend/src/shared/Clicker/PointsZoom/PointsZoom.tsx +++ b/frontend/src/shared/Clicker/PointsZoom/PointsZoom.tsx @@ -4,7 +4,7 @@ import { formatNumber } from '../../../utils/formatNumber'; import { ETextStyles } from '../../texts'; import ReactDOM from 'react-dom'; import { useDispatch } from 'react-redux'; -import { meRequest, meRequestError, updateEnergyRequestAsync, updatePointsRequestAsync } from '../../../store/me/actions'; +import { IUserData, meRequest, meRequestSuccess, updateEnergyRequestAsync, updatePointsRequestAsync } from '../../../store/me/actions'; import { checkIOS } from '../../../utils/checkMobile'; import axios from 'axios'; import { useAppSelector } from '../../hooks/useAppSelector'; @@ -12,56 +12,82 @@ import { saveMult } from '../../../store/mult'; interface IPointsZoom { points: number, - setClose(a:boolean): void, - className ?: string, + setClose(a: boolean): void, + className?: string, closePointsAnim: boolean, setClosePointsAnim(a: boolean): void, - setCoins(a:number):void, + setCoins(a: number): void, setCloseError(a: boolean): void, setEnergy(a: number): void, setMult(a: number): void, + setClickTime(a: number): void, + clickTime: number, + setCloseAutoClick(a: boolean): void, + sameCoords: boolean, + setSameCoords(a: boolean): void, } -export function PointsZoom({ points, setMult, setClose, setCoins, className, closePointsAnim, setClosePointsAnim, setCloseError, setEnergy }: IPointsZoom) { +export function PointsZoom({ points, sameCoords, setSameCoords, setCloseAutoClick, setMult, setClose, setCoins, className, closePointsAnim, setClosePointsAnim, setCloseError, setEnergy, clickTime, setClickTime }: IPointsZoom) { const [open, setOpen] = useState(true); const node = document.querySelector('#modal_root'); const urlClick = useAppSelector(state => state.urlClick); const token = useAppSelector(state => state.token); const [sizeHand, setSizeHand] = useState(30); - const energy = Number(useAppSelector(state=>state.me.data.energy)); + const energy = Number(useAppSelector(state => state.me.data.energy)); + const userData = useAppSelector(state => state.me.data); if (!node) return null; const dispatch = useDispatch(); const sendClicks = () => { const initPoints = points; - dispatch(meRequest()); - axios.post(`${urlClick}/api/v1/batch-click/`, - { - count: initPoints - }, - { - headers: { - "Authorization": `TelegramToken ${token}` - } - } - ).then(resp => { - const data = resp.data; - const click = Number(data.click.value); - const encodeMult = btoa(click.toString()); - sessionStorage.setItem('mt', encodeMult); - setMult(Number(click.toFixed(2))); - const energy = Number(data.energy); - dispatch(saveMult(Number(click.toFixed(2)))); - dispatch(updateEnergyRequestAsync(energy)); - dispatch(updatePointsRequestAsync()); - }).catch(err => { - console.log(err); - setCloseError(false); + const clickTimeInit = clickTime; + let initSameCoords = sameCoords; + let avtTime = 500; + if (points > 1) { + avtTime = clickTimeInit / (initPoints - 1); + } + + //block function + initSameCoords = false; + + setClickTime(0); + setSameCoords(false); + + if (avtTime < 100 && initSameCoords && points > 30) { + setCloseAutoClick(false); const returnEnergy = energy + initPoints; setEnergy(returnEnergy); dispatch(updateEnergyRequestAsync(returnEnergy)); - dispatch(meRequestError(String(err))); - }) + } else { + dispatch(meRequest()); + axios.post(`${urlClick}/api/v1/batch-click/`, + { + count: initPoints + }, + { + headers: { + "Authorization": `TelegramToken ${token}` + } + } + ).then(resp => { + const data = resp.data; + const click = Number(data.click.value); + const encodeMult = btoa(click.toString()); + sessionStorage.setItem('mt', encodeMult); + setMult(Number(click.toFixed(2))); + const energy = Number(data.energy); + dispatch(saveMult(Number(click.toFixed(2)))); + dispatch(updateEnergyRequestAsync(energy)); + dispatch(updatePointsRequestAsync()); + }).catch(err => { + console.log(err); + setCloseError(false); + const returnEnergy = energy + initPoints; + setEnergy(returnEnergy); + dispatch(updateEnergyRequestAsync(returnEnergy)); + dispatch(meRequestSuccess(userData)); + }) + } }; useEffect(() => { @@ -89,7 +115,7 @@ export function PointsZoom({ points, setMult, setClose, setCoins, className, clo setSizeHand(30); }, 100); - return () => clearTimeout(timer); + return () => clearTimeout(timer); }, [points]); return ( diff --git a/frontend/src/shared/Clicker/SectionsBlock/SectionsBlock.tsx b/frontend/src/shared/Clicker/SectionsBlock/SectionsBlock.tsx index b7c93ab..882d402 100644 --- a/frontend/src/shared/Clicker/SectionsBlock/SectionsBlock.tsx +++ b/frontend/src/shared/Clicker/SectionsBlock/SectionsBlock.tsx @@ -9,6 +9,8 @@ import { useNavigate } from 'react-router-dom'; import { UsersIcons } from '../../Elements/UsersIcons'; import { formatNumber } from '../../../utils/formatNumber'; import { useAppSelector } from '../../hooks/useAppSelector'; +import { isWhiteList } from '../../../utils/isWhiteList'; +import { IUserRank } from '../../../store/friends/actions'; interface ISectionsBlock { mult:number; @@ -18,9 +20,30 @@ export function SectionsBlock({ mult }: ISectionsBlock) { const [close, setClose] = useState(true); const navigate = useNavigate(); const referralStorage = Number(useAppSelector(state => state.me.data.referralStorage)); - //const referralStorage = 500; const maxReferralStorage = useAppSelector(state => state.me.data.maxStorage); const [referralPercent, serReferralPercent] = useState(0); + const [isDev, setIsDev] = useState(true); + const userRank = useAppSelector(state => state.me.data.rank); + const rankData = useAppSelector>(state => state.rank.data); + const [topImgs, setTopImgs] = useState>([]); + + useEffect(() => { + const whiteList = isWhiteList(); + setIsDev(!whiteList) + }, []); + + useEffect(() => { + const imgs:Array = []; + if(rankData.length != 0) { + for (let i = 0; i < rankData.length; i++) { + if (i < 3 && rankData[i].avatar) { + //@ts-ignore + imgs.push(rankData[i].avatar); + } + } + setTopImgs(imgs); + } + }, [rankData]); useEffect(() => { if(referralStorage >= maxReferralStorage) { @@ -31,8 +54,6 @@ export function SectionsBlock({ mult }: ISectionsBlock) { }, [referralStorage, maxReferralStorage]); - const isDev = true; - const multipCards = [ { title: 'Что он делает', @@ -54,13 +75,13 @@ export function SectionsBlock({ mult }: ISectionsBlock) { return (
- {!isDev ? navigate('/rating') : navigate('/dev?type=rating')}}> + { !isDev ? navigate('/rating') : navigate('/dev?type=rating') }}> {
# - {formatNumber(1)} + {isDev ? '?' : (userRank ? formatNumber(userRank) : '?')}
- +
}
{ setClose(false) }}> diff --git a/frontend/src/shared/Elements/DevPopup/DevPopup.tsx b/frontend/src/shared/Elements/DevPopup/DevPopup.tsx index 1e3a569..c69f916 100644 --- a/frontend/src/shared/Elements/DevPopup/DevPopup.tsx +++ b/frontend/src/shared/Elements/DevPopup/DevPopup.tsx @@ -7,13 +7,14 @@ interface IDevPopup { setClose(a: boolean): void title: string, text: string, + img?: string, } -export function DevPopup({ setClose, title, text }: IDevPopup) { +export function DevPopup({ setClose, title, text, img }: IDevPopup) { return (
-
+

{title}

{text}

diff --git a/frontend/src/shared/Elements/RatingCard/RatingCard.tsx b/frontend/src/shared/Elements/RatingCard/RatingCard.tsx index 05995ea..afd78b2 100644 --- a/frontend/src/shared/Elements/RatingCard/RatingCard.tsx +++ b/frontend/src/shared/Elements/RatingCard/RatingCard.tsx @@ -3,28 +3,36 @@ import styles from './ratingcard.module.css'; import { EIcons, Icon } from '../../Icons'; import { PointsBlock } from '../PointsBlock'; import { ETextStyles } from '../../texts'; +import { PersonIcon } from '../PersonIcon'; interface IRatingCard { number: number, name: string, - score: string + score: string, + index: number, + friend ?: boolean, + img: string } -export function RatingCard({number, name, score}: IRatingCard) { +export function RatingCard({number, name, score, index, friend=false, img}: IRatingCard) { + let order = number; + if(friend) { + order = index; + } return ( -
+
- {(number === 1) &&
+ {order === 1 &&
} - {(number === 2) &&
+ {order === 2 &&
} - {(number === 3) &&
+ {order === 3 &&
} - {(number > 3) &&
{number}
} -
+ {(order > 3 ) &&
{order}
} +

{name}

diff --git a/frontend/src/shared/Elements/RatingCard/ratingcard.module.css b/frontend/src/shared/Elements/RatingCard/ratingcard.module.css index 46f9e16..03b03f9 100644 --- a/frontend/src/shared/Elements/RatingCard/ratingcard.module.css +++ b/frontend/src/shared/Elements/RatingCard/ratingcard.module.css @@ -21,10 +21,10 @@ .img { margin-right: 4px; - width: 20px; + /*width: 20px; height: 20px; border-radius: 20px; - background-color: var(--white); + background-color: var(--white);*/ } .name { diff --git a/frontend/src/shared/Elements/UsersIcons/UsersIcons.tsx b/frontend/src/shared/Elements/UsersIcons/UsersIcons.tsx index c541fee..8e914f5 100644 --- a/frontend/src/shared/Elements/UsersIcons/UsersIcons.tsx +++ b/frontend/src/shared/Elements/UsersIcons/UsersIcons.tsx @@ -11,9 +11,9 @@ interface IUsersIcons { export function UsersIcons({ size = 25, imgs = [], className = '' }: IUsersIcons) { return (
- - - + + +
); } diff --git a/frontend/src/shared/Pages/ClickerPage/ClickerPage.tsx b/frontend/src/shared/Pages/ClickerPage/ClickerPage.tsx index 071ccae..248ca5c 100644 --- a/frontend/src/shared/Pages/ClickerPage/ClickerPage.tsx +++ b/frontend/src/shared/Pages/ClickerPage/ClickerPage.tsx @@ -30,6 +30,9 @@ export function ClickerPage({ name, points, img, energy }: IClickerPageInterface const [closeError, setCloseError] = useState(true); const [animClose, setAnimClose] = useState(false); const [initEnergy, setInitEnergy] = useState(energy); + const [clickTime, setClickTime] = useState(0); + const [closeAutoClick, setCloseAutoClick] = useState(true); + const [sameCoords, setSameCoords] = useState(false); useEffect(() => { setMult(savedMult); @@ -54,13 +57,13 @@ export function ClickerPage({ name, points, img, energy }: IClickerPageInterface return (
- {!closePoints && } + {!closePoints && }

Мои рекорды

670 && 'calc(100vh - 355px)'}`}}> - +
{styleIndex != 0 &&
@@ -69,6 +72,9 @@ export function ClickerPage({ name, points, img, energy }: IClickerPageInterface {!closeError && } />} + {!closeAutoClick && + } />}
); } diff --git a/frontend/src/shared/Pages/ErrorPage/ErrorPage.tsx b/frontend/src/shared/Pages/ErrorPage/ErrorPage.tsx index d13d034..e9b75c1 100644 --- a/frontend/src/shared/Pages/ErrorPage/ErrorPage.tsx +++ b/frontend/src/shared/Pages/ErrorPage/ErrorPage.tsx @@ -5,14 +5,16 @@ import { ETextStyles } from '../../texts'; interface IErrorPage { detail: String, + title?: string, + text?: string, + fullScreen ?: boolean } -export function ErrorPage({ detail }: IErrorPage) { - console.log(detail) +export function ErrorPage({ detail, title, text, fullScreen=true }: IErrorPage) { return ( -
-

Возникла ошибка при загрузке ваших данных

-

Попробуйте перезагрузить страницу или войдите позже.

+
+

{title ? title : 'Возникла ошибка при загрузке ваших данных'}

+

{text ? text : 'Попробуйте перезагрузить страницу или войдите позже.'}

diff --git a/frontend/src/shared/Pages/ErrorPage/errorpage.module.css b/frontend/src/shared/Pages/ErrorPage/errorpage.module.css index fbc1cb6..8d0ac0f 100644 --- a/frontend/src/shared/Pages/ErrorPage/errorpage.module.css +++ b/frontend/src/shared/Pages/ErrorPage/errorpage.module.css @@ -4,7 +4,14 @@ justify-content: center; flex-direction: column; width: 100%; - height: 100vh; +} + +.fullscreen { + height: 100vh; +} + +.margin { + margin: 50px 0; } .title { diff --git a/frontend/src/shared/Pages/RatingPage/RatingPage.tsx b/frontend/src/shared/Pages/RatingPage/RatingPage.tsx index 68dccc0..98c5625 100644 --- a/frontend/src/shared/Pages/RatingPage/RatingPage.tsx +++ b/frontend/src/shared/Pages/RatingPage/RatingPage.tsx @@ -1,64 +1,54 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import styles from './ratingpage.module.css'; import { RatingCard } from '../../Elements/RatingCard'; import { ETextStyles } from '../../texts'; import { generateRandomString } from '../../../utils/generateRandom'; +import { useRankData } from '../../hooks/useRankData'; +import { Spinner } from '../../Elements/Spinner'; +import { ErrorPage } from '../ErrorPage'; +import { checkWhiteList } from '../../hooks/checkWhiteList'; export function RatingPage() { + const { dataRank, loadingRank, errorRank } = useRankData(); + const [topBlock, setTopBlock] = useState(
); + const [otherBlock, setOtherBlock] = useState(
); + checkWhiteList(); - const rating = [ - { - name: 'Anficee', - score: '1000000' - }, - { - name: 'Maria', - score: '300000' - }, - { - name: 'Greg', - score: '90000' - }, - { - name: 'Kate', - score: '80000' - }, - { - name: 'Eva', - score: '70000' - }, - { - name: 'Ты', - score: '50000' - }, - { - name: 'Bill', - score: '40000' - }, - { - name: 'Bradley', - score: '30000' - }, - ]; + useEffect(() => { + if (dataRank.length != 0) { + const firstBlock = dataRank.map((user, index) => { + if (index < 3) { + return ; + } + }); + //@ts-ignore + setTopBlock(firstBlock); + + const ratingBlock = dataRank.map((user, index) => { + if (index > 2) { + return ; + } + }); + //@ts-ignore + setOtherBlock(ratingBlock); - const ratingBlock = rating.map((user, index) => { - if(index > 3) { - return ; } - }) + }, [dataRank]) - const firstBlock = rating.map((user, index) => { - if(index < 3) { - return ; - } - }) return (
-

Рейтинг игроков

-
{firstBlock}
-
{ratingBlock}
+ {loadingRank &&
} + {!loadingRank &&
+ {errorRank ? : +
+

Рейтинг игроков

+
{topBlock}
+
{otherBlock}
+
+ } +
}
); } diff --git a/frontend/src/shared/Pages/RatingPage/ratingpage.module.css b/frontend/src/shared/Pages/RatingPage/ratingpage.module.css index 0840cc7..0ce70f1 100644 --- a/frontend/src/shared/Pages/RatingPage/ratingpage.module.css +++ b/frontend/src/shared/Pages/RatingPage/ratingpage.module.css @@ -20,4 +20,12 @@ display: flex; flex-direction: column; gap: 8px; +} + +.spinnerContainer { + display: flex; + width: 100vw; + height: 100vh; + align-items: center; + justify-content: center; } \ No newline at end of file diff --git a/frontend/src/shared/Pages/RoutePage/RoutePage.tsx b/frontend/src/shared/Pages/RoutePage/RoutePage.tsx index b4b7cfa..676c691 100644 --- a/frontend/src/shared/Pages/RoutePage/RoutePage.tsx +++ b/frontend/src/shared/Pages/RoutePage/RoutePage.tsx @@ -14,6 +14,7 @@ import { updateBackground } from '../../../utils/updateBackground'; import { ErrorPage } from '../ErrorPage'; import { useNavigate } from 'react-router-dom'; import { DevPage } from '../DevPage'; +import { useRankData } from '../../hooks/useRankData'; interface IRoutePage { page: string @@ -22,6 +23,7 @@ interface IRoutePage { export function RoutePage({ page }: IRoutePage) { const verified = useTgData(); const { dataUser, loadingUser, errorUser } = useUserData(); + useRankData(); const navigate = useNavigate(); //@ts-ignore const tg = window.Telegram.WebApp; diff --git a/frontend/src/shared/Pages/StoragePage/StoragePage.tsx b/frontend/src/shared/Pages/StoragePage/StoragePage.tsx index e4c1d11..9230801 100644 --- a/frontend/src/shared/Pages/StoragePage/StoragePage.tsx +++ b/frontend/src/shared/Pages/StoragePage/StoragePage.tsx @@ -10,22 +10,28 @@ import { StoragePageBlock } from '../../Storage/StoragePageBlock'; import { FriendsPageBlock } from '../../Storage/FriendsPageBlock'; import { useAppSelector } from '../../hooks/useAppSelector'; import { useNavigate } from 'react-router-dom'; +import { isWhiteList } from '../../../utils/isWhiteList'; export function StoragePage() { const userId = useAppSelector(state => state.userTg.id); const [page, setPage] = useState('storage'); const refLink = `https://t.me/sapphirecrown_bot?start=user_${userId}`; const [showNotif, setShow] = useState(false); + const [isDev, setIsDev] = useState(true); const navigate = useNavigate(); + useEffect(() => { + const whiteList = isWhiteList(); + setIsDev(!whiteList) + }, []); + return (

Реферальная программа

setPage('storage')}/> - { - navigate('/dev?type=friends') - //setPage('friends') + { + isDev ? navigate('/dev?type=friends') : setPage('friends') } } />
diff --git a/frontend/src/shared/Storage/FriendsPageBlock/FriendsPageBlock.tsx b/frontend/src/shared/Storage/FriendsPageBlock/FriendsPageBlock.tsx index 31b1d29..61aca8e 100644 --- a/frontend/src/shared/Storage/FriendsPageBlock/FriendsPageBlock.tsx +++ b/frontend/src/shared/Storage/FriendsPageBlock/FriendsPageBlock.tsx @@ -1,7 +1,11 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import styles from './friendspageblock.module.css'; import { ETextStyles } from '../../texts'; import { RatingCard } from '../../Elements/RatingCard'; +import { generateRandomString } from '../../../utils/generateRandom'; +import { useFriendsData } from '../../hooks/useFriendsData'; +import { ErrorPage } from '../../Pages/ErrorPage'; +import { Spinner } from '../../Elements/Spinner'; interface IRating { name: string, @@ -9,49 +13,38 @@ interface IRating { } export function FriendsPageBlock() { + const { dataFriends, loadingFriends, errorFriends } = useFriendsData(); + const [ratingBlock, setRatingBlock] = useState(
); + const loading = true; - //const rating: Array = []; + useEffect(() => { + if (dataFriends.length != 0) { + const block = dataFriends.map((user, index) => { + return ; + }); + + //@ts-ignore + setRatingBlock(block); + } + + }, [dataFriends]) - const rating = [ - { - name: 'Anficee', - score: '1000000' - }, - { - name: 'Maria', - score: '300000' - }, - { - name: 'Greg', - score: '90000' - }, - { - name: 'Kate', - score: '80000' - }, - { - name: 'Eva', - score: '70000' - }, - { - name: 'Bill', - score: '40000' - }, - { - name: 'Bradley', - score: '30000' - }, - ]; - - const ratingBlock = rating.map((user, index) => { - return ; - }) return (
- {(rating.length > 0) &&

Рейтинг друзей

} -
{ratingBlock}
-
+ {loadingFriends &&
} + {!loadingFriends && +
+ {(!errorFriends) ? +
+ {(dataFriends.length > 0) &&

Рейтинг друзей

} +
{ratingBlock}
+
: + + } +
+ } +

Мало друзей?

Используй все свои социальные сети! Больше друзей — больше доход в хранилище.

diff --git a/frontend/src/shared/Storage/FriendsPageBlock/friendspageblock.module.css b/frontend/src/shared/Storage/FriendsPageBlock/friendspageblock.module.css index 8cda6d0..5204fa0 100644 --- a/frontend/src/shared/Storage/FriendsPageBlock/friendspageblock.module.css +++ b/frontend/src/shared/Storage/FriendsPageBlock/friendspageblock.module.css @@ -24,4 +24,12 @@ .marginTop { margin-top: 60px; +} + +.spinnerContainer { + margin: 60px 0; + display: flex; + width: 100%; + align-items: center; + justify-content: center; } \ No newline at end of file diff --git a/frontend/src/shared/hooks/checkWhiteList.ts b/frontend/src/shared/hooks/checkWhiteList.ts new file mode 100644 index 0000000..21fc2ec --- /dev/null +++ b/frontend/src/shared/hooks/checkWhiteList.ts @@ -0,0 +1,14 @@ +import { useEffect } from 'react'; +import { isWhiteList } from '../../utils/isWhiteList'; +import { useNavigate } from 'react-router-dom'; + +export function checkWhiteList() { + const check = isWhiteList(); + const navigate = useNavigate(); + + useEffect(() => { + if(!check) { + navigate('/'); + } + }, []); +} \ No newline at end of file diff --git a/frontend/src/shared/hooks/useFriendsData.ts b/frontend/src/shared/hooks/useFriendsData.ts new file mode 100644 index 0000000..95a9dd7 --- /dev/null +++ b/frontend/src/shared/hooks/useFriendsData.ts @@ -0,0 +1,24 @@ +import { useDispatch } from 'react-redux'; +import { useEffect } from 'react'; +import { useAppSelector } from './useAppSelector'; +import { IUserRank, friendsRequestAsync } from '../../store/friends/actions'; +import { isWhiteList } from '../../utils/isWhiteList'; + +export function useFriendsData() { + const dataFriends = useAppSelector>(state => state.friends.data); + const loadingFriends = useAppSelector(state => state.friends.loading); + const errorFriends = useAppSelector(state => state.friends.error); + const token = useAppSelector(state => state.token); + const dispatch = useDispatch(); + + useEffect(() => { + const whiteList = isWhiteList(); + + if(whiteList) { + dispatch(friendsRequestAsync()); + } + + }, [token]); + + return { dataFriends, loadingFriends, errorFriends }; +} \ No newline at end of file diff --git a/frontend/src/shared/hooks/useRankData.ts b/frontend/src/shared/hooks/useRankData.ts new file mode 100644 index 0000000..e8782b3 --- /dev/null +++ b/frontend/src/shared/hooks/useRankData.ts @@ -0,0 +1,23 @@ +import { useDispatch } from 'react-redux'; +import { useEffect } from 'react'; +import { useAppSelector } from './useAppSelector'; +import { IUserRank } from '../../store/friends/actions'; +import { rankRequestAsync } from '../../store/rank/actions'; +import { isWhiteList } from '../../utils/isWhiteList'; + +export function useRankData() { + const dataRank = useAppSelector>(state => state.rank.data); + const loadingRank = useAppSelector(state => state.rank.loading); + const errorRank = useAppSelector(state => state.rank.error); + const token = useAppSelector(state => state.token); + const dispatch = useDispatch(); + + useEffect(() => { + const whiteList = isWhiteList(); + if(whiteList) { + dispatch(rankRequestAsync()); + } + }, [token]); + + return { dataRank, loadingRank, errorRank }; +} \ No newline at end of file diff --git a/frontend/src/shared/hooks/useTgData.ts b/frontend/src/shared/hooks/useTgData.ts index 5d26d04..0401cad 100644 --- a/frontend/src/shared/hooks/useTgData.ts +++ b/frontend/src/shared/hooks/useTgData.ts @@ -19,7 +19,8 @@ export function useTgData() { const [user, token]: [IUserTg, string] = verificationTg(); if (token.length != 0 && user.id && user.id.length != 0) { setVerified(true); - dispatch(saveToken(token)); + //dispatch(saveToken(token)); + dispatch(saveToken(savedToken)); dispatch(setUserTg(user)); } else { setVerified(false); diff --git a/frontend/src/store/friends/actions.ts b/frontend/src/store/friends/actions.ts new file mode 100644 index 0000000..ac2803d --- /dev/null +++ b/frontend/src/store/friends/actions.ts @@ -0,0 +1,97 @@ +import { Action, ActionCreator } from "redux"; +import { ThunkAction } from "redux-thunk"; +import { RootState } from "../reducer"; +import axios from "axios"; +import { updateRank } from "../me/actions"; + +export interface IUserRank { + tgId?: string, + username?: string, + points?: string, + rank?: string, + avatar?: string, +} + +export const FRIENDS_REQUEST = 'FRIENDS_REQUEST'; + +export type FriendsRequestAction = { + type: typeof FRIENDS_REQUEST +}; + +export const friendsRequest: ActionCreator = () => ({ + type: FRIENDS_REQUEST, +}); + +export const FRIENDS_REQUEST_SUCCESS = 'FRIENDS_REQUEST_SUCCESS'; + +export type FriendsRequestSuccessAction = { + type: typeof FRIENDS_REQUEST_SUCCESS; + data: Array; +}; + +export const friendsRequestSuccess: ActionCreator = (data: Array) => ({ + type: FRIENDS_REQUEST_SUCCESS, + data +}); + +export const FRIENDS_REQUEST_ERROR = 'FRIENDS_REQUEST_ERROR'; + +export type FriendsRequestErrorAction = { + type: typeof FRIENDS_REQUEST_ERROR; + error: String; +}; + +export const friendsRequestError: ActionCreator = (error: String) => ({ + type: FRIENDS_REQUEST_ERROR, + error +}); + +export const friendsRequestAsync = (): ThunkAction> => (dispatch, getState) => { + const URL = getState().url; + const token = getState().token; + const userTg = getState().userTg.id; + + if(token) { + axios.get(`${URL}/api/v1/users/rank/friends`, + { + headers: { + "Authorization": `TelegramToken ${token}` + } + } + ).then(resp => { + const data = resp.data; + const result = []; + if (data.length != 0) { + for (let i = 0; i < data.length; i++) { + let avatar = ''; + if (data[i].avatar) { + avatar = `${URL}${data[i].avatar}`; + } + const item = { + tgId: data[i].tg_id, + username: data[i].username, + points: data[i].points, + rank: data[i].rank, + avatar: avatar + } + if(Number(item.tgId) != Number(userTg)) { + result.push(item); + } + + if(Number(data[i].tg_id) === Number(userTg)) { + dispatch(updateRank(Number(data[i].rank))) + } + } + } + dispatch(friendsRequestSuccess(result)); + }).catch((err) => { + console.log(err); + if (err.response.data.detail) { + dispatch(friendsRequestError(String(err.response.data.detail))); + } else { + dispatch(friendsRequestError(String(err))); + } + }) + } + +} \ No newline at end of file diff --git a/frontend/src/store/friends/reducer.ts b/frontend/src/store/friends/reducer.ts new file mode 100644 index 0000000..e7c9353 --- /dev/null +++ b/frontend/src/store/friends/reducer.ts @@ -0,0 +1,36 @@ +import { Reducer } from 'react'; +import { IUserRank, FRIENDS_REQUEST, FRIENDS_REQUEST_ERROR, FRIENDS_REQUEST_SUCCESS, FriendsRequestAction, FriendsRequestErrorAction, FriendsRequestSuccessAction } from './actions'; + +export type RankState = { + loading: boolean, + error: String, + data: Array +} + +type FriendsAction = FriendsRequestAction | FriendsRequestSuccessAction | FriendsRequestErrorAction; + +export const friendsReducer: Reducer = (state, action) => { + switch (action.type) { + case FRIENDS_REQUEST: + return { + ...state, + loading: true, + error: '' + }; + case FRIENDS_REQUEST_ERROR: + return { + ...state, + error: action.error, + loading: false, + }; + case FRIENDS_REQUEST_SUCCESS: + return { + ...state, + data: action.data, + loading: false, + error: '' + }; + default: + return state; + } +} \ No newline at end of file diff --git a/frontend/src/store/me/actions.ts b/frontend/src/store/me/actions.ts index 84956bc..ad24f6e 100644 --- a/frontend/src/store/me/actions.ts +++ b/frontend/src/store/me/actions.ts @@ -3,6 +3,7 @@ import { ThunkAction } from "redux-thunk"; import { RootState } from "../reducer"; import axios from "axios"; import { saveMult } from "../mult"; +import { saveToken } from "../token"; export interface IUserData { tgId?: number; @@ -13,6 +14,7 @@ export interface IUserData { energy?: string; referralStorage?: string; maxStorage: number; + rank ?: number } export const ME_REQUEST = 'ME_REQUEST'; @@ -66,8 +68,10 @@ export const meRequestAsync = (): ThunkAction { - axios.post(`${URLClick}/api/v1/click/`, - {}, + axios.post(`${URLClick}/api/v1/batch-click/`, + { + count: 1 + }, { headers: { "Authorization": `TelegramToken ${token}` @@ -78,6 +82,9 @@ export const meRequestAsync = (): ThunkAction(saveMult(click)); const clickCode = btoa(click.toString()); sessionStorage.setItem('mt', clickCode); + + const energy = Number(resp.data.energy); + dispatch(updateEnergyRequestAsync(energy)); }); }; @@ -170,7 +177,7 @@ export const meRequestAsync = (): ThunkAction { const token = resp.data.token; - getState().token = token; + dispatch(saveToken(resp.data.token)); if (token && !meData.username) { dispatch(meRequest()); let urlUser = ''; @@ -304,4 +311,12 @@ export const emptyReferralStorage = (): ThunkAction> => (dispatch, getState) => { + const meData = getState().me.data; + + let newData = meData; + newData.rank = rank; + dispatch(meRequestSuccess(newData)); } \ No newline at end of file diff --git a/frontend/src/store/rank/actions.ts b/frontend/src/store/rank/actions.ts new file mode 100644 index 0000000..13797f7 --- /dev/null +++ b/frontend/src/store/rank/actions.ts @@ -0,0 +1,135 @@ +import { Action, ActionCreator } from "redux"; +import { ThunkAction } from "redux-thunk"; +import { RootState } from "../reducer"; +import axios from "axios"; +import { IUserRank } from "../friends/actions"; +import { updateRank } from "../me/actions"; + +export const RANK_REQUEST = 'RANK_REQUEST'; + +export type RankRequestAction = { + type: typeof RANK_REQUEST +}; + +export const rankRequest: ActionCreator = () => ({ + type: RANK_REQUEST, +}); + +export const RANK_REQUEST_SUCCESS = 'RANK_REQUEST_SUCCESS'; + +export type RankRequestSuccessAction = { + type: typeof RANK_REQUEST_SUCCESS; + data: Array; +}; + +export const rankRequestSuccess: ActionCreator = (data: Array) => ({ + type: RANK_REQUEST_SUCCESS, + data +}); + +export const RANK_REQUEST_ERROR = 'RANK_REQUEST_ERROR'; + +export type RankRequestErrorAction = { + type: typeof RANK_REQUEST_ERROR; + error: String; +}; + +export const rankRequestError: ActionCreator = (error: String) => ({ + type: RANK_REQUEST_ERROR, + error +}); + +export const rankRequestAsync = (): ThunkAction> => (dispatch, getState) => { + const URL = getState().url; + const token = getState().token; + const userTg = getState().userTg.id; + const result: Array = []; + + if(token) { + axios.get(`${URL}/api/v1/users/rank/top?limit=3`, + { + headers: { + "Authorization": `TelegramToken ${token}` + } + } + ).then(resp => { + const dataTop = resp.data; + if (dataTop.length != 0) { + for (let i = 0; i < dataTop.length; i++) { + let avatar = ''; + if (dataTop[i].avatar) { + avatar = `${URL}${dataTop[i].avatar}`; + } + let username = dataTop[i].username; + if (Number(dataTop[i].tg_id) === Number(userTg)) { + username = 'Ты'; + } + const item = { + tgId: dataTop[i].tg_id, + username: username, + points: dataTop[i].points, + rank: dataTop[i].rank, + avatar: avatar + } + result.push(item); + + if (Number(dataTop[i].tg_id) === Number(userTg)) { + dispatch(updateRank(Number(dataTop[i].rank))) + } + } + } + axios.get(`${URL}/api/v1/users/rank/neighbours?limit=20`, + { + headers: { + "Authorization": `TelegramToken ${token}` + } + } + ).then(resp2 => { + const data = resp2.data; + if (data.length != 0) { + for (let i = 0; i < data.length; i++) { + let avatar = ''; + if (data[i].avatar) { + avatar = `${URL}${data[i].avatar}`; + } + let username = data[i].username; + if (Number(data[i].tg_id) === Number(userTg)) { + username = 'Ты'; + } + const item = { + tgId: data[i].tg_id, + username: username, + points: data[i].points, + rank: data[i].rank, + avatar: avatar + } + + if (Number(data[i].rank) > 3) { + result.push(item); + } + + if (Number(data[i].tg_id) === Number(userTg)) { + dispatch(updateRank(Number(data[i].rank))) + } + } + } + dispatch(rankRequestSuccess(result)); + }).catch((err) => { + console.log(err); + if (err.response.data.detail) { + dispatch(rankRequestError(String(err.response.data.detail))); + } else { + dispatch(rankRequestError(String(err))); + } + }) + }).catch((err) => { + console.log(err); + if (err.response.data.detail) { + dispatch(rankRequestError(String(err.response.data.detail))); + } else { + dispatch(rankRequestError(String(err))); + } + }) + } + +} \ No newline at end of file diff --git a/frontend/src/store/rank/reducer.ts b/frontend/src/store/rank/reducer.ts new file mode 100644 index 0000000..19d9e28 --- /dev/null +++ b/frontend/src/store/rank/reducer.ts @@ -0,0 +1,32 @@ +import { Reducer } from 'react'; +import { RANK_REQUEST, RANK_REQUEST_ERROR, RANK_REQUEST_SUCCESS, RankRequestAction, RankRequestErrorAction, RankRequestSuccessAction } from './actions'; +import { RankState } from '../friends/reducer'; + + +type RankAction = RankRequestAction | RankRequestSuccessAction | RankRequestErrorAction; + +export const rankReducer: Reducer = (state, action) => { + switch (action.type) { + case RANK_REQUEST: + return { + ...state, + loading: true, + error: '' + }; + case RANK_REQUEST_ERROR: + return { + ...state, + error: action.error, + loading: false, + }; + case RANK_REQUEST_SUCCESS: + return { + ...state, + data: action.data, + loading: false, + error: '' + }; + default: + return state; + } +} \ No newline at end of file diff --git a/frontend/src/store/reducer.ts b/frontend/src/store/reducer.ts index 4bfe9bc..3fa4735 100644 --- a/frontend/src/store/reducer.ts +++ b/frontend/src/store/reducer.ts @@ -5,6 +5,10 @@ import { MeState, meReducer } from './me/reducer'; import { ME_REQUEST, ME_REQUEST_ERROR, ME_REQUEST_SUCCESS } from './me/actions'; import { SET_REFERRAL } from './referral'; import { SET_MULT } from './mult'; +import { RankState, friendsReducer } from './friends/reducer'; +import { FRIENDS_REQUEST, FRIENDS_REQUEST_ERROR, FRIENDS_REQUEST_SUCCESS } from './friends/actions'; +import { RANK_REQUEST, RANK_REQUEST_ERROR, RANK_REQUEST_SUCCESS } from './rank/actions'; +import { rankReducer } from './rank/reducer'; export type RootState = { url: string, @@ -16,6 +20,8 @@ export type RootState = { me: MeState, referral: string, mult: number, + friends: RankState, + rank: RankState, }; //'http://127.0.0.1:8000' @@ -39,6 +45,16 @@ const initialState: RootState = { maxStorage: 0 } }, + friends: { + loading: false, + error: '', + data: [] + }, + rank: { + loading: false, + error: '', + data: [] + }, referral: '', mult: 1, }; @@ -78,6 +94,20 @@ export const rootReducer: Reducer = (state = initialState, action) => ...state, me: meReducer(state.me, action) }; + case FRIENDS_REQUEST: + case FRIENDS_REQUEST_SUCCESS: + case FRIENDS_REQUEST_ERROR: + return { + ...state, + friends: friendsReducer(state.friends, action) + }; + case RANK_REQUEST: + case RANK_REQUEST_SUCCESS: + case RANK_REQUEST_ERROR: + return { + ...state, + rank: rankReducer(state.rank, action) + }; default: return state; } diff --git a/frontend/src/utils/isWhiteList.js b/frontend/src/utils/isWhiteList.js new file mode 100644 index 0000000..03be0ec --- /dev/null +++ b/frontend/src/utils/isWhiteList.js @@ -0,0 +1,20 @@ +import { getTgUserId } from "./verification"; + +export const isWhiteList = () => { + let isWhiteList = false; + //123456, + const whiteList = [ + 342495217, 6374536117, 322861155, 5219438370, 193428034, 402449803, + 406350282, 1083462027, + ]; + + const userId = Number(getTgUserId()); + + whiteList.map((item) => { + if (Number(item) === userId) { + isWhiteList = true; + } + }); + + return isWhiteList; +} \ No newline at end of file diff --git a/frontend/src/utils/verification.js b/frontend/src/utils/verification.js index c7316d5..249a280 100644 --- a/frontend/src/utils/verification.js +++ b/frontend/src/utils/verification.js @@ -52,4 +52,18 @@ export const verificationTg = () => { user.lastName = 'Name';*/ return [user, token]; +} + +export const getTgUserId = () => { + let id = ''; + + if(window.Telegram) { + const tg = window.Telegram.WebApp; + id = tg.initDataUnsafe?.user?.id; + } + + //локально + //id = "123456"; + + return id; } \ No newline at end of file